]> Sergey Matveev's repositories - btrtrc.git/blob - iplist/iplist.go
Print iplist.Range nicely
[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 // Parse a line of the PeerGuardian Text Lists (P2P) Format. Returns !ok but
54 // no error if a line doesn't contain a range but isn't erroneous, such as
55 // comment and blank lines.
56 func ParseBlocklistP2PLine(l string) (r Range, ok bool, err error) {
57         l = strings.TrimSpace(l)
58         if l == "" || strings.HasPrefix(l, "#") {
59                 return
60         }
61         sms := regexp.MustCompile(`(.*):([\d.]+)-([\d.]+)`).FindStringSubmatch(l)
62         if sms == nil {
63                 err = fmt.Errorf("error parsing %q", l)
64                 return
65         }
66         r.Description = sms[1]
67         r.First = net.ParseIP(sms[2])
68         r.Last = net.ParseIP(sms[3])
69         if r.First == nil || r.Last == nil {
70                 return
71         }
72         ok = true
73         return
74 }