]> Sergey Matveev's repositories - btrtrc.git/blob - bep40.go
bep40Priority: Include error in return
[btrtrc.git] / bep40.go
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 type ipPort struct {
17         IP   net.IP
18         Port uint16
19 }
20
21 func sameSubnet(ones, bits int, a, b net.IP) bool {
22         mask := net.CIDRMask(ones, bits)
23         return a.Mask(mask).Equal(b.Mask(mask))
24 }
25
26 func ipv4Mask(a, b net.IP) net.IPMask {
27         if !sameSubnet(16, 32, a, b) {
28                 return net.IPv4Mask(0xff, 0xff, 0x55, 0x55)
29         }
30         if !sameSubnet(24, 32, a, b) {
31                 return net.IPv4Mask(0xff, 0xff, 0xff, 0x55)
32         }
33         return net.IPv4Mask(0xff, 0xff, 0xff, 0xff)
34 }
35
36 func mask(prefix, bytes int) net.IPMask {
37         ret := make(net.IPMask, bytes)
38         for i := range ret {
39                 ret[i] = 0x55
40         }
41         for i := 0; i < prefix; i++ {
42                 ret[i] = 0xff
43         }
44         return ret
45 }
46
47 func ipv6Mask(a, b net.IP) net.IPMask {
48         for i := 6; i <= 16; i++ {
49                 if !sameSubnet(i*8, 128, a, b) {
50                         return mask(i, 16)
51                 }
52         }
53         panic(fmt.Sprintf("%s %s", a, b))
54 }
55
56 func bep40PriorityBytes(a, b ipPort) ([]byte, error) {
57         if a.IP.Equal(b.IP) {
58                 var ret [4]byte
59                 binary.BigEndian.PutUint16(ret[0:2], a.Port)
60                 binary.BigEndian.PutUint16(ret[2:4], b.Port)
61                 return ret[:], nil
62         }
63         if a4, b4 := a.IP.To4(), b.IP.To4(); a4 != nil && b4 != nil {
64                 m := ipv4Mask(a.IP, b.IP)
65                 return append(a4.Mask(m), b4.Mask(m)...), nil
66         }
67         if a6, b6 := a.IP.To16(), b.IP.To16(); a6 != nil && b6 != nil {
68                 m := ipv6Mask(a.IP, b.IP)
69                 return append(a6.Mask(m), b6.Mask(m)...), nil
70         }
71         return nil, errors.New("incomparable IPs")
72 }
73
74 func bep40Priority(a, b ipPort) (peerPriority, error) {
75         bs, err := bep40PriorityBytes(a, b)
76         if err != nil {
77                 return 0, err
78         }
79         i := len(bs) / 2
80         _a, _b := bs[:i], bs[i:]
81         if bytes.Compare(_a, _b) > 0 {
82                 bs = append(_b, _a...)
83         }
84         return crc32.Checksum(bs, table), nil
85 }
86
87 func bep40PriorityIgnoreError(a, b ipPort) peerPriority {
88         prio, _ := bep40Priority(a, b)
89         return prio
90 }