mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-14 05:11:19 -04:00
Add a orport-srcaddr
server transport option.
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
This commit is contained in:
parent
9d72b30603
commit
0780f2e809
6 changed files with 260 additions and 11 deletions
159
server/randaddr_test.go
Normal file
159
server/randaddr_test.go
Normal file
|
@ -0,0 +1,159 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue