From 1852bca7207bdcf6e251234d2d1c4f40dae0b93b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 4 Sep 2022 18:15:03 +0100 Subject: [PATCH] SOCKS support --- src/core/link.go | 41 +++++++++++++++------------------ src/core/link_socks.go | 52 ++++++++++++++++++++++++++++++++++++++++++ src/core/link_tcp.go | 27 ++++++++-------------- src/core/link_tls.go | 10 ++++---- 4 files changed, 85 insertions(+), 45 deletions(-) create mode 100644 src/core/link_socks.go diff --git a/src/core/link.go b/src/core/link.go index e0bd4592..08e8dfc2 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -27,6 +27,7 @@ type links struct { tcp *linkTCP // TCP interface support tls *linkTLS // TLS interface support unix *linkUNIX // UNIX interface support + socks *linkSOCKS // SOCKS interface support _links map[linkInfo]*link // *link is nil if connection in progress // TODO timeout (to remove from switch), read from config.ReadTimeout } @@ -68,6 +69,7 @@ func (l *links) init(c *Core) error { l.tcp = l.newLinkTCP() l.tls = l.newLinkTLS(l.tcp) l.unix = l.newLinkUNIX() + l.socks = l.newLinkSOCKS() l._links = make(map[linkInfo]*link) var listeners []ListenAddress @@ -113,65 +115,58 @@ func (l *links) call(u *url.URL, sintf string) error { if l.isConnectedTo(info) { return fmt.Errorf("already connected to this node") } - tcpOpts := tcpOptions{ - linkOptions: linkOptions{ - pinnedEd25519Keys: map[keyArray]struct{}{}, - }, + options := linkOptions{ + pinnedEd25519Keys: map[keyArray]struct{}{}, } for _, pubkey := range u.Query()["key"] { if sigPub, err := hex.DecodeString(pubkey); err == nil { var sigPubKey keyArray copy(sigPubKey[:], sigPub) - tcpOpts.pinnedEd25519Keys[sigPubKey] = struct{}{} + options.pinnedEd25519Keys[sigPubKey] = struct{}{} } } switch info.linkType { case "tcp": go func() { - if err := l.tcp.dial(u, tcpOpts, sintf); err != nil { + if err := l.tcp.dial(u, options, sintf); err != nil { l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err) } }() - /* - case "socks": - tcpOpts.socksProxyAddr = u.Host - if u.User != nil { - tcpOpts.socksProxyAuth = &proxy.Auth{} - tcpOpts.socksProxyAuth.User = u.User.Username() - tcpOpts.socksProxyAuth.Password, _ = u.User.Password() - } - tcpOpts.upgrade = l.tcp.tls.forDialer // TODO make this configurable - pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/") - go l.tcp.call(pathtokens[0], tcpOpts, sintf) - */ + case "socks": + go func() { + if err := l.socks.dial(u, options); err != nil { + l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err) + } + }() case "tls": // SNI headers must contain hostnames and not IP addresses, so we must make sure // that we do not populate the SNI with an IP literal. We do this by splitting // the host-port combo from the query option and then seeing if it parses to an // IP address successfully or not. + var tlsSNI string if sni := u.Query().Get("sni"); sni != "" { if net.ParseIP(sni) == nil { - tcpOpts.tlsSNI = sni + tlsSNI = sni } } // If the SNI is not configured still because the above failed then we'll try // again but this time we'll use the host part of the peering URI instead. - if tcpOpts.tlsSNI == "" { + if tlsSNI == "" { if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil { - tcpOpts.tlsSNI = host + tlsSNI = host } } go func() { - if err := l.tls.dial(u, tcpOpts, sintf); err != nil { + if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil { l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err) } }() case "unix": go func() { - if err := l.unix.dial(u, tcpOpts.linkOptions, sintf); err != nil { + if err := l.unix.dial(u, options, sintf); err != nil { l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err) } }() diff --git a/src/core/link_socks.go b/src/core/link_socks.go new file mode 100644 index 00000000..ad5b8c98 --- /dev/null +++ b/src/core/link_socks.go @@ -0,0 +1,52 @@ +package core + +import ( + "fmt" + "net" + "net/url" + "strings" + + "golang.org/x/net/proxy" +) + +type linkSOCKS struct { + *links +} + +func (l *links) newLinkSOCKS() *linkSOCKS { + lt := &linkSOCKS{ + links: l, + } + return lt +} + +func (l *linkSOCKS) dial(url *url.URL, options linkOptions) error { + info := linkInfoFor("socks", "", url.Path) + if l.links.isConnectedTo(info) { + return fmt.Errorf("duplicate connection attempt") + } + proxyAuth := &proxy.Auth{} + proxyAuth.User = url.User.Username() + proxyAuth.Password, _ = url.User.Password() + dialer, err := proxy.SOCKS5("tcp", url.Host, proxyAuth, proxy.Direct) + if err != nil { + return fmt.Errorf("failed to configure proxy") + } + pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/") + conn, err := dialer.Dial("tcp", pathtokens[0]) + if err != nil { + return err + } + return l.handler(url.String(), info, conn, options, false) +} + +func (l *linkSOCKS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { + return l.links.create( + conn, // connection + name, // connection name + info, // connection info + incoming, // not incoming + false, // not forced + options, // connection options + ) +} diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 767df0c8..fcac8b4d 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -9,7 +9,6 @@ import ( "time" "github.com/Arceliar/phony" - "golang.org/x/net/proxy" ) type linkTCP struct { @@ -19,14 +18,6 @@ type linkTCP struct { _listeners map[*Listener]context.CancelFunc } -type tcpOptions struct { - linkOptions - socksProxyAddr string // nolint:unused - socksProxyAuth *proxy.Auth // nolint:unused - socksPeerAddr string // nolint:unused - tlsSNI string -} - func (l *links) newLinkTCP() *linkTCP { lt := &linkTCP{ links: l, @@ -39,7 +30,7 @@ func (l *links) newLinkTCP() *linkTCP { return lt } -func (l *linkTCP) dial(url *url.URL, options tcpOptions, sintf string) error { +func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { info := linkInfoFor("tcp", sintf, strings.SplitN(url.Host, "%", 2)[0]) if l.links.isConnectedTo(info) { return fmt.Errorf("duplicate connection attempt") @@ -94,7 +85,7 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { addr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tls://%s", addr) info := linkInfoFor("tcp", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) - if err = l.handler(name, info, conn, tcpOptions{}, true); err != nil { + if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -105,14 +96,14 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { return entry, nil } -func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options tcpOptions, incoming bool) error { +func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { return l.links.create( - conn, // connection - name, // connection name - info, // connection info - incoming, // not incoming - false, // not forced - options.linkOptions, // connection options + conn, // connection + name, // connection name + info, // connection info + incoming, // not incoming + false, // not forced + options, // connection options ) } diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 40c3196c..e6ffd5a8 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -46,7 +46,7 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { return lt } -func (l *linkTLS) dial(url *url.URL, options tcpOptions, sintf string) error { +func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { info := linkInfoFor("tls", sintf, strings.SplitN(url.Host, "%", 2)[0]) if l.links.isConnectedTo(info) { return fmt.Errorf("duplicate connection attempt") @@ -60,9 +60,11 @@ func (l *linkTLS) dial(url *url.URL, options tcpOptions, sintf string) error { if err != nil { return err } + tlsconfig := l.config.Clone() + tlsconfig.ServerName = sni tlsdialer := &tls.Dialer{ NetDialer: dialer, - Config: l.config, + Config: tlsconfig, } conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String()) if err != nil { @@ -106,7 +108,7 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { addr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tls://%s", addr) info := linkInfoFor("tls", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) - if err = l.handler(name, info, conn, tcpOptions{}, true); err != nil { + if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -164,6 +166,6 @@ func (l *linkTLS) generateConfig() (*tls.Config, error) { }, nil } -func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options tcpOptions, incoming bool) error { +func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { return l.tcp.handler(name, info, conn, options, incoming) }