mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 11:11:30 -04:00
The option controls what source address to use when dialing the (Ext)ORPort. Using a source address other than 127.0.0.1, or a range of addresses, can help with localhost ephemeral port exhaustion. https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/issues/40198
159 lines
4 KiB
Go
159 lines
4 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"net"
|
|
"testing"
|
|
)
|
|
|
|
func mustParseCIDR(s string) *net.IPNet {
|
|
_, ipnet, err := net.ParseCIDR(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ipnet
|
|
}
|
|
|
|
func TestRandAddr(t *testing.T) {
|
|
outer:
|
|
for _, ipnet := range []*net.IPNet{
|
|
mustParseCIDR("127.0.0.1/0"),
|
|
mustParseCIDR("127.0.0.1/24"),
|
|
mustParseCIDR("127.0.0.55/32"),
|
|
mustParseCIDR("2001:db8::1234/0"),
|
|
mustParseCIDR("2001:db8::1234/32"),
|
|
mustParseCIDR("2001:db8::1234/128"),
|
|
// Non-canonical masks (that don't consist of 1s followed by 0s)
|
|
// work too, why not.
|
|
&net.IPNet{
|
|
IP: net.IP{1, 2, 3, 4},
|
|
Mask: net.IPMask{0x00, 0x07, 0xff, 0xff},
|
|
},
|
|
} {
|
|
for i := 0; i < 100; i++ {
|
|
ip, err := randIPAddr(ipnet)
|
|
if err != nil {
|
|
t.Errorf("%v returned error %v", ipnet, err)
|
|
continue outer
|
|
}
|
|
if !ipnet.Contains(ip) {
|
|
t.Errorf("%v does not contain %v", ipnet, ip)
|
|
continue outer
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRandAddrUnequalLengths(t *testing.T) {
|
|
for _, ipnet := range []*net.IPNet{
|
|
&net.IPNet{
|
|
IP: net.IP{1, 2, 3, 4},
|
|
Mask: net.CIDRMask(32, 128),
|
|
},
|
|
&net.IPNet{
|
|
IP: net.IP{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
|
Mask: net.CIDRMask(24, 32),
|
|
},
|
|
&net.IPNet{
|
|
IP: net.IP{1, 2, 3, 4},
|
|
Mask: net.IPMask{},
|
|
},
|
|
&net.IPNet{
|
|
IP: net.IP{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
|
Mask: net.IPMask{},
|
|
},
|
|
} {
|
|
_, err := randIPAddr(ipnet)
|
|
if err == nil {
|
|
t.Errorf("%v did not result in error, but should have", ipnet)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRandAddr(b *testing.B) {
|
|
for _, test := range []struct {
|
|
label string
|
|
ipnet net.IPNet
|
|
}{
|
|
{"IPv4/32", net.IPNet{IP: net.IP{127, 0, 0, 1}, Mask: net.CIDRMask(32, 32)}},
|
|
{"IPv4/24", net.IPNet{IP: net.IP{127, 0, 0, 1}, Mask: net.CIDRMask(32, 32)}},
|
|
{"IPv6/64", net.IPNet{
|
|
IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12, 0x34},
|
|
Mask: net.CIDRMask(64, 128),
|
|
}},
|
|
{"IPv6/128", net.IPNet{
|
|
IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12, 0x34},
|
|
Mask: net.CIDRMask(128, 128),
|
|
}},
|
|
} {
|
|
b.Run(test.label, func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := randIPAddr(&test.ipnet)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func ipNetEqual(a, b *net.IPNet) bool {
|
|
if !a.IP.Equal(b.IP) {
|
|
return false
|
|
}
|
|
// Comparing masks for equality is a little tricky because they may be
|
|
// different lengths. For masks in canonical form (those for which
|
|
// Size() returns other than (0, 0)), we consider two masks equal if the
|
|
// numbers of bits *not* covered by the prefix are equal; e.g.
|
|
// (120, 128) is equal to (24, 32), because they both have 8 bits not in
|
|
// the prefix. If either mask is not in canonical form, we require them
|
|
// to be equal as byte arrays (which includes length).
|
|
aOnes, aBits := a.Mask.Size()
|
|
bOnes, bBits := b.Mask.Size()
|
|
if aBits == 0 || bBits == 0 {
|
|
return bytes.Equal(a.Mask, b.Mask)
|
|
} else {
|
|
return aBits-aOnes == bBits-bOnes
|
|
}
|
|
}
|
|
|
|
func TestParseIPCIDR(t *testing.T) {
|
|
// Well-formed inputs.
|
|
for _, test := range []struct {
|
|
input string
|
|
expected *net.IPNet
|
|
}{
|
|
{"127.0.0.123", mustParseCIDR("127.0.0.123/32")},
|
|
{"127.0.0.123/0", mustParseCIDR("127.0.0.123/0")},
|
|
{"127.0.0.123/24", mustParseCIDR("127.0.0.123/24")},
|
|
{"127.0.0.123/32", mustParseCIDR("127.0.0.123/32")},
|
|
{"2001:db8::1234", mustParseCIDR("2001:db8::1234/128")},
|
|
{"2001:db8::1234/0", mustParseCIDR("2001:db8::1234/0")},
|
|
{"2001:db8::1234/32", mustParseCIDR("2001:db8::1234/32")},
|
|
{"2001:db8::1234/128", mustParseCIDR("2001:db8::1234/128")},
|
|
} {
|
|
ipnet, err := parseIPCIDR(test.input)
|
|
if err != nil {
|
|
t.Errorf("%q returned error %v", test.input, err)
|
|
continue
|
|
}
|
|
if !ipNetEqual(ipnet, test.expected) {
|
|
t.Errorf("%q → %v, expected %v", test.input, ipnet, test.expected)
|
|
}
|
|
}
|
|
|
|
// Bad inputs.
|
|
for _, input := range []string{
|
|
"",
|
|
"1.2.3",
|
|
"1.2.3/16",
|
|
"2001:db8:1234",
|
|
"2001:db8:1234/64",
|
|
"localhost",
|
|
} {
|
|
_, err := parseIPCIDR(input)
|
|
if err == nil {
|
|
t.Errorf("%q did not result in error, but should have", input)
|
|
}
|
|
}
|
|
}
|