]> Sergey Matveev's repositories - btrtrc.git/blob - iplist/iplist.go
iplist: Benchmark parsing lines
[btrtrc.git] / iplist / iplist.go
1 package iplist
2
3 import (
4         "bytes"
5         "fmt"
6         "net"
7         "regexp"
8         "sort"
9         "strings"
10 )
11
12 type IPList struct {
13         ranges []Range
14 }
15
16 type Range struct {
17         First, Last net.IP
18         Description string
19 }
20
21 func (r *Range) String() string {
22         return fmt.Sprintf("%s-%s (%s)", r.First, r.Last, r.Description)
23 }
24
25 // Create a new IP list. The given range must already sorted by the lower IP
26 // in the range. Behaviour is undefined for lists of overlapping ranges.
27 func New(initSorted []Range) *IPList {
28         return &IPList{
29                 ranges: initSorted,
30         }
31 }
32
33 // Return the range the given IP is in. Returns nil if no range is found.
34 func (me *IPList) Lookup(ip net.IP) (r *Range) {
35         // Find the index of the first range for which the following range exceeds
36         // it.
37         i := sort.Search(len(me.ranges), func(i int) bool {
38                 if i+1 >= len(me.ranges) {
39                         return true
40                 }
41                 return bytes.Compare(ip, me.ranges[i+1].First) < 0
42         })
43         if i == len(me.ranges) {
44                 return
45         }
46         r = &me.ranges[i]
47         if bytes.Compare(ip, r.First) < 0 || bytes.Compare(ip, r.Last) > 0 {
48                 r = nil
49         }
50         return
51 }
52
53 var p2pBlocklistLineRe = regexp.MustCompile(`(.*):([\d.]+)-([\d.]+)`)
54
55 // Parse a line of the PeerGuardian Text Lists (P2P) Format. Returns !ok but
56 // no error if a line doesn't contain a range but isn't erroneous, such as
57 // comment and blank lines.
58 func ParseBlocklistP2PLine(l string) (r Range, ok bool, err error) {
59         l = strings.TrimSpace(l)
60         if l == "" || strings.HasPrefix(l, "#") {
61                 return
62         }
63         sms := p2pBlocklistLineRe.FindStringSubmatch(l)
64         if sms == nil {
65                 err = fmt.Errorf("error parsing %q", l)
66                 return
67         }
68         r.Description = sms[1]
69         r.First = net.ParseIP(sms[2])
70         r.Last = net.ParseIP(sms[3])
71         if r.First == nil || r.Last == nil {
72                 return
73         }
74         ok = true
75         return
76 }