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  }