-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathforwarder.go
More file actions
123 lines (103 loc) · 2.96 KB
/
forwarder.go
File metadata and controls
123 lines (103 loc) · 2.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package main
import (
"context"
"fmt"
"io"
"net"
"sync"
)
type PortForwarder struct {
tunnel *Tunnel
msgChan <-chan IPCMessage
listeners map[int]net.Listener
mutex sync.RWMutex
}
func NewPortForwarder(tunnel *Tunnel, msgChan <-chan IPCMessage) *PortForwarder {
return &PortForwarder{
tunnel: tunnel,
msgChan: msgChan,
listeners: make(map[int]net.Listener),
}
}
func (pf *PortForwarder) Run(ctx context.Context) {
for {
select {
case <-ctx.Done():
pf.closeAllListeners()
return
case msg := <-pf.msgChan:
if msg.Type == "BIND" {
if err := pf.handleBind(msg.Port); err != nil {
logger.Errorf("Failed to handle bind for port %d: %v", msg.Port, err)
}
}
}
}
}
func (pf *PortForwarder) handleBind(port int) error {
pf.mutex.Lock()
defer pf.mutex.Unlock()
// Check if we're already listening on this port
if _, exists := pf.listeners[port]; exists {
return nil // Already listening
}
// Create a listener on the WireGuard IP
// For now, listen on all interfaces since we don't have a proper WireGuard interface
// In a full implementation, this would listen specifically on the WireGuard IP
wgIP := pf.tunnel.ourIP.String()
listenAddr := fmt.Sprintf("%s:%d", wgIP, port)
logger.Debugf("Port forwarder: attempting to listen on %s", listenAddr)
// Try to listen on the WireGuard IP - this will likely fail without a real interface
// but it demonstrates the correct approach
listener, err := net.Listen("tcp", listenAddr)
if err != nil {
// Fallback: listen on localhost for testing
logger.Debugf("Port forwarder: failed to listen on WireGuard IP (%v), falling back to localhost", err)
listener, err = net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
return fmt.Errorf("failed to create port forwarder listener: %w", err)
}
logger.Infof("Port forwarder: listening on 127.0.0.1:%d (fallback)", port)
} else {
logger.Infof("Port forwarder: successfully listening on %s", listenAddr)
}
pf.listeners[port] = listener
// Start accepting connections in background
go pf.acceptConnections(listener, port)
return nil
}
func (pf *PortForwarder) acceptConnections(listener net.Listener, port int) {
for {
conn, err := listener.Accept()
if err != nil {
// Listener was closed
break
}
// Handle connection in background
go pf.handleConnection(conn, port)
}
}
func (pf *PortForwarder) handleConnection(wgConn net.Conn, port int) {
defer wgConn.Close()
// Connect to localhost port
localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
logger.Errorf("Failed to connect to localhost:%d: %v", port, err)
return
}
defer localConn.Close()
// Relay data bidirectionally
go func() {
io.Copy(localConn, wgConn)
localConn.Close()
}()
io.Copy(wgConn, localConn)
}
func (pf *PortForwarder) closeAllListeners() {
pf.mutex.Lock()
defer pf.mutex.Unlock()
for port, listener := range pf.listeners {
listener.Close()
delete(pf.listeners, port)
}
}