master
go 98 lines 2.18 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 //go:build linux || freebsd || openbsd || netbsd || dragonfly
4
5 package isc_dhcpd
6
7 import (
8 "bufio"
9 "bytes"
10 "net/netip"
11 "os"
12 )
13
14 /*
15 Documentation (v4.4): https://kb.isc.org/docs/en/isc-dhcp-44-manual-pages-dhcpdleases
16
17 DHCPv4 prepare declaration:
18 prepare ip-address {
19 statements...
20 }
21
22 DHCPv6 prepare declaration:
23 ia_ta IAID_DUID {
24 cltt date;
25 iaaddr ipv6-address {
26 statements...
27 }
28 }
29 ia_na IAID_DUID {
30 cltt date;
31 iaaddr ipv6-address {
32 statements...
33 }
34 }
35 ia_pd IAID_DUID {
36 cltt date;
37 iaprefix ipv6-address/prefix-length {
38 statements...
39 }
40 }
41 */
42
43 type leaseEntry struct {
44 addr netip.Addr
45 bindingState string
46 }
47
48 func (l leaseEntry) isAddrValid() bool { return l.addr.IsValid() }
49 func (l leaseEntry) hasBindingState() bool { return l.bindingState != "" }
50
51 func parseDHCPdLeasesFile(filepath string) ([]leaseEntry, error) {
52 f, err := os.Open(filepath)
53 if err != nil {
54 return nil, err
55 }
56 defer func() { _ = f.Close() }()
57
58 leasesSet := make(map[string]leaseEntry)
59 l := leaseEntry{}
60 sc := bufio.NewScanner(f)
61
62 for sc.Scan() {
63 bs := bytes.TrimSpace(sc.Bytes())
64 switch {
65 case !l.isAddrValid() && bytes.HasPrefix(bs, []byte("lease")):
66 // "lease 192.168.0.1 {" => "192.168.0.1"
67 s := string(bs)
68 if addr, err := netip.ParseAddr(s[6 : len(s)-2]); err == nil && addr.IsValid() {
69 l.addr = addr
70 }
71 case !l.isAddrValid() && bytes.HasPrefix(bs, []byte("iaaddr")):
72 // "iaaddr 1985:470:1f0b:c9a::001 {" => "1985:470:1f0b:c9a::001"
73 s := string(bs)
74 if addr, err := netip.ParseAddr(s[7 : len(s)-2]); err == nil && addr.IsValid() {
75 l.addr = addr
76 }
77 case l.isAddrValid() && !l.hasBindingState() && bytes.HasPrefix(bs, []byte("binding state")):
78 // "binding state active;" => "active"
79 s := string(bs)
80 l.bindingState = s[14 : len(s)-1]
81 case bytes.HasPrefix(bs, []byte("}")):
82 if l.isAddrValid() && l.hasBindingState() {
83 leasesSet[l.addr.String()] = l
84 }
85 l = leaseEntry{}
86 }
87 }
88
89 if len(leasesSet) == 0 {
90 return nil, nil
91 }
92
93 leases := make([]leaseEntry, 0, len(leasesSet))
94 for _, l := range leasesSet {
95 leases = append(leases, l)
96 }
97 return leases, nil
98 }