all: sync with master

This commit is contained in:
Ainar Garipov 2022-09-14 16:36:29 +03:00
parent b34d119255
commit 77d04d44eb
91 changed files with 591 additions and 553 deletions

View file

@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.18'
'GO_VERSION': '1.18.6'
'NODE_VERSION': '14'
'on':
@ -31,7 +31,7 @@
'with':
'fetch-depth': 0
- 'name': 'Set up Go'
'uses': 'actions/setup-go@v2'
'uses': 'actions/setup-go@v3'
'with':
'go-version': '${{ env.GO_VERSION }}'
- 'name': 'Set up Node'
@ -72,7 +72,7 @@
'with':
'fetch-depth': 0
- 'name': 'Set up Go'
'uses': 'actions/setup-go@v2'
'uses': 'actions/setup-go@v3'
'with':
'go-version': '${{ env.GO_VERSION }}'
- 'name': 'Set up Node'
@ -112,7 +112,9 @@
# Use always() to signal to the runner that this job must run even if the
# previous ones failed.
'if':
${{ always() &&
${{
always() &&
github.repository_owner == 'AdguardTeam' &&
(
github.event_name == 'push' ||
github.event.pull_request.head.repo.full_name == github.repository

View file

@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.18'
'GO_VERSION': '1.18.6'
'on':
'push':
@ -17,7 +17,7 @@
'steps':
- 'uses': 'actions/checkout@v2'
- 'name': 'Set up Go'
'uses': 'actions/setup-go@v2'
'uses': 'actions/setup-go@v3'
'with':
'go-version': '${{ env.GO_VERSION }}'
- 'name': 'run-lint'
@ -43,7 +43,9 @@
# Use always() to signal to the runner that this job must run even if the
# previous ones failed.
'if':
${{ always() &&
${{
always() &&
github.repository_owner == 'AdguardTeam' &&
(
github.event_name == 'push' ||
github.event.pull_request.head.repo.full_name == github.repository

View file

@ -26,12 +26,39 @@ and this project adheres to
<!--
## [v0.107.13] - 2022-10-05 (APPROX.)
## [v0.107.14] - 2022-10-05 (APPROX.)
See also the [v0.107.14 GitHub milestone][ms-v0.107.14].
[ms-v0.107.14]: https://github.com/AdguardTeam/AdGuardHome/milestone/50?closed=1
-->
## [v0.107.13] - 2022-09-14
See also the [v0.107.13 GitHub milestone][ms-v0.107.13].
### Added
- The new optional `dns.ipset_file` property in the configuration file. It
allows loading the `ipset` list from a file, just like `dns.upstream_dns_file`
does for upstream servers ([#4686]).
### Changed
- The minimum DHCP message size is reassigned back to BOOTP's constraint of 300
bytes ([#4904]).
### Fixed
- Panic when adding a static lease within the disabled DHCP server ([#4722]).
[#4686]: https://github.com/AdguardTeam/AdGuardHome/issues/4686
[#4722]: https://github.com/AdguardTeam/AdGuardHome/issues/4722
[#4904]: https://github.com/AdguardTeam/AdGuardHome/issues/4904
[ms-v0.107.13]: https://github.com/AdguardTeam/AdGuardHome/milestone/49?closed=1
-->
@ -1203,11 +1230,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.13...HEAD
[v0.107.13]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.11...v0.107.13
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.14...HEAD
[v0.107.14]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.13...v0.107.14
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.12...HEAD
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.13...HEAD
[v0.107.13]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.12...v0.107.13
[v0.107.12]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.11...v0.107.12
[v0.107.11]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.10...v0.107.11
[v0.107.10]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.9...v0.107.10

View file

@ -70,7 +70,7 @@
"dhcp_warning": "Калі вы ўсё адно хочаце ўключыць DHCP-сервер, пераканайцеся, што ў сеціве больш няма актыўных DHCP-сервераў. Інакш гэта можа зламаць доступ у сеціва для падлучаных прылад!",
"dhcp_error": "AdGuard Home не можа вызначыць, ці ёсць у сетцы іншы актыўны DHCP-сервер",
"dhcp_static_ip_error": "Для таго, каб выкарыстоўваць DHCP-сервер, павінен быць усталяваны статычны IP-адрас. Мы не змаглі вызначыць, ці выкарыстоўвае гэты інтэрфейс сеціва статычны IP-адрас. Калі ласка, усталюйце яго ручна.",
"dhcp_dynamic_ip_found": "Ваша сістэма выкарыстоўвае дынамічны IP-адрас для інтэрфейсу <0>{{interfaceName}}</0>. Каб выкарыстоўваць DHCP-сервер трэба ўсталяваць статычны IP-адрас. Ваш бягучы IP-адрас <0>{{ipAddress}}</0>. Мы аўтаматычна ўсталюем яго як статычны, калі вы націснеце кнопку Ўключыць DHCP.",
"dhcp_dynamic_ip_found": "Ваша сістэма выкарыстоўвае дынамічны IP-адрас для інтэрфейсу <0>{{interfaceName}}</0>. Каб выкарыстоўваць DHCP-сервер трэба ўсталяваць статычны IP-адрас. Ваш бягучы IP-адрас <0>{{ipAddress}}</0>. Мы аўтаматычна ўсталюем яго як статычны, калі вы націснеце кнопку «Ўключыць DHCP».",
"dhcp_lease_added": "Статычная арэнда «{{key}}» паспяхова дададзена",
"dhcp_lease_deleted": "Статычная арэнда «{{key}}» паспяхова выдалена",
"dhcp_new_static_lease": "Новая статычная арэнда",
@ -447,7 +447,7 @@
"access_disallowed_title": "Забароненыя кліенты",
"access_disallowed_desc": "Спіс CIDR, IP-адрасоў або <a>ClientID</a>. Калі ў гэтым спісе ёсць запісы, AdGuard Home выдаліць запыты ад гэтых кліентаў. Гэта поле ігнаруецца, калі ёсць запісы ў Дазволеныя кліенты.",
"access_blocked_title": "Заблакаваныя дамены",
"access_blocked_desc": "Не блытайце гэта з фільтрамі. AdGuard Home будзе ігнараваць DNS-запыты з гэтымі даменамі.",
"access_blocked_desc": "Не блытаць з фільтрамі. AdGuard Home выдаляе запыты DNS, якія адпавядаюць гэтым даменам, і гэтыя запыты нават не з'яўляюцца ў журнале запытаў. Вы можаце ўказаць дакладныя даменныя імёны, падстаноўныя знакі або правілы фільтрацыі URL-адрасоў, напрыклад, «example.org», «*.example.org» ці «||example.org^» адпаведна.",
"access_settings_saved": "Налады доступу паспяхова захаваны",
"updates_checked": "Даступная новая версія AdGuard Home",
"updates_version_equal": "Версія AdGuard Home актуальная",

View file

@ -447,7 +447,7 @@
"access_disallowed_title": "Заборонені клієнти",
"access_disallowed_desc": "Перелік CIDR, IP-адрес та <a>ClientIDs</a>. Якщо налаштовано, AdGuard Home буде скасовувати запити від цих клієнтів. Проте якщо налаштовано список Дозволених клієнтів, то це поле проігнорується.",
"access_blocked_title": "Заборонені домени",
"access_blocked_desc": "Не плутайте з фільтрами. AdGuard Home буде ігнорувати DNS-запити з цими доменами, такі запити навіть не будуть записані до журналу. Ви можете вказати точні доменні імена, замінні знаки та правила фільтрування URL-адрес, наприклад, 'example.org', '*.example.org' або '||example.org^' відповідно.",
"access_blocked_desc": "Не плутайте з фільтрами. AdGuard Home буде ігнорувати DNS-запити з цими доменами, такі запити навіть не будуть записані до журналу. Ви можете вказати точні доменні імена, замінні знаки та правила фільтрування URL-адрес, наприклад, «example.org», «*.example.org» або «||example.org^» відповідно.",
"access_settings_saved": "Налаштування доступу успішно збережено",
"updates_checked": "Доступна нова версія AdGuard Home",
"updates_version_equal": "AdGuard Home останньої версії",

View file

@ -1,5 +1,4 @@
//go:build darwin || freebsd
// +build darwin freebsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build darwin || freebsd
// +build darwin freebsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build openbsd
// +build openbsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build openbsd
// +build openbsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build !(windows || linux)
// +build !windows,!linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd netbsd openbsd solaris
//go:build darwin || freebsd || openbsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build !linux
// +build !linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build darwin || freebsd || openbsd
// +build darwin freebsd openbsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build darwin
// +build darwin
package aghnet

View file

@ -1,5 +1,4 @@
//go:build freebsd
// +build freebsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build freebsd
// +build freebsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package aghnet

View file

@ -1,5 +1,4 @@
//go:build openbsd
// +build openbsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build openbsd
// +build openbsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build openbsd || freebsd || linux || darwin
// +build openbsd freebsd linux darwin
//go:build darwin || freebsd || linux || openbsd
package aghnet

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build !windows
// +build !windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build !windows
// +build !windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghnet

View file

@ -1,5 +1,4 @@
//go:build mips || mips64
// +build mips mips64
// This file is an adapted version of github.com/josharian/native.

View file

@ -1,5 +1,4 @@
//go:build amd64 || 386 || arm || arm64 || mipsle || mips64le || ppc64le
// +build amd64 386 arm arm64 mipsle mips64le ppc64le
// This file is an adapted version of github.com/josharian/native.

View file

@ -1,5 +1,4 @@
//go:build darwin || netbsd || openbsd
// +build darwin netbsd openbsd
//go:build darwin || openbsd
package aghos

View file

@ -1,5 +1,4 @@
//go:build freebsd
// +build freebsd
package aghos

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package aghos

View file

@ -1,5 +1,4 @@
//go:build darwin || freebsd || linux || openbsd
// +build darwin freebsd linux openbsd
package aghos

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghos

View file

@ -1,5 +1,4 @@
//go:build !(windows || plan9)
// +build !windows,!plan9
//go:build !windows
package aghos

View file

@ -1,5 +1,4 @@
//go:build windows || plan9
// +build windows plan9
//go:build windows
package aghos

View file

@ -1,5 +1,4 @@
//go:build darwin || freebsd || linux || netbsd || openbsd
// +build darwin freebsd linux netbsd openbsd
//go:build darwin || freebsd || linux || openbsd
package aghos

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package aghos

View file

@ -1,5 +1,4 @@
//go:build freebsd || openbsd
// +build freebsd openbsd
package dhcpd
@ -10,11 +9,10 @@ import (
// broadcast sends resp to the broadcast address specific for network interface.
func (c *dhcpConn) broadcast(respData []byte, peer *net.UDPAddr) (n int, err error) {
// Despite the fact that server4.NewIPv4UDPConn explicitly sets socket
// options to allow broadcasting, it also binds the connection to a
// specific interface. On FreeBSD and OpenBSD net.UDPConn.WriteTo
// causes errors while writing to the addresses that belong to another
// interface. So, use the broadcast address specific for the interface
// bound.
// options to allow broadcasting, it also binds the connection to a specific
// interface. On FreeBSD and OpenBSD net.UDPConn.WriteTo causes errors
// while writing to the addresses that belong to another interface. So, use
// the broadcast address specific for the interface bound.
peer.IP = c.bcastIP
return c.udpConn.WriteTo(respData, peer)

View file

@ -1,5 +1,4 @@
//go:build freebsd || openbsd
// +build freebsd openbsd
package dhcpd

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || linux || netbsd || solaris
// +build aix darwin dragonfly linux netbsd solaris
//go:build darwin || linux
package dhcpd

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || linux || netbsd || solaris
// +build aix darwin dragonfly linux netbsd solaris
//go:build darwin || linux
package dhcpd

View file

@ -1,10 +1,40 @@
package dhcpd
import (
"fmt"
"net"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
)
// ServerConfig is the configuration for the DHCP server. The order of YAML
// fields is important, since the YAML configuration file follows it.
type ServerConfig struct {
// Called when the configuration is changed by HTTP request
ConfigModified func() `yaml:"-"`
// Register an HTTP handler
HTTPRegister aghhttp.RegisterFunc `yaml:"-"`
Enabled bool `yaml:"enabled"`
InterfaceName string `yaml:"interface_name"`
// LocalDomainName is the domain name used for DHCP hosts. For example,
// a DHCP client with the hostname "myhost" can be addressed as "myhost.lan"
// when LocalDomainName is "lan".
LocalDomainName string `yaml:"local_domain_name"`
Conf4 V4ServerConf `yaml:"dhcpv4"`
Conf6 V6ServerConf `yaml:"dhcpv6"`
WorkDir string `yaml:"-"`
DBFilePath string `yaml:"-"`
}
// DHCPServer - DHCP server interface
type DHCPServer interface {
// ResetLeases resets leases.
@ -80,6 +110,86 @@ type V4ServerConf struct {
notify func(uint32)
}
// errNilConfig is an error returned by validation method if the config is nil.
const errNilConfig errors.Error = "nil config"
// ensureV4 returns a 4-byte version of ip. An error is returned if the passed
// ip is not an IPv4.
func ensureV4(ip net.IP) (ip4 net.IP, err error) {
if ip == nil {
return nil, fmt.Errorf("%v is not an IP address", ip)
}
ip4 = ip.To4()
if ip4 == nil {
return nil, fmt.Errorf("%v is not an IPv4 address", ip)
}
return ip4, nil
}
// Validate returns an error if c is not a valid configuration.
//
// TODO(e.burkov): Don't set the config fields when the server itself will stop
// containing the config.
func (c *V4ServerConf) Validate() (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
if c == nil {
return errNilConfig
}
var gatewayIP net.IP
gatewayIP, err = ensureV4(c.GatewayIP)
if err != nil {
// Don't wrap an errors since it's inforative enough as is and there is
// an annotation deferred already.
return err
}
if c.SubnetMask == nil {
return fmt.Errorf("invalid subnet mask: %v", c.SubnetMask)
}
subnetMask := net.IPMask(netutil.CloneIP(c.SubnetMask.To4()))
c.subnet = &net.IPNet{
IP: gatewayIP,
Mask: subnetMask,
}
c.broadcastIP = aghnet.BroadcastFromIPNet(c.subnet)
c.ipRange, err = newIPRange(c.RangeStart, c.RangeEnd)
if err != nil {
// Don't wrap an errors since it's inforative enough as is and there is
// an annotation deferred already.
return err
}
if c.ipRange.contains(gatewayIP) {
return fmt.Errorf("gateway ip %v in the ip range: %v-%v",
gatewayIP,
c.RangeStart,
c.RangeEnd,
)
}
if !c.subnet.Contains(c.RangeStart) {
return fmt.Errorf("range start %v is outside network %v",
c.RangeStart,
c.subnet,
)
}
if !c.subnet.Contains(c.RangeEnd) {
return fmt.Errorf("range end %v is outside network %v",
c.RangeEnd,
c.subnet,
)
}
return nil
}
// V6ServerConf - server configuration
type V6ServerConf struct {
Enabled bool `yaml:"-" json:"-"`

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package dhcpd

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package dhcpd

View file

@ -32,7 +32,7 @@ func normalizeIP(ip net.IP) net.IP {
}
// Load lease table from DB
func (s *Server) dbLoad() (err error) {
func (s *server) dbLoad() (err error) {
dynLeases := []*Lease{}
staticLeases := []*Lease{}
v6StaticLeases := []*Lease{}
@ -132,7 +132,7 @@ func normalizeLeases(staticLeases, dynLeases []*Lease) []*Lease {
}
// Store lease table in DB
func (s *Server) dbStore() (err error) {
func (s *server) dbStore() (err error) {
// Use an empty slice here as opposed to nil so that it doesn't write
// "null" into the database file if leases are empty.
leases := []leaseJSON{}

View file

@ -6,12 +6,11 @@ import (
"fmt"
"net"
"path/filepath"
"runtime"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
)
const (
@ -21,9 +20,19 @@ const (
// TODO(e.burkov): Remove it when static leases determining mechanism
// will be improved.
leaseExpireStatic = 1
// DefaultDHCPLeaseTTL is the default time-to-live for leases.
DefaultDHCPLeaseTTL = uint32(timeutil.Day / time.Second)
// DefaultDHCPTimeoutICMP is the default timeout for waiting ICMP responses.
DefaultDHCPTimeoutICMP = 1000
)
var webHandlersRegistered = false
// Currently used defaults for ifaceDNSAddrs.
const (
defaultMaxAttempts int = 10
defaultBackoff time.Duration = 500 * time.Millisecond
)
// Lease contains the necessary information about a DHCP lease
type Lease struct {
@ -119,30 +128,6 @@ func (l *Lease) UnmarshalJSON(data []byte) (err error) {
return nil
}
// ServerConfig is the configuration for the DHCP server. The order of YAML
// fields is important, since the YAML configuration file follows it.
type ServerConfig struct {
// Called when the configuration is changed by HTTP request
ConfigModified func() `yaml:"-"`
// Register an HTTP handler
HTTPRegister aghhttp.RegisterFunc `yaml:"-"`
Enabled bool `yaml:"enabled"`
InterfaceName string `yaml:"interface_name"`
// LocalDomainName is the domain name used for DHCP hosts. For example,
// a DHCP client with the hostname "myhost" can be addressed as "myhost.lan"
// when LocalDomainName is "lan".
LocalDomainName string `yaml:"local_domain_name"`
Conf4 V4ServerConf `yaml:"dhcpv4"`
Conf6 V6ServerConf `yaml:"dhcpv6"`
WorkDir string `yaml:"-"`
DBFilePath string `yaml:"-"`
}
// OnLeaseChangedT is a callback for lease changes.
type OnLeaseChangedT func(flags int)
@ -156,8 +141,68 @@ const (
LeaseChangedDBStore
)
// Server - the current state of the DHCP server
type Server struct {
// GetLeasesFlags are the flags for GetLeases.
type GetLeasesFlags uint8
// GetLeasesFlags values
const (
LeasesDynamic GetLeasesFlags = 0b01
LeasesStatic GetLeasesFlags = 0b10
LeasesAll = LeasesDynamic | LeasesStatic
)
// Interface is the DHCP server that deals with both IP address families.
type Interface interface {
Start() (err error)
Stop() (err error)
Enabled() (ok bool)
Leases(flags GetLeasesFlags) (leases []*Lease)
SetOnLeaseChanged(onLeaseChanged OnLeaseChangedT)
FindMACbyIP(ip net.IP) (mac net.HardwareAddr)
WriteDiskConfig(c *ServerConfig)
}
// MockInterface is a mock Interface implementation.
//
// TODO(e.burkov): Move to aghtest when the API stabilized.
type MockInterface struct {
OnStart func() (err error)
OnStop func() (err error)
OnEnabled func() (ok bool)
OnLeases func(flags GetLeasesFlags) (leases []*Lease)
OnSetOnLeaseChanged func(f OnLeaseChangedT)
OnFindMACbyIP func(ip net.IP) (mac net.HardwareAddr)
OnWriteDiskConfig func(c *ServerConfig)
}
var _ Interface = (*MockInterface)(nil)
// Start implements the Interface for *MockInterface.
func (s *MockInterface) Start() (err error) { return s.OnStart() }
// Stop implements the Interface for *MockInterface.
func (s *MockInterface) Stop() (err error) { return s.OnStop() }
// Enabled implements the Interface for *MockInterface.
func (s *MockInterface) Enabled() (ok bool) { return s.OnEnabled() }
// Leases implements the Interface for *MockInterface.
func (s *MockInterface) Leases(flags GetLeasesFlags) (ls []*Lease) { return s.OnLeases(flags) }
// SetOnLeaseChanged implements the Interface for *MockInterface.
func (s *MockInterface) SetOnLeaseChanged(f OnLeaseChangedT) { s.OnSetOnLeaseChanged(f) }
// FindMACbyIP implements the Interface for *MockInterface.
func (s *MockInterface) FindMACbyIP(ip net.IP) (mac net.HardwareAddr) { return s.OnFindMACbyIP(ip) }
// WriteDiskConfig implements the Interface for *MockInterface.
func (s *MockInterface) WriteDiskConfig(c *ServerConfig) { s.OnWriteDiskConfig(c) }
// server is the DHCP service that handles DHCPv4, DHCPv6, and HTTP API.
type server struct {
srv4 DHCPServer
srv6 DHCPServer
@ -169,27 +214,15 @@ type Server struct {
onLeaseChanged []OnLeaseChangedT
}
// GetLeasesFlags are the flags for GetLeases.
type GetLeasesFlags uint8
// type check
var _ Interface = (*server)(nil)
// GetLeasesFlags values
const (
LeasesDynamic GetLeasesFlags = 0b0001
LeasesStatic GetLeasesFlags = 0b0010
LeasesAll = LeasesDynamic | LeasesStatic
)
// ServerInterface is an interface for servers.
type ServerInterface interface {
Enabled() (ok bool)
Leases(flags GetLeasesFlags) (leases []*Lease)
SetOnLeaseChanged(onLeaseChanged OnLeaseChangedT)
}
// Create - create object
func Create(conf *ServerConfig) (s *Server, err error) {
s = &Server{
// Create initializes and returns the DHCP server handling both address
// families. It also registers the corresponding HTTP API endpoints.
//
// TODO(e.burkov): Don't register handlers, see TODO on [aghhttp.RegisterFunc].
func Create(conf *ServerConfig) (s *server, err error) {
s = &server{
conf: &ServerConfig{
ConfigModified: conf.ConfigModified,
@ -204,35 +237,20 @@ func Create(conf *ServerConfig) (s *Server, err error) {
},
}
if !webHandlersRegistered && s.conf.HTTPRegister != nil {
if runtime.GOOS == "windows" {
// Our DHCP server doesn't work on Windows yet, so
// signal that to the front with an HTTP 501.
//
// TODO(a.garipov): This needs refactoring. We
// shouldn't even try and initialize a DHCP server on
// Windows, but there are currently too many
// interconnected parts--such as HTTP handlers and
// frontend--to make that work properly.
s.registerNotImplementedHandlers()
} else {
s.registerHandlers()
}
webHandlersRegistered = true
}
s.registerHandlers()
v4conf := conf.Conf4
v4conf.Enabled = s.conf.Enabled
if len(v4conf.RangeStart) == 0 {
v4conf.Enabled = false
}
v4conf.InterfaceName = s.conf.InterfaceName
v4conf.notify = s.onNotify
s.srv4, err = v4Create(v4conf)
v4conf.Enabled = s.conf.Enabled && len(v4conf.RangeStart) != 0
s.srv4, err = v4Create(&v4conf)
if err != nil {
return nil, fmt.Errorf("creating dhcpv4 srv: %w", err)
if v4conf.Enabled {
return nil, fmt.Errorf("creating dhcpv4 srv: %w", err)
}
log.Error("creating dhcpv4 srv: %s", err)
}
v6conf := conf.Conf6
@ -265,12 +283,12 @@ func Create(conf *ServerConfig) (s *Server, err error) {
}
// Enabled returns true when the server is enabled.
func (s *Server) Enabled() (ok bool) {
func (s *server) Enabled() (ok bool) {
return s.conf.Enabled
}
// resetLeases resets all leases in the lease database.
func (s *Server) resetLeases() (err error) {
func (s *server) resetLeases() (err error) {
err = s.srv4.ResetLeases(nil)
if err != nil {
return err
@ -287,7 +305,7 @@ func (s *Server) resetLeases() (err error) {
}
// server calls this function after DB is updated
func (s *Server) onNotify(flags uint32) {
func (s *server) onNotify(flags uint32) {
if flags == LeaseChangedDBStore {
err := s.dbStore()
if err != nil {
@ -301,31 +319,28 @@ func (s *Server) onNotify(flags uint32) {
}
// SetOnLeaseChanged - set callback
func (s *Server) SetOnLeaseChanged(onLeaseChanged OnLeaseChangedT) {
func (s *server) SetOnLeaseChanged(onLeaseChanged OnLeaseChangedT) {
s.onLeaseChanged = append(s.onLeaseChanged, onLeaseChanged)
}
func (s *Server) notify(flags int) {
if len(s.onLeaseChanged) == 0 {
return
}
func (s *server) notify(flags int) {
for _, f := range s.onLeaseChanged {
f(flags)
}
}
// WriteDiskConfig - write configuration
func (s *Server) WriteDiskConfig(c *ServerConfig) {
func (s *server) WriteDiskConfig(c *ServerConfig) {
c.Enabled = s.conf.Enabled
c.InterfaceName = s.conf.InterfaceName
c.LocalDomainName = s.conf.LocalDomainName
s.srv4.WriteDiskConfig4(&c.Conf4)
s.srv6.WriteDiskConfig6(&c.Conf6)
}
// Start will listen on port 67 and serve DHCP requests.
func (s *Server) Start() (err error) {
func (s *server) Start() (err error) {
err = s.srv4.Start()
if err != nil {
return err
@ -340,7 +355,7 @@ func (s *Server) Start() (err error) {
}
// Stop closes the listening UDP socket
func (s *Server) Stop() (err error) {
func (s *server) Stop() (err error) {
err = s.srv4.Stop()
if err != nil {
return err
@ -356,12 +371,12 @@ func (s *Server) Stop() (err error) {
// Leases returns the list of active IPv4 and IPv6 DHCP leases. It's safe for
// concurrent use.
func (s *Server) Leases(flags GetLeasesFlags) (leases []*Lease) {
func (s *server) Leases(flags GetLeasesFlags) (leases []*Lease) {
return append(s.srv4.GetLeases(flags), s.srv6.GetLeases(flags)...)
}
// FindMACbyIP - find a MAC address by IP address in the currently active DHCP leases
func (s *Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
func (s *server) FindMACbyIP(ip net.IP) net.HardwareAddr {
if ip.To4() != nil {
return s.srv4.FindMACbyIP(ip)
}
@ -369,6 +384,6 @@ func (s *Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
}
// AddStaticLease - add static v4 lease
func (s *Server) AddStaticLease(l *Lease) error {
func (s *server) AddStaticLease(l *Lease) error {
return s.srv4.AddStaticLease(l)
}

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package dhcpd
@ -26,13 +25,13 @@ func testNotify(flags uint32) {
// Leases database store/load.
func TestDB(t *testing.T) {
var err error
s := Server{
s := server{
conf: &ServerConfig{
DBFilePath: dbFilename,
},
}
s.srv4, err = v4Create(V4ServerConf{
s.srv4, err = v4Create(&V4ServerConf{
Enabled: true,
RangeStart: net.IP{192, 168, 10, 100},
RangeEnd: net.IP{192, 168, 10, 200},
@ -88,32 +87,6 @@ func TestDB(t *testing.T) {
assert.Equal(t, leases[0].Expiry.Unix(), ll[1].Expiry.Unix())
}
func TestIsValidSubnetMask(t *testing.T) {
testCases := []struct {
mask net.IP
want bool
}{{
mask: net.IP{255, 255, 255, 0},
want: true,
}, {
mask: net.IP{255, 255, 254, 0},
want: true,
}, {
mask: net.IP{255, 255, 252, 0},
want: true,
}, {
mask: net.IP{255, 255, 253, 0},
}, {
mask: net.IP{255, 255, 255, 1},
}}
for _, tc := range testCases {
t.Run(tc.mask.String(), func(t *testing.T) {
assert.Equal(t, tc.want, isValidSubnetMask(tc.mask))
})
}
}
func TestNormalizeLeases(t *testing.T) {
dynLeases := []*Lease{{
HWAddr: net.HardwareAddr{1, 2, 3, 4},
@ -174,7 +147,7 @@ func TestV4Server_badRange(t *testing.T) {
notify: testNotify,
}
_, err := v4Create(conf)
_, err := v4Create(&conf)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
}

View file

@ -1,36 +0,0 @@
package dhcpd
import (
"encoding/binary"
"fmt"
"net"
)
func tryTo4(ip net.IP) (ip4 net.IP, err error) {
if ip == nil {
return nil, fmt.Errorf("%v is not an IP address", ip)
}
ip4 = ip.To4()
if ip4 == nil {
return nil, fmt.Errorf("%v is not an IPv4 address", ip)
}
return ip4, nil
}
// Return TRUE if subnet mask is correct (e.g. 255.255.255.0)
func isValidSubnetMask(mask net.IP) bool {
var n uint32
n = binary.BigEndian.Uint32(mask)
for i := 0; i != 32; i++ {
if n == 0 {
break
}
if (n & 0x80000000) == 0 {
return false
}
n <<= 1
}
return true
}

View file

@ -1,3 +1,5 @@
//go:build darwin || freebsd || linux || openbsd
package dhcpd
import (
@ -8,14 +10,12 @@ import (
"net/http"
"os"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/timeutil"
)
type v4ServerConfJSON struct {
@ -26,12 +26,12 @@ type v4ServerConfJSON struct {
LeaseDuration uint32 `json:"lease_duration"`
}
func v4JSONToServerConf(j *v4ServerConfJSON) V4ServerConf {
func (j *v4ServerConfJSON) toServerConf() *V4ServerConf {
if j == nil {
return V4ServerConf{}
return &V4ServerConf{}
}
return V4ServerConf{
return &V4ServerConf{
GatewayIP: j.GatewayIP,
SubnetMask: j.SubnetMask,
RangeStart: j.RangeStart,
@ -66,7 +66,7 @@ type dhcpStatusResponse struct {
Enabled bool `json:"enabled"`
}
func (s *Server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
func (s *server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
status := &dhcpStatusResponse{
Enabled: s.conf.Enabled,
IfaceName: s.conf.InterfaceName,
@ -81,6 +81,7 @@ func (s *Server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
status.StaticLeases = s.Leases(LeasesStatic)
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(status)
if err != nil {
aghhttp.Error(
@ -93,28 +94,26 @@ func (s *Server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
}
}
func (s *Server) enableDHCP(ifaceName string) (code int, err error) {
func (s *server) enableDHCP(ifaceName string) (code int, err error) {
var hasStaticIP bool
hasStaticIP, err = aghnet.IfaceHasStaticIP(ifaceName)
if err != nil {
if errors.Is(err, os.ErrPermission) {
// ErrPermission may happen here on Linux systems where
// AdGuard Home is installed using Snap. That doesn't
// necessarily mean that the machine doesn't have
// a static IP, so we can assume that it has and go on.
// If the machine doesn't, we'll get an error later.
// ErrPermission may happen here on Linux systems where AdGuard Home
// is installed using Snap. That doesn't necessarily mean that the
// machine doesn't have a static IP, so we can assume that it has
// and go on. If the machine doesn't, we'll get an error later.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2667.
//
// TODO(a.garipov): I was thinking about moving this
// into IfaceHasStaticIP, but then we wouldn't be able
// to log it. Think about it more.
// TODO(a.garipov): I was thinking about moving this into
// IfaceHasStaticIP, but then we wouldn't be able to log it. Think
// about it more.
log.Info("error while checking static ip: %s; "+
"assuming machine has static ip and going on", err)
hasStaticIP = true
} else if errors.Is(err, aghnet.ErrNoStaticIPInfo) {
// Couldn't obtain a definitive answer. Assume static
// IP an go on.
// Couldn't obtain a definitive answer. Assume static IP an go on.
log.Info("can't check for static ip; " +
"assuming machine has static ip and going on")
hasStaticIP = true
@ -149,34 +148,39 @@ type dhcpServerConfigJSON struct {
Enabled aghalg.NullBool `json:"enabled"`
}
func (s *Server) handleDHCPSetConfigV4(
func (s *server) handleDHCPSetConfigV4(
conf *dhcpServerConfigJSON,
) (srv4 DHCPServer, enabled bool, err error) {
) (srv DHCPServer, enabled bool, err error) {
if conf.V4 == nil {
return nil, false, nil
}
v4Conf := v4JSONToServerConf(conf.V4)
v4Conf := conf.V4.toServerConf()
v4Conf.Enabled = conf.Enabled == aghalg.NBTrue
if len(v4Conf.RangeStart) == 0 {
v4Conf.Enabled = false
}
enabled = v4Conf.Enabled
v4Conf.InterfaceName = conf.InterfaceName
c4 := V4ServerConf{}
s.srv4.WriteDiskConfig4(&c4)
// Set the default values for the fields not configurable via web API.
c4 := &V4ServerConf{
notify: s.onNotify,
ICMPTimeout: s.conf.Conf4.ICMPTimeout,
Options: s.conf.Conf4.Options,
}
s.srv4.WriteDiskConfig4(c4)
v4Conf.notify = c4.notify
v4Conf.ICMPTimeout = c4.ICMPTimeout
v4Conf.Options = c4.Options
srv4, err = v4Create(v4Conf)
srv4, err := v4Create(v4Conf)
return srv4, enabled, err
return srv4, srv4.enabled(), err
}
func (s *Server) handleDHCPSetConfigV6(
func (s *server) handleDHCPSetConfigV6(
conf *dhcpServerConfigJSON,
) (srv6 DHCPServer, enabled bool, err error) {
if conf.V6 == nil {
@ -205,7 +209,7 @@ func (s *Server) handleDHCPSetConfigV6(
return srv6, enabled, err
}
func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
conf := &dhcpServerConfigJSON{}
conf.Enabled = aghalg.BoolToNullBool(s.conf.Enabled)
conf.InterfaceName = s.conf.InterfaceName
@ -287,7 +291,7 @@ type netInterfaceJSON struct {
Addrs6 []net.IP `json:"ipv6_addresses"`
}
func (s *Server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
response := map[string]netInterfaceJSON{}
ifaces, err := net.Interfaces()
@ -410,7 +414,7 @@ type dhcpSearchResult struct {
// . Search for another DHCP server running
// . Check if a static IP is configured for the network interface
// Respond with results
func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) {
func (s *server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) {
// This use of ReadAll is safe, because request's body is now limited.
body, err := io.ReadAll(r.Body)
if err != nil {
@ -482,7 +486,7 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
}
}
func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) {
func (s *server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) {
l := &Lease{}
err := json.NewDecoder(r.Body).Decode(l)
if err != nil {
@ -497,20 +501,16 @@ func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
return
}
ip4 := l.IP.To4()
if ip4 == nil {
var srv DHCPServer
if ip4 := l.IP.To4(); ip4 != nil {
l.IP = ip4
srv = s.srv4
} else {
l.IP = l.IP.To16()
err = s.srv6.AddStaticLease(l)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
}
return
srv = s.srv6
}
l.IP = ip4
err = s.srv4.AddStaticLease(l)
err = srv.AddStaticLease(l)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@ -518,7 +518,7 @@ func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
}
}
func (s *Server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) {
func (s *server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) {
l := &Lease{}
err := json.NewDecoder(r.Body).Decode(l)
if err != nil {
@ -555,14 +555,7 @@ func (s *Server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Requ
}
}
const (
// DefaultDHCPLeaseTTL is the default time-to-live for leases.
DefaultDHCPLeaseTTL = uint32(timeutil.Day / time.Second)
// DefaultDHCPTimeoutICMP is the default timeout for waiting ICMP responses.
DefaultDHCPTimeoutICMP = 1000
)
func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
func (s *server) handleReset(w http.ResponseWriter, r *http.Request) {
err := s.Stop()
if err != nil {
aghhttp.Error(r, w, http.StatusInternalServerError, "stopping dhcp: %s", err)
@ -586,7 +579,7 @@ func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
DBFilePath: s.conf.DBFilePath,
}
v4conf := V4ServerConf{
v4conf := &V4ServerConf{
LeaseDuration: DefaultDHCPLeaseTTL,
ICMPTimeout: DefaultDHCPTimeoutICMP,
notify: s.onNotify,
@ -602,7 +595,7 @@ func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
s.conf.ConfigModified()
}
func (s *Server) handleResetLeases(w http.ResponseWriter, r *http.Request) {
func (s *server) handleResetLeases(w http.ResponseWriter, r *http.Request) {
err := s.resetLeases()
if err != nil {
msg := "resetting leases: %s"
@ -612,7 +605,11 @@ func (s *Server) handleResetLeases(w http.ResponseWriter, r *http.Request) {
}
}
func (s *Server) registerHandlers() {
func (s *server) registerHandlers() {
if s.conf.HTTPRegister == nil {
return
}
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/status", s.handleDHCPStatus)
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/interfaces", s.handleDHCPInterfaces)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/set_config", s.handleDHCPSetConfig)
@ -622,44 +619,3 @@ func (s *Server) registerHandlers() {
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", s.handleReset)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset_leases", s.handleResetLeases)
}
// jsonError is a generic JSON error response.
//
// TODO(a.garipov): Merge together with the implementations in .../home and
// other packages after refactoring the web handler registering.
type jsonError struct {
// Message is the error message, an opaque string.
Message string `json:"message"`
}
// notImplemented returns a handler that replies to any request with an HTTP 501
// Not Implemented status and a JSON error with the provided message msg.
//
// TODO(a.garipov): Either take the logger from the server after we've
// refactored logging or make this not a method of *Server.
func (s *Server) notImplemented(msg string) (f func(http.ResponseWriter, *http.Request)) {
return func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotImplemented)
err := json.NewEncoder(w).Encode(&jsonError{
Message: msg,
})
if err != nil {
log.Debug("writing 501 json response: %s", err)
}
}
}
func (s *Server) registerNotImplementedHandlers() {
h := s.notImplemented("dhcp is not supported on windows")
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/status", h)
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/interfaces", h)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/set_config", h)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/find_active_dhcp", h)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", h)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", h)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", h)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset_leases", h)
}

View file

@ -0,0 +1,55 @@
//go:build windows
package dhcpd
import (
"encoding/json"
"net/http"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/log"
)
// jsonError is a generic JSON error response.
//
// TODO(a.garipov): Merge together with the implementations in .../home and
// other packages after refactoring the web handler registering.
type jsonError struct {
// Message is the error message, an opaque string.
Message string `json:"message"`
}
// notImplemented is a handler that replies to any request with an HTTP 501 Not
// Implemented status and a JSON error with the provided message msg.
//
// TODO(a.garipov): Either take the logger from the server after we've
// refactored logging or make this not a method of *Server.
func (s *server) notImplemented(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotImplemented)
err := json.NewEncoder(w).Encode(&jsonError{
Message: aghos.Unsupported("dhcp").Error(),
})
if err != nil {
log.Debug("writing 501 json response: %s", err)
}
}
// registerHandlers sets the handlers for DHCP HTTP API that always respond with
// an HTTP 501, since DHCP server doesn't work on Windows yet.
//
// TODO(a.garipov): This needs refactoring. We shouldn't even try and
// initialize a DHCP server on Windows, but there are currently too many
// interconnected parts--such as HTTP handlers and frontend--to make that work
// properly.
func (s *server) registerHandlers() {
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/status", s.notImplemented)
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/interfaces", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/set_config", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/find_active_dhcp", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset_leases", s.notImplemented)
}

View file

@ -1,23 +1,28 @@
//go:build windows
package dhcpd
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestServer_notImplemented(t *testing.T) {
s := &Server{}
h := s.notImplemented("never!")
s := &server{}
w := httptest.NewRecorder()
r, err := http.NewRequest(http.MethodGet, "/unsupported", nil)
require.NoError(t, err)
h(w, r)
s.notImplemented(w, r)
assert.Equal(t, http.StatusNotImplemented, w.Code)
assert.Equal(t, `{"message":"never!"}`+"\n", w.Body.String())
wantStr := fmt.Sprintf("{%q:%q}", "message", aghos.Unsupported("dhcp"))
assert.JSONEq(t, wantStr, w.Body.String())
}

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package dhcpd
@ -197,10 +196,10 @@ func parseDHCPOption(s string) (code dhcpv4.OptionCode, val dhcpv4.OptionValue,
// prepareOptions builds the set of DHCP options according to host requirements
// document and values from conf.
func prepareOptions(conf V4ServerConf) (implicit, explicit dhcpv4.Options) {
func (s *v4Server) prepareOptions() {
// Set default values of host configuration parameters listed in Appendix A
// of RFC-2131.
implicit = dhcpv4.OptionsFromList(
s.implicitOpts = dhcpv4.OptionsFromList(
// IP-Layer Per Host
// An Internet host that includes embedded gateway code MUST have a
@ -376,14 +375,14 @@ func prepareOptions(conf V4ServerConf) (implicit, explicit dhcpv4.Options) {
// Set the Router Option to working subnet's IP since it's initialized
// with the address of the gateway.
dhcpv4.OptRouter(conf.subnet.IP),
dhcpv4.OptRouter(s.conf.subnet.IP),
dhcpv4.OptSubnetMask(conf.subnet.Mask),
dhcpv4.OptSubnetMask(s.conf.subnet.Mask),
)
// Set values for explicitly configured options.
explicit = dhcpv4.Options{}
for i, o := range conf.Options {
s.explicitOpts = dhcpv4.Options{}
for i, o := range s.conf.Options {
code, val, err := parseDHCPOption(o)
if err != nil {
log.Error("dhcpv4: bad option string at index %d: %s", i, err)
@ -391,17 +390,15 @@ func prepareOptions(conf V4ServerConf) (implicit, explicit dhcpv4.Options) {
continue
}
explicit.Update(dhcpv4.Option{Code: code, Value: val})
s.explicitOpts.Update(dhcpv4.Option{Code: code, Value: val})
// Remove those from the implicit options.
delete(implicit, code.Code())
delete(s.implicitOpts, code.Code())
}
log.Debug("dhcpv4: implicit options:\n%s", implicit.Summary(nil))
log.Debug("dhcpv4: explicit options:\n%s", explicit.Summary(nil))
log.Debug("dhcpv4: implicit options:\n%s", s.implicitOpts.Summary(nil))
log.Debug("dhcpv4: explicit options:\n%s", s.explicitOpts.Summary(nil))
if len(explicit) == 0 {
explicit = nil
if len(s.explicitOpts) == 0 {
s.explicitOpts = nil
}
return implicit, explicit
}

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package dhcpd
@ -250,17 +249,21 @@ func TestPrepareOptions(t *testing.T) {
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
implicit, explicit := prepareOptions(V4ServerConf{
s := &v4Server{
conf: &V4ServerConf{
// Just to avoid nil pointer dereference.
subnet: &net.IPNet{},
Options: tc.opts,
})
},
}
assert.Equal(t, tc.wantExplicit, explicit)
t.Run(tc.name, func(t *testing.T) {
s.prepareOptions()
for c := range explicit {
assert.NotContains(t, implicit, c)
assert.Equal(t, tc.wantExplicit, s.explicitOpts)
for c := range s.explicitOpts {
assert.NotContains(t, s.implicitOpts, c)
}
})
}

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package dhcpd

View file

@ -1,12 +0,0 @@
package dhcpd
import (
"time"
)
// Currently used defaults for ifaceDNSAddrs.
const (
defaultMaxAttempts int = 10
defaultBackoff time.Duration = 500 * time.Millisecond
)

View file

@ -1,5 +1,4 @@
//go:build windows
// +build windows
package dhcpd
@ -9,15 +8,19 @@ import "net"
type winServer struct{}
func (s *winServer) ResetLeases(_ []*Lease) (err error) { return nil }
func (s *winServer) GetLeases(_ GetLeasesFlags) (leases []*Lease) { return nil }
func (s *winServer) getLeasesRef() []*Lease { return nil }
func (s *winServer) AddStaticLease(_ *Lease) (err error) { return nil }
func (s *winServer) RemoveStaticLease(_ *Lease) (err error) { return nil }
func (s *winServer) FindMACbyIP(ip net.IP) (mac net.HardwareAddr) { return nil }
func (s *winServer) WriteDiskConfig4(c *V4ServerConf) {}
func (s *winServer) WriteDiskConfig6(c *V6ServerConf) {}
func (s *winServer) Start() (err error) { return nil }
func (s *winServer) Stop() (err error) { return nil }
func v4Create(conf V4ServerConf) (DHCPServer, error) { return &winServer{}, nil }
func v6Create(conf V6ServerConf) (DHCPServer, error) { return &winServer{}, nil }
// type check
var _ DHCPServer = winServer{}
func (winServer) ResetLeases(_ []*Lease) (err error) { return nil }
func (winServer) GetLeases(_ GetLeasesFlags) (leases []*Lease) { return nil }
func (winServer) getLeasesRef() []*Lease { return nil }
func (winServer) AddStaticLease(_ *Lease) (err error) { return nil }
func (winServer) RemoveStaticLease(_ *Lease) (err error) { return nil }
func (winServer) FindMACbyIP(_ net.IP) (mac net.HardwareAddr) { return nil }
func (winServer) WriteDiskConfig4(_ *V4ServerConf) {}
func (winServer) WriteDiskConfig6(_ *V6ServerConf) {}
func (winServer) Start() (err error) { return nil }
func (winServer) Stop() (err error) { return nil }
func v4Create(_ *V4ServerConf) (s DHCPServer, err error) { return winServer{}, nil }
func v6Create(_ V6ServerConf) (s DHCPServer, err error) { return winServer{}, nil }

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package dhcpd
@ -30,8 +29,9 @@ import (
//
// TODO(a.garipov): Think about unifying this and v6Server.
type v4Server struct {
conf V4ServerConf
srv *server4.Server
conf *V4ServerConf
srv *server4.Server
// implicitOpts are the options listed in Appendix A of RFC 2131 initialized
// with default values. It must not have intersections with [explicitOpts].
@ -55,9 +55,15 @@ type v4Server struct {
leases []*Lease
}
func (s *v4Server) enabled() (ok bool) {
return s.conf != nil && s.conf.Enabled
}
// WriteDiskConfig4 - write configuration
func (s *v4Server) WriteDiskConfig4(c *V4ServerConf) {
*c = s.conf
if s.conf != nil {
*c = *s.conf
}
}
// WriteDiskConfig6 - write configuration
@ -114,8 +120,8 @@ func (s *v4Server) validHostnameForClient(cliHostname string, ip net.IP) (hostna
func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
if !s.conf.Enabled {
return
if s.conf == nil {
return nil
}
s.leasedOffsets = newBitSet()
@ -129,12 +135,7 @@ func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
err = s.addLease(l)
if err != nil {
// TODO(a.garipov): Wrap and bubble up the error.
log.Error(
"dhcpv4: reset: re-adding a lease for %s (%s): %s",
l.IP,
l.HWAddr,
err,
)
log.Error("dhcpv4: reset: re-adding a lease for %s (%s): %s", l.IP, l.HWAddr, err)
continue
}
@ -336,11 +337,19 @@ func (s *v4Server) rmLease(lease *Lease) (err error) {
return errors.Error("lease not found")
}
// ErrUnconfigured is returned from the server's method when it requires the
// server to be configured and it's not.
const ErrUnconfigured errors.Error = "server is unconfigured"
// AddStaticLease implements the DHCPServer interface for *v4Server. It is safe
// for concurrent use.
func (s *v4Server) AddStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
if s.conf == nil {
return ErrUnconfigured
}
ip := l.IP.To4()
if ip == nil {
return fmt.Errorf("invalid ip %q, only ipv4 is supported", l.IP)
@ -414,6 +423,10 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
func (s *v4Server) RemoveStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
if s.conf == nil {
return ErrUnconfigured
}
if len(l.IP) != 4 {
return fmt.Errorf("invalid IP")
}
@ -1086,12 +1099,6 @@ func (s *v4Server) packetHandler(conn net.PacketConn, peer net.Addr, req *dhcpv4
s.send(peer, conn, req, resp)
}
// minDHCPMsgSize is the minimum length of the encoded DHCP message in bytes
// according to RFC-2131.
//
// See https://datatracker.ietf.org/doc/html/rfc2131#section-2.
const minDHCPMsgSize = 576
// send writes resp for peer to conn considering the req's parameters according
// to RFC-2131.
//
@ -1133,16 +1140,6 @@ func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DH
}
pktData := resp.ToBytes()
pktLen := len(pktData)
if pktLen < minDHCPMsgSize {
// Expand the packet to match the minimum DHCP message length. Although
// the dhpcv4 package deals with the BOOTP's lower packet length
// constraint, it seems some clients expecting the length being at least
// 576 bytes as per RFC 2131 (and an obsolete RFC 1533).
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/4337.
pktData = append(pktData, make([]byte, minDHCPMsgSize-pktLen)...)
}
log.Debug("dhcpv4: sending %d bytes to %s: %s", len(pktData), peer, resp.Summary())
@ -1156,7 +1153,7 @@ func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DH
func (s *v4Server) Start() (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
if !s.conf.Enabled {
if !s.enabled() {
return nil
}
@ -1248,62 +1245,20 @@ func (s *v4Server) Stop() (err error) {
}
// Create DHCPv4 server
func v4Create(conf V4ServerConf) (srv DHCPServer, err error) {
s := &v4Server{}
s.conf = conf
s.leaseHosts = stringutil.NewSet()
// TODO(a.garipov): Don't use a disabled server in other places or just
// use an interface.
if !conf.Enabled {
return s, nil
func v4Create(conf *V4ServerConf) (srv *v4Server, err error) {
s := &v4Server{
leaseHosts: stringutil.NewSet(),
}
var routerIP net.IP
routerIP, err = tryTo4(s.conf.GatewayIP)
err = conf.Validate()
if err != nil {
return s, fmt.Errorf("dhcpv4: %w", err)
// TODO(a.garipov): Don't use a disabled server in other places or just
// use an interface.
return s, err
}
if s.conf.SubnetMask == nil {
return s, fmt.Errorf("dhcpv4: invalid subnet mask: %v", s.conf.SubnetMask)
}
subnetMask := make([]byte, 4)
copy(subnetMask, s.conf.SubnetMask.To4())
s.conf.subnet = &net.IPNet{
IP: routerIP,
Mask: subnetMask,
}
s.conf.broadcastIP = aghnet.BroadcastFromIPNet(s.conf.subnet)
s.conf.ipRange, err = newIPRange(conf.RangeStart, conf.RangeEnd)
if err != nil {
return s, fmt.Errorf("dhcpv4: %w", err)
}
if s.conf.ipRange.contains(routerIP) {
return s, fmt.Errorf("dhcpv4: gateway ip %v in the ip range: %v-%v",
routerIP,
conf.RangeStart,
conf.RangeEnd,
)
}
if !s.conf.subnet.Contains(conf.RangeStart) {
return s, fmt.Errorf("dhcpv4: range start %v is outside network %v",
conf.RangeStart,
s.conf.subnet,
)
}
if !s.conf.subnet.Contains(conf.RangeEnd) {
return s, fmt.Errorf("dhcpv4: range end %v is outside network %v",
conf.RangeEnd,
s.conf.subnet,
)
}
s.conf = &V4ServerConf{}
*s.conf = *conf
// TODO(a.garipov, d.seregin): Check that every lease is inside the IPRange.
s.leasedOffsets = newBitSet()
@ -1315,7 +1270,7 @@ func v4Create(conf V4ServerConf) (srv DHCPServer, err error) {
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)
}
s.implicitOpts, s.explicitOpts = prepareOptions(s.conf)
s.prepareOptions()
return s, nil
}

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package dhcpd
@ -30,19 +29,16 @@ var (
DefaultSubnetMask = net.IP{255, 255, 255, 0}
)
func notify4(flags uint32) {
}
// defaultV4ServerConf returns the default configuration for *v4Server to use in
// tests.
func defaultV4ServerConf() (conf V4ServerConf) {
return V4ServerConf{
func defaultV4ServerConf() (conf *V4ServerConf) {
return &V4ServerConf{
Enabled: true,
RangeStart: DefaultRangeStart,
RangeEnd: DefaultRangeEnd,
GatewayIP: DefaultGatewayIP,
SubnetMask: DefaultSubnetMask,
notify: notify4,
notify: testNotify,
dnsIPAddrs: []net.IP{DefaultSelfIP},
}
}
@ -350,13 +346,10 @@ func TestV4Server_handle_optionsPriority(t *testing.T) {
defer func() { s.implicitOpts.Update(dhcpv4.OptDNS(defaultIP)) }()
}
ss, err := v4Create(conf)
var err error
s, err = v4Create(conf)
require.NoError(t, err)
var ok bool
s, ok = ss.(*v4Server)
require.True(t, ok)
s.conf.dnsIPAddrs = []net.IP{defaultIP}
return s
@ -490,10 +483,9 @@ func TestV4Server_updateOptions(t *testing.T) {
require.NoError(t, err)
require.IsType(t, (*v4Server)(nil), s)
s4, _ := s.(*v4Server)
t.Run(tc.name, func(t *testing.T) {
s4.updateOptions(req, resp)
s.updateOptions(req, resp)
for c, v := range tc.wantOpts {
if v == nil {
@ -596,13 +588,9 @@ func TestV4DynamicLease_Get(t *testing.T) {
"82 ip 1.2.3.4",
}
var err error
sIface, err := v4Create(conf)
s, err := v4Create(conf)
require.NoError(t, err)
s, ok := sIface.(*v4Server)
require.True(t, ok)
s.conf.dnsIPAddrs = []net.IP{{192, 168, 10, 1}}
s.implicitOpts.Update(dhcpv4.OptDNS(s.conf.dnsIPAddrs...))

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package dhcpd
@ -173,7 +172,7 @@ func (s *v6Server) rmDynamicLease(lease *Lease) (err error) {
func (s *v6Server) AddStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
if len(l.IP) != 16 {
if len(l.IP) != net.IPv6len {
return fmt.Errorf("invalid IP")
}

View file

@ -1,5 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
//go:build darwin || freebsd || linux || openbsd
package dhcpd

View file

@ -127,9 +127,14 @@ type FilteringConfig struct {
// IpsetList is the ipset configuration that allows AdGuard Home to add
// IP addresses of the specified domain names to an ipset list. Syntax:
//
// DOMAIN[,DOMAIN].../IPSET_NAME
// DOMAIN[,DOMAIN].../IPSET_NAME
//
// This field is ignored if [IpsetListFileName] is set.
IpsetList []string `yaml:"ipset"`
// IpsetListFileName, if set, points to the file with ipset configuration.
// The format is the same as in [IpsetList].
IpsetListFileName string `yaml:"ipset_file"`
}
// TLSConfig is the TLS configuration for HTTPS, DNS-over-HTTPS, and DNS-over-TLS
@ -399,6 +404,26 @@ func setProxyUpstreamMode(
}
}
// prepareIpsetListSettings reads and prepares the ipset configuration either
// from a file or from the data in the configuration file.
func (s *Server) prepareIpsetListSettings() (err error) {
fn := s.conf.IpsetListFileName
if fn == "" {
return s.ipset.init(s.conf.IpsetList)
}
data, err := os.ReadFile(fn)
if err != nil {
return err
}
ipsets := stringutil.SplitTrimmed(string(data), "\n")
log.Debug("dns: using %d ipset rules from file %q", len(ipsets), fn)
return s.ipset.init(ipsets)
}
// prepareTLS - prepares TLS configuration for the DNS proxy
func (s *Server) prepareTLS(proxyConfig *proxy.Config) error {
if len(s.conf.CertificateChainData) == 0 || len(s.conf.PrivateKeyData) == 0 {

View file

@ -267,7 +267,7 @@ func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := &Server{
dhcpServer: &testDHCP{},
dhcpServer: testDHCP,
localDomainSuffix: defaultLocalDomainSuffix,
tableHostToIP: hostToIPTable{
"example." + defaultLocalDomainSuffix: knownIP,
@ -378,7 +378,7 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
for _, tc := range testCases {
s := &Server{
dhcpServer: &testDHCP{},
dhcpServer: testDHCP,
localDomainSuffix: tc.suffix,
tableHostToIP: hostToIPTable{
"example." + tc.suffix: knownIP,

View file

@ -58,10 +58,10 @@ type hostToIPTable = map[string]net.IP
//
// The zero Server is empty and ready for use.
type Server struct {
dnsProxy *proxy.Proxy // DNS proxy instance
dnsFilter *filtering.DNSFilter // DNS filter instance
dhcpServer dhcpd.ServerInterface // DHCP server instance (optional)
queryLog querylog.QueryLog // Query log instance
dnsProxy *proxy.Proxy // DNS proxy instance
dnsFilter *filtering.DNSFilter // DNS filter instance
dhcpServer dhcpd.Interface // DHCP server instance (optional)
queryLog querylog.QueryLog // Query log instance
stats stats.Interface
access *accessCtx
@ -110,7 +110,7 @@ type DNSCreateParams struct {
DNSFilter *filtering.DNSFilter
Stats stats.Interface
QueryLog querylog.QueryLog
DHCPServer dhcpd.ServerInterface
DHCPServer dhcpd.Interface
PrivateNets netutil.SubnetSet
Anonymizer *aghnet.IPMut
LocalDomain string
@ -446,10 +446,10 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
s.initDefaultSettings()
err = s.ipset.init(s.conf.IpsetList)
err = s.prepareIpsetListSettings()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
return fmt.Errorf("preparing ipset settings: %w", err)
}
err = s.prepareUpstreamSettings()

View file

@ -72,7 +72,7 @@ func createTestServer(
var err error
s, err = NewServer(DNSCreateParams{
DHCPServer: &testDHCP{},
DHCPServer: testDHCP,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
})
@ -776,7 +776,7 @@ func TestBlockedCustomIP(t *testing.T) {
f := filtering.New(&filtering.Config{}, filters)
s, err := NewServer(DNSCreateParams{
DHCPServer: &testDHCP{},
DHCPServer: testDHCP,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
})
@ -910,7 +910,7 @@ func TestRewrite(t *testing.T) {
f.SetEnabled(true)
s, err := NewServer(DNSCreateParams{
DHCPServer: &testDHCP{},
DHCPServer: testDHCP,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
})
@ -1005,26 +1005,36 @@ func publicKey(priv any) any {
}
}
type testDHCP struct{}
func (d *testDHCP) Enabled() (ok bool) { return true }
func (d *testDHCP) Leases(flags dhcpd.GetLeasesFlags) (leases []*dhcpd.Lease) {
return []*dhcpd.Lease{{
IP: net.IP{192, 168, 12, 34},
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
Hostname: "myhost",
}}
var testDHCP = &dhcpd.MockInterface{
OnStart: func() (err error) { panic("not implemented") },
OnStop: func() (err error) { panic("not implemented") },
OnEnabled: func() (ok bool) { return true },
OnLeases: func(flags dhcpd.GetLeasesFlags) (leases []*dhcpd.Lease) {
return []*dhcpd.Lease{{
IP: net.IP{192, 168, 12, 34},
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
Hostname: "myhost",
}}
},
OnSetOnLeaseChanged: func(olct dhcpd.OnLeaseChangedT) {},
OnFindMACbyIP: func(ip net.IP) (mac net.HardwareAddr) { panic("not implemented") },
OnWriteDiskConfig: func(c *dhcpd.ServerConfig) { panic("not implemented") },
}
func (d *testDHCP) SetOnLeaseChanged(onLeaseChanged dhcpd.OnLeaseChangedT) {}
// func (*testDHCP) Leases(flags dhcpd.GetLeasesFlags) (leases []*dhcpd.Lease) {
// return []*dhcpd.Lease{{
// IP: net.IP{192, 168, 12, 34},
// HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
// Hostname: "myhost",
// }}
// }
func TestPTRResponseFromDHCPLeases(t *testing.T) {
const localDomain = "lan"
s, err := NewServer(DNSCreateParams{
DNSFilter: filtering.New(&filtering.Config{}, nil),
DHCPServer: &testDHCP{},
DHCPServer: testDHCP,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
LocalDomain: localDomain,
})
@ -1097,7 +1107,7 @@ func TestPTRResponseFromHosts(t *testing.T) {
var s *Server
s, err = NewServer(DNSCreateParams{
DHCPServer: &testDHCP{},
DHCPServer: testDHCP,
DNSFilter: flt,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
})

View file

@ -39,7 +39,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
f.SetEnabled(true)
s, err := NewServer(DNSCreateParams{
DHCPServer: &testDHCP{},
DHCPServer: testDHCP,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
})

View file

@ -126,7 +126,7 @@ type clientsContainer struct {
allTags *stringutil.Set
// dhcpServer is used for looking up clients IP addresses by MAC addresses
dhcpServer *dhcpd.Server
dhcpServer dhcpd.Interface
// dnsServer is used for checking clients IP status access list status
dnsServer *dnsforward.Server
@ -146,7 +146,7 @@ type clientsContainer struct {
// Note: this function must be called only once
func (clients *clientsContainer) Init(
objects []*clientObject,
dhcpServer *dhcpd.Server,
dhcpServer dhcpd.Interface,
etcHosts *aghnet.HostsContainer,
arpdb aghnet.ARPDB,
) {

View file

@ -279,8 +279,6 @@ func TestClientsAddExisting(t *testing.T) {
t.Skip("skipping dhcp test on windows")
}
var err error
ip := net.IP{1, 2, 3, 4}
// First, init a DHCP server with a single static lease.
@ -296,13 +294,15 @@ func TestClientsAddExisting(t *testing.T) {
},
}
clients.dhcpServer, err = dhcpd.Create(config)
dhcpServer, err := dhcpd.Create(config)
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, func() (err error) {
return os.Remove("leases.db")
})
err = clients.dhcpServer.AddStaticLease(&dhcpd.Lease{
clients.dhcpServer = dhcpServer
err = dhcpServer.AddStaticLease(&dhcpd.Lease{
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: ip,
Hostname: "testhost",

View file

@ -433,9 +433,7 @@ func (c *configuration) write() (err error) {
}
if Context.dhcpServer != nil {
c := &dhcpd.ServerConfig{}
Context.dhcpServer.WriteDiskConfig(c)
config.DHCP = c
Context.dhcpServer.WriteDiskConfig(config.DHCP)
}
config.Clients.Persistent = Context.clients.forConfig()

View file

@ -52,7 +52,7 @@ type homeContext struct {
rdns *RDNS // rDNS module
whois *WHOIS // WHOIS module
dnsFilter *filtering.DNSFilter // DNS filtering module
dhcpServer *dhcpd.Server // DHCP module
dhcpServer dhcpd.Interface // DHCP module
auth *Auth // HTTP authentication module
filters Filtering // DNS filtering module
web *Web // Web (HTTP, HTTPS) module
@ -641,14 +641,9 @@ func configureLogger(args options) {
log.Fatalf("cannot initialize syslog: %s", err)
}
} else {
logFilePath := filepath.Join(Context.workDir, ls.File)
if filepath.IsAbs(ls.File) {
logFilePath = ls.File
}
_, err := os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644)
if err != nil {
log.Fatalf("cannot create a log file: %s", err)
logFilePath := ls.File
if !filepath.IsAbs(logFilePath) {
logFilePath = filepath.Join(Context.workDir, logFilePath)
}
log.SetOutput(&lumberjack.Logger{

View file

@ -1,5 +1,4 @@
//go:build linux
// +build linux
package home

View file

@ -1,5 +1,4 @@
//go:build openbsd
// +build openbsd
package home

View file

@ -1,5 +1,4 @@
//go:build !(openbsd || linux)
// +build !openbsd,!linux
package home

View file

@ -10,7 +10,7 @@ require (
github.com/kyoh86/looppointer v0.1.7
github.com/securego/gosec/v2 v2.13.1
golang.org/x/tools v0.1.13-0.20220803210227-8b9a1fbdf5c3
golang.org/x/vuln v0.0.0-20220902211423-27dd78d2ca39
golang.org/x/vuln v0.0.0-20220912202342-0ed43f12cb05
honnef.co/go/tools v0.3.3
mvdan.cc/gofumpt v0.3.1
mvdan.cc/unparam v0.0.0-20220831102321-2fc90a84c7ec
@ -25,10 +25,10 @@ require (
github.com/kyoh86/nolint v0.0.1 // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 // indirect
golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
golang.org/x/sys v0.0.0-20220906135438-9e1f76180b77 // indirect
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

View file

@ -55,8 +55,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 h1:Ic/qN6TEifvObMGQy72k0n1LlJr7DjWWEi+MOsDOiSk=
golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -86,8 +86,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220906135438-9e1f76180b77 h1:C1tElbkWrsSkn3IRl1GCW/gETw1TywWIPgwZtXTZbYg=
golang.org/x/sys v0.0.0-20220906135438-9e1f76180b77/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -102,8 +102,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.13-0.20220803210227-8b9a1fbdf5c3 h1:aE4T3aJwdCNz+s35ScSQYUzeGu7BOLDHZ1bBHVurqqY=
golang.org/x/tools v0.1.13-0.20220803210227-8b9a1fbdf5c3/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/vuln v0.0.0-20220902211423-27dd78d2ca39 h1:501+NfNjDh4IT4HOzdeezTOFD7njtY49aXJN1oY3E1s=
golang.org/x/vuln v0.0.0-20220902211423-27dd78d2ca39/go.mod h1:7tDfEDtOLlzHQRi4Yzfg5seVBSvouUIjyPzBx4q5CxQ=
golang.org/x/vuln v0.0.0-20220912202342-0ed43f12cb05 h1:NWQHMTdThZhCArzUbnu1Bh+l3LdwUfjZws+ivBR2sxM=
golang.org/x/vuln v0.0.0-20220912202342-0ed43f12cb05/go.mod h1:7tDfEDtOLlzHQRi4Yzfg5seVBSvouUIjyPzBx4q5CxQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View file

@ -1,5 +1,4 @@
//go:build !race
// +build !race
package version

View file

@ -1,5 +1,4 @@
//go:build race
// +build race
package version

View file

@ -4,6 +4,15 @@
## v0.108.0: API changes
## v0.107.12: API changes
### `GET /control/blocked_services/services`
* The new `GET /control/blocked_services/services` HTTP API allows inspecting
all available services.
## v0.107.7: API changes
### The new optional field `"ecs"` in `QueryLogItem`
* The new optional field `"ecs"` in `GET /control/querylog` contains the IP

View file

@ -874,6 +874,19 @@
'summary': 'Set (dis)allowed clients, blocked hosts, etc.'
'tags':
- 'clients'
'/blocked_services/services':
'get':
'tags':
- 'blocked_services'
'operationId': 'blockedServicesAvailableServices'
'summary': 'Get available services to use for blocking'
'responses':
'200':
'description': 'OK.'
'content':
'application/json':
'schema':
'$ref': '#/components/schemas/BlockedServicesArray'
'/blocked_services/list':
'get':
'tags':

View file

@ -218,7 +218,10 @@ exit_on_output gofumpt --extra -e -l .
"$GO" vet ./...
govulncheck ./...
# TODO(a.garipov): Reenable this once https://github.com/golang/go/issues/55035
# is fixed.
#
# govulncheck ./...
# Apply more lax standards to the code we haven't properly refactored yet.
gocyclo --over 17 ./internal/querylog/