github.com/soulteary/pocket-bookcase@v0.0.0-20240428065142-0b5a9a0fc98a/internal/webserver/utils_ip_test.go (about) 1 package webserver 2 3 import ( 4 "fmt" 5 "math/rand" 6 "net" 7 "net/http" 8 "net/http/httptest" 9 "strings" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 ) 14 15 func TestParseCidr(t *testing.T) { 16 res := parseCIDR("192.168.0.0/16", "internal 192.168.x.x") 17 assert.Equal(t, res.IP, net.IP([]byte{192, 168, 0, 0})) 18 assert.Equal(t, res.Mask, net.IPMask([]byte{255, 255, 0, 0})) 19 } 20 21 func TestParseCidrInvalidAddr(t *testing.T) { 22 assert.Panics(t, func() { parseCIDR("192.168.0.0/34", "internal 192.168.x.x") }) 23 } 24 25 func TestIsPrivateIP(t *testing.T) { 26 assert.True(t, IsPrivateIP(net.ParseIP("127.0.0.1")), "should be private") 27 assert.True(t, IsPrivateIP(net.ParseIP("192.168.254.254")), "should be private") 28 assert.True(t, IsPrivateIP(net.ParseIP("10.255.0.3")), "should be private") 29 assert.True(t, IsPrivateIP(net.ParseIP("172.16.255.255")), "should be private") 30 assert.True(t, IsPrivateIP(net.ParseIP("172.31.255.255")), "should be private") 31 assert.True(t, !IsPrivateIP(net.ParseIP("128.0.0.1")), "should be private") 32 assert.True(t, !IsPrivateIP(net.ParseIP("192.169.255.255")), "should not be private") 33 assert.True(t, !IsPrivateIP(net.ParseIP("9.255.0.255")), "should not be private") 34 assert.True(t, !IsPrivateIP(net.ParseIP("172.32.255.255")), "should not be private") 35 36 assert.True(t, IsPrivateIP(net.ParseIP("::0")), "should be private") 37 assert.True(t, IsPrivateIP(net.ParseIP("::1")), "should be private") 38 assert.True(t, !IsPrivateIP(net.ParseIP("::2")), "should not be private") 39 40 assert.True(t, IsPrivateIP(net.ParseIP("fe80::1")), "should be private") 41 assert.True(t, IsPrivateIP(net.ParseIP("febf::1")), "should be private") 42 assert.True(t, !IsPrivateIP(net.ParseIP("fec0::1")), "should not be private") 43 assert.True(t, !IsPrivateIP(net.ParseIP("feff::1")), "should not be private") 44 45 assert.True(t, IsPrivateIP(net.ParseIP("ff00::1")), "should be private") 46 assert.True(t, IsPrivateIP(net.ParseIP("ff10::1")), "should be private") 47 assert.True(t, IsPrivateIP(net.ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), "should be private") 48 49 assert.True(t, IsPrivateIP(net.ParseIP("2002::")), "should be private") 50 assert.True(t, IsPrivateIP(net.ParseIP("2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), "should be private") 51 assert.True(t, IsPrivateIP(net.ParseIP("0100::")), "should be private") 52 assert.True(t, IsPrivateIP(net.ParseIP("0100::0000:ffff:ffff:ffff:ffff")), "should be private") 53 assert.True(t, !IsPrivateIP(net.ParseIP("0100::0001:0000:0000:0000:0000")), "should be private") 54 } 55 56 func TestIsIpValidAndPublic(t *testing.T) { 57 // test empty address 58 assert.False(t, IsIPValidAndPublic("")) 59 // test public address 60 assert.True(t, IsIPValidAndPublic("31.41.244.124")) 61 assert.True(t, IsIPValidAndPublic("62.233.50.248")) 62 // trim head or tail space 63 assert.True(t, IsIPValidAndPublic(" 62.233.50.249")) 64 assert.True(t, IsIPValidAndPublic(" 62.233.50.250 ")) 65 assert.True(t, IsIPValidAndPublic("62.233.50.251 ")) 66 // test private address 67 assert.False(t, IsIPValidAndPublic("10.1.123.52")) 68 assert.False(t, IsIPValidAndPublic("192.168.123.24")) 69 assert.False(t, IsIPValidAndPublic("172.17.0.1")) 70 } 71 72 func BenchmarkIsPrivateIPv4(b *testing.B) { 73 // range: 2-254 74 n1 := 2 + rand.Intn(252) 75 n2 := 2 + rand.Intn(252) 76 for i := 0; i < b.N; i++ { 77 IsPrivateIP(net.ParseIP(fmt.Sprintf("192.168.%d.%d", n1, n2))) 78 } 79 } 80 81 func BenchmarkIsPrivateIPv6(b *testing.B) { 82 n1 := 2 + rand.Intn(252) 83 for i := 0; i < b.N; i++ { 84 IsPrivateIP(net.ParseIP(fmt.Sprintf("2002::%d", n1))) 85 } 86 } 87 88 func testIsPublicHttpRequestAddressHelper( 89 t *testing.T, wantIP string, headers map[string]string, isPublic bool, 90 ) { 91 testIsPublicHttpRequestAddressHelperWrapped(t, nil, wantIP, headers, isPublic) 92 } 93 94 func testIsPublicHttpRequestAddressHelperWrapped( 95 t *testing.T, r *http.Request, wantIP string, headers map[string]string, isPublic bool, 96 ) { 97 var ( 98 err error 99 userIP string 100 ) 101 if r == nil { 102 r = httptest.NewRequest("GET", "/", nil) 103 } 104 for k, v := range headers { 105 r.Header.Set(k, v) 106 } 107 108 origVal := GetUserRealIP(r) 109 if strings.Contains(origVal, ":") { 110 userIP, _, err = net.SplitHostPort(origVal) 111 if err != nil { 112 t.Error(err) 113 } 114 } else { 115 userIP = origVal 116 } 117 118 if isPublic { 119 // should equal first ip in list 120 assert.Equal(t, wantIP, userIP) 121 assert.True(t, IsIPValidAndPublic(userIP)) 122 } else { 123 assert.Equal(t, origVal, r.RemoteAddr) 124 assert.False(t, IsIPValidAndPublic(userIP)) 125 } 126 } 127 128 func TestGetUserRealIPWithSetRemoteAddr(t *testing.T) { 129 // Test Public RemoteAddr 130 testIsPublicHttpRequestAddressHelper(t, "", nil, false) 131 132 r := httptest.NewRequest("GET", "/", nil) 133 wantIP := "34.23.123.122" 134 r.RemoteAddr = fmt.Sprintf("%s:1234", wantIP) 135 testIsPublicHttpRequestAddressHelperWrapped(t, r, wantIP, nil, true) 136 } 137 138 func TestGetUserRealIPWithInvalidRemoteAddr(t *testing.T) { 139 // Test Public RemoteAddr 140 testIsPublicHttpRequestAddressHelper(t, "", nil, false) 141 142 r := httptest.NewRequest("GET", "/", nil) 143 wantIP := "34.23.123.122" 144 // without port 145 r.RemoteAddr = wantIP 146 testIsPublicHttpRequestAddressHelperWrapped(t, r, wantIP, nil, true) 147 } 148 149 func TestGetUserRealIPWithEmptyHeader(t *testing.T) { 150 // Test Empty X-Real-IP 151 testIsPublicHttpRequestAddressHelper(t, "", nil, false) 152 } 153 154 func TestGetUserRealIPWithInvalidHeaderValue(t *testing.T) { 155 for _, name := range userRealIpHeaderCandidates { 156 // invalid ip 157 m := map[string]string{ 158 name: "31.41.24a.12", 159 } 160 testIsPublicHttpRequestAddressHelper(t, "", m, false) 161 } 162 } 163 164 func TestGetUserRealIPWithXRealIpHeader(t *testing.T) { 165 // Test public Real IP 166 for _, name := range userRealIpHeaderCandidates { 167 wantIP := "31.41.242.12" 168 m := map[string]string{ 169 name: wantIP, 170 } 171 testIsPublicHttpRequestAddressHelper(t, wantIP, m, true) 172 } 173 } 174 175 func TestGetUserRealIPWithPrivateXRealIpHeader(t *testing.T) { 176 for _, name := range userRealIpHeaderCandidates { 177 wantIP := "192.168.123.123" 178 // test private ip in header 179 m := map[string]string{ 180 name: wantIP, 181 } 182 testIsPublicHttpRequestAddressHelper(t, wantIP, m, false) 183 } 184 } 185 186 func TestGetUserRealIPWithXRealIpListHeader(t *testing.T) { 187 // Test Real IP List 188 for _, name := range userRealIpHeaderCandidates { 189 ipList := []string{"34.23.123.122", "34.23.123.123"} 190 // should equal first ip in list 191 wantIP := ipList[0] 192 // test private ip in header 193 m := map[string]string{ 194 name: strings.Join(ipList, ", "), 195 } 196 testIsPublicHttpRequestAddressHelper(t, wantIP, m, true) 197 } 198 } 199 200 func TestGetUserRealIPWithXRealIpHeaderIgnoreComma(t *testing.T) { 201 // Test Real IP List with leading or tailing comma 202 wantIP := "34.23.123.124" 203 ipVariants := []string{ 204 ",34.23.123.124", " ,34.23.123.124", "\t,34.23.123.124", 205 ",34.23.123.124,", " ,34.23.123.124, ", "\t,34.23.123.124,\t", 206 "34.23.123.124,", "34.23.123.124, ", "34.23.123.124,\t"} 207 for _, variant := range ipVariants { 208 for _, name := range userRealIpHeaderCandidates { 209 m := map[string]string{name: variant} 210 testIsPublicHttpRequestAddressHelper(t, wantIP, m, true) 211 } 212 } 213 } 214 215 func TestGetUserRealIPWithDifferentHeaderOrder(t *testing.T) { 216 var m map[string]string 217 wantIP := "34.23.123.124" 218 m = map[string]string{ 219 "X-Real-Ip": "192.168.123.122", 220 "X-Forwarded-For": wantIP, 221 } 222 223 testIsPublicHttpRequestAddressHelper(t, wantIP, m, true) 224 m = map[string]string{ 225 "X-Real-Ip": wantIP, 226 "X-Forwarded-For": "192.168.123.122", 227 } 228 testIsPublicHttpRequestAddressHelper(t, wantIP, m, true) 229 }