]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Start implementing BEP 40
authorMatt Joiner <anacrolix@gmail.com>
Thu, 29 Mar 2018 03:27:48 +0000 (14:27 +1100)
committerMatt Joiner <anacrolix@gmail.com>
Thu, 29 Mar 2018 03:27:48 +0000 (14:27 +1100)
bep40.go [new file with mode: 0644]
bep40_test.go [new file with mode: 0644]

diff --git a/bep40.go b/bep40.go
new file mode 100644 (file)
index 0000000..b92ef9d
--- /dev/null
+++ b/bep40.go
@@ -0,0 +1,82 @@
+package torrent
+
+import (
+       "bytes"
+       "encoding/binary"
+       "fmt"
+       "hash/crc32"
+
+       "net"
+)
+
+var table = crc32.MakeTable(crc32.Castagnoli)
+
+type peerPriority = uint32
+
+type ipPort struct {
+       IP   net.IP
+       Port uint16
+}
+
+func sameSubnet(ones, bits int, a, b net.IP) bool {
+       mask := net.CIDRMask(ones, bits)
+       return a.Mask(mask).Equal(b.Mask(mask))
+}
+
+func ipv4Mask(a, b net.IP) net.IPMask {
+       if !sameSubnet(16, 32, a, b) {
+               return net.IPv4Mask(0xff, 0xff, 0x55, 0x55)
+       }
+       if !sameSubnet(24, 32, a, b) {
+               return net.IPv4Mask(0xff, 0xff, 0xff, 0x55)
+       }
+       return net.IPv4Mask(0xff, 0xff, 0xff, 0xff)
+}
+
+func mask(prefix, bytes int) net.IPMask {
+       ret := make(net.IPMask, bytes)
+       for i := range ret {
+               ret[i] = 0x55
+       }
+       for i := 0; i < prefix; i++ {
+               ret[i] = 0xff
+       }
+       return ret
+}
+
+func ipv6Mask(a, b net.IP) net.IPMask {
+       for i := 6; i <= 16; i++ {
+               if !sameSubnet(i*8, 128, a, b) {
+                       return mask(i, 16)
+               }
+       }
+       panic(fmt.Sprintf("%s %s", a, b))
+}
+
+func bep40PriorityBytes(a, b ipPort) []byte {
+       if a.IP.Equal(b.IP) {
+               var ret [4]byte
+               binary.BigEndian.PutUint16(ret[0:2], a.Port)
+               binary.BigEndian.PutUint16(ret[2:4], b.Port)
+               return ret[:]
+       }
+       if a4, b4 := a.IP.To4(), b.IP.To4(); a4 != nil && b4 != nil {
+               m := ipv4Mask(a.IP, b.IP)
+               return append(a4.Mask(m), b4.Mask(m)...)
+       }
+       if a6, b6 := a.IP.To16(), b.IP.To16(); a6 != nil && b6 != nil {
+               m := ipv6Mask(a.IP, b.IP)
+               return append(a6.Mask(m), b6.Mask(m)...)
+       }
+       panic(fmt.Sprintf("%s %s", a.IP, b.IP))
+}
+
+func bep40Priority(a, b ipPort) peerPriority {
+       bs := bep40PriorityBytes(a, b)
+       i := len(bs) / 2
+       _a, _b := bs[:i], bs[i:]
+       if bytes.Compare(_a, _b) > 0 {
+               bs = append(_b, _a...)
+       }
+       return crc32.Checksum(bs, table)
+}
diff --git a/bep40_test.go b/bep40_test.go
new file mode 100644 (file)
index 0000000..4c82a5a
--- /dev/null
@@ -0,0 +1,27 @@
+package torrent
+
+import (
+       "net"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestBep40Priority(t *testing.T) {
+       assert.EqualValues(t, 0xec2d7224, bep40Priority(
+               ipPort{net.ParseIP("123.213.32.10"), 0},
+               ipPort{net.ParseIP("98.76.54.32"), 0},
+       ))
+       assert.EqualValues(t, 0xec2d7224, bep40Priority(
+               ipPort{net.ParseIP("98.76.54.32"), 0},
+               ipPort{net.ParseIP("123.213.32.10"), 0},
+       ))
+       assert.Equal(t, peerPriority(0x99568189), bep40Priority(
+               ipPort{net.ParseIP("123.213.32.10"), 0},
+               ipPort{net.ParseIP("123.213.32.234"), 0},
+       ))
+       assert.EqualValues(t, "\x00\x00\x00\x00", bep40PriorityBytes(
+               ipPort{net.ParseIP("123.213.32.234"), 0},
+               ipPort{net.ParseIP("123.213.32.234"), 0},
+       ))
+}