diff --git a/app/internal/socks5/server.go b/app/internal/socks5/server.go index 5fefb45..e451637 100644 --- a/app/internal/socks5/server.go +++ b/app/internal/socks5/server.go @@ -254,6 +254,7 @@ func (s *Server) udpServer(udpConn *net.UDPConn, hyUDP client.HyUDPConn) error { if err != nil { continue } + addr = addr[1:] // Remove the leading length byte d := socks5.NewDatagram(atyp, addr, port, bs) _, _ = udpConn.WriteToUDP(d.Bytes(), clientAddr) } diff --git a/app/internal/socks5/server_test.go b/app/internal/socks5/server_test.go new file mode 100644 index 0000000..5fcb103 --- /dev/null +++ b/app/internal/socks5/server_test.go @@ -0,0 +1,128 @@ +package socks5 + +import ( + "io" + "net" + "os/exec" + "testing" + "time" + + "github.com/apernet/hysteria/core/client" +) + +type mockEchoHyClient struct{} + +func (c *mockEchoHyClient) DialTCP(addr string) (net.Conn, error) { + return &mockEchoTCPConn{ + BufChan: make(chan []byte, 10), + }, nil +} + +func (c *mockEchoHyClient) ListenUDP() (client.HyUDPConn, error) { + return &mockEchoUDPConn{ + BufChan: make(chan mockEchoUDPPacket, 10), + }, nil +} + +func (c *mockEchoHyClient) Close() error { + return nil +} + +type mockEchoTCPConn struct { + BufChan chan []byte +} + +func (c *mockEchoTCPConn) Read(b []byte) (n int, err error) { + buf := <-c.BufChan + if buf == nil { + // EOF + return 0, io.EOF + } + return copy(b, buf), nil +} + +func (c *mockEchoTCPConn) Write(b []byte) (n int, err error) { + c.BufChan <- b + return len(b), nil +} + +func (c *mockEchoTCPConn) Close() error { + close(c.BufChan) + return nil +} + +func (c *mockEchoTCPConn) LocalAddr() net.Addr { + // Not implemented + return nil +} + +func (c *mockEchoTCPConn) RemoteAddr() net.Addr { + // Not implemented + return nil +} + +func (c *mockEchoTCPConn) SetDeadline(t time.Time) error { + // Not implemented + return nil +} + +func (c *mockEchoTCPConn) SetReadDeadline(t time.Time) error { + // Not implemented + return nil +} + +func (c *mockEchoTCPConn) SetWriteDeadline(t time.Time) error { + // Not implemented + return nil +} + +type mockEchoUDPPacket struct { + Data []byte + Addr string +} + +type mockEchoUDPConn struct { + BufChan chan mockEchoUDPPacket +} + +func (c *mockEchoUDPConn) Receive() ([]byte, string, error) { + p := <-c.BufChan + if p.Data == nil { + // EOF + return nil, "", io.EOF + } + return p.Data, p.Addr, nil +} + +func (c *mockEchoUDPConn) Send(bytes []byte, s string) error { + c.BufChan <- mockEchoUDPPacket{ + Data: bytes, + Addr: s, + } + return nil +} + +func (c *mockEchoUDPConn) Close() error { + close(c.BufChan) + return nil +} + +func TestServer(t *testing.T) { + // Start the server + s := &Server{ + HyClient: &mockEchoHyClient{}, + } + l, err := net.Listen("tcp", "127.0.0.1:11080") + if err != nil { + t.Fatal(err) + } + defer l.Close() + go s.Serve(l) + + // Run the Python test script + cmd := exec.Command("python", "server_test.py") + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Failed to run test script: %v\n%s", err, out) + } +} diff --git a/app/internal/socks5/server_test.py b/app/internal/socks5/server_test.py new file mode 100644 index 0000000..4515561 --- /dev/null +++ b/app/internal/socks5/server_test.py @@ -0,0 +1,38 @@ +import socket +import socks +import os + +ADDR = "127.0.0.1" +PORT = 11080 + + +def test_tcp(size, count, it): + for i in range(it): + s = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM) + s.set_proxy(socks.SOCKS5, ADDR, PORT) + + s.connect(("test_tcp", 12345)) + for j in range(count): + payload = os.urandom(size) + s.send(payload) + rsp = s.recv(size) + assert rsp == payload + s.close() + + +def test_udp(size, count, it): + for i in range(it): + s = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM) + s.set_proxy(socks.SOCKS5, ADDR, PORT) + + for j in range(count): + payload = os.urandom(size) + s.sendto(payload, ("test_udp", 12345)) + rsp, addr = s.recvfrom(size) + assert rsp == payload and addr == (b"test_udp", 12345) + s.close() + + +if __name__ == "__main__": + test_tcp(1024, 1024, 10) + test_udp(1024, 1024, 10)