"Fossies" - the Fresh Open Source Software Archive

Member "AdGuardHome-0.104.3/internal/dhcpd/dhcpd.go" (19 Nov 2020, 6157 Bytes) of package /linux/misc/dns/AdGuardHome-0.104.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "dhcpd.go": 0.104.1_vs_0.104.3.

    1 // Package dhcpd provides a DHCP server.
    2 package dhcpd
    3 
    4 import (
    5     "encoding/hex"
    6     "net"
    7     "net/http"
    8     "path/filepath"
    9     "runtime"
   10     "strconv"
   11     "strings"
   12     "time"
   13 
   14     "github.com/AdguardTeam/AdGuardHome/internal/util"
   15     "github.com/AdguardTeam/golibs/log"
   16 )
   17 
   18 const (
   19     defaultDiscoverTime = time.Second * 3
   20     leaseExpireStatic   = 1
   21 )
   22 
   23 var webHandlersRegistered = false
   24 
   25 // Lease contains the necessary information about a DHCP lease
   26 type Lease struct {
   27     HWAddr   net.HardwareAddr `json:"mac"`
   28     IP       net.IP           `json:"ip"`
   29     Hostname string           `json:"hostname"`
   30 
   31     // Lease expiration time
   32     // 1: static lease
   33     Expiry time.Time `json:"expires"`
   34 }
   35 
   36 // ServerConfig - DHCP server configuration
   37 // field ordering is important -- yaml fields will mirror ordering from here
   38 type ServerConfig struct {
   39     Enabled       bool   `yaml:"enabled"`
   40     InterfaceName string `yaml:"interface_name"`
   41 
   42     Conf4 V4ServerConf `yaml:"dhcpv4"`
   43     Conf6 V6ServerConf `yaml:"dhcpv6"`
   44 
   45     WorkDir    string `yaml:"-"`
   46     DBFilePath string `yaml:"-"` // path to DB file
   47 
   48     // Called when the configuration is changed by HTTP request
   49     ConfigModified func() `yaml:"-"`
   50 
   51     // Register an HTTP handler
   52     HTTPRegister func(string, string, func(http.ResponseWriter, *http.Request)) `yaml:"-"`
   53 }
   54 
   55 type OnLeaseChangedT func(flags int)
   56 
   57 // flags for onLeaseChanged()
   58 const (
   59     LeaseChangedAdded = iota
   60     LeaseChangedAddedStatic
   61     LeaseChangedRemovedStatic
   62 
   63     LeaseChangedDBStore
   64 )
   65 
   66 // Server - the current state of the DHCP server
   67 type Server struct {
   68     srv4 DHCPServer
   69     srv6 DHCPServer
   70 
   71     conf ServerConfig
   72 
   73     // Called when the leases DB is modified
   74     onLeaseChanged []OnLeaseChangedT
   75 }
   76 
   77 type ServerInterface interface {
   78     Leases(flags int) []Lease
   79     SetOnLeaseChanged(onLeaseChanged OnLeaseChangedT)
   80 }
   81 
   82 // CheckConfig checks the configuration
   83 func (s *Server) CheckConfig(config ServerConfig) error {
   84     return nil
   85 }
   86 
   87 // Create - create object
   88 func Create(config ServerConfig) *Server {
   89     s := &Server{}
   90 
   91     s.conf.Enabled = config.Enabled
   92     s.conf.InterfaceName = config.InterfaceName
   93     s.conf.HTTPRegister = config.HTTPRegister
   94     s.conf.ConfigModified = config.ConfigModified
   95     s.conf.DBFilePath = filepath.Join(config.WorkDir, dbFilename)
   96 
   97     if !webHandlersRegistered && s.conf.HTTPRegister != nil {
   98         if runtime.GOOS == "windows" {
   99             // Our DHCP server doesn't work on Windows yet, so
  100             // signal that to the front with an HTTP 501.
  101             //
  102             // TODO(a.garipov): This needs refactoring.  We
  103             // shouldn't even try and initialize a DHCP server on
  104             // Windows, but there are currently too many
  105             // interconnected parts--such as HTTP handlers and
  106             // frontend--to make that work properly.
  107             s.registerNotImplementedHandlers()
  108         } else {
  109             s.registerHandlers()
  110         }
  111 
  112         webHandlersRegistered = true
  113     }
  114 
  115     var err4, err6 error
  116     v4conf := config.Conf4
  117     v4conf.Enabled = s.conf.Enabled
  118     if len(v4conf.RangeStart) == 0 {
  119         v4conf.Enabled = false
  120     }
  121     v4conf.InterfaceName = s.conf.InterfaceName
  122     v4conf.notify = s.onNotify
  123     s.srv4, err4 = v4Create(v4conf)
  124 
  125     v6conf := config.Conf6
  126     v6conf.Enabled = s.conf.Enabled
  127     if len(v6conf.RangeStart) == 0 {
  128         v6conf.Enabled = false
  129     }
  130     v6conf.InterfaceName = s.conf.InterfaceName
  131     v6conf.notify = s.onNotify
  132     s.srv6, err6 = v6Create(v6conf)
  133 
  134     if err4 != nil {
  135         log.Error("%s", err4)
  136         return nil
  137     }
  138     if err6 != nil {
  139         log.Error("%s", err6)
  140         return nil
  141     }
  142 
  143     if s.conf.Enabled && !v4conf.Enabled && !v6conf.Enabled {
  144         log.Error("Can't enable DHCP server because neither DHCPv4 nor DHCPv6 servers are configured")
  145         return nil
  146     }
  147 
  148     // we can't delay database loading until DHCP server is started,
  149     //  because we need static leases functionality available beforehand
  150     s.dbLoad()
  151     return s
  152 }
  153 
  154 // server calls this function after DB is updated
  155 func (s *Server) onNotify(flags uint32) {
  156     if flags == LeaseChangedDBStore {
  157         s.dbStore()
  158         return
  159     }
  160 
  161     s.notify(int(flags))
  162 }
  163 
  164 // SetOnLeaseChanged - set callback
  165 func (s *Server) SetOnLeaseChanged(onLeaseChanged OnLeaseChangedT) {
  166     s.onLeaseChanged = append(s.onLeaseChanged, onLeaseChanged)
  167 }
  168 
  169 func (s *Server) notify(flags int) {
  170     if len(s.onLeaseChanged) == 0 {
  171         return
  172     }
  173     for _, f := range s.onLeaseChanged {
  174         f(flags)
  175     }
  176 }
  177 
  178 // WriteDiskConfig - write configuration
  179 func (s *Server) WriteDiskConfig(c *ServerConfig) {
  180     c.Enabled = s.conf.Enabled
  181     c.InterfaceName = s.conf.InterfaceName
  182     s.srv4.WriteDiskConfig4(&c.Conf4)
  183     s.srv6.WriteDiskConfig6(&c.Conf6)
  184 }
  185 
  186 // Start will listen on port 67 and serve DHCP requests.
  187 func (s *Server) Start() error {
  188     err := s.srv4.Start()
  189     if err != nil {
  190         log.Error("DHCPv4: start: %s", err)
  191         return err
  192     }
  193 
  194     err = s.srv6.Start()
  195     if err != nil {
  196         log.Error("DHCPv6: start: %s", err)
  197         return err
  198     }
  199 
  200     return nil
  201 }
  202 
  203 // Stop closes the listening UDP socket
  204 func (s *Server) Stop() {
  205     s.srv4.Stop()
  206     s.srv6.Stop()
  207 }
  208 
  209 // flags for Leases() function
  210 const (
  211     LeasesDynamic = 1
  212     LeasesStatic  = 2
  213     LeasesAll     = LeasesDynamic | LeasesStatic
  214 )
  215 
  216 // Leases returns the list of current DHCP leases (thread-safe)
  217 func (s *Server) Leases(flags int) []Lease {
  218     result := s.srv4.GetLeases(flags)
  219 
  220     v6leases := s.srv6.GetLeases(flags)
  221     result = append(result, v6leases...)
  222 
  223     return result
  224 }
  225 
  226 // FindMACbyIP - find a MAC address by IP address in the currently active DHCP leases
  227 func (s *Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
  228     if ip.To4() != nil {
  229         return s.srv4.FindMACbyIP(ip)
  230     }
  231     return s.srv6.FindMACbyIP(ip)
  232 }
  233 
  234 // AddStaticLease - add static v4 lease
  235 func (s *Server) AddStaticLease(lease Lease) error {
  236     return s.srv4.AddStaticLease(lease)
  237 }
  238 
  239 // Parse option string
  240 // Format:
  241 // CODE TYPE VALUE
  242 func parseOptionString(s string) (uint8, []byte) {
  243     s = strings.TrimSpace(s)
  244     scode := util.SplitNext(&s, ' ')
  245     t := util.SplitNext(&s, ' ')
  246     sval := util.SplitNext(&s, ' ')
  247 
  248     code, err := strconv.Atoi(scode)
  249     if err != nil || code <= 0 || code > 255 {
  250         return 0, nil
  251     }
  252 
  253     var val []byte
  254 
  255     switch t {
  256     case "hex":
  257         val, err = hex.DecodeString(sval)
  258         if err != nil {
  259             return 0, nil
  260         }
  261 
  262     case "ip":
  263         ip := net.ParseIP(sval)
  264         if ip == nil {
  265             return 0, nil
  266         }
  267         val = ip
  268         if ip.To4() != nil {
  269             val = ip.To4()
  270         }
  271 
  272     default:
  273         return 0, nil
  274     }
  275 
  276     return uint8(code), val
  277 }