Skip to content

Commit 42873be

Browse files
committed
Reusable peer lookup/dial logic
1 parent 75d2080 commit 42873be

File tree

12 files changed

+193
-124
lines changed

12 files changed

+193
-124
lines changed

cmd/yggdrasil/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,16 @@ func main() {
191191

192192
// Set up the Yggdrasil node itself.
193193
{
194+
iprange := net.IPNet{
195+
IP: net.ParseIP("200::"),
196+
Mask: net.CIDRMask(7, 128),
197+
}
194198
options := []core.SetupOption{
195199
core.NodeInfo(cfg.NodeInfo),
196200
core.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
201+
core.PeerFilter(func(ip net.IP) bool {
202+
return !iprange.Contains(ip)
203+
}),
197204
}
198205
for _, addr := range cfg.Listen {
199206
options = append(options, core.ListenAddress(addr))

contrib/mobile/mobile.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,15 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
5353
}
5454
// Set up the Yggdrasil node itself.
5555
{
56-
options := []core.SetupOption{}
56+
iprange := net.IPNet{
57+
IP: net.ParseIP("200::"),
58+
Mask: net.CIDRMask(7, 128),
59+
}
60+
options := []core.SetupOption{
61+
core.PeerFilter(func(ip net.IP) bool {
62+
return !iprange.Contains(ip)
63+
}),
64+
}
5765
for _, peer := range m.config.Peers {
5866
options = append(options, core.Peer{URI: peer})
5967
}

src/core/core.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type Core struct {
4040
tls *tls.Config // immutable after startup
4141
//_peers map[Peer]*linkInfo // configurable after startup
4242
_listeners map[ListenAddress]struct{} // configurable after startup
43+
peerFilter func(ip net.IP) bool // immutable after startup
4344
nodeinfo NodeInfo // immutable after startup
4445
nodeinfoPrivacy NodeInfoPrivacy // immutable after startup
4546
_allowedPublicKeys map[[32]byte]struct{} // configurable after startup

src/core/link.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ const ErrLinkPasswordInvalid = linkError("invalid password supplied")
127127
const ErrLinkUnrecognisedSchema = linkError("link schema unknown")
128128
const ErrLinkMaxBackoffInvalid = linkError("max backoff duration invalid")
129129
const ErrLinkSNINotSupported = linkError("SNI not supported on this link type")
130+
const ErrLinkNoSuitableIPs = linkError("no suitable remote IPs")
130131

131132
func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
132133
var retErr error
@@ -653,6 +654,43 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
653654
return err
654655
}
655656

657+
func (l *links) findSuitableIP(url *url.URL, fn func(hostname string, ip net.IP, port int) (net.Conn, error)) (net.Conn, error) {
658+
host, p, err := net.SplitHostPort(url.Host)
659+
if err != nil {
660+
return nil, err
661+
}
662+
port, err := strconv.Atoi(p)
663+
if err != nil {
664+
return nil, err
665+
}
666+
resp, err := net.LookupIP(host)
667+
if err != nil {
668+
return nil, err
669+
}
670+
var _ips [64]net.IP
671+
ips := _ips[:0]
672+
for _, ip := range resp {
673+
if l.core.config.peerFilter != nil && !l.core.config.peerFilter(ip) {
674+
continue
675+
}
676+
ips = append(ips, ip)
677+
}
678+
if len(ips) == 0 {
679+
return nil, ErrLinkNoSuitableIPs
680+
}
681+
for _, ip := range ips {
682+
var conn net.Conn
683+
if conn, err = fn(host, ip, port); err != nil {
684+
url := *url
685+
url.RawQuery = ""
686+
l.core.log.Debugln("Dialling", url.Redacted(), "reported error:", err)
687+
continue
688+
}
689+
return conn, nil
690+
}
691+
return nil, err
692+
}
693+
656694
func urlForLinkInfo(u url.URL) url.URL {
657695
u.RawQuery = ""
658696
return u

src/core/link_quic.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,23 @@ func (l *links) newLinkQUIC() *linkQUIC {
5151
}
5252

5353
func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
54-
qc, err := quic.DialAddr(ctx, url.Host, l.tlsconfig, l.quicconfig)
55-
if err != nil {
56-
return nil, err
57-
}
58-
qs, err := qc.OpenStreamSync(ctx)
59-
if err != nil {
60-
return nil, err
61-
}
62-
return &linkQUICStream{
63-
Connection: qc,
64-
Stream: qs,
65-
}, nil
54+
tlsconfig := l.tlsconfig.Clone()
55+
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
56+
tlsconfig.ServerName = hostname
57+
hostport := net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port))
58+
qc, err := quic.DialAddr(ctx, hostport, l.tlsconfig, l.quicconfig)
59+
if err != nil {
60+
return nil, err
61+
}
62+
qs, err := qc.OpenStreamSync(ctx)
63+
if err != nil {
64+
return nil, err
65+
}
66+
return &linkQUICStream{
67+
Connection: qc,
68+
Stream: qs,
69+
}, nil
70+
})
6671
}
6772

