Egor: 1 Add upstream connections over Tor 6 files changed, 32 insertions(+), 7 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~emersion/soju-dev/patches/51045/mbox | git am -3Learn more about email & git
where "Tor" means SOCKS5 with a hardcoded address --- This is good enough for my purposes, but I understand it's probably better to implement the ability to specify any proxy address. So, should I make a more generic SOCKS5 patch? And if so, what would be a good URL format for it? I'm thinking of using a query parameter to specify the proxy host, like so: ircs://example.com/?socks5=127.0.0.1:9050 doc/soju.1.scd | 4 ++++ go.mod | 1 + go.sum | 2 ++ service.go | 4 ++-- upstream.go | 26 ++++++++++++++++++++++---- user.go | 2 +- 6 files changed, 32 insertions(+), 7 deletions(-) diff --git a/doc/soju.1.scd b/doc/soju.1.scd index f06d5f8..c818a15 100644 --- a/doc/soju.1.scd +++ b/doc/soju.1.scd @@ -262,6 +262,10 @@ character. - _[ircs://]<host>[:port]_ connects with TLS over TCP - _irc+insecure://<host>[:port]_ connects with plain-text TCP - _irc+unix:///<path>_ connects to a Unix socket + - _ircs+tor://<host>[:port]_ connects with TLS over SOCKS5 proxy at + 127.0.0.1:9050 + - _irc+tor://<host>[:port]_ connects with plain-text over SOCKS5 proxy at + 127.0.0.1:9050 For example, to connect to Libera Chat: diff --git a/go.mod b/go.mod index d6aa0cb..44bc574 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/pires/go-proxyproto v0.7.0 github.com/prometheus/client_golang v1.18.0 golang.org/x/crypto v0.21.0 + golang.org/x/net v0.21.0 golang.org/x/time v0.5.0 gopkg.in/irc.v4 v4.0.0 modernc.org/sqlite v1.29.5 diff --git a/go.sum b/go.sum index aff0fcc..117d641 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/service.go b/service.go index a39b276..d699366 100644 --- a/service.go +++ b/service.go @@ -500,9 +500,9 @@ func (fs *networkFlagSet) update(network *database.Network) error { if addrParts := strings.SplitN(*fs.Addr, "://", 2); len(addrParts) == 2 { scheme := addrParts[0] switch scheme { - case "ircs", "irc+insecure", "unix": + case "ircs", "irc+insecure", "unix", "ircs+tor", "irc+tor": default: - return fmt.Errorf("unknown scheme %q (supported schemes: ircs, irc+insecure, unix)", scheme) + return fmt.Errorf("unknown scheme %q (supported schemes: ircs, irc+insecure, unix, ircs+tor, irc+tor)", scheme) } } network.Addr = *fs.Addr diff --git a/upstream.go b/upstream.go index ca17ea8..8ac95f8 100644 --- a/upstream.go +++ b/upstream.go @@ -18,6 +18,8 @@ import ( "strings" "time" + "golang.org/x/net/proxy" + "github.com/emersion/go-sasl" "gopkg.in/irc.v4" @@ -250,7 +252,7 @@ func connectToUpstream(ctx context.Context, network *network) (*upstreamConn, er var netConn net.Conn switch u.Scheme { - case "ircs": + case "ircs", "ircs+tor": addr := u.Host host, _, err := net.SplitHostPort(u.Host) if err != nil { @@ -314,7 +316,11 @@ func connectToUpstream(ctx context.Context, network *network) (*upstreamConn, er } logger.Printf("connecting to TLS server at address %q", addr) - netConn, err = dialTCP(ctx, network.user, addr) + if u.Scheme == "ircs+tor" { + netConn, err = dialTor(ctx, addr) + } else { + netConn, err = dialTCP(ctx, network.user, addr) + } if err != nil { return nil, err } @@ -323,14 +329,18 @@ func connectToUpstream(ctx context.Context, network *network) (*upstreamConn, er // the new connection with identd ASAP. See: // https://todo.sr.ht/~emersion/soju/69#event-41859 netConn = tls.Client(netConn, tlsConfig) - case "irc+insecure": + case "irc+insecure", "irc+tor": addr := u.Host if _, _, err := net.SplitHostPort(addr); err != nil { addr = u.Host + ":6667" } logger.Printf("connecting to plain-text server at address %q", addr) - netConn, err = dialTCP(ctx, network.user, addr) + if u.Scheme == "irc+tor" { + netConn, err = dialTor(ctx, addr) + } else { + netConn, err = dialTCP(ctx, network.user, addr) + } if err != nil { return nil, err } @@ -373,6 +383,14 @@ func connectToUpstream(ctx context.Context, network *network) (*upstreamConn, er return uc, nil } +func dialTor(ctx context.Context, addr string) (net.Conn, error) { + dialer, err := proxy.SOCKS5("tcp", "127.0.0.1:9050", nil, proxy.Direct) + if err != nil { + return nil, err + } + return dialer.(proxy.ContextDialer).DialContext(ctx, "tcp", addr) +} + func dialTCP(ctx context.Context, user *user, addr string) (net.Conn, error) { var dialer net.Dialer upstreamUserIPs := user.srv.Config().UpstreamUserIPs diff --git a/user.go b/user.go index 8c0fd6e..d02da00 100644 --- a/user.go +++ b/user.go @@ -964,7 +964,7 @@ func (u *user) checkNetwork(record *database.Network) error { return fmt.Errorf("%v:// URL must not have a fragment", url.Scheme) } switch url.Scheme { - case "ircs", "irc+insecure": + case "ircs", "irc+insecure", "ircs+tor", "irc+tor": if url.Host == "" { return fmt.Errorf("%v:// URL must have a host", url.Scheme) } -- 2.39.2