* [Galene] Turn binding to the ANY Address - even when specified
@ 2025-01-15 11:30 Dirk-Willem van Gulik
2025-01-15 14:03 ` [Galene] " Juliusz Chroboczek
0 siblings, 1 reply; 6+ messages in thread
From: Dirk-Willem van Gulik @ 2025-01-15 11:30 UTC (permalink / raw)
To: galene
When starting galene with an explcit IP Address on a machine with multiple Ip addresses; e.g. with:
./galene -turn 11.123.4.123:1194 ....
netstat/lsof shows that it is still bound to the ANY address:
# lsof -n | grep LISTEN
....
galene 49935 galene 7u IPv4 0xfffff80019daf000 0 TCP *:1194->*:* (LISTEN)
Is this the expected behaviour (i.e. intentional and for a reason) - or a blemish/thing that I should try to fix ?
With kind regards,
Dw.
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Galene] Re: Turn binding to the ANY Address - even when specified
2025-01-15 11:30 [Galene] Turn binding to the ANY Address - even when specified Dirk-Willem van Gulik
@ 2025-01-15 14:03 ` Juliusz Chroboczek
2025-01-15 15:56 ` Dirk-Willem van Gulik
0 siblings, 1 reply; 6+ messages in thread
From: Juliusz Chroboczek @ 2025-01-15 14:03 UTC (permalink / raw)
To: Dirk-Willem van Gulik; +Cc: galene
> When starting galene with an explcit IP Address on a machine with multiple Ip addresses; e.g. with:
>
> ./galene -turn 11.123.4.123:1194 ....
>
> netstat/lsof shows that it is still bound to the ANY address:
>
> # lsof -n | grep LISTEN
> ....
> galene 49935 galene 7u IPv4 0xfffff80019daf000 0 TCP *:1194->*:* (LISTEN)
>
> Is this the expected behaviour (i.e. intentional and for a reason) - or
> a blemish/thing that I should try to fix ?
Only the address you specified is advertised to clients, but the server
listens on the wildcard address. This allows us to change the server's
address without tearing down the socket when running with "-turn auto".
It would make the code moderately more complex to change that, so let me
know if it's important for you.
-- Juliusz
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Galene] Re: Turn binding to the ANY Address - even when specified
2025-01-15 14:03 ` [Galene] " Juliusz Chroboczek
@ 2025-01-15 15:56 ` Dirk-Willem van Gulik
2025-01-16 12:15 ` Dirk-Willem van Gulik
0 siblings, 1 reply; 6+ messages in thread
From: Dirk-Willem van Gulik @ 2025-01-15 15:56 UTC (permalink / raw)
To: Juliusz Chroboczek; +Cc: galene
[-- Attachment #1: Type: text/plain, Size: 2053 bytes --]
> On 15 Jan 2025, at 15:03, Juliusz Chroboczek <jch@irif.fr> wrote:
>
>> When starting galene with an explcit IP Address on a machine with multiple Ip addresses; e.g. with:
>>
>> ./galene -turn 11.123.4.123:1194 ....
>>
>> netstat/lsof shows that it is still bound to the ANY address:
>>
>> # lsof -n | grep LISTEN
>> ....
>> galene 49935 galene 7u IPv4 0xfffff80019daf000 0 TCP *:1194->*:* (LISTEN)
>>
>> Is this the expected behaviour (i.e. intentional and for a reason) - or
>> a blemish/thing that I should try to fix ?
>
> Only the address you specified is advertised to clients, but the server
> listens on the wildcard address. This allows us to change the server's
> address without tearing down the socket when running with "-turn auto".
So I think below is a fairly simple change - where the 'auto' case is kept as is - and the listener is ONLY bound to a specific IP if it is specified. Otherwise it becomes *:1234 or a found public IP address.
So now [-turn [ip|fqdn]:<port>] behaves exactly like -http.
> So let me know if it's important for you.
So being able to bind it is convenient on a machine with multiple IPs or a machine that straddles networks. As otherwise the galene starting `second' looses the race for 0.0.0.0. . Or a machine with an anal firewall/ACL system - the listen() gets a permission error.
With kind regards,
Dw.
diff --git a/turnserver/turnserver.go b/turnserver/turnserver.go
index 1dcebe0..2210801 100644
--- a/turnserver/turnserver.go
+++ b/turnserver/turnserver.go
@@ -132,7 +132,7 @@ func Start() error {
if a == nil {
return errors.New("couldn't parse address")
}
- pcc, lc := listener(net.IP{0, 0, 0, 0}, addr.Port, a)
+ pcc, lc := listener(a, addr.Port, a)
if pcc != nil {
pccs = append(pccs, *pcc)
server.addresses = append(server.addresses, &net.UDPAddr{
[-- Attachment #2: Type: text/html, Size: 11924 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Galene] Re: Turn binding to the ANY Address - even when specified
2025-01-15 15:56 ` Dirk-Willem van Gulik
@ 2025-01-16 12:15 ` Dirk-Willem van Gulik
2025-01-16 12:55 ` Juliusz Chroboczek
0 siblings, 1 reply; 6+ messages in thread
From: Dirk-Willem van Gulik @ 2025-01-16 12:15 UTC (permalink / raw)
To: Juliusz Chroboczek; +Cc: galene
[-- Attachment #1: Type: text/plain, Size: 1763 bytes --]
On 15 Jan 2025, at 16:56, Dirk-Willem van Gulik <dirkx@webweaving.org> wrote:
>
>> On 15 Jan 2025, at 15:03, Juliusz Chroboczek <jch@irif.fr> wrote:
>>
>>> When starting galene with an explcit IP Address on a machine with multiple Ip addresses; e.g. with:
>>>
>>> ./galene -turn 11.123.4.123:1194 ....
>>>
>>> netstat/lsof shows that it is still bound to the ANY address:
>>>
>>> # lsof -n | grep LISTEN
>>> ....
>>> galene 49935 galene 7u IPv4 0xfffff80019daf000 0 TCP *:1194->*:* (LISTEN)
>>>
>>> Is this the expected behaviour (i.e. intentional and for a reason) - or
>>> a blemish/thing that I should try to fix ?
>>
>> Only the address you specified is advertised to clients, but the server
>> listens on the wildcard address. This allows us to change the server's
>> address without tearing down the socket when running with "-turn auto".
>
> So I think below is a fairly simple change - where the 'auto' case is kept as is - and the listener is ONLY bound to a specific IP if it is specified. Otherwise it becomes *:1234 or a found public IP address.
>
> So now [-turn [ip|fqdn]:<port>] behaves exactly like -http.
>
>> So let me know if it's important for you.
>
>
> So being able to bind it is convenient on a machine with multiple IPs or a machine that straddles networks. As otherwise the galene starting `second' looses the race for 0.0.0.0. . Or a machine with an anal firewall/ACL system - the listen() gets a permission error.
Ignore this - there is a whole general class of cases when you have VLANs, IPv6 or an iSCSI that surfaces as an interface on a machine which goes wrong.
Will repost with a better solution once I've gone through all the edge cases.
Dw
[-- Attachment #2: Type: text/html, Size: 3833 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Galene] Re: Turn binding to the ANY Address - even when specified
2025-01-16 12:15 ` Dirk-Willem van Gulik
@ 2025-01-16 12:55 ` Juliusz Chroboczek
2025-01-16 17:20 ` Dirk-Willem van Gulik
0 siblings, 1 reply; 6+ messages in thread
From: Juliusz Chroboczek @ 2025-01-16 12:55 UTC (permalink / raw)
To: Dirk-Willem van Gulik; +Cc: galene
> Ignore this - there is a whole general class ...
The wonders of debugging ICE issues ;-) Been there, done that, given up.
(I'm sure there's a Latin saying to that effect somewhere.)
-- Juliusz
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Galene] Re: Turn binding to the ANY Address - even when specified
2025-01-16 12:55 ` Juliusz Chroboczek
@ 2025-01-16 17:20 ` Dirk-Willem van Gulik
0 siblings, 0 replies; 6+ messages in thread
From: Dirk-Willem van Gulik @ 2025-01-16 17:20 UTC (permalink / raw)
To: Juliusz Chroboczek; +Cc: galene
[-- Attachment #1: Type: text/plain, Size: 12084 bytes --]
On 16 Jan 2025, at 13:55, Juliusz Chroboczek <jch@irif.fr> wrote:
>
>> Ignore this - there is a whole general class ...
>
> The wonders of debugging ICE issues ;-) Been there, done that, given up.
Right - on https://github.com/dirkx/galene/tree/fix_bind_turn_ip is a sketch for the ICE T-Shirt.
The main concept of this sketch is to augment the IP specified for TURN into a range of gradually more precise versions:
auto
:1234 simply set the port, still bind to 0.0.0.0
1.2.3.4:1234 bind specifically to that
all the way to
1.2.3.4:9999/4.3.2.1:1234
Where 1.2.3.4 is the external/visible/relay address (on port 9999) and galene actually bind()s/listen()s to 4.3.2.1, port 1234. With 1.2.3.4:9999/:1234 (i.e no listen address) going to the 0.0.0.0/bind anything. :1234, :1234/:4321 do not - they go, as before to scan of the public/non RFC1918 addresses.
Does it make sense to spend a few hours to making this draft a bit more real & systematically tested ? Once thing I've not quite understanding yet is why the relay generator just needs the relay IP but not the relay port.
Dw.
commit dac7ae531fb1ed9ec15c484de562674f1e73822b
Author: Dirk-Willem van Gulik <dirkx@webweaving.org>
Date: Thu Jan 16 18:00:36 2025 +0100
First draft of exposed/internal address separation.
diff --git a/INSTALL b/INSTALL
index c9bccaa..5843f47 100644
--- a/INSTALL
+++ b/INSTALL
@@ -210,8 +210,18 @@ Galène includes an IPv4-only TURN server, which is controlled by the
the one given in the option; this is useful when running behind NAT
with port forwarding set up.
+ * If an IP address is in the form [<ip>][:<port>][/[<ip>][:<port>] then
+ the left side is taken as the external (i.e. exposed and coded into
+ a turn URI) pair. And the additional right hand side as the internal
+ IP/port pair that is mapped to those external addresses. So for
+ example a configuration of 203.0.113.1:1194/10.11.0.1:2194 is for
+ a TURN server that is exposed (e.g. redirected from a firewall)
+ as 203.0.113.1:1194 but is known in the DMZ or on the other side
+ of NAT as running on 10.11.0.1:2194.
+
* the default value is `auto`, which behaves like `:1194` if there is no
- `data/ice-servers.json` file, and like `""` otherwise.
+ `data/ice-servers.json` file, and like `""` otherwise. In this case
+ galene will bind to the ANY address.
If the server is not accessible from the Internet, e.g. because of NAT or
because it is behind a restrictive firewall, then you should configure
diff --git a/turnserver/turnserver.go b/turnserver/turnserver.go
index 1dcebe0..9ab37fe 100644
--- a/turnserver/turnserver.go
+++ b/turnserver/turnserver.go
@@ -8,21 +8,101 @@ import (
"net"
"strconv"
"sync"
+ "fmt"
+ "strings"
"github.com/pion/turn/v2"
"github.com/pion/webrtc/v3"
)
+const DEFAULT_PORT = 1194
+
var username string
var password string
var Address string
+// Generalize a TCP/IP address & port pair. When the
+// port is '0' - an `off' is assumed.
+//
+type UDPTCPAddr struct {
+ IP net.IP
+ Port int
+ Zone string // IPv6 scoped addressing zone
+}
+
+// General inside/outside address; where the two are
+// the same in the cannonical simple case; but, for example
+// in a DMZ or when NAT-ting; the internal address is
+// that what the turns server listens on (i.e bind()); whereas
+// any turn:// URI's and so on are constructed with the
+// outside address. The term 'Paired' is taken from NAT.
+//
+type PairedAddr struct {
+ exposedAddr UDPTCPAddr // Or Relay Address
+ internalAddr UDPTCPAddr
+}
+
+func (addr PairedAddr) String() string {
+ return fmt.Sprintf("%v/%v", addr.exposedAddr, addr.internalAddr);
+}
+
+func ResolveUDPTCPAddr(address string) (*UDPTCPAddr, error) {
+ // We're doing a 'cheat' here; and rely on the UDP translator; as we know
+ // that UDP/TCP are indentical with regard to port/addr structure.
+ addr, err := net.ResolveUDPAddr("udp", address)
+ if err != nil {
+ return nil, err
+ }
+ // Complete the cheat by just copying out the address details; but not the network familly
+ r := UDPTCPAddr { addr.IP, addr.Port, addr.Zone }
+ return &r, nil
+}
+
+func NewUDPTCPAddr(Address string) (*UDPTCPAddr, error) {
+ if Address == "" || Address == "off" {
+ return &UDPTCPAddr{}, nil
+ }
+ ad := Address
+ if Address == "auto" {
+ ad = fmt.Sprintf(":%v", DEFAULT_PORT)
+ } else
+ if strings.Index(ad,":") == -1 {
+ ad = fmt.Sprintf("%v:%v", ad, DEFAULT_PORT)
+ }
+ addr, err := ResolveUDPTCPAddr(ad)
+ if err != nil {
+ return nil, err
+ }
+ return addr, nil
+}
+
+func NewPairedAddr(str string) (*PairedAddr, error) {
+ i := strings.Index(str,"/")
+ left :=str
+ if i > 1 {
+ left = str[0:i]
+ str = str[i+1:]
+ }
+ exposedAddr, err := NewUDPTCPAddr(left)
+ if err != nil {
+ return nil, err
+ }
+ internalAddr, err := NewUDPTCPAddr(str)
+ if err != nil {
+ return nil, err
+ }
+
+ e := PairedAddr { *exposedAddr, *internalAddr }
+ return &e, nil
+}
+
var server struct {
mu sync.Mutex
addresses []net.Addr
server *turn.Server
}
+// Remove any RFC 1918 addreses from a list of addresses.
func publicAddresses() ([]net.IP, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
@@ -57,34 +137,38 @@ func listener(a net.IP, port int, relay net.IP) (*turn.PacketConnConfig, *turn.L
var lc *turn.ListenerConfig
- s := net.JoinHostPort(a.String(), strconv.Itoa(port))
+ as := a.String()
+ if a == nil { as = "" }
+ s := net.JoinHostPort(as, strconv.Itoa(port))
- var g turn.RelayAddressGenerator
+ var g turn.RelayAddressGenerator
+ raddr := a.String()
if relay == nil || relay.IsUnspecified() {
g = &turn.RelayAddressGeneratorNone{
Address: a.String(),
}
} else {
+ raddr = relay.String()
g = &turn.RelayAddressGeneratorStatic{
RelayAddress: relay,
Address: a.String(),
}
}
- p, err := net.ListenPacket("udp4", s)
+ p, err := net.ListenPacket("udp", s)
if err == nil {
pcc = &turn.PacketConnConfig{
PacketConn: p,
RelayAddressGenerator: g,
}
+ log.Printf("TURN: listener on udp:%v, visible address: %v",s,raddr)
} else {
log.Printf("TURN: listenPacket(%v): %v", s, err)
}
- l, err := net.Listen("tcp4", s)
+ l, err := net.Listen("tcp", s)
if err == nil {
lc = &turn.ListenerConfig{
Listener: l,
RelayAddressGenerator: g,
}
+ log.Printf("TURN: listener on tcp:%v, visible address: %v",s,raddr)
} else {
log.Printf("TURN: listen(%v): %v", s, err)
}
@@ -99,20 +183,13 @@ func Start() error {
if server.server != nil {
return nil
}
-
- if Address == "" {
- return errors.New("built-in TURN server disabled")
- }
-
- ad := Address
- if Address == "auto" {
- ad = ":1194"
+ addressPair, err := NewPairedAddr(Address)
+ if err != nil {
+ return errors.New(fmt.Sprintf("TURN: Address error: %v", err))
+ }
+ if addressPair.internalAddr.Port == 0 {
+ return errors.New("TURN: built-in TURN server disabled")
}
- addr, err := net.ResolveUDPAddr("udp4", ad)
- if err != nil {
- return err
- }
-
username = "galene"
buf := make([]byte, 6)
_, err = rand.Read(buf)
@@ -126,26 +203,29 @@ func Start() error {
var lcs []turn.ListenerConfig
var pccs []turn.PacketConnConfig
-
- if addr.IP != nil && !addr.IP.IsUnspecified() {
- a := addr.IP.To4()
+ if addressPair.exposedAddr.IP != nil && !addressPair.exposedAddr.IP.IsUnspecified() {
+ a := addressPair.exposedAddr.IP.To4()
if a == nil {
- return errors.New("couldn't parse address")
+ return errors.New("couldn't parse address/not an IPv4 address")
}
- pcc, lc := listener(net.IP{0, 0, 0, 0}, addr.Port, a)
+ pcc, lc := listener(addressPair.internalAddr.IP, addressPair.internalAddr.Port, addressPair.exposedAddr.IP)
if pcc != nil {
pccs = append(pccs, *pcc)
server.addresses = append(server.addresses, &net.UDPAddr{
- IP: a,
- Port: addr.Port,
+ IP: addressPair.exposedAddr.IP,
+ Port: addressPair.exposedAddr.Port,
})
+ log.Printf("TURN: External address udp:%v:%v",
+ addressPair.exposedAddr.IP, addressPair.exposedAddr.Port)
}
if lc != nil {
lcs = append(lcs, *lc)
server.addresses = append(server.addresses, &net.TCPAddr{
- IP: a,
- Port: addr.Port,
+ IP: addressPair.exposedAddr.IP,
+ Port: addressPair.exposedAddr.Port,
})
+ log.Printf("TURN: External address tcp:%v:%v",
+ addressPair.exposedAddr.IP, addressPair.exposedAddr.Port)
}
} else {
as, err := publicAddresses()
@@ -158,24 +238,26 @@ func Start() error {
}
for _, a := range as {
- pcc, lc := listener(a, addr.Port, nil)
+ pcc, lc := listener(a, addressPair.internalAddr.Port, nil)
if pcc != nil {
pccs = append(pccs, *pcc)
server.addresses = append(server.addresses,
&net.UDPAddr{
IP: a,
- Port: addr.Port,
+ Port: addressPair.exposedAddr.Port,
},
)
+ log.Printf("TURN: external address udp:%v:%v", a, addressPair.exposedAddr.Port)
}
if lc != nil {
lcs = append(lcs, *lc)
server.addresses = append(server.addresses,
&net.TCPAddr{
IP: a,
- Port: addr.Port,
+ Port: addressPair.exposedAddr.Port,
},
)
+ log.Printf("TURN: external address tcp:%v:%v", a, addressPair.exposedAddr.Port)
}
}
}
@@ -183,8 +265,7 @@ func Start() error {
if len(pccs) == 0 && len(lcs) == 0 {
return errors.New("couldn't establish any listeners")
}
-
- log.Printf("Starting built-in TURN server on %v", addr.String())
+ log.Printf("TURN: Starting built-in TURN server")
server.server, err = turn.NewServer(turn.ServerConfig{
Realm: "galene.org",
diff --git a/turnserver/turnserver_test.go b/turnserver/turnserver_test.go
new file mode 100644
index 0000000..efbb870
--- /dev/null
+++ b/turnserver/turnserver_test.go
@@ -0,0 +1,39 @@
+package turnserver
+
+import (
+ "testing"
+)
+
+func TestParseAddr(t *testing.T) {
+ a := []struct{ p, g string }{
+ {"", "{<nil> 0 }/{<nil> 0 }"},
+ {"off", "{<nil> 0 }/{<nil> 0 }"},
+ {"auto", "{<nil> 1194 }/{<nil> 1194 }"},
+ {":1234", "{<nil> 1234 }/{<nil> 1234 }"},
+ {":1234/:4321", "{<nil> 1234 }/{<nil> 4321 }"},
+ {"10.11.0.1:1234", "{10.11.0.1 1234 }/{10.11.0.1 1234 }"},
+ {"10.11.0.1:1234/:4321", "{10.11.0.1 1234 }/{<nil> 4321 }"},
+ {"10.11.0.1:1234/1.2.3.4:4321", "{10.11.0.1 1234 }/{1.2.3.4 4321 }"},
+ {"always-1-2-3-4.webweaving.org", "{1.2.3.4 1194 }/{1.2.3.4 1194 }"},
+ {"always-1-2-3-4.webweaving.org:4321", "{1.2.3.4 4321 }/{1.2.3.4 4321 }"},
+ {"always-1-2-3-4.webweaving.org:4321/:1234", "{1.2.3.4 4321 }/{<nil> 1234 }"},
+ {"always-1-2-3-4.webweaving.org:4321/127.0.0.1:1234", "{1.2.3.4 4321 }/{127.0.0.1 1234 }"},
+ {"always-1-2-3-4.webweaving.org:4321/127.0.0.1", "{1.2.3.4 4321 }/{127.0.0.1 1194 }"},
+/* Only works on an pure IPv4 machine
+ {"localhost:1234/1.2.3.4:4321", "{127.0.0.1 1234 }/{1.2.3.4 4321 }"},
+ {"always-1-2-3-4.webweaving.org:4321/localhost", "{1.2.3.4 4321 }/{127.0.0.1 1194 }"},
+ {"always-1-2-3-4.webweaving.org:4321/localhost:1234", "{1.2.3.4 4321 }/{127.0.0.1 1234 }"},
+*/
+ }
+
+ for _, pg := range a {
+ g, err := NewPairedAddr(pg.p)
+ if err != nil {
+ t.Errorf("Error: '%v' (not expected)", err)
+ }
+ if g.String() != pg.g {
+ t.Errorf("'%v', got '%v', expected '%v'",
+ pg.p, g, pg.g)
+ }
+ }
+}
[-- Attachment #2: Type: text/html, Size: 29935 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-01-16 17:24 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-01-15 11:30 [Galene] Turn binding to the ANY Address - even when specified Dirk-Willem van Gulik
2025-01-15 14:03 ` [Galene] " Juliusz Chroboczek
2025-01-15 15:56 ` Dirk-Willem van Gulik
2025-01-16 12:15 ` Dirk-Willem van Gulik
2025-01-16 12:55 ` Juliusz Chroboczek
2025-01-16 17:20 ` Dirk-Willem van Gulik
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox