From 110613b234025078990143749cd30f0b8a964e92 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 8 Nov 2022 21:59:13 +0000 Subject: [PATCH 1/5] Try all addresses when connecting to a DNS name Fixes #980 --- src/core/link_tcp.go | 56 +++++++++++++++++++++++--------------------- src/core/link_tls.go | 55 +++++++++++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 47 deletions(-) diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 9c3c3290..4392fb73 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/url" + "strconv" "strings" "time" @@ -31,24 +32,39 @@ func (l *links) newLinkTCP() *linkTCP { } func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { - addr, err := net.ResolveTCPAddr("tcp", url.Host) - if err != nil { - return err - } - dialer, err := l.dialerFor(addr, sintf) - if err != nil { - return err - } - info := linkInfoFor("tcp", sintf, tcpIDFor(dialer.LocalAddr, addr)) + info := linkInfoFor("tcp", sintf, url.Host) if l.links.isConnectedTo(info) { return nil } - conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) + host, p, err := net.SplitHostPort(url.Host) if err != nil { return err } - uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") - return l.handler(uri, info, conn, options, false, false) + port, err := strconv.Atoi(p) + if err != nil { + return err + } + ips, err := net.LookupIP(host) + if err != nil { + return err + } + for _, ip := range ips { + addr := &net.TCPAddr{ + IP: ip, + Port: port, + } + dialer, err := l.dialerFor(addr, sintf) + if err != nil { + continue + } + conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) + if err != nil { + continue + } + uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + return l.handler(uri, info, conn, options, false, false) + } + return fmt.Errorf("failed to connect via %d addresses", len(ips)) } func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { @@ -82,10 +98,9 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { cancel() break } - laddr := conn.LocalAddr().(*net.TCPAddr) raddr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tcp://%s", raddr) - info := linkInfoFor("tcp", sintf, tcpIDFor(laddr, raddr)) + info := linkInfoFor("tcp", sintf, raddr.String()) if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } @@ -180,16 +195,3 @@ func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error) } return dialer, nil } - -func tcpIDFor(local net.Addr, remoteAddr *net.TCPAddr) string { - if localAddr, ok := local.(*net.TCPAddr); ok && localAddr.IP.Equal(remoteAddr.IP) { - // Nodes running on the same host — include both the IP and port. - return remoteAddr.String() - } - if remoteAddr.IP.IsLinkLocalUnicast() { - // Nodes discovered via multicast — include the IP only. - return remoteAddr.IP.String() - } - // Nodes connected remotely — include both the IP and port. - return remoteAddr.String() -} diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 4eeb8710..8e7f870a 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -13,6 +13,7 @@ import ( "math/big" "net" "net/url" + "strconv" "strings" "time" @@ -47,30 +48,45 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { } func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { - addr, err := net.ResolveTCPAddr("tcp", url.Host) - if err != nil { - return err - } - dialer, err := l.tcp.dialerFor(addr, sintf) - if err != nil { - return err - } - info := linkInfoFor("tls", sintf, tcpIDFor(dialer.LocalAddr, addr)) + info := linkInfoFor("tls", sintf, url.Host) if l.links.isConnectedTo(info) { return nil } - tlsconfig := l.config.Clone() - tlsconfig.ServerName = sni - tlsdialer := &tls.Dialer{ - NetDialer: dialer, - Config: tlsconfig, - } - conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String()) + host, p, err := net.SplitHostPort(url.Host) if err != nil { return err } - uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") - return l.handler(uri, info, conn, options, false, false) + port, err := strconv.Atoi(p) + if err != nil { + return err + } + ips, err := net.LookupIP(host) + if err != nil { + return err + } + for _, ip := range ips { + addr := &net.TCPAddr{ + IP: ip, + Port: port, + } + dialer, err := l.tcp.dialerFor(addr, sintf) + if err != nil { + continue + } + tlsconfig := l.config.Clone() + tlsconfig.ServerName = sni + tlsdialer := &tls.Dialer{ + NetDialer: dialer, + Config: tlsconfig, + } + conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String()) + if err != nil { + continue + } + uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + return l.handler(uri, info, conn, options, false, false) + } + return fmt.Errorf("failed to connect via %d addresses", len(ips)) } func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { @@ -105,10 +121,9 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { cancel() break } - laddr := conn.LocalAddr().(*net.TCPAddr) raddr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tls://%s", raddr) - info := linkInfoFor("tls", sintf, tcpIDFor(laddr, raddr)) + info := linkInfoFor("tls", sintf, raddr.String()) if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } From 723097fbf61f447abd6cd027f183bdea30598054 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 26 Nov 2022 16:18:15 +0000 Subject: [PATCH 2/5] Deduplicate some logic --- src/core/link_tcp.go | 43 +++++++++++++++++++++++++++++++++++-------- src/core/link_tls.go | 35 +++++++++-------------------------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index c8020fe4..60054d45 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -31,19 +31,26 @@ func (l *links) newLinkTCP() *linkTCP { return lt } -func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { +type tcpDialer struct { + info linkInfo + dialer *net.Dialer + addr *net.TCPAddr +} + +func (l *linkTCP) dialersFor(url *url.URL, options linkOptions, sintf string) ([]*tcpDialer, error) { host, p, err := net.SplitHostPort(url.Host) if err != nil { - return err + return nil, err } port, err := strconv.Atoi(p) if err != nil { - return err + return nil, err } ips, err := net.LookupIP(host) if err != nil { - return err + return nil, err } + dialers := make([]*tcpDialer, 0, len(ips)) for _, ip := range ips { addr := &net.TCPAddr{ IP: ip, @@ -55,10 +62,30 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { } info := linkInfoFor("tcp", sintf, tcpIDFor(dialer.LocalAddr, addr)) if l.links.isConnectedTo(info) { - return nil + return nil, nil } - conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) + dialers = append(dialers, &tcpDialer{ + info: info, + dialer: dialer, + addr: addr, + }) + } + return dialers, nil +} + +func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { + dialers, err := l.dialersFor(url, options, sintf) + if err != nil { + return err + } + if len(dialers) == 0 { + return nil + } + for _, d := range dialers { + var conn net.Conn + conn, err = d.dialer.DialContext(l.core.ctx, "tcp", d.addr.String()) if err != nil { + l.core.log.Warnf("Failed to connect to %s: %s", d.addr, err) continue } name := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") @@ -66,9 +93,9 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { url: url, sintf: sintf, } - return l.handler(dial, name, info, conn, options, false, false) + return l.handler(dial, name, d.info, conn, options, false, false) } - return fmt.Errorf("failed to connect via %d addresses", len(ips)) + return fmt.Errorf("failed to connect via %d address(es), last error: %w", len(dialers), err) } func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 33ea4dc3..6323a723 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -13,7 +13,6 @@ import ( "math/big" "net" "net/url" - "strconv" "strings" "time" @@ -48,38 +47,22 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { } func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { - host, p, err := net.SplitHostPort(url.Host) + dialers, err := l.tcp.dialersFor(url, options, sintf) if err != nil { return err } - port, err := strconv.Atoi(p) - if err != nil { - return err + if len(dialers) == 0 { + return nil } - ips, err := net.LookupIP(host) - if err != nil { - return err - } - for _, ip := range ips { - addr := &net.TCPAddr{ - IP: ip, - Port: port, - } - dialer, err := l.tcp.dialerFor(addr, sintf) - if err != nil { - continue - } - info := linkInfoFor("tls", sintf, tcpIDFor(dialer.LocalAddr, addr)) - if l.links.isConnectedTo(info) { - return nil - } + for _, d := range dialers { tlsconfig := l.config.Clone() tlsconfig.ServerName = sni tlsdialer := &tls.Dialer{ - NetDialer: dialer, + NetDialer: d.dialer, Config: tlsconfig, } - conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String()) + var conn net.Conn + conn, err = tlsdialer.DialContext(l.core.ctx, "tcp", d.addr.String()) if err != nil { continue } @@ -88,9 +71,9 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err url: url, sintf: sintf, } - return l.handler(dial, name, info, conn, options, false, false) + return l.handler(dial, name, d.info, conn, options, false, false) } - return fmt.Errorf("failed to connect via %d addresses", len(ips)) + return fmt.Errorf("failed to connect via %d address(es), last error: %w", len(dialers), err) } func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { From 6d6c4089575dfbd1619623c442de4419b3445430 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Feb 2023 21:31:20 +0000 Subject: [PATCH 3/5] Test against Go 1.20, maybe fix lint issue --- .github/workflows/ci.yml | 8 ++++---- src/tun/tun.go | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c99ac06..77475403 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18", "1.19"] + goversion: ["1.17", "1.18", "1.19", "1.20"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18", "1.19"] + goversion: ["1.17", "1.18", "1.19", "1.20"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18", "1.19"] + goversion: ["1.17", "1.18", "1.19", "1.20"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] @@ -128,4 +128,4 @@ jobs: - name: Check all tests passed uses: re-actors/alls-green@release/v1 with: - jobs: ${{ toJSON(needs) }} \ No newline at end of file + jobs: ${{ toJSON(needs) }} diff --git a/src/tun/tun.go b/src/tun/tun.go index ce1bd169..9ec7bc90 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -107,7 +107,8 @@ func (tun *TunAdapter) _start() error { } tun.addr = tun.rwc.Address() tun.subnet = tun.rwc.Subnet() - addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) + prefix := address.GetPrefix() + addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix)-1) if tun.config.name == "none" || tun.config.name == "dummy" { tun.log.Debugln("Not starting TUN as ifname is none or dummy") tun.isEnabled = false From 1dd1d0ab8c55bba222f4ef678011b97a0382316e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Feb 2023 21:32:26 +0000 Subject: [PATCH 4/5] Build packages with Go 1.20 --- .github/workflows/pkg.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index f282cc21..dd43fce8 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: "1.20" - name: Build package env: @@ -56,7 +56,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: "1.20" - name: Build package env: @@ -87,7 +87,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: "1.20" - name: Build package run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }} @@ -122,7 +122,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: "1.20" - name: Build package env: From 38736358dda588e30cbce931f7f41fdf26f5d5f0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Feb 2023 21:35:56 +0000 Subject: [PATCH 5/5] Fix lint error properly this time --- src/tun/tun.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tun/tun.go b/src/tun/tun.go index 9ec7bc90..fcd597b8 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -108,7 +108,7 @@ func (tun *TunAdapter) _start() error { tun.addr = tun.rwc.Address() tun.subnet = tun.rwc.Subnet() prefix := address.GetPrefix() - addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix)-1) + addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix[:])-1) if tun.config.name == "none" || tun.config.name == "dummy" { tun.log.Debugln("Not starting TUN as ifname is none or dummy") tun.isEnabled = false