mirror of
https://github.com/livekit/livekit.git
synced 2026-04-01 21:45:46 +00:00
Now only set mapping when user_external_ip enabled or node_ip is explicitly set. If multiple local address resolved to same external ip, only the first one will be mapped to external, avoid candidate conflict between different clients.
142 lines
2.9 KiB
Go
142 lines
2.9 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/pion/stun"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func (conf *Config) determineIP() (string, error) {
|
|
if conf.RTC.UseExternalIP {
|
|
stunServers := conf.RTC.STUNServers
|
|
if len(stunServers) == 0 {
|
|
stunServers = DefaultStunServers
|
|
}
|
|
var err error
|
|
for i := 0; i < 3; i++ {
|
|
var ip string
|
|
ip, err = GetExternalIP(stunServers, nil)
|
|
if err == nil {
|
|
return ip, nil
|
|
} else {
|
|
time.Sleep(500 * time.Millisecond)
|
|
}
|
|
}
|
|
return "", errors.Errorf("could not resolve external IP: %v", err)
|
|
}
|
|
|
|
// use local ip instead
|
|
addresses, err := GetLocalIPAddresses()
|
|
if len(addresses) > 0 {
|
|
return addresses[0], err
|
|
}
|
|
return "", err
|
|
}
|
|
|
|
func GetLocalIPAddresses() ([]string, error) {
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
loopBacks := make([]string, 0)
|
|
addresses := make([]string, 0)
|
|
for _, iface := range ifaces {
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, addr := range addrs {
|
|
var ip net.IP
|
|
switch typedAddr := addr.(type) {
|
|
case *net.IPNet:
|
|
ip = typedAddr.IP.To4()
|
|
case *net.IPAddr:
|
|
ip = typedAddr.IP.To4()
|
|
default:
|
|
continue
|
|
}
|
|
if ip == nil {
|
|
continue
|
|
}
|
|
if ip.IsLoopback() {
|
|
loopBacks = append(loopBacks, ip.String())
|
|
} else {
|
|
addresses = append(addresses, ip.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(addresses) > 0 {
|
|
return addresses, nil
|
|
}
|
|
if len(loopBacks) > 0 {
|
|
return loopBacks, nil
|
|
}
|
|
return nil, fmt.Errorf("could not find local IP address")
|
|
}
|
|
|
|
// GetExternalIP return external IP for localAddr from stun server. If localAddr is nil, a local address is chosen automatically.
|
|
func GetExternalIP(stunServers []string, localAddr net.Addr) (string, error) {
|
|
if len(stunServers) == 0 {
|
|
return "", errors.New("STUN servers are required but not defined")
|
|
}
|
|
dialer := &net.Dialer{
|
|
LocalAddr: localAddr,
|
|
}
|
|
conn, err := dialer.Dial("udp4", stunServers[0])
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
c, err := stun.NewClient(conn)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer c.Close()
|
|
|
|
message, err := stun.Build(stun.TransactionID, stun.BindingRequest)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var stunErr error
|
|
// sufficiently large buffer to not block it
|
|
ipChan := make(chan string, 20)
|
|
err = c.Start(message, func(res stun.Event) {
|
|
if res.Error != nil {
|
|
stunErr = res.Error
|
|
return
|
|
}
|
|
|
|
var xorAddr stun.XORMappedAddress
|
|
if err := xorAddr.GetFrom(res.Message); err != nil {
|
|
stunErr = err
|
|
return
|
|
}
|
|
ip := xorAddr.IP.To4()
|
|
if ip != nil {
|
|
ipChan <- ip.String()
|
|
}
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
select {
|
|
case nodeIP := <-ipChan:
|
|
return nodeIP, nil
|
|
case <-ctx.Done():
|
|
msg := "could not determine public IP"
|
|
if stunErr != nil {
|
|
return "", errors.Wrap(stunErr, msg)
|
|
} else {
|
|
return "", fmt.Errorf(msg)
|
|
}
|
|
}
|
|
}
|