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