Skip to content

Commit 2e7b7ca

Browse files
authored
add -dynamic-destination flag (path-network#5)
Authored-by: Julien Xfennec <xfennec@cqfd-corp.org>
1 parent 02c6d29 commit 2e7b7ca

File tree

7 files changed

+136
-20
lines changed

7 files changed

+136
-20
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ Usage of ./go-mmproxy:
5757
Path to a file that contains allowed subnets of the proxy servers
5858
-close-after int
5959
Number of seconds after which UDP socket will be cleaned up (default 60)
60+
-dynamic-destination
61+
Traffic will be forwarded to the destination specified in the PROXY protocol header
6062
-l string
6163
Address the proxy listens on (default "0.0.0.0:8443")
6264
-listeners int

main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func init() {
3636
flag.StringVar(&listenAddrStr, "l", "0.0.0.0:8443", "Address the proxy listens on")
3737
flag.StringVar(&targetAddr4Str, "4", "127.0.0.1:443", "Address to which IPv4 traffic will be forwarded to")
3838
flag.StringVar(&targetAddr6Str, "6", "[::1]:443", "Address to which IPv6 traffic will be forwarded to")
39+
flag.BoolVar(&opts.DynamicDestination, "dynamic-destination", false, "Traffic will be forwarded to the destination specified in the PROXY protocol header")
3940
flag.IntVar(&opts.Mark, "mark", 0, "The mark that will be set on outbound packets")
4041
flag.IntVar(&opts.Verbose, "v", 0, `0 - no logging of individual connections
4142
1 - log errors occurring in individual connections
@@ -44,7 +45,7 @@ func init() {
4445
"Path to a file that contains allowed subnets of the proxy servers")
4546
flag.IntVar(&listeners, "listeners", 1,
4647
"Number of listener sockets that will be opened for the listen address (Linux 3.9+)")
47-
flag.IntVar(&udpCloseAfterInt, "close-after", 60, "Number of seconds after which UDP socket will be cleaned up")
48+
flag.IntVar(&udpCloseAfterInt, "close-after", 60, "Number of seconds after which UDP socket will be cleaned up on inactivity")
4849
}
4950

5051
func listen(ctx context.Context, listenerNum int, parentLogger *slog.Logger, listenErrors chan<- error) {

tcp/tcp.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,17 @@ func handleConnection(conn net.Conn, opts *utils.Options, logger *slog.Logger) {
5252
return
5353
}
5454

55-
saddr, _, restBytes, err := proxyprotocol.ReadRemoteAddr(buffer[:n], utils.TCP)
55+
saddr, daddr, restBytes, err := proxyprotocol.ReadRemoteAddr(buffer[:n], utils.TCP)
5656
if err != nil {
5757
logger.Debug("failed to parse PROXY header", "error", err, slog.Bool("dropConnection", true))
5858
return
5959
}
6060

6161
targetAddr := opts.TargetAddr6
6262
if saddr.IsValid() {
63-
if saddr.Addr().Is4() {
63+
if opts.DynamicDestination && daddr.IsValid() {
64+
targetAddr = daddr
65+
} else if saddr.Addr().Is4() {
6466
targetAddr = opts.TargetAddr4
6567
}
6668
} else {

tests/tcp_test.go

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func TestListen(t *testing.T) {
7575
receivedData4 := make(chan listenResult, 1)
7676
go runServer(t, "127.0.0.1:54321", receivedData4)
7777

78-
time.Sleep(1 * time.Second)
78+
time.Sleep(100 * time.Millisecond)
7979

8080
conn, err := net.Dial("tcp", "127.0.0.1:12345")
8181
if err != nil {
@@ -123,7 +123,7 @@ func TestListen_unknown(t *testing.T) {
123123
receivedData4 := make(chan listenResult, 1)
124124
go runServer(t, "127.0.0.1:54322", receivedData4)
125125

126-
time.Sleep(1 * time.Second)
126+
time.Sleep(100 * time.Millisecond)
127127

128128
conn, err := net.Dial("tcp", "127.0.0.1:12346")
129129
if err != nil {
@@ -171,7 +171,7 @@ func TestListen_proxyV2(t *testing.T) {
171171
receivedData4 := make(chan listenResult, 1)
172172
go runServer(t, "127.0.0.1:54323", receivedData4)
173173

174-
time.Sleep(1 * time.Second)
174+
time.Sleep(100 * time.Millisecond)
175175

176176
conn, err := net.Dial("tcp", "127.0.0.1:12347")
177177
if err != nil {
@@ -200,3 +200,52 @@ func TestListen_proxyV2(t *testing.T) {
200200
t.Errorf("Unexpected source address: %v", result.saddr)
201201
}
202202
}
203+
204+
func TestTCPListen_DynamicDestination(t *testing.T) {
205+
opts := utils.Options{
206+
Protocol: utils.TCP,
207+
ListenAddr: netip.MustParseAddrPort("0.0.0.0:12350"),
208+
TargetAddr4: netip.MustParseAddrPort("127.0.0.1:443"),
209+
TargetAddr6: netip.MustParseAddrPort("[::1]:443"),
210+
DynamicDestination: true,
211+
Mark: 0,
212+
AllowedSubnets: nil,
213+
Verbose: 2,
214+
}
215+
216+
lvl := slog.LevelInfo
217+
if opts.Verbose > 0 {
218+
lvl = slog.LevelDebug
219+
}
220+
221+
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: lvl}))
222+
223+
listenConfig := net.ListenConfig{}
224+
errors := make(chan error, 1)
225+
ctx, cancel := context.WithCancel(context.Background())
226+
defer cancel()
227+
228+
go tcp.Listen(ctx, &listenConfig, &opts, logger, errors)
229+
230+
receivedData4 := make(chan listenResult, 1)
231+
go runServer(t, "127.0.0.1:56324", receivedData4)
232+
233+
time.Sleep(100 * time.Millisecond)
234+
235+
conn, err := net.Dial("tcp", "127.0.0.1:12350")
236+
if err != nil {
237+
t.Fatalf("Failed to connect to server: %v", err)
238+
}
239+
defer conn.Close()
240+
241+
conn.Write([]byte("PROXY TCP4 192.168.0.1 127.0.0.1 56324 56324\r\nmoredata"))
242+
result := <-receivedData4
243+
244+
if !reflect.DeepEqual(result.data, []byte("moredata")) {
245+
t.Errorf("Unexpected data: %v", result.data)
246+
}
247+
248+
if result.saddr.String() != "192.168.0.1:56324" {
249+
t.Errorf("Unexpected source address: %v", result.saddr)
250+
}
251+
}

tests/udp_test.go

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func TestListenUDP(t *testing.T) {
6565
receivedData4 := make(chan listenResult, 1)
6666
go runUDPServer(t, "127.0.0.1:54323", receivedData4)
6767

68-
time.Sleep(1 * time.Second)
68+
time.Sleep(100 * time.Millisecond)
6969

7070
conn, err := net.Dial("udp", "127.0.0.1:12347")
7171
if err != nil {
@@ -94,3 +94,62 @@ func TestListenUDP(t *testing.T) {
9494
t.Errorf("Unexpected source address: %v", result.saddr)
9595
}
9696
}
97+
98+
func TestListenUDP_DynamicDestination(t *testing.T) {
99+
opts := utils.Options{
100+
Protocol: utils.UDP,
101+
ListenAddr: netip.MustParseAddrPort("0.0.0.0:12348"),
102+
TargetAddr4: netip.MustParseAddrPort("127.0.0.1:443"),
103+
TargetAddr6: netip.MustParseAddrPort("[::1]:443"),
104+
DynamicDestination: true,
105+
Mark: 0,
106+
AllowedSubnets: nil,
107+
Verbose: 2,
108+
}
109+
110+
lvl := slog.LevelInfo
111+
if opts.Verbose > 0 {
112+
lvl = slog.LevelDebug
113+
}
114+
115+
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: lvl}))
116+
117+
listenConfig := net.ListenConfig{}
118+
errors := make(chan error, 1)
119+
120+
ctx, cancel := context.WithCancel(context.Background())
121+
defer cancel()
122+
go udp.Listen(ctx, &listenConfig, &opts, logger, errors)
123+
124+
receivedData4 := make(chan listenResult, 1)
125+
go runUDPServer(t, "127.0.0.1:56324", receivedData4)
126+
127+
time.Sleep(100 * time.Millisecond)
128+
129+
conn, err := net.Dial("udp", "127.0.0.1:12348")
130+
if err != nil {
131+
t.Fatalf("Failed to connect to server: %v", err)
132+
}
133+
defer conn.Close()
134+
135+
buf := []byte{0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A}
136+
buf = append(buf, 0x21) // PROXY
137+
buf = append(buf, 0x12) // UDP4
138+
buf = append(buf, 0x00, 0x0C) // 12 bytes
139+
buf = append(buf, 192, 168, 0, 1) // saddr
140+
buf = append(buf, 127, 0, 0, 1) // daddr
141+
buf = append(buf, 0xDC, 0x04) // sport 56324
142+
buf = append(buf, 0xDC, 0x04) // sport 56324
143+
buf = append(buf, []byte("moredata")...)
144+
145+
conn.Write(buf)
146+
result := <-receivedData4
147+
148+
if !reflect.DeepEqual(result.data, []byte("moredata")) {
149+
t.Errorf("Unexpected data: %v", result.data)
150+
}
151+
152+
if result.saddr.String() != "192.168.0.1:56324" {
153+
t.Errorf("Unexpected source address: %v", result.saddr)
154+
}
155+
}

udp/udp.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,18 @@ func copyFromUpstream(downstream net.PacketConn, conn *connection) {
8383
}
8484
}
8585

86-
func getSocketFromMap(downstream net.PacketConn, opts *utils.Options, downstreamAddr, saddr netip.AddrPort, logger *slog.Logger,
87-
connMap map[netip.AddrPort]*connection, socketClosures chan<- netip.AddrPort) (*connection, error) {
86+
func getSocketFromMap(downstream net.PacketConn, opts *utils.Options, downstreamAddr, saddr, daddr netip.AddrPort,
87+
logger *slog.Logger, connMap map[netip.AddrPort]*connection, socketClosures chan<- netip.AddrPort) (*connection, error) {
8888
if conn := connMap[saddr]; conn != nil {
8989
atomic.AddInt64(conn.lastActivity, 1)
9090
return conn, nil
9191
}
9292

9393
targetAddr := opts.TargetAddr6
9494
if saddr.IsValid() {
95-
if saddr.Addr().Is4() {
95+
if opts.DynamicDestination && daddr.IsValid() {
96+
targetAddr = daddr
97+
} else if saddr.Addr().Is4() {
9698
targetAddr = opts.TargetAddr4
9799
}
98100
} else {
@@ -162,7 +164,7 @@ func Listen(ctx context.Context, listenConfig *net.ListenConfig, opts *utils.Opt
162164
continue
163165
}
164166

165-
saddr, _, restBytes, err := proxyprotocol.ReadRemoteAddr(buffer[:n], utils.UDP)
167+
saddr, daddr, restBytes, err := proxyprotocol.ReadRemoteAddr(buffer[:n], utils.UDP)
166168
if err != nil {
167169
logger.Debug("failed to parse PROXY header", "error", err, slog.String("remoteAddr", remoteAddr.String()))
168170
continue
@@ -181,7 +183,7 @@ func Listen(ctx context.Context, listenConfig *net.ListenConfig, opts *utils.Opt
181183
}
182184
}
183185

184-
conn, err := getSocketFromMap(ln, opts, remoteAddr, saddr, logger, connectionMap, socketClosures)
186+
conn, err := getSocketFromMap(ln, opts, remoteAddr, saddr, daddr, logger, connectionMap, socketClosures)
185187
if err != nil {
186188
continue
187189
}

utils/utils.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@ const (
2222
)
2323

2424
type Options struct {
25-
Protocol Protocol
26-
ListenAddr netip.AddrPort
27-
TargetAddr4 netip.AddrPort
28-
TargetAddr6 netip.AddrPort
29-
Mark int
30-
Verbose int
31-
AllowedSubnets []netip.Prefix
32-
UDPCloseAfter time.Duration
25+
Protocol Protocol
26+
ListenAddr netip.AddrPort
27+
TargetAddr4 netip.AddrPort
28+
TargetAddr6 netip.AddrPort
29+
DynamicDestination bool
30+
Mark int
31+
Verbose int
32+
AllowedSubnets []netip.Prefix
33+
UDPCloseAfter time.Duration
3334
}
3435

3536
func CheckOriginAllowed(remoteIP netip.Addr, allowedSubnets []netip.Prefix) bool {

0 commit comments

Comments
 (0)