wgengine/netstack: make SOCKS5 resolve names to IPv6 if self node when no IPv4

For instance, ephemeral nodes with only IPv6 addresses can now
SOCKS5-dial out to names like "foo" and resolve foo's IPv6 address
rather than foo's IPv4 address and get a "no route"
(*tcpip.ErrNoRoute) error from netstack's dialer.

Per https://github.com/tailscale/tailscale/issues/2268#issuecomment-870027626
which is only part of the isuse.

Updates #2268

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2021-06-28 15:16:37 -07:00
parent 1147c7fd4f
commit 722859b476
2 changed files with 127 additions and 3 deletions

View File

@ -177,21 +177,33 @@ func (ns *Impl) Start() error {
func DNSMapFromNetworkMap(nm *netmap.NetworkMap) DNSMap {
ret := make(DNSMap)
suffix := nm.MagicDNSSuffix()
have4 := false
if nm.Name != "" && len(nm.Addresses) > 0 {
ip := nm.Addresses[0].IP()
ret[strings.TrimRight(nm.Name, ".")] = ip
if dnsname.HasSuffix(nm.Name, suffix) {
ret[dnsname.TrimSuffix(nm.Name, suffix)] = ip
}
for _, a := range nm.Addresses {
if a.IP().Is4() {
have4 = true
}
}
}
for _, p := range nm.Peers {
if p.Name != "" && len(p.Addresses) > 0 {
ip := p.Addresses[0].IP()
if p.Name == "" {
continue
}
for _, a := range p.Addresses {
ip := a.IP()
if ip.Is4() && !have4 {
continue
}
ret[strings.TrimRight(p.Name, ".")] = ip
if dnsname.HasSuffix(p.Name, suffix) {
ret[dnsname.TrimSuffix(p.Name, suffix)] = ip
}
break
}
}
return ret

View File

@ -0,0 +1,112 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package netstack
import (
"reflect"
"testing"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/netmap"
)
func TestDNSMapFromNetworkMap(t *testing.T) {
pfx := netaddr.MustParseIPPrefix
ip := netaddr.MustParseIP
tests := []struct {
name string
nm *netmap.NetworkMap
want DNSMap
}{
{
name: "self",
nm: &netmap.NetworkMap{
Name: "foo.tailnet",
Addresses: []netaddr.IPPrefix{
pfx("100.102.103.104/32"),
pfx("100::123/128"),
},
},
want: DNSMap{
"foo": ip("100.102.103.104"),
"foo.tailnet": ip("100.102.103.104"),
},
},
{
name: "self_and_peers",
nm: &netmap.NetworkMap{
Name: "foo.tailnet",
Addresses: []netaddr.IPPrefix{
pfx("100.102.103.104/32"),
pfx("100::123/128"),
},
Peers: []*tailcfg.Node{
{
Name: "a.tailnet",
Addresses: []netaddr.IPPrefix{
pfx("100.0.0.201/32"),
pfx("100::201/128"),
},
},
{
Name: "b.tailnet",
Addresses: []netaddr.IPPrefix{
pfx("100::202/128"),
},
},
},
},
want: DNSMap{
"foo": ip("100.102.103.104"),
"foo.tailnet": ip("100.102.103.104"),
"a": ip("100.0.0.201"),
"a.tailnet": ip("100.0.0.201"),
"b": ip("100::202"),
"b.tailnet": ip("100::202"),
},
},
{
name: "self_has_v6_only",
nm: &netmap.NetworkMap{
Name: "foo.tailnet",
Addresses: []netaddr.IPPrefix{
pfx("100::123/128"),
},
Peers: []*tailcfg.Node{
{
Name: "a.tailnet",
Addresses: []netaddr.IPPrefix{
pfx("100.0.0.201/32"),
pfx("100::201/128"),
},
},
{
Name: "b.tailnet",
Addresses: []netaddr.IPPrefix{
pfx("100::202/128"),
},
},
},
},
want: DNSMap{
"foo": ip("100::123"),
"foo.tailnet": ip("100::123"),
"a": ip("100::201"),
"a.tailnet": ip("100::201"),
"b": ip("100::202"),
"b.tailnet": ip("100::202"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := DNSMapFromNetworkMap(tt.nm)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("mismatch:\n got %v\nwant %v\n", got, tt.want)
}
})
}
}