diff --git a/dht_test.go b/dht_test.go index bd5cf7b362..12adc38b0c 100644 --- a/dht_test.go +++ b/dht_test.go @@ -28,7 +28,7 @@ func TestSetNilBigInt(t *testing.T) { } func TestMarshalCompactNodeInfo(t *testing.T) { - cni := krpc.CompactIPv4NodeInfo{krpc.NodeInfo{ + cni := krpc.CompactNodeInfo{krpc.NodeInfo{ ID: [20]byte{'a', 'b', 'c'}, }} addr, err := net.ResolveUDPAddr("udp4", "1.2.3.4:5") diff --git a/krpc/CompactIPv4NodeAddrs.go b/krpc/CompactIPv4NodeAddrs.go deleted file mode 100644 index 73d856e52a..0000000000 --- a/krpc/CompactIPv4NodeAddrs.go +++ /dev/null @@ -1,36 +0,0 @@ -package krpc - -import "github.com/anacrolix/missinggo/slices" - -type CompactIPv4NodeAddrs []NodeAddr - -func (CompactIPv4NodeAddrs) ElemSize() int { return 6 } - -func (me CompactIPv4NodeAddrs) MarshalBinary() ([]byte, error) { - return marshalBinarySlice(slices.Map(func(addr NodeAddr) NodeAddr { - if a:= addr.IP.To4(); a != nil { - addr.IP = a - } - return addr - }, me).(CompactIPv4NodeAddrs)) -} - -func (me CompactIPv4NodeAddrs) MarshalBencode() ([]byte, error) { - return bencodeBytesResult(me.MarshalBinary()) -} - -func (me *CompactIPv4NodeAddrs) UnmarshalBinary(b []byte) error { - return unmarshalBinarySlice(me, b) -} - -func (me *CompactIPv4NodeAddrs) UnmarshalBencode(b []byte) error { - return unmarshalBencodedBinary(me, b) -} - -func (me CompactIPv4NodeAddrs) NodeAddrs() []NodeAddr { - return me -} - -func (me CompactIPv4NodeAddrs) Index(x NodeAddr) int { - return addrIndex(me, x) -} diff --git a/krpc/CompactIPv6NodeAddrs.go b/krpc/CompactIPv6NodeAddrs.go index 6fc7c65982..b4b3523ccb 100644 --- a/krpc/CompactIPv6NodeAddrs.go +++ b/krpc/CompactIPv6NodeAddrs.go @@ -7,9 +7,8 @@ type CompactIPv6NodeAddrs []NodeAddr func (CompactIPv6NodeAddrs) ElemSize() int { return 18 } func (me CompactIPv6NodeAddrs) MarshalBinary() ([]byte, error) { - return marshalBinarySlice(slices.Map(func(na NodeAddr) NodeAddr { - na.IP = na.IP.To16() - return na + return marshalBinarySlice(slices.Map(func(addr NodeAddr) NodeAddr { + return addr.Compacted() }, me).(CompactIPv6NodeAddrs)) } diff --git a/krpc/CompactNodeAddrs.go b/krpc/CompactNodeAddrs.go new file mode 100644 index 0000000000..310ec55487 --- /dev/null +++ b/krpc/CompactNodeAddrs.go @@ -0,0 +1,40 @@ +package krpc + +import "github.com/anacrolix/missinggo/slices" + +const ( + ipv4AddrSize = 6 + i2pAddrSize = 34 +) + +var nodeAddrNumBytes = ipv4AddrSize + +type CompactNodeAddrs []NodeAddr + +func (CompactNodeAddrs) ElemSize() int { return nodeAddrNumBytes } + +func (me CompactNodeAddrs) MarshalBinary() ([]byte, error) { + return marshalBinarySlice(slices.Map(func(addr NodeAddr) NodeAddr { + return addr.Compacted() + }, me).(CompactNodeAddrs)) +} + +func (me CompactNodeAddrs) MarshalBencode() ([]byte, error) { + return bencodeBytesResult(me.MarshalBinary()) +} + +func (me *CompactNodeAddrs) UnmarshalBinary(b []byte) error { + return unmarshalBinarySlice(me, b) +} + +func (me *CompactNodeAddrs) UnmarshalBencode(b []byte) error { + return unmarshalBencodedBinary(me, b) +} + +func (me CompactNodeAddrs) NodeAddrs() []NodeAddr { + return me +} + +func (me CompactNodeAddrs) Index(x NodeAddr) int { + return addrIndex(me, x) +} diff --git a/krpc/CompactIPv4NodeInfo.go b/krpc/CompactNodeInfo.go similarity index 53% rename from krpc/CompactIPv4NodeInfo.go rename to krpc/CompactNodeInfo.go index e829ff34aa..3716258ec0 100644 --- a/krpc/CompactIPv4NodeInfo.go +++ b/krpc/CompactNodeInfo.go @@ -3,11 +3,11 @@ package krpc import "github.com/anacrolix/missinggo/slices" type ( - CompactIPv4NodeInfo []NodeInfo + CompactNodeInfo []NodeInfo ) -func (CompactIPv4NodeInfo) ElemSize() int { - return 26 +func (CompactNodeInfo) ElemSize() int { + return 20 + nodeAddrNumBytes } // func (me *CompactIPv4NodeInfo) Scrub() { @@ -17,21 +17,21 @@ func (CompactIPv4NodeInfo) ElemSize() int { // }) // } -func (me CompactIPv4NodeInfo) MarshalBinary() ([]byte, error) { +func (me CompactNodeInfo) MarshalBinary() ([]byte, error) { return marshalBinarySlice(slices.Map(func(ni NodeInfo) NodeInfo { - ni.Addr.IP = ni.Addr.IP.To4() + ni.Addr = ni.Addr.Compacted() return ni - }, me).(CompactIPv4NodeInfo)) + }, me).(CompactNodeInfo)) } -func (me CompactIPv4NodeInfo) MarshalBencode() ([]byte, error) { +func (me CompactNodeInfo) MarshalBencode() ([]byte, error) { return bencodeBytesResult(me.MarshalBinary()) } -func (me *CompactIPv4NodeInfo) UnmarshalBinary(b []byte) error { +func (me *CompactNodeInfo) UnmarshalBinary(b []byte) error { return unmarshalBinarySlice(me, b) } -func (me *CompactIPv4NodeInfo) UnmarshalBencode(b []byte) error { +func (me *CompactNodeInfo) UnmarshalBencode(b []byte) error { return unmarshalBencodedBinary(me, b) } diff --git a/krpc/compact_test.go b/krpc/compact_test.go index 7ed995ddce..8e0d5487ee 100644 --- a/krpc/compact_test.go +++ b/krpc/compact_test.go @@ -9,47 +9,74 @@ import ( ) func TestUnmarshalSlice(t *testing.T) { - var data CompactIPv4NodeInfo + var data CompactNodeInfo err := data.UnmarshalBencode([]byte("52:" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x03\x04\x05\x06\x07")) require.NoError(t, err) require.Len(t, data, 2) - assert.Equal(t, "1.2.3.4", data[0].Addr.IP.String()) - assert.Equal(t, "2.3.4.5", data[1].Addr.IP.String()) + assert.Equal(t, "1.2.3.4", data[0].Addr.Host()) + assert.Equal(t, "2.3.4.5", data[1].Addr.Host()) } +func TestUnmarshalSliceI2P(t *testing.T) { + SetNetworkType(I2PNet) + defer SetNetworkType(IPNet) + + nodeId := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + port := "\x01\x01" + + dest1 := makeTestAddr32(dest32_3) + dest2 := makeTestAddr32(dest32_1) + + var data CompactNodeInfo + err := data.UnmarshalBencode([]byte("108:" + + nodeId + string(dest1) + port + + nodeId + string(dest2) + port)) + + require.NoError(t, err) + require.Len(t, data, 2) + assert.Equal(t, dest32_3+suffixb32, data[0].Addr.Host()) + assert.Equal(t, dest32_1+suffixb32, data[1].Addr.Host()) +} + +var testNodeIPAddr1 = NewNodeIPAddr(IPv4(172, 16, 1, 1), 11) +var testNodeIPAddr2 = NewNodeIPAddr(IPv4(192, 168, 0, 3), 11) +var testNodeIPAddr3 = NewNodeIPAddr(IPv4(127, 0, 0, 1), 11) + var nodeAddrIndexTests4 = []struct { - v CompactIPv4NodeAddrs + v CompactNodeAddrs a NodeAddr out int }{ - {[]NodeAddr{{IPv4(172, 16, 1, 1), 11}, {IPv4(192, 168, 0, 3), 11}}, NodeAddr{IPv4(172, 16, 1, 1), 11}, 0}, - {[]NodeAddr{{IPv4(172, 16, 1, 1), 11}, {IPv4(192, 168, 0, 3), 11}}, NodeAddr{IPv4(192, 168, 0, 3), 11}, 1}, - {[]NodeAddr{{IPv4(172, 16, 1, 1), 11}, {IPv4(192, 168, 0, 3), 11}}, NodeAddr{IPv4(127, 0, 0, 1), 11}, -1}, - {[]NodeAddr{}, NodeAddr{IPv4(127, 0, 0, 1), 11}, -1}, - {[]NodeAddr{}, NodeAddr{}, -1}, + {[]NodeAddr{testNodeIPAddr1, testNodeIPAddr2}, testNodeIPAddr1, 0}, + {[]NodeAddr{testNodeIPAddr1, testNodeIPAddr2}, testNodeIPAddr2, 1}, + {[]NodeAddr{testNodeIPAddr1, testNodeIPAddr2}, testNodeIPAddr3, -1}, + {[]NodeAddr{}, testNodeIPAddr3, -1}, } func TestNodeAddrIndex4(t *testing.T) { for _, tc := range nodeAddrIndexTests4 { out := tc.v.Index(tc.a) if out != tc.out { - t.Errorf("CompactIPv4NodeAddrs(%v).Index(%v) = %v, want %v", tc.v, tc.a, out, tc.out) + t.Errorf("IPv4:CompactNodeAddrs(%v).Index(%v) = %v, want %v", tc.v, tc.a, out, tc.out) } } } +var testNodeIPv6Addr1 = NewNodeIPAddr(ParseIP("2001::1"), 11) +var testNodeIPv6Addr2 = NewNodeIPAddr(ParseIP("4004::1"), 11) +var testNodeIPv6Addr3 = NewNodeIPAddr(ParseIP("::1"), 11) + var nodeAddrIndexTests6 = []struct { v CompactIPv6NodeAddrs a NodeAddr out int }{ - {[]NodeAddr{{ParseIP("2001::1"), 11}, {ParseIP("4004::1"), 11}}, NodeAddr{ParseIP("2001::1"), 11}, 0}, - {[]NodeAddr{{ParseIP("2001::1"), 11}, {ParseIP("4004::1"), 11}}, NodeAddr{ParseIP("4004::1"), 11}, 1}, - {[]NodeAddr{{ParseIP("2001::1"), 11}, {ParseIP("4004::1"), 11}}, NodeAddr{ParseIP("::1"), 11}, -1}, - {[]NodeAddr{}, NodeAddr{ParseIP("::1"), 11}, -1}, - {[]NodeAddr{}, NodeAddr{}, -1}, + {[]NodeAddr{testNodeIPv6Addr1, testNodeIPv6Addr2}, testNodeIPv6Addr1, 0}, + {[]NodeAddr{testNodeIPv6Addr1, testNodeIPv6Addr2}, testNodeIPv6Addr2, 1}, + {[]NodeAddr{testNodeIPv6Addr1, testNodeIPv6Addr2}, testNodeIPv6Addr3, -1}, + {[]NodeAddr{}, testNodeIPv6Addr3, -1}, } func TestNodeAddrIndex6(t *testing.T) { @@ -61,22 +88,94 @@ func TestNodeAddrIndex6(t *testing.T) { } } +var testNodeI2PAddr1 = NewNodeI2PAddr(makeTestAddr32(dest32_1), 2) +var testNodeI2PAddr2 = NewNodeI2PAddr(makeTestAddr64(dest64_2), 2) +var testNodeI2PAddr3 = NewNodeI2PAddr(makeTestAddr32(dest32_3), 2) +var testNodeI2PAddr4 = NewNodeI2PAddr(makeTestAddr64(dest64_3), 2) + +var nodeAddrIndexTestsI2P = []struct { + v CompactNodeAddrs + a NodeAddr + out int +}{ + {[]NodeAddr{testNodeI2PAddr1, testNodeI2PAddr2}, testNodeI2PAddr1, 0}, + {[]NodeAddr{testNodeI2PAddr2, testNodeI2PAddr3}, testNodeI2PAddr3, 1}, + {[]NodeAddr{testNodeI2PAddr1, testNodeI2PAddr2}, testNodeI2PAddr3, -1}, +} + +func TestNodeAddrIndexI2P(t *testing.T) { + for _, tc := range nodeAddrIndexTestsI2P { + out := tc.v.Index(tc.a) + if out != tc.out { + t.Errorf("I2P:CompactNodeAddrs(%v).Index(%v) = %v, want %v", tc.v, tc.a, out, tc.out) + } + } +} + +func i2pNodeAddrBytes(addr NodeAddr) []byte { + var addrBytes []byte = addr.I2PAddress + portBytes := []byte{0, 2} + + return append(addrBytes, portBytes...) +} + +func TestMarshalI2PCompactNodeAddrs(t *testing.T) { + SetNetworkType(I2PNet) + defer SetNetworkType(IPNet) + + testNodeI2PAddr3Bytes := i2pNodeAddrBytes(testNodeI2PAddr3) + + testNodeI2PAddr2Compacted := testNodeI2PAddr2.Compacted() + testNodeI2PAddr2CompactedBytes := i2pNodeAddrBytes(testNodeI2PAddr2Compacted) + + var marshalI2PSliceTests = []struct { + in CompactNodeAddrs + out []byte + panics bool + }{ + {[]NodeAddr{testNodeI2PAddr1}, i2pNodeAddrBytes(testNodeI2PAddr1), false}, + {[]NodeAddr{testNodeI2PAddr3}, testNodeI2PAddr3Bytes, false}, + {[]NodeAddr{testNodeI2PAddr2}, testNodeI2PAddr2CompactedBytes, false}, + {[]NodeAddr{testNodeI2PAddr3, testNodeI2PAddr2}, + append(testNodeI2PAddr3Bytes, testNodeI2PAddr2CompactedBytes...), + false}, + {[]NodeAddr{NewNodeI2PAddr(nil, 0)}, nil, true}, + } + + for _, tc := range marshalI2PSliceTests { + runFunc := assert.NotPanics + if tc.panics { + runFunc = assert.Panics + } + runFunc(t, func() { + out, err := tc.in.MarshalBinary() + require.NoError(t, err) + assert.Equal(t, tc.out, out, "for input %v, %v", tc.in) + }) + } +} + +var testMarshalIPv4Addr1 = NewNodeIPAddr(net.IP{172, 16, 1, 1}, 3) +var testMarshalIPv4Addr2 = NewNodeIPAddr(net.IPv4(172, 16, 1, 1), 4) +var testMarshalIPv4Addr3 = NewNodeIPAddr(net.IPv4(172, 16, 1, 1), 5) +var testMarshalIPv4Addr4 = NewNodeIPAddr(net.IPv4(192, 168, 0, 3), 6) + var marshalIPv4SliceTests = []struct { - in CompactIPv4NodeAddrs + in CompactNodeAddrs out []byte panics bool }{ - {[]NodeAddr{{net.IP{172, 16, 1, 1}, 3}}, []byte{172, 16, 1, 1, 0, 3}, false}, - {[]NodeAddr{{net.IPv4(172, 16, 1, 1), 4}}, []byte{172, 16, 1, 1, 0, 4}, false}, - {[]NodeAddr{{net.IPv4(172, 16, 1, 1), 5}, {net.IPv4(192, 168, 0, 3), 6}}, []byte{ + {[]NodeAddr{testMarshalIPv4Addr1}, []byte{172, 16, 1, 1, 0, 3}, false}, + {[]NodeAddr{testMarshalIPv4Addr2}, []byte{172, 16, 1, 1, 0, 4}, false}, + {[]NodeAddr{testMarshalIPv4Addr3, testMarshalIPv4Addr4}, []byte{ 172, 16, 1, 1, 0, 5, 192, 168, 0, 3, 0, 6, }, false}, - {[]NodeAddr{{ParseIP("2001::1"), 7}}, nil, true}, - {[]NodeAddr{{nil, 8}}, nil, true}, + {[]NodeAddr{NewNodeIPAddr(ParseIP("2001::1"), 7)}, nil, true}, + {[]NodeAddr{NewNodeIPAddr(nil, 8)}, nil, true}, } -func TestMarshalCompactIPv4NodeAddrs(t *testing.T) { +func TestMarshalIPv4CompactNodeAddrs(t *testing.T) { for _, tc := range marshalIPv4SliceTests { runFunc := assert.NotPanics if tc.panics { diff --git a/krpc/i2p_utils.go b/krpc/i2p_utils.go new file mode 100644 index 0000000000..79d24e0e21 --- /dev/null +++ b/krpc/i2p_utils.go @@ -0,0 +1,46 @@ +package krpc + +import ( + "crypto/sha256" + "encoding/base32" + "encoding/base64" +) + +var ( + i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~") + i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567") +) + +type I2PDest []byte + +func (i2p I2PDest) String() string { + if len(i2p) == 32 { + b32addr := make([]byte, 56) + i2pB32enc.Encode(b32addr, i2p[:]) + + return string(b32addr[:52]) + ".b32.i2p" + } + + buf := make([]byte, i2pB64enc.EncodedLen(len(i2p))) + i2pB64enc.Encode(buf, i2p) + + return string(buf) +} + +//Return a 32 byte representation of the destination +func (i2p I2PDest) Compact() I2PDest { + if len(i2p) == 32 { + return i2p + } + + //invalid destination + if len(i2p) < 387 || len(i2p) > 475 { + return I2PDest{} + } + + sum := sha256.Sum256(i2p) + addr := make(I2PDest, 32) + copy(addr[:], sum[:]) + + return addr +} diff --git a/krpc/i2p_utils_test.go b/krpc/i2p_utils_test.go new file mode 100644 index 0000000000..ab37a623d7 --- /dev/null +++ b/krpc/i2p_utils_test.go @@ -0,0 +1,77 @@ +package krpc + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + decode32 = i2pB32enc.DecodeString +) + +const suffixb32 = ".b32.i2p" + +func makeTestAddr32(addr string) I2PDest { + b, err := i2pB32enc.DecodeString(addr + "====") + if err != nil { + panic(err) + } + + var dest I2PDest = b + return dest +} + +func makeTestAddr64(addr string) I2PDest { + b, err := i2pB64enc.DecodeString(addr) + if err != nil { + panic(err) + } + + var dest I2PDest = b + return dest +} + +const ( + dest32_1 = "udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna" + dest32_2 = "tmipbl5d7ctnz3cib4yd2yivlrssrtpmuuzyqdpqkelzmnqllhda" + dest32_3 = "lhbd7ojcaiofbfku7ixh47qj537g572zmhdc4oilvugzxdpdghua" + + dest64_1 = "8ZAW~KzGFMUEj0pdchy6GQOOZbuzbqpWtiApEj8LHy2~O~58XKxRrA43cA23a9oDpNZDqWhRWEtehSnX5NoCwJcXWWdO1ksKEUim6cQLP-VpQyuZTIIqwSADwgoe6ikxZG0NGvy5FijgxF4EW9zg39nhUNKRejYNHhOBZKIX38qYyXoB8XCVJybKg89aMMPsCT884F0CLBKbHeYhpYGmhE4YW~aV21c5pebivvxeJPWuTBAOmYxAIgJE3fFU-fucQn9YyGUFa8F3t-0Vco-9qVNSEWfgrdXOdKT6orr3sfssiKo3ybRWdTpxycZ6wB4qHWgTSU5A-gOA3ACTCMZBsASN3W5cz6GRZCspQ0HNu~R~nJ8V06Mmw~iVYOu5lDvipmG6-dJky6XRxCedczxMM1GWFoieQ8Ysfuxq-j8keEtaYmyUQme6TcviCEvQsxyVirr~dTC-F8aZ~y2AlG5IJz5KD02nO6TRkI2fgjHhv9OZ9nskh-I2jxAzFP6Is1kyAAAA" + dest64_2 = "Gw1kgEBbxZjENfiTdTQNRZYBwwyJVjXtF~t5D0-XMmeVeizW-s~90~XTtAqQ8n41roBCWtr9lrAhJ8S1drBivatp85G3bXH~eV0ZYhmcFTLd-6UUP2eFbG~0Fmmvf-Pb6UFH9J0yKBdkqLaQB82AHWbz9CTNIf~3xAMBit2AJ8XQZ8haLcIH1kxUYae1~mkgiFPPFXg1MxONOjjJ9vaTDLeYofyS8hG95s1hp60x5xNGG6gi2pmCGopQDX46ZrzpNcaZkGHey4uEZGcSiYTm7S1hycQApBYNvCv4QvV92E0eFYqCm6thUOV7K78mii5agaqpcumDBy2PXLnwR0XrqjZnKBxydCcS-HockXR7nVykJL3moQOKswoMEChXMzQqD~RUrrmzHE80oXwZjExGNnp1hI7jZevYg38voDE3TT-3IT84kuLeb-1yH0p-HbiKBk4VLOpRsFpLD9V-tl0w9j7GWOchWX78Xxq7NTWa~xaQdrrCw60Ztw4Zzu2taMekBQAEAAcAAA==" + dest64_3 = "GKapJ8koUcBj~jmQzHsTYxDg2tpfWj0xjQTzd8BhfC9c3OS5fwPBNajgF-eOD6eCjFTqTlorlh7Hnd8kXj1qblUGXT-tDoR9~YV8dmXl51cJn9MVTRrEqRWSJVXbUUz9t5Po6Xa247Vr0sJn27R4KoKP8QVj1GuH6dB3b6wTPbOamC3dkO18vkQkfZWUdRMDXk0d8AdjB0E0864nOT~J9Fpnd2pQE5uoFT6P0DqtQR2jsFvf9ME61aqLvKPPWpkgdn4z6Zkm-NJOcDz2Nv8Si7hli94E9SghMYRsdjU-knObKvxiagn84FIwcOpepxuG~kFXdD5NfsH0v6Uri3usE3XWD7Pw6P8qVYF39jUIq4OiNMwPnNYzy2N4mDMQdsdHO3LUVh~DEppOy9AAmEoHDjjJxt2BFBbGxfdpZCpENkwvmZeYUyNCCzASqTOOlNzdpne8cuesn3NDXIpNnqEE6Oe5Qm5YOJykrX~Vx~cFFT3QzDGkIjjxlFBsjUJyYkFjBQAEAAcAAA==" +) + +func TestStringB32(t *testing.T) { + dest1 := makeTestAddr32(dest32_1) + assert.EqualValues(t, dest32_1+suffixb32, dest1.String()) + + dest2 := makeTestAddr32(dest32_2) + assert.EqualValues(t, dest32_2+suffixb32, dest2.String()) +} + +func TestStringFullDestB64(t *testing.T) { + dest1 := makeTestAddr64(dest64_1) + assert.EqualValues(t, dest64_1, dest1.String()) + + dest2 := makeTestAddr64(dest64_2) + assert.EqualValues(t, dest64_2, dest2.String()) +} + +func TestCompact(t *testing.T) { + dest1 := makeTestAddr32(dest32_1) + dest1Compacted := dest1.Compact() + assert.EqualValues(t, dest1.String(), dest1Compacted.String()) + + dest2 := makeTestAddr64(dest64_2) + dest2Compacted := dest2.Compact() + dest2CompactB32 := makeTestAddr32(dest32_2) + assert.EqualValues(t, dest2CompactB32.String(), dest2Compacted.String()) + + dest3 := makeTestAddr64(dest64_3) + dest3Compacted := dest3.Compact() + assert.NotEqualValues(t, dest3.String(), dest3Compacted.String()) + + dest3CompactB32 := makeTestAddr32(dest32_3) + assert.EqualValues(t, dest3CompactB32.String(), dest3Compacted.String()) +} diff --git a/krpc/msg.go b/krpc/msg.go index e217ddac9e..39877a9617 100644 --- a/krpc/msg.go +++ b/krpc/msg.go @@ -77,7 +77,7 @@ type Return struct { // K closest nodes to the requested target. Included in responses to queries that imply // traversal, for example get_peers, find_nodes, get, sample_infohashes. - Nodes CompactIPv4NodeInfo `bencode:"nodes,omitempty"` + Nodes CompactNodeInfo `bencode:"nodes,omitempty"` Nodes6 CompactIPv6NodeInfo `bencode:"nodes6,omitempty"` Token *string `bencode:"token,omitempty"` // Token for future announce_peer or put (BEP 44) diff --git a/krpc/msg_test.go b/krpc/msg_test.go index 30df18003c..409d703a00 100644 --- a/krpc/msg_test.go +++ b/krpc/msg_test.go @@ -76,7 +76,7 @@ func TestMarshalUnmarshalMsg(t *testing.T) { Y: "r", T: "\x8c%", R: &Return{ - Nodes: CompactIPv4NodeInfo{ + Nodes: CompactNodeInfo{ NodeInfo{ Addr: NodeAddr{ IP: net.IPv4(1, 2, 3, 4).To4(), diff --git a/krpc/nodeaddr.go b/krpc/nodeaddr.go index 61e7fc186d..828a9a302c 100644 --- a/krpc/nodeaddr.go +++ b/krpc/nodeaddr.go @@ -9,22 +9,86 @@ import ( "github.com/anacrolix/torrent/bencode" ) +const ( + IPNet = iota + I2PNet +) + +var networkType = IPNet + +// Switch between IP Addresses and I2P destinations +// This determines how bencoded data is interpreted and unmarshaled +func SetNetworkType(netType int) { + networkType = netType + + if networkType == I2PNet { + nodeAddrNumBytes = i2pAddrSize + } else { + nodeAddrNumBytes = ipv4AddrSize + } +} + +const ( + IPV4Addr = iota + IPV6Addr + I2PAddr +) + type NodeAddr struct { - IP net.IP - Port int + IP net.IP + Port int + I2PAddress I2PDest + hostType int +} + +func NewNodeIPAddr(ip net.IP, port int) NodeAddr { + hostAddrType := IPV4Addr + if ip.To4() == nil { + hostAddrType = IPV6Addr + } + + return NodeAddr{ + IP: ip, + Port: port, + hostType: hostAddrType, + } +} + +func NewNodeI2PAddr(dest I2PDest, port int) NodeAddr { + return NodeAddr{ + I2PAddress: dest, + Port: port, + hostType: I2PAddr, + } } // A zero Port is taken to mean no port provided, per BEP 7. func (me NodeAddr) String() string { if me.Port == 0 { - return me.IP.String() + return me.Host() } - return net.JoinHostPort(me.IP.String(), strconv.FormatInt(int64(me.Port), 10)) + return net.JoinHostPort(me.Host(), strconv.FormatInt(int64(me.Port), 10)) +} + +func (me NodeAddr) Host() string { + if me.hostType == I2PAddr { + return me.I2PAddress.String() + } + return me.IP.String() } func (me *NodeAddr) UnmarshalBinary(b []byte) error { - me.IP = make(net.IP, len(b)-2) - copy(me.IP, b[:len(b)-2]) + if networkType == I2PNet { + me.hostType = I2PAddr + me.I2PAddress = b[:len(b)-2] + } else { + me.IP = make(net.IP, len(b)-2) + copy(me.IP, b[:len(b)-2]) + if me.IP.To4() == nil { + me.hostType = IPV6Addr + } + } + me.Port = int(binary.BigEndian.Uint16(b[len(b)-2:])) return nil } @@ -40,7 +104,12 @@ func (me *NodeAddr) UnmarshalBencode(b []byte) (err error) { func (me NodeAddr) MarshalBinary() ([]byte, error) { var b bytes.Buffer - b.Write(me.IP) + if me.hostType == I2PAddr { + b.Write(me.I2PAddress) + } else { + b.Write(me.IP) + } + binary.Write(&b, binary.BigEndian, uint16(me.Port)) return b.Bytes(), nil } @@ -56,11 +125,39 @@ func (me NodeAddr) UDP() *net.UDPAddr { } } +//TODO: should this be removed? Only used in test func (me *NodeAddr) FromUDPAddr(ua *net.UDPAddr) { me.IP = ua.IP me.Port = ua.Port } +func (me NodeAddr) Compacted() NodeAddr { + if me.hostType == I2PAddr { + return NodeAddr{ + I2PAddress: me.I2PAddress.Compact(), + Port: me.Port, + hostType: me.hostType, + } + } + + var ip net.IP + if me.hostType == IPV4Addr { + ip = me.IP.To4() + } else if me.hostType == IPV6Addr { + ip = me.IP.To16() + } + + return NodeAddr{ + IP: ip, + Port: me.Port, + hostType: me.hostType, + } +} + func (me NodeAddr) Equal(x NodeAddr) bool { + if me.hostType == I2PAddr { + return bytes.Equal(me.I2PAddress, x.I2PAddress) && me.Port == x.Port + } + return me.IP.Equal(x.IP) && me.Port == x.Port } diff --git a/krpc/nodeaddr_test.go b/krpc/nodeaddr_test.go index 65d7e35211..550954be37 100644 --- a/krpc/nodeaddr_test.go +++ b/krpc/nodeaddr_test.go @@ -23,14 +23,20 @@ var naEqualTests = []struct { a, b NodeAddr out bool }{ - {NodeAddr{IPv4(172, 16, 1, 1), 11}, NodeAddr{IPv4(172, 16, 1, 1), 11}, true}, - {NodeAddr{IPv4(172, 16, 1, 1), 11}, NodeAddr{IPv4(172, 16, 1, 1), 22}, false}, - {NodeAddr{IPv4(172, 16, 1, 1), 11}, NodeAddr{IPv4(192, 168, 0, 3), 11}, false}, - {NodeAddr{IPv4(172, 16, 1, 1), 11}, NodeAddr{IPv4(192, 168, 0, 3), 22}, false}, - {NodeAddr{ParseIP("2001:db8:1:2::1"), 11}, NodeAddr{ParseIP("2001:db8:1:2::1"), 11}, true}, - {NodeAddr{ParseIP("2001:db8:1:2::1"), 11}, NodeAddr{ParseIP("2001:db8:1:2::1"), 22}, false}, - {NodeAddr{ParseIP("2001:db8:1:2::1"), 11}, NodeAddr{ParseIP("fe80::420b"), 11}, false}, - {NodeAddr{ParseIP("2001:db8:1:2::1"), 11}, NodeAddr{ParseIP("fe80::420b"), 22}, false}, + {NewNodeIPAddr(IPv4(172, 16, 1, 1), 11), NewNodeIPAddr(IPv4(172, 16, 1, 1), 11), true}, + {NewNodeIPAddr(IPv4(172, 16, 1, 1), 11), NewNodeIPAddr(IPv4(172, 16, 1, 1), 22), false}, + {NewNodeIPAddr(IPv4(172, 16, 1, 1), 11), NewNodeIPAddr(IPv4(192, 168, 0, 3), 11), false}, + {NewNodeIPAddr(IPv4(172, 16, 1, 1), 11), NewNodeIPAddr(IPv4(192, 168, 0, 3), 22), false}, + {NewNodeIPAddr(ParseIP("2001:db8:1:2::1"), 11), NewNodeIPAddr(ParseIP("2001:db8:1:2::1"), 11), true}, + {NewNodeIPAddr(ParseIP("2001:db8:1:2::1"), 11), NewNodeIPAddr(ParseIP("2001:db8:1:2::1"), 22), false}, + {NewNodeIPAddr(ParseIP("2001:db8:1:2::1"), 11), NewNodeIPAddr(ParseIP("fe80::420b"), 11), false}, + {NewNodeIPAddr(ParseIP("2001:db8:1:2::1"), 11), NewNodeIPAddr(ParseIP("fe80::420b"), 22), false}, + {NewNodeI2PAddr(makeTestAddr32(dest32_2), 22), NewNodeI2PAddr(makeTestAddr32(dest32_2), 22), true}, + {NewNodeI2PAddr(makeTestAddr64(dest64_1), 77), NewNodeI2PAddr(makeTestAddr64(dest64_1), 77), true}, + {NewNodeI2PAddr(makeTestAddr32(dest32_1), 11), NewNodeI2PAddr(makeTestAddr32(dest32_2), 11), false}, + {NewNodeI2PAddr(makeTestAddr64(dest64_2), 11), NewNodeI2PAddr(makeTestAddr64(dest64_3), 11), false}, + {NewNodeI2PAddr(makeTestAddr32(dest32_3), 11), NewNodeI2PAddr(makeTestAddr64(dest64_3), 11), false}, + {NewNodeI2PAddr(makeTestAddr32(dest32_3), 11), NewNodeI2PAddr(makeTestAddr64(dest64_1), 11), false}, } func TestNodeAddrEqual(t *testing.T) { diff --git a/krpc/nodeinfo.go b/krpc/nodeinfo.go index 6fa45a536b..baff448a4b 100644 --- a/krpc/nodeinfo.go +++ b/krpc/nodeinfo.go @@ -3,7 +3,6 @@ package krpc import ( "bytes" "encoding" - "encoding/binary" "fmt" "math" "math/rand" @@ -21,9 +20,10 @@ func (me NodeInfo) String() string { func RandomNodeInfo(ipLen int) (ni NodeInfo) { rand.Read(ni.ID[:]) - ni.Addr.IP = make(net.IP, ipLen) - rand.Read(ni.Addr.IP) - ni.Addr.Port = rand.Intn(math.MaxUint16 + 1) + ip := make(net.IP, ipLen) + rand.Read(ip) + port := rand.Intn(math.MaxUint16 + 1) + ni.Addr = NewNodeIPAddr(ip, port) return } @@ -35,8 +35,16 @@ var _ interface { func (ni NodeInfo) MarshalBinary() ([]byte, error) { var w bytes.Buffer w.Write(ni.ID[:]) - w.Write(ni.Addr.IP) - binary.Write(&w, binary.BigEndian, uint16(ni.Addr.Port)) + addrBytes, err := ni.Addr.MarshalBinary() + if err != nil { + return nil, err + } + + _, err = w.Write(addrBytes) + if err != nil { + return nil, err + } + return w.Bytes(), nil } diff --git a/server.go b/server.go index f40da773a3..b5522f4ad0 100644 --- a/server.go +++ b/server.go @@ -437,7 +437,7 @@ func filterPeers(querySourceIp net.IP, queryWants []krpc.Want, allPeers []krpc.N return nil, false } }(peer.IP); ok { - filtered = append(filtered, krpc.NodeAddr{ip, peer.Port}) + filtered = append(filtered, krpc.NewNodeIPAddr(ip, peer.Port)) } } return @@ -544,7 +544,7 @@ func (s *Server) handleQuery(source Addr, m krpc.Msg) { if ps := s.config.PeerStore; ps != nil { go ps.AddPeer( peer_store.InfoHash(args.InfoHash), - krpc.NodeAddr{source.IP(), port}, + krpc.NewNodeIPAddr(source.IP(), port), ) }