github.com/mholt/caddy-l4@v0.0.0-20241104153248-ec8fae209322/modules/l4socks/socks4_matcher_test.go (about) 1 package l4socks 2 3 import ( 4 "context" 5 "io" 6 "net" 7 "testing" 8 9 "github.com/caddyserver/caddy/v2" 10 "go.uber.org/zap" 11 12 "github.com/mholt/caddy-l4/layer4" 13 ) 14 15 func assertNoError(t *testing.T, err error) { 16 t.Helper() 17 if err != nil { 18 t.Fatalf("Unexpected error: %s\n", err) 19 } 20 } 21 22 func TestSocks4Matcher_Match(t *testing.T) { 23 var curlSocks4Example1 = []byte{0x04, 0x01, 0x00, 0x50, 0x5d, 0xb8, 0xd8, 0x22, 0x00} 24 var curlSocks4Example2 = []byte{0x04, 0x01, 0x01, 0xbb, 0xa5, 0xe3, 0x14, 0xcf, 0x00} 25 26 var curlSocks4aExample1 = []byte{0x04, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, 0x00, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x00} 27 var curlSocks4aExample2 = []byte{0x04, 0x01, 0x01, 0xbb, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x61, 0x64, 0x64, 0x79, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x00} 28 29 type test struct { 30 matcher *Socks4Matcher 31 data []byte 32 shouldMatch bool 33 } 34 35 tests := []test{ 36 // match with defaults 37 {matcher: &Socks4Matcher{}, data: curlSocks4Example1, shouldMatch: true}, 38 {matcher: &Socks4Matcher{}, data: curlSocks4aExample1, shouldMatch: true}, 39 {matcher: &Socks4Matcher{}, data: curlSocks4Example2, shouldMatch: true}, 40 {matcher: &Socks4Matcher{}, data: curlSocks4aExample2, shouldMatch: true}, 41 {matcher: &Socks4Matcher{}, data: []byte("Hello World"), shouldMatch: false}, 42 43 // match only BIND 44 {matcher: &Socks4Matcher{Commands: []string{"BIND"}}, data: curlSocks4Example1, shouldMatch: false}, 45 {matcher: &Socks4Matcher{Commands: []string{"BIND"}}, data: curlSocks4aExample1, shouldMatch: false}, 46 47 // match destination ip 48 {matcher: &Socks4Matcher{Networks: []string{"127.0.0.1"}}, data: curlSocks4Example1, shouldMatch: false}, 49 {matcher: &Socks4Matcher{Networks: []string{"127.0.0.1"}}, data: curlSocks4Example2, shouldMatch: false}, 50 51 {matcher: &Socks4Matcher{Networks: []string{"165.227.0.0/8"}}, data: curlSocks4Example1, shouldMatch: false}, 52 {matcher: &Socks4Matcher{Networks: []string{"165.227.0.0/8"}}, data: curlSocks4Example2, shouldMatch: true}, 53 54 {matcher: &Socks4Matcher{Networks: []string{"165.227.0.0/8", "::1"}}, data: curlSocks4Example1, shouldMatch: false}, 55 {matcher: &Socks4Matcher{Networks: []string{"165.227.0.0/8", "::1"}}, data: curlSocks4Example2, shouldMatch: true}, 56 57 {matcher: &Socks4Matcher{Networks: []string{"127.0.0.1"}}, data: curlSocks4aExample1, shouldMatch: false}, 58 {matcher: &Socks4Matcher{Networks: []string{"0.0.0.0/0"}}, data: curlSocks4aExample2, shouldMatch: true}, 59 60 // match destination port 61 {matcher: &Socks4Matcher{Ports: []uint16{80, 1234}}, data: curlSocks4Example1, shouldMatch: true}, 62 {matcher: &Socks4Matcher{Ports: []uint16{80, 1234}}, data: curlSocks4Example2, shouldMatch: false}, 63 {matcher: &Socks4Matcher{Ports: []uint16{80, 1234}}, data: curlSocks4aExample1, shouldMatch: true}, 64 {matcher: &Socks4Matcher{Ports: []uint16{80, 1234}}, data: curlSocks4aExample2, shouldMatch: false}, 65 } 66 67 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()}) 68 defer cancel() 69 70 for i, tc := range tests { 71 func() { 72 err := tc.matcher.Provision(ctx) 73 assertNoError(t, err) 74 75 in, out := net.Pipe() 76 defer func() { 77 _, _ = io.Copy(io.Discard, out) 78 _ = out.Close() 79 }() 80 81 cx := layer4.WrapConnection(out, []byte{}, zap.NewNop()) 82 go func() { 83 _, err := in.Write(tc.data) 84 assertNoError(t, err) 85 _ = in.Close() 86 }() 87 88 matched, err := tc.matcher.Match(cx) 89 assertNoError(t, err) 90 91 if matched != tc.shouldMatch { 92 if tc.shouldMatch { 93 t.Fatalf("test %d: matcher did not match | %+v\n", i, tc.matcher) 94 } else { 95 t.Fatalf("test %d: matcher should not match | %+v\n", i, tc.matcher) 96 } 97 } 98 }() 99 } 100 } 101 102 func TestSocks4Matcher_InvalidCommand(t *testing.T) { 103 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()}) 104 defer cancel() 105 106 handler := &Socks4Matcher{Commands: []string{"Foo"}} 107 err := handler.Provision(ctx) 108 109 if err == nil || err.Error() != "unknown command \"Foo\" has to be one of [\"CONNECT\", \"BIND\"]" { 110 t.Fatalf("Wrong error: %v\n", err) 111 } 112 }