6873
func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {

src/core/link_socks.go

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,41 @@ func (l *links) newLinkSOCKS() *linkSOCKS {
2323
}
2424

2525
func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
26-
if url.Scheme != "sockstls" && options.tlsSNI != "" {
27-
return nil, ErrLinkSNINotSupported
28-
}
2926
var proxyAuth *proxy.Auth
3027
if url.User != nil && url.User.Username() != "" {
3128
proxyAuth = &proxy.Auth{
3229
User: url.User.Username(),
3330
}
3431
proxyAuth.Password, _ = url.User.Password()
3532
}
36-
dialer, err := proxy.SOCKS5("tcp", url.Host, proxyAuth, proxy.Direct)
37-
if err != nil {
38-
return nil, fmt.Errorf("failed to configure proxy")
39-
}
40-
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
41-
conn, err := dialer.Dial("tcp", pathtokens[0])
42-
if err != nil {
43-
return nil, fmt.Errorf("failed to dial: %w", err)
44-
}
45-
if url.Scheme == "sockstls" {
46-
tlsconfig := l.tls.config.Clone()
47-
tlsconfig.ServerName = options.tlsSNI
48-
conn = tls.Client(conn, tlsconfig)
49-
}
50-
return conn, nil
33+
tlsconfig := l.tls.config.Clone()
34+
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
35+
hostport := net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port))
36+
dialer, err := l.tcp.dialerFor(&net.TCPAddr{
37+
IP: ip,
38+
Port: port,
39+
}, info.sintf)
40+
if err != nil {
41+
return nil, err
42+
}
43+
proxy, err := proxy.SOCKS5("tcp", hostport, proxyAuth, dialer)
44+
if err != nil {
45+
return nil, err
46+
}
47+
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
48+
conn, err := proxy.Dial("tcp", pathtokens[0])
49+
if err != nil {
50+
return nil, err
51+
}
52+
if url.Scheme == "sockstls" {
53+
tlsconfig.ServerName = hostname
54+
if sni := options.tlsSNI; sni != "" {
55+
tlsconfig.ServerName = sni
56+
}
57+
conn = tls.Client(conn, tlsconfig)
58+
}
59+
return conn, nil
60+
})
5161
}
5262

5363
func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {

src/core/link_tcp.go

Lines changed: 6 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"net"
77
"net/url"
8-
"strconv"
98
"time"
109

1110
"github.com/Arceliar/phony"
@@ -34,59 +33,18 @@ type tcpDialer struct {
3433
addr *net.TCPAddr
3534
}
3635

37-
func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error) {
38-
host, p, err := net.SplitHostPort(url.Host)
39-
if err != nil {
40-
return nil, err
41-
}
42-
port, err := strconv.Atoi(p)
43-
if err != nil {
44-
return nil, err
45-
}
46-
ips, err := net.LookupIP(host)
47-
if err != nil {
48-
return nil, err
49-
}
50-
dialers := make([]*tcpDialer, 0, len(ips))
51-
for _, ip := range ips {
36+
func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
37+
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
5238
addr := &net.TCPAddr{
5339
IP: ip,
5440
Port: port,
5541
}
56-
dialer, err := l.dialerFor(addr, info.sintf)
57-
if err != nil {
58-
continue
59-
}
60-
dialers = append(dialers, &tcpDialer{
61-
info: info,
62-
dialer: dialer,
63-
addr: addr,
64-
})
65-
}
66-
return dialers, nil
67-
}
68-
69-
func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
70-
if options.tlsSNI != "" {
71-
return nil, ErrLinkSNINotSupported
72-
}
73-
dialers, err := l.dialersFor(url, info)
74-
if err != nil {
75-
return nil, err
76-
}
77-
if len(dialers) == 0 {
78-
return nil, nil
79-
}
80-
for _, d := range dialers {
81-
var conn net.Conn
82-
conn, err = d.dialer.DialContext(ctx, "tcp", d.addr.String())
42+
dialer, err := l.tcp.dialerFor(addr, info.sintf)
8343
if err != nil {
84-
l.core.log.Warnf("Failed to connect to %s: %s", d.addr, err)
85-
continue
44+
return nil, err
8645
}
87-
return conn, nil
88-
}
89-
return nil, err
46+
return dialer.DialContext(ctx, "tcp", addr.String())
47+
})
9048
}
9149

