github.com/anacrolix/torrent@v1.61.0/bep40.go (about) 1 package torrent 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "hash/crc32" 9 "net" 10 ) 11 12 var table = crc32.MakeTable(crc32.Castagnoli) 13 14 type peerPriority = uint32 15 16 func sameSubnet(ones, bits int, a, b net.IP) bool { 17 mask := net.CIDRMask(ones, bits) 18 return a.Mask(mask).Equal(b.Mask(mask)) 19 } 20 21 func ipv4Mask(a, b net.IP) net.IPMask { 22 if !sameSubnet(16, 32, a, b) { 23 return net.IPv4Mask(0xff, 0xff, 0x55, 0x55) 24 } 25 if !sameSubnet(24, 32, a, b) { 26 return net.IPv4Mask(0xff, 0xff, 0xff, 0x55) 27 } 28 return net.IPv4Mask(0xff, 0xff, 0xff, 0xff) 29 } 30 31 func mask(prefix, bytes int) net.IPMask { 32 ret := make(net.IPMask, bytes) 33 for i := range ret { 34 ret[i] = 0x55 35 } 36 for i := 0; i < prefix; i++ { 37 ret[i] = 0xff 38 } 39 return ret 40 } 41 42 func ipv6Mask(a, b net.IP) net.IPMask { 43 for i := 6; i <= 16; i++ { 44 if !sameSubnet(i*8, 128, a, b) { 45 return mask(i, 16) 46 } 47 } 48 panic(fmt.Sprintf("%s %s", a, b)) 49 } 50 51 func bep40PriorityBytes(a, b IpPort) ([]byte, error) { 52 if a.IP.Equal(b.IP) { 53 var ret [4]byte 54 binary.BigEndian.PutUint16(ret[0:2], a.Port) 55 binary.BigEndian.PutUint16(ret[2:4], b.Port) 56 return ret[:], nil 57 } 58 if a4, b4 := a.IP.To4(), b.IP.To4(); a4 != nil && b4 != nil { 59 m := ipv4Mask(a.IP, b.IP) 60 return append(a4.Mask(m), b4.Mask(m)...), nil 61 } 62 if a6, b6 := a.IP.To16(), b.IP.To16(); a6 != nil && b6 != nil { 63 m := ipv6Mask(a.IP, b.IP) 64 return append(a6.Mask(m), b6.Mask(m)...), nil 65 } 66 return nil, errors.New("incomparable IPs") 67 } 68 69 func bep40Priority(a, b IpPort) (peerPriority, error) { 70 bs, err := bep40PriorityBytes(a, b) 71 if err != nil { 72 return 0, err 73 } 74 i := len(bs) / 2 75 _a, _b := bs[:i], bs[i:] 76 if bytes.Compare(_a, _b) > 0 { 77 bs = append(_b, _a...) 78 } 79 return crc32.Checksum(bs, table), nil 80 } 81 82 func bep40PriorityIgnoreError(a, b IpPort) peerPriority { 83 prio, _ := bep40Priority(a, b) 84 return prio 85 }