SOCKS support

This commit is contained in:
Neil Alexander 2022-09-04 18:15:03 +01:00
parent 18eef141d0
commit 1852bca720
4 changed files with 85 additions and 45 deletions

View file

@ -27,6 +27,7 @@ type links struct {
tcp *linkTCP // TCP interface support tcp *linkTCP // TCP interface support
tls *linkTLS // TLS interface support tls *linkTLS // TLS interface support
unix *linkUNIX // UNIX interface support unix *linkUNIX // UNIX interface support
socks *linkSOCKS // SOCKS interface support
_links map[linkInfo]*link // *link is nil if connection in progress _links map[linkInfo]*link // *link is nil if connection in progress
// TODO timeout (to remove from switch), read from config.ReadTimeout // 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.tcp = l.newLinkTCP()
l.tls = l.newLinkTLS(l.tcp) l.tls = l.newLinkTLS(l.tcp)
l.unix = l.newLinkUNIX() l.unix = l.newLinkUNIX()
l.socks = l.newLinkSOCKS()
l._links = make(map[linkInfo]*link) l._links = make(map[linkInfo]*link)
var listeners []ListenAddress var listeners []ListenAddress
@ -113,65 +115,58 @@ func (l *links) call(u *url.URL, sintf string) error {
if l.isConnectedTo(info) { if l.isConnectedTo(info) {
return fmt.Errorf("already connected to this node") return fmt.Errorf("already connected to this node")
} }
tcpOpts := tcpOptions{ options := linkOptions{
linkOptions: linkOptions{
pinnedEd25519Keys: map[keyArray]struct{}{}, pinnedEd25519Keys: map[keyArray]struct{}{},
},
} }
for _, pubkey := range u.Query()["key"] { for _, pubkey := range u.Query()["key"] {
if sigPub, err := hex.DecodeString(pubkey); err == nil { if sigPub, err := hex.DecodeString(pubkey); err == nil {
var sigPubKey keyArray var sigPubKey keyArray
copy(sigPubKey[:], sigPub) copy(sigPubKey[:], sigPub)
tcpOpts.pinnedEd25519Keys[sigPubKey] = struct{}{} options.pinnedEd25519Keys[sigPubKey] = struct{}{}
} }
} }
switch info.linkType { switch info.linkType {
case "tcp": case "tcp":
go func() { 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) l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err)
} }
}() }()
/*
case "socks": case "socks":
tcpOpts.socksProxyAddr = u.Host go func() {
if u.User != nil { if err := l.socks.dial(u, options); err != nil {
tcpOpts.socksProxyAuth = &proxy.Auth{} l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err)
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 "tls": case "tls":
// SNI headers must contain hostnames and not IP addresses, so we must make sure // 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 // 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 // the host-port combo from the query option and then seeing if it parses to an
// IP address successfully or not. // IP address successfully or not.
var tlsSNI string
if sni := u.Query().Get("sni"); sni != "" { if sni := u.Query().Get("sni"); sni != "" {
if net.ParseIP(sni) == nil { 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 // 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. // 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 { if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil {
tcpOpts.tlsSNI = host tlsSNI = host
} }
} }
go func() { 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) l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err)
} }
}() }()
case "unix": case "unix":
go func() { 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) l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err)
} }
}() }()

52
src/core/link_socks.go Normal file
View file

@ -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
)
}

View file

@ -9,7 +9,6 @@ import (
"time" "time"
"github.com/Arceliar/phony" "github.com/Arceliar/phony"
"golang.org/x/net/proxy"
) )
type linkTCP struct { type linkTCP struct {
@ -19,14 +18,6 @@ type linkTCP struct {
_listeners map[*Listener]context.CancelFunc _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 { func (l *links) newLinkTCP() *linkTCP {
lt := &linkTCP{ lt := &linkTCP{
links: l, links: l,
@ -39,7 +30,7 @@ func (l *links) newLinkTCP() *linkTCP {
return lt 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]) info := linkInfoFor("tcp", sintf, strings.SplitN(url.Host, "%", 2)[0])
if l.links.isConnectedTo(info) { if l.links.isConnectedTo(info) {
return fmt.Errorf("duplicate connection attempt") 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) addr := conn.RemoteAddr().(*net.TCPAddr)
name := fmt.Sprintf("tls://%s", addr) name := fmt.Sprintf("tls://%s", addr)
info := linkInfoFor("tcp", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) 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) 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 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( return l.links.create(
conn, // connection conn, // connection
name, // connection name name, // connection name
info, // connection info info, // connection info
incoming, // not incoming incoming, // not incoming
false, // not forced false, // not forced
options.linkOptions, // connection options options, // connection options
) )
} }

View file

@ -46,7 +46,7 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
return lt 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]) info := linkInfoFor("tls", sintf, strings.SplitN(url.Host, "%", 2)[0])
if l.links.isConnectedTo(info) { if l.links.isConnectedTo(info) {
return fmt.Errorf("duplicate connection attempt") 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 { if err != nil {
return err return err
} }
tlsconfig := l.config.Clone()
tlsconfig.ServerName = sni
tlsdialer := &tls.Dialer{ tlsdialer := &tls.Dialer{
NetDialer: dialer, NetDialer: dialer,
Config: l.config, Config: tlsconfig,
} }
conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String()) conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String())
if err != nil { if err != nil {
@ -106,7 +108,7 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) {
addr := conn.RemoteAddr().(*net.TCPAddr) addr := conn.RemoteAddr().(*net.TCPAddr)
name := fmt.Sprintf("tls://%s", addr) name := fmt.Sprintf("tls://%s", addr)
info := linkInfoFor("tls", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) 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) l.core.log.Errorln("Failed to create inbound link:", err)
} }
} }
@ -164,6 +166,6 @@ func (l *linkTLS) generateConfig() (*tls.Config, error) {
}, nil }, 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) return l.tcp.handler(name, info, conn, options, incoming)
} }