9250
func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) {

src/core/link_tls.go

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,26 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
3232
}
3333

3434
func (l *linkTLS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
35-
dialers, err := l.tcp.dialersFor(url, info)
36-
if err != nil {
37-
return nil, err
38-
}
39-
if len(dialers) == 0 {
40-
return nil, nil
41-
}
42-
for _, d := range dialers {
43-
tlsconfig := l.config.Clone()
44-
tlsconfig.ServerName = options.tlsSNI
45-
tlsdialer := &tls.Dialer{
46-
NetDialer: d.dialer,
47-
Config: tlsconfig,
35+
tlsconfig := l.config.Clone()
36+
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
37+
tlsconfig.ServerName = hostname
38+
if sni := options.tlsSNI; sni != "" {
39+
tlsconfig.ServerName = sni
4840
}
49-
var conn net.Conn
50-
conn, err = tlsdialer.DialContext(ctx, "tcp", d.addr.String())
41+
addr := &net.TCPAddr{
42+
IP: ip,
43+
Port: port,
44+
}
45+
dialer, err := l.tcp.dialerFor(addr, info.sintf)
5146
if err != nil {
52-
continue
47+
return nil, err
5348
}
54-
return conn, nil
55-
}
56-
return nil, err
49+
tlsdialer := &tls.Dialer{
50+
NetDialer: dialer,
51+
Config: tlsconfig,
52+
}
53+
return tlsdialer.DialContext(ctx, "tcp", addr.String())
54+
})
5755
}
5856

5957
func (l *linkTLS) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) {

src/core/link_unix.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ func (l *links) newLinkUNIX() *linkUNIX {
3131
}
3232

3333
func (l *linkUNIX) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
34-
if options.tlsSNI != "" {
35-
return nil, ErrLinkSNINotSupported
36-
}
3734
addr, err := net.ResolveUnixAddr("unix", url.Path)
3835
if err != nil {
3936
return nil, err

src/core/link_ws.go

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package core
22

33
import (
44
"context"
5+
"fmt"
56
"net"
67
"net/http"
78
"net/url"
@@ -87,18 +88,35 @@ func (l *links) newLinkWS() *linkWS {
8788
}
8889

8990
func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
90-
if options.tlsSNI != "" {
91-
return nil, ErrLinkSNINotSupported
92-
}
93-
wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{
94-
Subprotocols: []string{"ygg-ws"},
91+
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
92+
u := *url
93+
u.Host = net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port))
94+
addr := &net.TCPAddr{
95+
IP: ip,
96+
Port: port,
97+
}
98+
dialer, err := l.tcp.dialerFor(addr, info.sintf)
99+
if err != nil {
100+
return nil, err
101+
}
102+
wsconn, _, err := websocket.Dial(ctx, u.String(), &websocket.DialOptions{
103+
HTTPClient: &http.Client{
104+
Transport: &http.Transport{
105+
Proxy: http.ProxyFromEnvironment,
106+
Dial: dialer.Dial,
107+
DialContext: dialer.DialContext,
108+
},
109+
},
110+
Subprotocols: []string{"ygg-ws"},
111+
Host: hostname,
112+
})
113+
if err != nil {
114+
return nil, err
115+
}
116+
return &linkWSConn{
117+
Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary),
118+
}, nil
95119
})
96-
if err != nil {
97-
return nil, err
98-
}
99-
return &linkWSConn{
100-
Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary),
101-
}, nil
102120
}
103121

104122
func (l *linkWS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {

0 commit comments

Comments
 (0)