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