summaryrefslogtreecommitdiffstats
path: root/transport/tor/tor.go
diff options
context:
space:
mode:
Diffstat (limited to 'transport/tor/tor.go')
-rw-r--r--transport/tor/tor.go129
1 files changed, 129 insertions, 0 deletions
diff --git a/transport/tor/tor.go b/transport/tor/tor.go
new file mode 100644
index 0000000..a6a1e35
--- /dev/null
+++ b/transport/tor/tor.go
@@ -0,0 +1,129 @@
+// Package tor implements Tor Hidden Service transport
+package tor
+
+import (
+ "fmt"
+ "net"
+ "net/url"
+
+ "golang.org/x/net/proxy"
+
+ "veilith/transport"
+)
+
+// TorTransport implements Tor-based networking
+type TorTransport struct {
+ socksProxy string // SOCKS5 proxy address (default: 127.0.0.1:9050)
+ dialer proxy.Dialer
+ listener net.Listener
+ closed bool
+}
+
+// NewTorTransport creates a new Tor transport
+func NewTorTransport(socksProxy string) (*TorTransport, error) {
+ if socksProxy == "" {
+ socksProxy = "127.0.0.1:9050" // Default Tor SOCKS proxy
+ }
+
+ // Create SOCKS5 dialer
+ proxyURL, err := url.Parse("socks5://" + socksProxy)
+ if err != nil {
+ return nil, fmt.Errorf("invalid SOCKS proxy address: %w", err)
+ }
+
+ dialer, err := proxy.FromURL(proxyURL, proxy.Direct)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create SOCKS dialer: %w", err)
+ }
+
+ return &TorTransport{
+ socksProxy: socksProxy,
+ dialer: dialer,
+ }, nil
+}
+
+// Connect connects to a Tor Hidden Service (.onion address)
+func (t *TorTransport) Connect(address string) (net.Conn, error) {
+ if t.closed {
+ return nil, fmt.Errorf("tor transport is closed")
+ }
+
+ // Validate .onion address
+ if !isOnionAddress(address) {
+ return nil, fmt.Errorf("invalid .onion address: %s", address)
+ }
+
+ // Connect via SOCKS proxy
+ conn, err := t.dialer.Dial("tcp", address)
+ if err != nil {
+ return nil, fmt.Errorf("failed to connect to %s: %w", address, err)
+ }
+
+ return conn, nil
+}
+
+// Listen starts a Tor Hidden Service listener
+// Note: This is a simplified implementation. In production, you'd need to:
+// 1. Configure torrc to create a hidden service
+// 2. Get the .onion address from Tor control port
+// 3. Listen on the local port specified in torrc
+func (t *TorTransport) Listen(address string) (net.Listener, error) {
+ if t.closed {
+ return nil, fmt.Errorf("tor transport is closed")
+ }
+
+ if t.listener != nil {
+ return nil, fmt.Errorf("already listening")
+ }
+
+ // Listen on localhost (Tor will forward to this)
+ // In production, parse the address to get the port
+ listener, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ return nil, fmt.Errorf("failed to create listener: %w", err)
+ }
+
+ t.listener = listener
+ return listener, nil
+}
+
+// Type returns the transport type
+func (t *TorTransport) Type() transport.TransportType {
+ return transport.TransportTor
+}
+
+// Close closes the Tor transport
+func (t *TorTransport) Close() error {
+ if t.closed {
+ return nil
+ }
+
+ t.closed = true
+
+ if t.listener != nil {
+ if err := t.listener.Close(); err != nil {
+ return fmt.Errorf("failed to close listener: %w", err)
+ }
+ t.listener = nil
+ }
+
+ return nil
+}
+
+// GetSOCKSProxy returns the SOCKS proxy address
+func (t *TorTransport) GetSOCKSProxy() string {
+ return t.socksProxy
+}
+
+// isOnionAddress checks if an address is a valid .onion address
+func isOnionAddress(address string) bool {
+ // Simple validation: check if it contains ".onion"
+ // In production, use regex to validate v3 onion addresses (56 chars base32)
+ return len(address) > 6 && address[len(address)-6:] == ".onion" ||
+ len(address) > 13 && address[len(address)-13:len(address)-7] == ".onion:"
+}
+
+// String returns a string representation
+func (t *TorTransport) String() string {
+ return fmt.Sprintf("TorTransport(proxy=%s, closed=%v)", t.socksProxy, t.closed)
+}