diff --git a/cmd/entry/proxy.go b/cmd/entry/proxy.go index 6915f63..29bd6ec 100644 --- a/cmd/entry/proxy.go +++ b/cmd/entry/proxy.go @@ -9,7 +9,7 @@ import ( func main() { // load the configuration // from the environment - cfg, err := config.LoadConfig[config.ProxyConfig]() + cfg, err := config.LoadConfig[config.EntryConfig]() if err != nil { panic(err) } diff --git a/config/config.go b/config/config.go index 161c76e..d9caf3d 100644 --- a/config/config.go +++ b/config/config.go @@ -9,8 +9,9 @@ import ( "github.com/joho/godotenv" ) -type ProxyConfig struct { - NostrRelays []string `env:"NOSTR_RELAYS" envSeparator:";"` +type EntryConfig struct { + NostrRelays []string `env:"NOSTR_RELAYS" envSeparator:";"` + PublicAddress string `env:"PUBLIC_ADDRESS"` } type ExitConfig struct { diff --git a/exit/exit.go b/exit/exit.go index 4936512..62d9b84 100644 --- a/exit/exit.go +++ b/exit/exit.go @@ -231,7 +231,7 @@ func (e *Exit) handleConnect(ctx context.Context, msg nostr.IncomingEvent, proto func (e *Exit) handleConnectReverse(ctx context.Context, protocolMessage *protocol.Message, isTLS bool) { e.mutexMap.Lock(protocolMessage.Key.String()) defer e.mutexMap.Unlock(protocolMessage.Key.String()) - connection, err := net.Dial("tcp", ":1234") + connection, err := net.Dial("tcp", protocolMessage.Destination) if err != nil { return } diff --git a/netstr/dial.go b/netstr/dial.go index 706e8e9..103b2c1 100644 --- a/netstr/dial.go +++ b/netstr/dial.go @@ -11,13 +11,20 @@ import ( "strings" ) +type DialOptions struct { + Pool *nostr.SimplePool + PublicAddress string + ConnectionID uuid.UUID + MessageType protocol.MessageType +} + // DialSocks connects to a destination using the provided SimplePool and returns a Dialer function. // It creates a new Connection using the specified context, private key, destination address, subscription flag, and connectionID. // It parses the destination address to get the public key and relays. // It creates a signed event using the private key, public key, and destination address. // It ensures that the relays are available in the pool and publishes the signed event to each relay. // Finally, it returns the Connection and nil error. If there are any errors, nil connection and the error are returned. -func DialSocks(pool *nostr.SimplePool, connectionID uuid.UUID) func(ctx context.Context, net_, addr string) (net.Conn, error) { +func DialSocks(options DialOptions) func(ctx context.Context, net_, addr string) (net.Conn, error) { return func(ctx context.Context, net_, addr string) (net.Conn, error) { addr = strings.ReplaceAll(addr, ".", "") key := nostr.GeneratePrivateKey() @@ -25,7 +32,7 @@ func DialSocks(pool *nostr.SimplePool, connectionID uuid.UUID) func(ctx context. WithPrivateKey(key), WithDst(addr), WithSub(), - WithUUID(connectionID)) + WithUUID(options.ConnectionID)) publicKey, relays, err := ParseDestination(addr) if err != nil { @@ -38,16 +45,20 @@ func DialSocks(pool *nostr.SimplePool, connectionID uuid.UUID) func(ctx context. return nil, err } opts := []protocol.MessageOption{ - protocol.WithType(protocol.MessageConnectReverse), - protocol.WithUUID(connectionID), - protocol.WithDestination(addr), + protocol.WithType(options.MessageType), + protocol.WithUUID(options.ConnectionID), + } + if options.PublicAddress != "" { + opts = append(opts, protocol.WithDestination(options.PublicAddress)) + } else { + opts = append(opts, protocol.WithDestination(addr)) // todo -- use public key instead } ev, err := signer.CreateSignedEvent(publicKey, protocol.KindEphemeralEvent, nostr.Tags{nostr.Tag{"p", publicKey}}, opts...) for _, relayUrl := range relays { - relay, err := pool.EnsureRelay(relayUrl) + relay, err := options.Pool.EnsureRelay(relayUrl) if err != nil { slog.Error("error creating relay", err) continue diff --git a/proxy/proxy.go b/proxy/proxy.go index e1695d8..00b4a51 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -11,14 +11,14 @@ import ( ) type Proxy struct { - config *config.ProxyConfig // the configuration for the gateway + config *config.EntryConfig // the configuration for the gateway // a list of nostr relays to publish events to relays []*nostr.Relay // deprecated -- should be used for default relay configuration pool *nostr.SimplePool socksServer *socks5.Server } -func New(ctx context.Context, config *config.ProxyConfig) *Proxy { +func New(ctx context.Context, config *config.EntryConfig) *Proxy { s := &Proxy{ config: config, pool: nostr.NewSimplePool(ctx), @@ -32,7 +32,7 @@ func New(ctx context.Context, config *config.ProxyConfig) *Proxy { BindIP: net.IP{0, 0, 0, 0}, Logger: nil, Dial: nil, - }, s.pool) + }, s.pool, config) if err != nil { panic(err) } diff --git a/socks5/request.go b/socks5/request.go index f9cf735..8c95308 100644 --- a/socks5/request.go +++ b/socks5/request.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/asmogo/nws/netstr" + "github.com/asmogo/nws/protocol" "github.com/google/uuid" "io" "net" @@ -169,10 +170,20 @@ func (s *Server) handleConnect(ctx context.Context, conn net.Conn, req *Request) // Attempt to connect dial := s.config.Dial ch := make(chan net.Conn) + connectionID := uuid.New() + options := netstr.DialOptions{ + Pool: s.pool, + PublicAddress: s.config.entryConfig.PublicAddress, + ConnectionID: connectionID, + } if dial == nil { - connectionID := uuid.New() - s.tcpListener.AddConnectChannel(connectionID, ch) - dial = netstr.DialSocks(s.pool, connectionID) + if s.tcpListener != nil { + s.tcpListener.AddConnectChannel(connectionID, ch) + options.MessageType = protocol.MessageConnectReverse + } else { + options.MessageType = protocol.MessageConnect + } + dial = netstr.DialSocks(options) } target, err := dial(ctx, "tcp", req.realDestAddr.FQDN) if err != nil { @@ -197,11 +208,14 @@ func (s *Server) handleConnect(ctx context.Context, conn net.Conn, req *Request) return fmt.Errorf("failed to send reply: %v", err) } // read - - // wait for the connection - connR := <-ch - defer connR.Close() - + var connR net.Conn + if options.MessageType == protocol.MessageConnectReverse { + // wait for the connection + connR = <-ch + defer connR.Close() + } else { + connR = target + } // Start proxying errCh := make(chan error, 2) go Proxy(connR, conn, errCh) diff --git a/socks5/socks5.go b/socks5/socks5.go index 6d6a8d2..475ecf6 100644 --- a/socks5/socks5.go +++ b/socks5/socks5.go @@ -3,6 +3,7 @@ package socks5 import ( "bufio" "fmt" + "github.com/asmogo/nws/config" "github.com/nbd-wtf/go-nostr" "log" "net" @@ -49,6 +50,8 @@ type Config struct { // Optional function for dialing out Dial func(ctx context.Context, network, addr string) (net.Conn, error) + + entryConfig *config.EntryConfig } var ErrorNoServerAvailable = fmt.Errorf("no socks server available") @@ -63,7 +66,7 @@ type Server struct { } // New creates a new Server and potentially returns an error -func New(conf *Config, pool *nostr.SimplePool) (*Server, error) { +func New(conf *Config, pool *nostr.SimplePool, config *config.EntryConfig) (*Server, error) { // Ensure we have at least one authentication method enabled if len(conf.AuthMethods) == 0 { if conf.Credentials != nil { @@ -87,15 +90,21 @@ func New(conf *Config, pool *nostr.SimplePool) (*Server, error) { if conf.Logger == nil { conf.Logger = log.New(os.Stdout, "", log.LstdFlags) } - listener, err := NewTCPListener() - if err != nil { - return nil, err + if conf.entryConfig == nil { + conf.entryConfig = config } - go listener.Start() + server := &Server{ - config: conf, - pool: pool, - tcpListener: listener, + config: conf, + pool: pool, + } + if conf.entryConfig.PublicAddress != "" { + listener, err := NewTCPListener() + if err != nil { + return nil, err + } + go listener.Start() + server.tcpListener = listener } server.authMethods = make(map[uint8]Authenticator)