Compare commits
2 Commits
v0.107.0-b
...
fix-stats-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02834e6b7b | ||
|
|
b45162a0f2 |
@@ -15,6 +15,8 @@ and this project adheres to
|
||||
|
||||
### Added
|
||||
|
||||
- DNS server IP addresses to the `mobileconfig` API responses ([#3568],
|
||||
[#3607]).
|
||||
- Setting the timeout for IP address pinging in the "Fastest IP address" mode
|
||||
through the new `fastest_timeout` field in the configuration file ([#1992]).
|
||||
- Static IP address detection on FreeBSD ([#3289]).
|
||||
@@ -114,9 +116,7 @@ In this release, the schema version has changed from 10 to 12.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Occasional panic during shutdown ([#3655]).
|
||||
- Addition of IPs into only one as opposed to all matching ipsets on Linux
|
||||
([#3638]).
|
||||
- Adding an IP into only one of the matching ipsets on Linux ([#3638]).
|
||||
- Removal of temporary filter files ([#3567]).
|
||||
- Panic when an upstream server responds with an empty question section
|
||||
([#3551]).
|
||||
@@ -203,7 +203,6 @@ In this release, the schema version has changed from 10 to 12.
|
||||
[#3579]: https://github.com/AdguardTeam/AdGuardHome/issues/3579
|
||||
[#3607]: https://github.com/AdguardTeam/AdGuardHome/issues/3607
|
||||
[#3638]: https://github.com/AdguardTeam/AdGuardHome/issues/3638
|
||||
[#3655]: https://github.com/AdguardTeam/AdGuardHome/issues/3655
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# Make sure to sync any changes with the branch overrides below.
|
||||
'variables':
|
||||
'channel': 'edge'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.6'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.3'
|
||||
|
||||
'stages':
|
||||
- 'Make release':
|
||||
@@ -266,7 +266,7 @@
|
||||
# need to build a few of these.
|
||||
'variables':
|
||||
'channel': 'beta'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.6'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.3'
|
||||
# release-vX.Y.Z branches are the branches from which the actual final release
|
||||
# is built.
|
||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||
@@ -276,4 +276,4 @@
|
||||
# are the ones that actually get released.
|
||||
'variables':
|
||||
'channel': 'release'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.6'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.3'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'key': 'AHBRTSPECS'
|
||||
'name': 'AdGuard Home - Build and run tests'
|
||||
'variables':
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.6'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.3'
|
||||
|
||||
'stages':
|
||||
- 'Tests':
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
"example_upstream_sdns": "možete koristiti <0>DNS Stamps</0> za <1>DNSCrypt</1> ili <2>DNS-over-HTTPS</2> rezolvere",
|
||||
"example_upstream_tcp": "zadani DNS (putem TCP)",
|
||||
"all_lists_up_to_date_toast": "Svi popisi su ažurirani",
|
||||
"updated_upstream_dns_toast": "Uzvodni poslužitelji uspješno su spremljeni",
|
||||
"updated_upstream_dns_toast": "Ažurirani su upstream DNS poslužitelji",
|
||||
"dns_test_ok_toast": "Odabrani DNS poslužitelji su trenutno aktivni",
|
||||
"dns_test_not_ok_toast": "\"{{key}}\" poslužitelja: ne može se upotrijebiti, provjerite jeste li to ispravno napisali",
|
||||
"unblock": "Odblokiraj",
|
||||
@@ -235,7 +235,7 @@
|
||||
"loading_table_status": "Učitavanje...",
|
||||
"page_table_footer_text": "Stranica",
|
||||
"rows_table_footer_text": "redova",
|
||||
"updated_custom_filtering_toast": "Prilagođena pravila uspješno su spremljena",
|
||||
"updated_custom_filtering_toast": "Ažurirana su prilagođena pravila filtriranja",
|
||||
"rule_removed_from_custom_filtering_toast": "Pravilo je uklonjeno iz prilagođenih pravila filtriranja: {{rule}}",
|
||||
"rule_added_to_custom_filtering_toast": "Pravilo je dodano u prilagođena pravila filtriranja: {{rule}}",
|
||||
"query_log_response_status": "Status: {{value}}",
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Potrebno je postaviti uređaj ili router da koristi DNS poslužitelj na sljedećim adresama:",
|
||||
"install_settings_all_interfaces": "Sva sučelja",
|
||||
"install_auth_title": "Autentikacija",
|
||||
"install_auth_desc": "Provjera autentičnosti lozinke na web-sučelje AdGuard Home admin mora biti konfigurirana. Čak i ako je AdGuard Home dostupan samo u vašoj lokalnoj mreži, i dalje je važno zaštititi ga od neograničenog pristupa.",
|
||||
"install_auth_desc": "Izrazito se preporučuje postavljanje autentikacije za web administratorsko sučelje AdGuard Home. Iako je dostupna samo u vašoj lokalnoj mreži, važno je zaštititi je od ne dozvoljenog pristupa.",
|
||||
"install_auth_username": "Korisničko ime",
|
||||
"install_auth_password": "Lozinka",
|
||||
"install_auth_confirm": "Potvrdi lozinku",
|
||||
@@ -503,7 +503,6 @@
|
||||
"statistics_clear_confirm": "Jeste li sigurni da želite poništiti statistiku?",
|
||||
"statistics_retention_confirm": "Jeste li sigurni da želite promijeniti zadržavanje statistike? Ako smanjite vrijednost intervala, neki će podaci biti izgubljeni",
|
||||
"statistics_cleared": "Statistika je uspješno uklonjenja",
|
||||
"statistics_enable": "Omogući statistiku",
|
||||
"interval_hours": "{{count}} sata/i",
|
||||
"interval_hours_plural": "{{count}} sata/i",
|
||||
"filters_configuration": "Postavke filtara",
|
||||
@@ -613,8 +612,6 @@
|
||||
"click_to_view_queries": "Kliknite za pregled upita",
|
||||
"port_53_faq_link": "Port 53 često zauzimaju usluge \"DNSStubListener\" ili \"systemd-resolved\". Molimo pročitajte <0>ove upute</0> o tome kako to riješiti.",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home odbaciti će sve DNS upite od ovog klijenta.",
|
||||
"filter_allowlist": "UPOZORENJE: Ova akcija će također isključiti pravilo \"{{disallowed_rule}}\" s popisa dopuštenih klijenata.",
|
||||
"last_rule_in_allowlist": "Ovaj klijent nije moguće onemogućiti jer će isključivanje pravila \"{{disallowed_rule}}\" ONEMOGUĆITI popis \"Dopušteni klijenti\".",
|
||||
"experimental": "Eksperimentalno",
|
||||
"use_saved_key": "Korištenje prethodno spremljenog ključa"
|
||||
"client_not_in_allowed_clients": "Klijent nije dopušten jer nije na popisu \"Dopuštenih klijenata\".",
|
||||
"experimental": "Eksperimentalno"
|
||||
}
|
||||
|
||||
@@ -112,8 +112,6 @@
|
||||
"for_last_24_hours": "în ultimele 24 ore",
|
||||
"for_last_days": "în ultima {{count}} zi",
|
||||
"for_last_days_plural": "pentru ultimele {{count}} zile",
|
||||
"stats_disabled": "Statisticile au fost dezactivate. Puteți să le porniți din <0>pagina de setări</0>.",
|
||||
"stats_disabled_short": "Statisticile au fost dezactivate",
|
||||
"no_domains_found": "Nu s-au găsit domenii",
|
||||
"requests_count": "Cont interogări",
|
||||
"top_blocked_domains": "Domeniile blocate cel mai des",
|
||||
@@ -208,7 +206,7 @@
|
||||
"example_upstream_sdns": "puteți utiliza <0>DNS Stamps</0> pentru rezolvere <1>DNSCrypt</1> sau <2>DNS-over-HTTPS</2>",
|
||||
"example_upstream_tcp": "DNS clasic (over TCP)",
|
||||
"all_lists_up_to_date_toast": "Toate listele sunt deja la zi",
|
||||
"updated_upstream_dns_toast": "Serverele din amonte au fost salvate cu succes",
|
||||
"updated_upstream_dns_toast": "Serverele DNS în amonte aduse la zi",
|
||||
"dns_test_ok_toast": "Serverele DNS specificate funcționează corect",
|
||||
"dns_test_not_ok_toast": "Serverul \"{{key}}\": nu a putut fi utilizat, verificați dacă l-ați scris corect",
|
||||
"unblock": "Deblocați",
|
||||
@@ -235,7 +233,7 @@
|
||||
"loading_table_status": "Se încarcă...",
|
||||
"page_table_footer_text": "Pagina",
|
||||
"rows_table_footer_text": "linii",
|
||||
"updated_custom_filtering_toast": "Regulile personalizate au fost salvate cu succes",
|
||||
"updated_custom_filtering_toast": "Reguli personalizate de filtrare aduse la zi",
|
||||
"rule_removed_from_custom_filtering_toast": "Regulă scoasă din regullei personalizate de filtrare: {{rule}}",
|
||||
"rule_added_to_custom_filtering_toast": "Regulă adăugată la regulile de filtrare personalizate: {{rule}}",
|
||||
"query_log_response_status": "Statut: {{value}}",
|
||||
@@ -306,7 +304,7 @@
|
||||
"install_settings_dns_desc": "Va trebui să configurați aparatele sau routerul pentru a utiliza serverul DNS pe următoarele adrese:",
|
||||
"install_settings_all_interfaces": "Toate interfețele",
|
||||
"install_auth_title": "Autentificare",
|
||||
"install_auth_desc": "Trebuie configurată autentificarea cu parolă la interfața web AdGuard Home admin. Chiar dacă AdGuard Home este accesibil numai în rețeaua locală, este important să îl protejați de accesul fără restricții.",
|
||||
"install_auth_desc": "Este foarte recomandat să configurați o parolă pentru accesul la interfața web de administrare AdGuard Home. Chiar dacă este accesibil numai în rețeaua dvs. locală, este încă important să îl protejați de accesul fără restricții.",
|
||||
"install_auth_username": "Nume utilizator",
|
||||
"install_auth_password": "Parola",
|
||||
"install_auth_confirm": "Confirmați parola",
|
||||
@@ -329,7 +327,7 @@
|
||||
"install_devices_windows_list_3": "În partea stângă a ecranului găsiți \"Schimbare setări adaptor\" și clicați pe el.",
|
||||
"install_devices_windows_list_4": "Selectați conexiunea activă, faceți clic dreapta pe ea și alegeți \"Proprietăți\".",
|
||||
"install_devices_windows_list_5": "Găsiți Internet Protocol Versiunea 4 (TCP/IPv4) din listă, selectați-l și apoi clicați din nou pe Proprietăți.",
|
||||
"install_devices_windows_list_6": "Alegeți „Utilizați următoarele adrese de server DNS” și introduceți adresele serverului dvs. AdGuard Home.",
|
||||
"install_devices_windows_list_6": "Alegeți Utilizați următoarele adrese de server DNS și introduceți adresele de server AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Clicați pe icoana Apple și accesați Preferințele Sistemului.",
|
||||
"install_devices_macos_list_2": "Clicați pe Network.",
|
||||
"install_devices_macos_list_3": "Selectați prima conexiune din listă și clicați pe Avansat.",
|
||||
@@ -503,7 +501,6 @@
|
||||
"statistics_clear_confirm": "Sunteți sigur că doriți să ștergeți statisticile?",
|
||||
"statistics_retention_confirm": "Sunteți sigur că doriți să schimbați păstrarea statisticilor? Dacă reduceți valoarea intervalului, unele date vor fi pierdute",
|
||||
"statistics_cleared": "Statisticile au fost șterse cu succes",
|
||||
"statistics_enable": "Activați statisticile",
|
||||
"interval_hours": "{{count}} oră",
|
||||
"interval_hours_plural": "{{count}} ore",
|
||||
"filters_configuration": "Configurația filtrelor",
|
||||
@@ -598,8 +595,6 @@
|
||||
"cache_ttl_min_override_desc": "Extinde valorile timp-de-viață scurte (secunde) primite de la serverul din amonte la stocarea în cache a răspunsurilor DNS",
|
||||
"cache_ttl_max_override_desc": "Setează o valoare maximă a timpului-de-viață (secunde) pentru intrările din memoria cache DNS",
|
||||
"ttl_cache_validation": "Valoarea TTL cache minimă trebuie să fie mai mică sau egală cu valoarea maximă",
|
||||
"cache_optimistic": "Caching optimistic",
|
||||
"cache_optimistic_desc": "Face ca AdGuard Home să răspundă din cache chiar și atunci când intrările au expirate și de asemenea, încearcă să le reîmprospăteze.",
|
||||
"filter_category_general": "General",
|
||||
"filter_category_security": "Securitate",
|
||||
"filter_category_regional": "Regional",
|
||||
@@ -613,8 +608,6 @@
|
||||
"click_to_view_queries": "Clicați pentru a vizualiza interogări",
|
||||
"port_53_faq_link": "Portul 53 este adesea ocupat de serviciile \"DNSStubListener\" sau \"systemd-resolved\". Vă rugăm să citiți <0>această instrucțiune</0> despre cum să rezolvați aceasta.",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home va renunța la toate interogările DNS de la acest client.",
|
||||
"filter_allowlist": "AVERTISMENT: Această acțiune va exclude și regula „{{disallowed_rule}}” din lista de clienți permiși.",
|
||||
"last_rule_in_allowlist": "Acest client nu poate fi exclus deoarece excluderea regulii „{{disallowed_rule}}” va DEZACTIVA lista „Clienți acceptați”.",
|
||||
"experimental": "Experimental",
|
||||
"use_saved_key": "Folosiți cheia salvată anterior"
|
||||
"client_not_in_allowed_clients": "Clientul nu este permis deoarece nu este în lista de \"Clienți permiși\".",
|
||||
"experimental": "Experimental"
|
||||
}
|
||||
|
||||
8
go.mod
8
go.mod
@@ -4,7 +4,7 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.39.8
|
||||
github.com/AdguardTeam/golibs v0.10.0
|
||||
github.com/AdguardTeam/golibs v0.9.3
|
||||
github.com/AdguardTeam/urlfilter v0.14.6
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.2
|
||||
@@ -12,14 +12,12 @@ require (
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-ping/ping v0.0.0-20210506233800-ff8be3320020
|
||||
github.com/google/go-cmp v0.5.5
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/google/renameio v1.0.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20210310193751-cfd4d47082c2
|
||||
github.com/kardianos/service v1.2.0
|
||||
github.com/lucas-clemente/quic-go v0.21.1
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7
|
||||
github.com/mdlayher/netlink v1.4.0
|
||||
github.com/mdlayher/raw v0.0.0-20210412142147-51b895745faf
|
||||
github.com/mdlayher/raw v0.0.0-20210412142147-51b895745faf // indirect
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/stretchr/objx v0.1.1 // indirect
|
||||
@@ -27,7 +25,7 @@ require (
|
||||
github.com/ti-mo/netfilter v0.4.0
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
|
||||
15
go.sum
15
go.sum
@@ -14,8 +14,8 @@ github.com/AdguardTeam/dnsproxy v0.39.8/go.mod h1:eDpJKAdkHORRwAedjuERv+7SWlcz4c
|
||||
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.9.2/go.mod h1:fCAMwPBJ8S7YMYbTWvYS+eeTLblP5E04IDtNAo7y7IY=
|
||||
github.com/AdguardTeam/golibs v0.10.0 h1:A7MXRfZ+ItpOyS9tWKtqrLj3vZtE9FJFC+dOVY/LcWs=
|
||||
github.com/AdguardTeam/golibs v0.10.0/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
||||
github.com/AdguardTeam/golibs v0.9.3 h1:noeKHJEzrSwxzX0Zi3USM3cXf1qQV99SO772jet/uEY=
|
||||
github.com/AdguardTeam/golibs v0.9.3/go.mod h1:fCAMwPBJ8S7YMYbTWvYS+eeTLblP5E04IDtNAo7y7IY=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/AdguardTeam/urlfilter v0.14.6 h1:emqoKZElooHACYehRBYENeKVN1a/rspxiqTIMYLuoIo=
|
||||
github.com/AdguardTeam/urlfilter v0.14.6/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
|
||||
@@ -93,8 +93,6 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU=
|
||||
@@ -270,7 +268,6 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -303,8 +300,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6 h1:Z04ewVs7JhXaYkmDhBERPi41gnltfQpMWDnTnQbaCqk=
|
||||
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -367,9 +364,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -381,7 +377,6 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
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=
|
||||
|
||||
68
internal/aghtime/duration.go
Normal file
68
internal/aghtime/duration.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// Package aghtime defines some types for convenient work with time values.
|
||||
package aghtime
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// Duration is a wrapper for time.Duration providing functionality for encoding.
|
||||
type Duration struct {
|
||||
// time.Duration is embedded here to avoid implementing all the methods.
|
||||
time.Duration
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface for Duration. It wraps
|
||||
// time.Duration.String method and additionally cuts off non-leading zero values
|
||||
// of minutes and seconds. Some values which are differ between the
|
||||
// implementations:
|
||||
//
|
||||
// Duration: "1m", time.Duration: "1m0s"
|
||||
// Duration: "1h", time.Duration: "1h0m0s"
|
||||
// Duration: "1h1m", time.Duration: "1h1m0s"
|
||||
//
|
||||
func (d Duration) String() (str string) {
|
||||
str = d.Duration.String()
|
||||
|
||||
const (
|
||||
tailMin = len(`0s`)
|
||||
tailMinSec = len(`0m0s`)
|
||||
|
||||
secsInHour = time.Hour / time.Second
|
||||
minsInHour = time.Hour / time.Minute
|
||||
)
|
||||
|
||||
switch rounded := d.Duration / time.Second; {
|
||||
case
|
||||
rounded == 0,
|
||||
rounded*time.Second != d.Duration,
|
||||
rounded%60 != 0:
|
||||
// Return the uncut value if it's either equal to zero or has
|
||||
// fractions of a second or even whole seconds in it.
|
||||
return str
|
||||
|
||||
case (rounded%secsInHour)/minsInHour != 0:
|
||||
return str[:len(str)-tailMin]
|
||||
|
||||
default:
|
||||
return str[:len(str)-tailMinSec]
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface for Duration.
|
||||
func (d Duration) MarshalText() (text []byte, err error) {
|
||||
return []byte(d.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface for
|
||||
// *Duration.
|
||||
//
|
||||
// TODO(e.burkov): Make it able to parse larger units like days.
|
||||
func (d *Duration) UnmarshalText(b []byte) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "unmarshaling duration: %w") }()
|
||||
|
||||
d.Duration, err = time.ParseDuration(string(b))
|
||||
|
||||
return err
|
||||
}
|
||||
240
internal/aghtime/duration_test.go
Normal file
240
internal/aghtime/duration_test.go
Normal file
@@ -0,0 +1,240 @@
|
||||
package aghtime
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestDuration_String(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
val time.Duration
|
||||
}{{
|
||||
name: "1s",
|
||||
val: time.Second,
|
||||
}, {
|
||||
name: "1m",
|
||||
val: time.Minute,
|
||||
}, {
|
||||
name: "1h",
|
||||
val: time.Hour,
|
||||
}, {
|
||||
name: "1m1s",
|
||||
val: time.Minute + time.Second,
|
||||
}, {
|
||||
name: "1h1m",
|
||||
val: time.Hour + time.Minute,
|
||||
}, {
|
||||
name: "1h0m1s",
|
||||
val: time.Hour + time.Second,
|
||||
}, {
|
||||
name: "1ms",
|
||||
val: time.Millisecond,
|
||||
}, {
|
||||
name: "1h0m0.001s",
|
||||
val: time.Hour + time.Millisecond,
|
||||
}, {
|
||||
name: "1.001s",
|
||||
val: time.Second + time.Millisecond,
|
||||
}, {
|
||||
name: "1m1.001s",
|
||||
val: time.Minute + time.Second + time.Millisecond,
|
||||
}, {
|
||||
name: "0s",
|
||||
val: 0,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
d := Duration{Duration: tc.val}
|
||||
assert.Equal(t, tc.name, d.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// durationEncodingTester is a helper struct to simplify testing different
|
||||
// Duration marshalling and unmarshaling cases.
|
||||
type durationEncodingTester struct {
|
||||
PtrMap map[string]*Duration `json:"ptr_map" yaml:"ptr_map"`
|
||||
PtrSlice []*Duration `json:"ptr_slice" yaml:"ptr_slice"`
|
||||
PtrValue *Duration `json:"ptr_value" yaml:"ptr_value"`
|
||||
PtrArray [1]*Duration `json:"ptr_array" yaml:"ptr_array"`
|
||||
Map map[string]Duration `json:"map" yaml:"map"`
|
||||
Slice []Duration `json:"slice" yaml:"slice"`
|
||||
Value Duration `json:"value" yaml:"value"`
|
||||
Array [1]Duration `json:"array" yaml:"array"`
|
||||
}
|
||||
|
||||
const nl = "\n"
|
||||
const (
|
||||
jsonStr = `{` +
|
||||
`"ptr_map":{"dur":"1ms"},` +
|
||||
`"ptr_slice":["1ms"],` +
|
||||
`"ptr_value":"1ms",` +
|
||||
`"ptr_array":["1ms"],` +
|
||||
`"map":{"dur":"1ms"},` +
|
||||
`"slice":["1ms"],` +
|
||||
`"value":"1ms",` +
|
||||
`"array":["1ms"]` +
|
||||
`}`
|
||||
yamlStr = `ptr_map:` + nl +
|
||||
` dur: 1ms` + nl +
|
||||
`ptr_slice:` + nl +
|
||||
`- 1ms` + nl +
|
||||
`ptr_value: 1ms` + nl +
|
||||
`ptr_array:` + nl +
|
||||
`- 1ms` + nl +
|
||||
`map:` + nl +
|
||||
` dur: 1ms` + nl +
|
||||
`slice:` + nl +
|
||||
`- 1ms` + nl +
|
||||
`value: 1ms` + nl +
|
||||
`array:` + nl +
|
||||
`- 1ms`
|
||||
)
|
||||
|
||||
// defaultTestDur is the default time.Duration value to be used throughout the tests of
|
||||
// Duration.
|
||||
const defaultTestDur = time.Millisecond
|
||||
|
||||
// checkFields verifies m's fields. It expects the m to be unmarshaled from
|
||||
// one of the constant strings above.
|
||||
func (m *durationEncodingTester) checkFields(t *testing.T, d Duration) {
|
||||
t.Run("pointers_map", func(t *testing.T) {
|
||||
require.NotNil(t, m.PtrMap)
|
||||
|
||||
fromPtrMap, ok := m.PtrMap["dur"]
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, fromPtrMap)
|
||||
|
||||
assert.Equal(t, d, *fromPtrMap)
|
||||
})
|
||||
|
||||
t.Run("pointers_slice", func(t *testing.T) {
|
||||
require.Len(t, m.PtrSlice, 1)
|
||||
|
||||
fromPtrSlice := m.PtrSlice[0]
|
||||
require.NotNil(t, fromPtrSlice)
|
||||
|
||||
assert.Equal(t, d, *fromPtrSlice)
|
||||
})
|
||||
|
||||
t.Run("pointers_array", func(t *testing.T) {
|
||||
fromPtrArray := m.PtrArray[0]
|
||||
require.NotNil(t, fromPtrArray)
|
||||
|
||||
assert.Equal(t, d, *fromPtrArray)
|
||||
})
|
||||
|
||||
t.Run("pointer_value", func(t *testing.T) {
|
||||
require.NotNil(t, m.PtrValue)
|
||||
|
||||
assert.Equal(t, d, *m.PtrValue)
|
||||
})
|
||||
|
||||
t.Run("map", func(t *testing.T) {
|
||||
fromMap, ok := m.Map["dur"]
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Equal(t, d, fromMap)
|
||||
})
|
||||
|
||||
t.Run("slice", func(t *testing.T) {
|
||||
require.Len(t, m.Slice, 1)
|
||||
|
||||
assert.Equal(t, d, m.Slice[0])
|
||||
})
|
||||
|
||||
t.Run("array", func(t *testing.T) {
|
||||
assert.Equal(t, d, m.Array[0])
|
||||
})
|
||||
|
||||
t.Run("value", func(t *testing.T) {
|
||||
assert.Equal(t, d, m.Value)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDuration_MarshalText(t *testing.T) {
|
||||
d := Duration{defaultTestDur}
|
||||
dPtr := &d
|
||||
|
||||
v := durationEncodingTester{
|
||||
PtrMap: map[string]*Duration{"dur": dPtr},
|
||||
PtrSlice: []*Duration{dPtr},
|
||||
PtrValue: dPtr,
|
||||
PtrArray: [1]*Duration{dPtr},
|
||||
Map: map[string]Duration{"dur": d},
|
||||
Slice: []Duration{d},
|
||||
Value: d,
|
||||
Array: [1]Duration{d},
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
t.Run("json", func(t *testing.T) {
|
||||
t.Cleanup(b.Reset)
|
||||
err := json.NewEncoder(b).Encode(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, jsonStr, b.String())
|
||||
})
|
||||
|
||||
t.Run("yaml", func(t *testing.T) {
|
||||
t.Cleanup(b.Reset)
|
||||
err := yaml.NewEncoder(b).Encode(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.YAMLEq(t, yamlStr, b.String(), b.String())
|
||||
})
|
||||
|
||||
t.Run("direct", func(t *testing.T) {
|
||||
data, err := d.MarshalText()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, []byte(defaultTestDur.String()), data)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDuration_UnmarshalText(t *testing.T) {
|
||||
d := Duration{defaultTestDur}
|
||||
var v *durationEncodingTester
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
v = &durationEncodingTester{}
|
||||
|
||||
r := strings.NewReader(jsonStr)
|
||||
err := json.NewDecoder(r).Decode(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
v.checkFields(t, d)
|
||||
})
|
||||
|
||||
t.Run("yaml", func(t *testing.T) {
|
||||
v = &durationEncodingTester{}
|
||||
|
||||
r := strings.NewReader(yamlStr)
|
||||
err := yaml.NewDecoder(r).Decode(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
v.checkFields(t, d)
|
||||
})
|
||||
|
||||
t.Run("direct", func(t *testing.T) {
|
||||
dd := &Duration{}
|
||||
|
||||
err := dd.UnmarshalText([]byte(d.String()))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, d, *dd)
|
||||
})
|
||||
|
||||
t.Run("bad_data", func(t *testing.T) {
|
||||
assert.Error(t, (&Duration{}).UnmarshalText([]byte(`abc`)))
|
||||
})
|
||||
}
|
||||
@@ -5,17 +5,33 @@ package dhcpd
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
)
|
||||
|
||||
// broadcast sends resp to the broadcast address specific for network interface.
|
||||
func (c *dhcpConn) broadcast(respData []byte, peer *net.UDPAddr) (n int, err error) {
|
||||
func (s *v4Server) broadcast(peer net.Addr, conn net.PacketConn, resp *dhcpv4.DHCPv4) {
|
||||
// peer is expected to be of type *net.UDPConn as the server4.NewServer
|
||||
// initializes it.
|
||||
udpPeer, ok := peer.(*net.UDPAddr)
|
||||
if !ok {
|
||||
log.Error("dhcpv4: peer is of unexpected type %T", peer)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
peer.IP = c.bcastIP
|
||||
// specific interface. On FreeBSD and OpenBSD conn.WriteTo causes
|
||||
// errors while writing to the addresses that belong to another
|
||||
// interface. So, use the broadcast address specific for the binded
|
||||
// interface.
|
||||
udpPeer.IP = s.conf.broadcastIP
|
||||
|
||||
return c.udpConn.WriteTo(respData, peer)
|
||||
log.Debug("dhcpv4: sending to %s: %s", peer, resp.Summary())
|
||||
|
||||
if _, err := conn.WriteTo(resp.ToBytes(), peer); err != nil {
|
||||
log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,17 @@ import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDHCPConn_Broadcast(t *testing.T) {
|
||||
func TestV4Server_Send_broadcast(t *testing.T) {
|
||||
b := &bytes.Buffer{}
|
||||
var peer *net.UDPAddr
|
||||
|
||||
udpConn := &fakePacketConn{
|
||||
conn := &fakePacketConn{
|
||||
writeTo: func(p []byte, addr net.Addr) (n int, err error) {
|
||||
udpPeer, ok := addr.(*net.UDPAddr)
|
||||
require.True(t, ok)
|
||||
@@ -30,22 +31,57 @@ func TestDHCPConn_Broadcast(t *testing.T) {
|
||||
return n, nil
|
||||
},
|
||||
}
|
||||
conn := &dhcpConn{
|
||||
udpConn: udpConn,
|
||||
bcastIP: net.IP{1, 2, 3, 255},
|
||||
}
|
||||
|
||||
defaultPeer := &net.UDPAddr{
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
// Use neither client nor server port.
|
||||
Port: 1234,
|
||||
}
|
||||
respData := (&dhcpv4.DHCPv4{}).ToBytes()
|
||||
s := &v4Server{
|
||||
conf: V4ServerConf{
|
||||
broadcastIP: net.IP{1, 2, 3, 255},
|
||||
},
|
||||
}
|
||||
|
||||
_, _ = conn.broadcast(respData, cloneUDPAddr(defaultPeer))
|
||||
testCases := []struct {
|
||||
name string
|
||||
req *dhcpv4.DHCPv4
|
||||
resp *dhcpv4.DHCPv4
|
||||
}{{
|
||||
name: "nak",
|
||||
req: &dhcpv4.DHCPv4{
|
||||
GatewayIPAddr: netutil.IPv4Zero(),
|
||||
},
|
||||
resp: &dhcpv4.DHCPv4{
|
||||
Options: dhcpv4.OptionsFromList(
|
||||
dhcpv4.OptMessageType(dhcpv4.MessageTypeNak),
|
||||
),
|
||||
},
|
||||
}, {
|
||||
name: "fully_unspecified",
|
||||
req: &dhcpv4.DHCPv4{
|
||||
GatewayIPAddr: netutil.IPv4Zero(),
|
||||
ClientIPAddr: netutil.IPv4Zero(),
|
||||
},
|
||||
resp: &dhcpv4.DHCPv4{
|
||||
Options: dhcpv4.OptionsFromList(
|
||||
dhcpv4.OptMessageType(dhcpv4.MessageTypeOffer),
|
||||
),
|
||||
},
|
||||
}}
|
||||
|
||||
assert.EqualValues(t, respData, b.Bytes())
|
||||
assert.Equal(t, &net.UDPAddr{
|
||||
IP: conn.bcastIP,
|
||||
Port: defaultPeer.Port,
|
||||
}, peer)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s.send(cloneUDPAddr(defaultPeer), conn, tc.req, tc.resp)
|
||||
assert.EqualValues(t, tc.resp.ToBytes(), b.Bytes())
|
||||
assert.Equal(t, &net.UDPAddr{
|
||||
IP: s.conf.broadcastIP,
|
||||
Port: defaultPeer.Port,
|
||||
Zone: defaultPeer.Zone,
|
||||
}, peer)
|
||||
})
|
||||
|
||||
b.Reset()
|
||||
peer = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,17 @@ package dhcpd
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
)
|
||||
|
||||
// broadcast sends resp to the broadcast address specific for network interface.
|
||||
func (c *dhcpConn) broadcast(respData []byte, peer *net.UDPAddr) (n int, err error) {
|
||||
func (s *v4Server) broadcast(peer net.Addr, conn net.PacketConn, resp *dhcpv4.DHCPv4) {
|
||||
respData := resp.ToBytes()
|
||||
|
||||
log.Debug("dhcpv4: sending to %s: %s", peer, resp.Summary())
|
||||
|
||||
// This write to 0xffffffff reverts some behavior changes made in
|
||||
// https://github.com/AdguardTeam/AdGuardHome/issues/3289. The DHCP
|
||||
// server should broadcast the message to 0xffffffff but it's
|
||||
@@ -19,13 +26,26 @@ func (c *dhcpConn) broadcast(respData []byte, peer *net.UDPAddr) (n int, err err
|
||||
// https://github.com/AdguardTeam/AdGuardHome/issues/3366.
|
||||
//
|
||||
// See also https://github.com/AdguardTeam/AdGuardHome/issues/3539.
|
||||
if n, err = c.udpConn.WriteTo(respData, peer); err != nil {
|
||||
return n, err
|
||||
if _, err := conn.WriteTo(respData, peer); err != nil {
|
||||
log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
|
||||
}
|
||||
|
||||
// peer is expected to be of type *net.UDPConn as the server4.NewServer
|
||||
// initializes it.
|
||||
udpPeer, ok := peer.(*net.UDPAddr)
|
||||
if !ok {
|
||||
log.Error("dhcpv4: peer is of unexpected type %T", peer)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Broadcast the message one more time using the interface-specific
|
||||
// broadcast address.
|
||||
peer.IP = c.bcastIP
|
||||
udpPeer.IP = s.conf.broadcastIP
|
||||
|
||||
return c.udpConn.WriteTo(respData, peer)
|
||||
log.Debug("dhcpv4: sending to %s: %s", peer, resp.Summary())
|
||||
|
||||
if _, err := conn.WriteTo(respData, peer); err != nil {
|
||||
log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,17 @@ import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDHCPConn_Broadcast(t *testing.T) {
|
||||
func TestV4Server_Send_broadcast(t *testing.T) {
|
||||
b := &bytes.Buffer{}
|
||||
var peers []*net.UDPAddr
|
||||
|
||||
udpConn := &fakePacketConn{
|
||||
conn := &fakePacketConn{
|
||||
writeTo: func(p []byte, addr net.Addr) (n int, err error) {
|
||||
udpPeer, ok := addr.(*net.UDPAddr)
|
||||
require.True(t, ok)
|
||||
@@ -30,27 +31,66 @@ func TestDHCPConn_Broadcast(t *testing.T) {
|
||||
return n, nil
|
||||
},
|
||||
}
|
||||
conn := &dhcpConn{
|
||||
udpConn: udpConn,
|
||||
bcastIP: net.IP{1, 2, 3, 255},
|
||||
}
|
||||
|
||||
defaultPeer := &net.UDPAddr{
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
// Use neither client nor server port.
|
||||
Port: 1234,
|
||||
}
|
||||
respData := (&dhcpv4.DHCPv4{}).ToBytes()
|
||||
s := &v4Server{
|
||||
conf: V4ServerConf{
|
||||
broadcastIP: net.IP{1, 2, 3, 255},
|
||||
},
|
||||
}
|
||||
|
||||
_, _ = conn.broadcast(respData, cloneUDPAddr(defaultPeer))
|
||||
testCases := []struct {
|
||||
name string
|
||||
req *dhcpv4.DHCPv4
|
||||
resp *dhcpv4.DHCPv4
|
||||
}{{
|
||||
name: "nak",
|
||||
req: &dhcpv4.DHCPv4{
|
||||
GatewayIPAddr: netutil.IPv4Zero(),
|
||||
},
|
||||
resp: &dhcpv4.DHCPv4{
|
||||
Options: dhcpv4.OptionsFromList(
|
||||
dhcpv4.OptMessageType(dhcpv4.MessageTypeNak),
|
||||
),
|
||||
},
|
||||
}, {
|
||||
name: "fully_unspecified",
|
||||
req: &dhcpv4.DHCPv4{
|
||||
GatewayIPAddr: netutil.IPv4Zero(),
|
||||
ClientIPAddr: netutil.IPv4Zero(),
|
||||
},
|
||||
resp: &dhcpv4.DHCPv4{
|
||||
Options: dhcpv4.OptionsFromList(
|
||||
dhcpv4.OptMessageType(dhcpv4.MessageTypeOffer),
|
||||
),
|
||||
},
|
||||
}}
|
||||
|
||||
// The same response is written twice but for different peers.
|
||||
assert.EqualValues(t, append(respData, respData...), b.Bytes())
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s.send(cloneUDPAddr(defaultPeer), conn, tc.req, tc.resp)
|
||||
|
||||
require.Len(t, peers, 2)
|
||||
// The same response is written twice.
|
||||
respData := tc.resp.ToBytes()
|
||||
assert.EqualValues(t, append(respData, respData...), b.Bytes())
|
||||
|
||||
assert.Equal(t, cloneUDPAddr(defaultPeer), peers[0])
|
||||
assert.Equal(t, &net.UDPAddr{
|
||||
IP: conn.bcastIP,
|
||||
Port: defaultPeer.Port,
|
||||
}, peers[1])
|
||||
require.Len(t, peers, 2)
|
||||
|
||||
assert.Equal(t, &net.UDPAddr{
|
||||
IP: defaultPeer.IP,
|
||||
Port: defaultPeer.Port,
|
||||
}, peers[0])
|
||||
assert.Equal(t, &net.UDPAddr{
|
||||
IP: s.conf.broadcastIP,
|
||||
Port: defaultPeer.Port,
|
||||
}, peers[1])
|
||||
})
|
||||
|
||||
b.Reset()
|
||||
peers = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4/server4"
|
||||
"github.com/mdlayher/ethernet"
|
||||
"github.com/mdlayher/raw"
|
||||
)
|
||||
|
||||
// dhcpUnicastAddr is the combination of MAC and IP addresses for responding to
|
||||
// the unconfigured host.
|
||||
type dhcpUnicastAddr struct {
|
||||
// raw.Addr is embedded here to make *dhcpUcastAddr a net.Addr without
|
||||
// actually implementing all methods. It also contains the client's
|
||||
// hardware address.
|
||||
raw.Addr
|
||||
|
||||
// yiaddr is an IP address just allocated by server for the host.
|
||||
yiaddr net.IP
|
||||
}
|
||||
|
||||
// dhcpConn is the net.PacketConn capable of handling both net.UDPAddr and
|
||||
// net.HardwareAddr.
|
||||
type dhcpConn struct {
|
||||
// udpConn is the connection for UDP addresses.
|
||||
udpConn net.PacketConn
|
||||
// bcastIP is the broadcast address specific for the configured
|
||||
// interface's subnet.
|
||||
bcastIP net.IP
|
||||
|
||||
// rawConn is the connection for MAC addresses.
|
||||
rawConn net.PacketConn
|
||||
// srcMAC is the hardware address of the configured network interface.
|
||||
srcMAC net.HardwareAddr
|
||||
// srcIP is the IP address of the configured network interface.
|
||||
srcIP net.IP
|
||||
}
|
||||
|
||||
// newDHCPConn creates the special connection for DHCP server.
|
||||
func (s *v4Server) newDHCPConn(ifi *net.Interface) (c net.PacketConn, err error) {
|
||||
// Create the raw connection.
|
||||
var ucast net.PacketConn
|
||||
if ucast, err = raw.ListenPacket(ifi, uint16(ethernet.EtherTypeIPv4), nil); err != nil {
|
||||
return nil, fmt.Errorf("creating raw udp connection: %w", err)
|
||||
}
|
||||
|
||||
// Create the UDP connection.
|
||||
var bcast net.PacketConn
|
||||
bcast, err = server4.NewIPv4UDPConn(ifi.Name, &net.UDPAddr{
|
||||
// TODO(e.burkov): Listening on zeroes makes the server handle
|
||||
// requests from all the interfaces. Inspect the ways to
|
||||
// specify the interface-specific listening addresses.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3539.
|
||||
IP: net.IP{0, 0, 0, 0},
|
||||
Port: dhcpv4.ServerPort,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating ipv4 udp connection: %w", err)
|
||||
}
|
||||
|
||||
return &dhcpConn{
|
||||
udpConn: bcast,
|
||||
bcastIP: s.conf.broadcastIP,
|
||||
rawConn: ucast,
|
||||
srcMAC: ifi.HardwareAddr,
|
||||
srcIP: s.conf.dnsIPAddrs[0],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// wrapErrs is a helper to wrap the errors from two independent underlying
|
||||
// connections.
|
||||
func (c *dhcpConn) wrapErrs(action string, udpConnErr, rawConnErr error) (err error) {
|
||||
switch {
|
||||
case udpConnErr != nil && rawConnErr != nil:
|
||||
return errors.List(fmt.Sprintf("%s both connections", action), udpConnErr, rawConnErr)
|
||||
case udpConnErr != nil:
|
||||
return fmt.Errorf("%s udp connection: %w", action, udpConnErr)
|
||||
case rawConnErr != nil:
|
||||
return fmt.Errorf("%s raw connection: %w", action, rawConnErr)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo implements net.PacketConn for *dhcpConn. It selects the underlying
|
||||
// connection to write to based on the type of addr.
|
||||
func (c *dhcpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
switch addr := addr.(type) {
|
||||
case *dhcpUnicastAddr:
|
||||
// Unicast the message to the client's MAC address. Use the raw
|
||||
// connection.
|
||||
//
|
||||
// Note: unicasting is performed on the only network interface
|
||||
// that is configured. For now it may be not what users expect
|
||||
// so additionally broadcast the message via UDP connection.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3539.
|
||||
var rerr error
|
||||
n, rerr = c.unicast(p, addr)
|
||||
|
||||
_, uerr := c.broadcast(p, &net.UDPAddr{
|
||||
IP: netutil.IPv4bcast(),
|
||||
Port: dhcpv4.ClientPort,
|
||||
})
|
||||
|
||||
return n, c.wrapErrs("writing to", uerr, rerr)
|
||||
case *net.UDPAddr:
|
||||
if addr.IP.Equal(net.IPv4bcast) {
|
||||
// Broadcast the message for the client which supports
|
||||
// it. Use the UDP connection.
|
||||
return c.broadcast(p, addr)
|
||||
}
|
||||
|
||||
// Unicast the message to the client's IP address. Use the UDP
|
||||
// connection.
|
||||
return c.udpConn.WriteTo(p, addr)
|
||||
default:
|
||||
return 0, fmt.Errorf("peer is of unexpected type %T", addr)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFrom implements net.PacketConn for *dhcpConn.
|
||||
func (c *dhcpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return c.udpConn.ReadFrom(p)
|
||||
}
|
||||
|
||||
// unicast wraps respData with required frames and writes it to the peer.
|
||||
func (c *dhcpConn) unicast(respData []byte, peer *dhcpUnicastAddr) (n int, err error) {
|
||||
var data []byte
|
||||
data, err = c.buildEtherPkt(respData, peer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return c.rawConn.WriteTo(data, &peer.Addr)
|
||||
}
|
||||
|
||||
// Close implements net.PacketConn for *dhcpConn.
|
||||
func (c *dhcpConn) Close() (err error) {
|
||||
rerr := c.rawConn.Close()
|
||||
if errors.Is(rerr, os.ErrClosed) {
|
||||
// Ignore the error since the actual file is closed already.
|
||||
rerr = nil
|
||||
}
|
||||
|
||||
return c.wrapErrs("closing", c.udpConn.Close(), rerr)
|
||||
}
|
||||
|
||||
// LocalAddr implements net.PacketConn for *dhcpConn.
|
||||
func (c *dhcpConn) LocalAddr() (a net.Addr) {
|
||||
return c.udpConn.LocalAddr()
|
||||
}
|
||||
|
||||
// SetDeadline implements net.PacketConn for *dhcpConn.
|
||||
func (c *dhcpConn) SetDeadline(t time.Time) (err error) {
|
||||
return c.wrapErrs("setting deadline on", c.udpConn.SetDeadline(t), c.rawConn.SetDeadline(t))
|
||||
}
|
||||
|
||||
// SetReadDeadline implements net.PacketConn for *dhcpConn.
|
||||
func (c *dhcpConn) SetReadDeadline(t time.Time) error {
|
||||
return c.wrapErrs(
|
||||
"setting reading deadline on",
|
||||
c.udpConn.SetReadDeadline(t),
|
||||
c.rawConn.SetReadDeadline(t),
|
||||
)
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements net.PacketConn for *dhcpConn.
|
||||
func (c *dhcpConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.wrapErrs(
|
||||
"setting writing deadline on",
|
||||
c.udpConn.SetWriteDeadline(t),
|
||||
c.rawConn.SetWriteDeadline(t),
|
||||
)
|
||||
}
|
||||
|
||||
// ipv4DefaultTTL is the default Time to Live value as recommended by
|
||||
// RFC-1700 (https://datatracker.ietf.org/doc/html/rfc1700) in seconds.
|
||||
const ipv4DefaultTTL = 64
|
||||
|
||||
// errInvalidPktDHCP is returned when the provided payload is not a valid DHCP
|
||||
// packet.
|
||||
const errInvalidPktDHCP errors.Error = "packet is not a valid dhcp packet"
|
||||
|
||||
// buildEtherPkt wraps the payload with IPv4, UDP and Ethernet frames. The
|
||||
// payload is expected to be an encoded DHCP packet.
|
||||
func (c *dhcpConn) buildEtherPkt(payload []byte, peer *dhcpUnicastAddr) (pkt []byte, err error) {
|
||||
dhcpLayer := gopacket.NewPacket(payload, layers.LayerTypeDHCPv4, gopacket.DecodeOptions{
|
||||
NoCopy: true,
|
||||
}).Layer(layers.LayerTypeDHCPv4)
|
||||
|
||||
// Check if the decoding succeeded and the resulting layer doesn't
|
||||
// contain any errors. It should guarantee panic-safe converting of the
|
||||
// layer into gopacket.SerializableLayer.
|
||||
if dhcpLayer == nil || dhcpLayer.LayerType() != layers.LayerTypeDHCPv4 {
|
||||
return nil, errInvalidPktDHCP
|
||||
}
|
||||
|
||||
udpLayer := &layers.UDP{
|
||||
SrcPort: dhcpv4.ServerPort,
|
||||
DstPort: dhcpv4.ClientPort,
|
||||
}
|
||||
ipv4Layer := &layers.IPv4{
|
||||
Version: uint8(layers.IPProtocolIPv4),
|
||||
Flags: layers.IPv4DontFragment,
|
||||
TTL: ipv4DefaultTTL,
|
||||
Protocol: layers.IPProtocolUDP,
|
||||
SrcIP: c.srcIP,
|
||||
DstIP: peer.yiaddr,
|
||||
}
|
||||
|
||||
// Ignore the error since it's only returned for invalid network layer's
|
||||
// type.
|
||||
_ = udpLayer.SetNetworkLayerForChecksum(ipv4Layer)
|
||||
ethLayer := &layers.Ethernet{
|
||||
SrcMAC: c.srcMAC,
|
||||
DstMAC: peer.HardwareAddr,
|
||||
EthernetType: layers.EthernetTypeIPv4,
|
||||
}
|
||||
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
err = gopacket.SerializeLayers(buf, gopacket.SerializeOptions{
|
||||
FixLengths: true,
|
||||
ComputeChecksums: true,
|
||||
}, ethLayer, ipv4Layer, udpLayer, dhcpLayer.(gopacket.SerializableLayer))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("serializing layers: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/mdlayher/raw"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDHCPConn_WriteTo_common(t *testing.T) {
|
||||
respData := (&dhcpv4.DHCPv4{}).ToBytes()
|
||||
udpAddr := &net.UDPAddr{
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
Port: dhcpv4.ClientPort,
|
||||
}
|
||||
|
||||
t.Run("unicast_ip", func(t *testing.T) {
|
||||
writeTo := func(_ []byte, addr net.Addr) (_ int, _ error) {
|
||||
assert.Equal(t, udpAddr, addr)
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
conn := &dhcpConn{udpConn: &fakePacketConn{writeTo: writeTo}}
|
||||
|
||||
_, err := conn.WriteTo(respData, udpAddr)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("unexpected_addr_type", func(t *testing.T) {
|
||||
type unexpectedAddrType struct {
|
||||
net.Addr
|
||||
}
|
||||
|
||||
conn := &dhcpConn{}
|
||||
n, err := conn.WriteTo(nil, &unexpectedAddrType{})
|
||||
require.Error(t, err)
|
||||
|
||||
assert.True(t, strings.Contains(err.Error(), "peer is of unexpected type"))
|
||||
assert.Zero(t, n)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildEtherPkt(t *testing.T) {
|
||||
conn := &dhcpConn{
|
||||
srcMAC: net.HardwareAddr{1, 2, 3, 4, 5, 6},
|
||||
srcIP: net.IP{1, 2, 3, 4},
|
||||
}
|
||||
peer := &dhcpUnicastAddr{
|
||||
Addr: raw.Addr{HardwareAddr: net.HardwareAddr{6, 5, 4, 3, 2, 1}},
|
||||
yiaddr: net.IP{4, 3, 2, 1},
|
||||
}
|
||||
payload := (&dhcpv4.DHCPv4{}).ToBytes()
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
pkt, err := conn.buildEtherPkt(payload, peer)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotEmpty(t, pkt)
|
||||
|
||||
actualPkt := gopacket.NewPacket(pkt, layers.LayerTypeEthernet, gopacket.DecodeOptions{
|
||||
NoCopy: true,
|
||||
})
|
||||
require.NotNil(t, actualPkt)
|
||||
|
||||
wantTypes := []gopacket.LayerType{
|
||||
layers.LayerTypeEthernet,
|
||||
layers.LayerTypeIPv4,
|
||||
layers.LayerTypeUDP,
|
||||
layers.LayerTypeDHCPv4,
|
||||
}
|
||||
actualLayers := actualPkt.Layers()
|
||||
require.Len(t, actualLayers, len(wantTypes))
|
||||
|
||||
for i, wantType := range wantTypes {
|
||||
layer := actualLayers[i]
|
||||
require.NotNil(t, layer)
|
||||
|
||||
assert.Equal(t, wantType, layer.LayerType())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("non-serializable", func(t *testing.T) {
|
||||
// Create an invalid DHCP packet.
|
||||
invalidPayload := []byte{1, 2, 3, 4}
|
||||
pkt, err := conn.buildEtherPkt(invalidPayload, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
assert.ErrorIs(t, err, errInvalidPktDHCP)
|
||||
assert.Empty(t, pkt)
|
||||
})
|
||||
|
||||
t.Run("serializing_error", func(t *testing.T) {
|
||||
// Create a peer with invalid MAC.
|
||||
badPeer := &dhcpUnicastAddr{
|
||||
Addr: raw.Addr{HardwareAddr: net.HardwareAddr{5, 4, 3, 2, 1}},
|
||||
yiaddr: net.IP{4, 3, 2, 1},
|
||||
}
|
||||
|
||||
pkt, err := conn.buildEtherPkt(payload, badPeer)
|
||||
require.Error(t, err)
|
||||
|
||||
assert.Empty(t, pkt)
|
||||
})
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func TestNormalizeLeases(t *testing.T) {
|
||||
}
|
||||
|
||||
// cloneUDPAddr returns a deep copy of a.
|
||||
func cloneUDPAddr(a *net.UDPAddr) (clone *net.UDPAddr) {
|
||||
func cloneUDPAddr(a *net.UDPAddr) (copy *net.UDPAddr) {
|
||||
return &net.UDPAddr{
|
||||
IP: netutil.CloneIP(a.IP),
|
||||
Port: a.Port,
|
||||
|
||||
@@ -157,8 +157,8 @@ func prepareOptions(conf V4ServerConf) (opts dhcpv4.Options) {
|
||||
dhcpv4.OptionPerformRouterDiscovery.Code(): []byte{1},
|
||||
// The all-routers address is preferred wherever possible, see
|
||||
// https://datatracker.ietf.org/doc/html/rfc1256#section-5.1.
|
||||
dhcpv4.OptionRouterSolicitationAddress.Code(): netutil.IPv4allrouter(),
|
||||
dhcpv4.OptionBroadcastAddress.Code(): netutil.IPv4bcast(),
|
||||
dhcpv4.OptionRouterSolicitationAddress.Code(): net.IPv4allrouter.To4(),
|
||||
dhcpv4.OptionBroadcastAddress.Code(): net.IPv4bcast.To4(),
|
||||
|
||||
// Link-Layer Per Interface
|
||||
|
||||
|
||||
@@ -16,11 +16,9 @@ import (
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/go-ping/ping"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4/server4"
|
||||
"github.com/mdlayher/raw"
|
||||
)
|
||||
|
||||
// v4Server is a DHCPv4 server.
|
||||
@@ -957,44 +955,43 @@ func (s *v4Server) packetHandler(conn net.PacketConn, peer net.Addr, req *dhcpv4
|
||||
//
|
||||
// See https://datatracker.ietf.org/doc/html/rfc2131#section-4.1.
|
||||
func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) {
|
||||
switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); {
|
||||
case giaddr != nil && !giaddr.IsUnspecified():
|
||||
var unicast bool
|
||||
if giaddr := req.GatewayIPAddr; giaddr != nil && !giaddr.IsUnspecified() {
|
||||
// Send any return messages to the server port on the BOOTP
|
||||
// relay agent whose address appears in giaddr.
|
||||
peer = &net.UDPAddr{
|
||||
IP: giaddr,
|
||||
Port: dhcpv4.ServerPort,
|
||||
}
|
||||
if mtype == dhcpv4.MessageTypeNak {
|
||||
// Set the broadcast bit in the DHCPNAK, so that the
|
||||
// relay agent broadcasted it to the client, because the
|
||||
// client may not have a correct network address or
|
||||
// subnet mask, and the client may not be answering ARP
|
||||
// requests.
|
||||
resp.SetBroadcast()
|
||||
}
|
||||
case mtype == dhcpv4.MessageTypeNak:
|
||||
unicast = true
|
||||
} else if mtype := resp.MessageType(); mtype == dhcpv4.MessageTypeNak {
|
||||
// Broadcast any DHCPNAK messages to 0xffffffff.
|
||||
case ciaddr != nil && !ciaddr.IsUnspecified():
|
||||
} else if ciaddr := req.ClientIPAddr; ciaddr != nil && !ciaddr.IsUnspecified() {
|
||||
// Unicast DHCPOFFER and DHCPACK messages to the address in
|
||||
// ciaddr.
|
||||
peer = &net.UDPAddr{
|
||||
IP: ciaddr,
|
||||
Port: dhcpv4.ClientPort,
|
||||
}
|
||||
case !req.IsBroadcast() && req.ClientHWAddr != nil:
|
||||
// Unicast DHCPOFFER and DHCPACK messages to the client's
|
||||
// hardware address and yiaddr.
|
||||
peer = &dhcpUnicastAddr{
|
||||
Addr: raw.Addr{HardwareAddr: req.ClientHWAddr},
|
||||
yiaddr: resp.YourIPAddr,
|
||||
}
|
||||
default:
|
||||
// Go on since peer is already set to broadcast.
|
||||
unicast = true
|
||||
}
|
||||
|
||||
// TODO(e.burkov): Unicast the message to the actual link-layer address
|
||||
// of the client if broadcast bit is not set. Perhaps, use custom
|
||||
// connection when creating the server.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3443.
|
||||
|
||||
if !unicast {
|
||||
s.broadcast(peer, conn, resp)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("dhcpv4: sending to %s: %s", peer, resp.Summary())
|
||||
if _, err := conn.WriteTo(resp.ToBytes(), peer); err != nil {
|
||||
|
||||
_, err := conn.WriteTo(resp.ToBytes(), peer)
|
||||
if err != nil {
|
||||
log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
|
||||
}
|
||||
}
|
||||
@@ -1032,18 +1029,11 @@ func (s *v4Server) Start() (err error) {
|
||||
|
||||
s.conf.dnsIPAddrs = dnsIPAddrs
|
||||
|
||||
var c net.PacketConn
|
||||
if c, err = s.newDHCPConn(iface); err != nil {
|
||||
return err
|
||||
laddr := &net.UDPAddr{
|
||||
IP: net.IP{0, 0, 0, 0},
|
||||
Port: dhcpv4.ServerPort,
|
||||
}
|
||||
|
||||
s.srv, err = server4.NewServer(
|
||||
iface.Name,
|
||||
nil,
|
||||
s.packetHandler,
|
||||
server4.WithConn(c),
|
||||
server4.WithDebugLogger(),
|
||||
)
|
||||
s.srv, err = server4.NewServer(iface.Name, laddr, s.packetHandler, server4.WithDebugLogger())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1127,7 +1117,7 @@ func v4Create(conf V4ServerConf) (srv DHCPServer, err error) {
|
||||
s.leasedOffsets = newBitSet()
|
||||
|
||||
if conf.LeaseDuration == 0 {
|
||||
s.conf.leaseTime = timeutil.Day
|
||||
s.conf.leaseTime = time.Hour * 24
|
||||
s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds())
|
||||
} else {
|
||||
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/mdlayher/raw"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -389,107 +390,67 @@ func (fc *fakePacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
return fc.writeTo(p, addr)
|
||||
}
|
||||
|
||||
func TestV4Server_Send(t *testing.T) {
|
||||
s := &v4Server{}
|
||||
func TestV4Server_Send_unicast(t *testing.T) {
|
||||
b := &bytes.Buffer{}
|
||||
var peer *net.UDPAddr
|
||||
|
||||
var (
|
||||
defaultIP = net.IP{99, 99, 99, 99}
|
||||
knownIP = net.IP{4, 2, 4, 2}
|
||||
knownMAC = net.HardwareAddr{6, 5, 4, 3, 2, 1}
|
||||
)
|
||||
conn := &fakePacketConn{
|
||||
writeTo: func(p []byte, addr net.Addr) (n int, err error) {
|
||||
udpPeer, ok := addr.(*net.UDPAddr)
|
||||
require.True(t, ok)
|
||||
|
||||
peer = cloneUDPAddr(udpPeer)
|
||||
|
||||
n, err = b.Write(p)
|
||||
require.NoError(t, err)
|
||||
|
||||
return n, nil
|
||||
},
|
||||
}
|
||||
|
||||
defaultPeer := &net.UDPAddr{
|
||||
IP: defaultIP,
|
||||
// Use neither client nor server port to check it actually
|
||||
// changed.
|
||||
Port: dhcpv4.ClientPort + dhcpv4.ServerPort,
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
// Use neither client nor server port.
|
||||
Port: 1234,
|
||||
}
|
||||
defaultResp := &dhcpv4.DHCPv4{}
|
||||
defaultResp := &dhcpv4.DHCPv4{
|
||||
OpCode: dhcpv4.OpcodeBootReply,
|
||||
}
|
||||
s := &v4Server{}
|
||||
|
||||
testCases := []struct {
|
||||
want net.Addr
|
||||
req *dhcpv4.DHCPv4
|
||||
resp *dhcpv4.DHCPv4
|
||||
name string
|
||||
name string
|
||||
req *dhcpv4.DHCPv4
|
||||
wantPeer net.Addr
|
||||
}{{
|
||||
name: "giaddr",
|
||||
req: &dhcpv4.DHCPv4{GatewayIPAddr: knownIP},
|
||||
resp: defaultResp,
|
||||
want: &net.UDPAddr{
|
||||
IP: knownIP,
|
||||
name: "relay_agent",
|
||||
req: &dhcpv4.DHCPv4{
|
||||
GatewayIPAddr: defaultPeer.IP,
|
||||
},
|
||||
wantPeer: &net.UDPAddr{
|
||||
IP: defaultPeer.IP,
|
||||
Port: dhcpv4.ServerPort,
|
||||
},
|
||||
}, {
|
||||
name: "nak",
|
||||
req: &dhcpv4.DHCPv4{},
|
||||
resp: &dhcpv4.DHCPv4{
|
||||
Options: dhcpv4.OptionsFromList(
|
||||
dhcpv4.OptMessageType(dhcpv4.MessageTypeNak),
|
||||
),
|
||||
name: "known_client",
|
||||
req: &dhcpv4.DHCPv4{
|
||||
GatewayIPAddr: netutil.IPv4Zero(),
|
||||
ClientIPAddr: net.IP{2, 3, 4, 5},
|
||||
},
|
||||
want: defaultPeer,
|
||||
}, {
|
||||
name: "ciaddr",
|
||||
req: &dhcpv4.DHCPv4{ClientIPAddr: knownIP},
|
||||
resp: &dhcpv4.DHCPv4{},
|
||||
want: &net.UDPAddr{
|
||||
IP: knownIP,
|
||||
wantPeer: &net.UDPAddr{
|
||||
IP: net.IP{2, 3, 4, 5},
|
||||
Port: dhcpv4.ClientPort,
|
||||
},
|
||||
}, {
|
||||
name: "chaddr",
|
||||
req: &dhcpv4.DHCPv4{ClientHWAddr: knownMAC},
|
||||
resp: &dhcpv4.DHCPv4{YourIPAddr: knownIP},
|
||||
want: &dhcpUnicastAddr{
|
||||
Addr: raw.Addr{HardwareAddr: knownMAC},
|
||||
yiaddr: knownIP,
|
||||
},
|
||||
}, {
|
||||
name: "who_are_you",
|
||||
req: &dhcpv4.DHCPv4{},
|
||||
resp: &dhcpv4.DHCPv4{},
|
||||
want: defaultPeer,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
conn := &fakePacketConn{
|
||||
writeTo: func(_ []byte, addr net.Addr) (_ int, _ error) {
|
||||
assert.Equal(t, tc.want, addr)
|
||||
|
||||
return 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
s.send(cloneUDPAddr(defaultPeer), conn, tc.req, tc.resp)
|
||||
s.send(defaultPeer, conn, tc.req, defaultResp)
|
||||
assert.EqualValues(t, defaultResp.ToBytes(), b.Bytes())
|
||||
assert.Equal(t, tc.wantPeer, peer)
|
||||
})
|
||||
|
||||
b.Reset()
|
||||
peer = nil
|
||||
}
|
||||
|
||||
t.Run("giaddr_nak", func(t *testing.T) {
|
||||
req := &dhcpv4.DHCPv4{
|
||||
GatewayIPAddr: knownIP,
|
||||
}
|
||||
// Ensure the request is for unicast.
|
||||
req.SetUnicast()
|
||||
resp := &dhcpv4.DHCPv4{
|
||||
Options: dhcpv4.OptionsFromList(
|
||||
dhcpv4.OptMessageType(dhcpv4.MessageTypeNak),
|
||||
),
|
||||
}
|
||||
want := &net.UDPAddr{
|
||||
IP: req.GatewayIPAddr,
|
||||
Port: dhcpv4.ServerPort,
|
||||
}
|
||||
|
||||
conn := &fakePacketConn{
|
||||
writeTo: func(_ []byte, addr net.Addr) (n int, err error) {
|
||||
assert.Equal(t, want, addr)
|
||||
|
||||
return 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
s.send(cloneUDPAddr(defaultPeer), conn, req, resp)
|
||||
assert.True(t, resp.IsBroadcast())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/insomniacslk/dhcp/dhcpv6"
|
||||
"github.com/insomniacslk/dhcp/dhcpv6/server6"
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
@@ -708,7 +707,7 @@ func v6Create(conf V6ServerConf) (DHCPServer, error) {
|
||||
}
|
||||
|
||||
if conf.LeaseDuration == 0 {
|
||||
s.conf.leaseTime = timeutil.Day
|
||||
s.conf.leaseTime = time.Hour * 24
|
||||
s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds())
|
||||
} else {
|
||||
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
@@ -18,7 +19,6 @@ import (
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
)
|
||||
|
||||
@@ -90,7 +90,7 @@ type FilteringConfig struct {
|
||||
FastestAddr bool `yaml:"fastest_addr"` // use Fastest Address algorithm
|
||||
// FastestTimeout replaces the default timeout for dialing IP addresses
|
||||
// when FastestAddr is true.
|
||||
FastestTimeout timeutil.Duration `yaml:"fastest_timeout"`
|
||||
FastestTimeout aghtime.Duration `yaml:"fastest_timeout"`
|
||||
|
||||
// Access settings
|
||||
// --
|
||||
|
||||
@@ -540,16 +540,8 @@ func (s *Server) processUpstream(ctx *dnsContext) (rc resultCode) {
|
||||
}
|
||||
}
|
||||
|
||||
// Process the request further since it wasn't filtered.
|
||||
|
||||
prx := s.proxy()
|
||||
if prx == nil {
|
||||
ctx.err = srvClosedErr
|
||||
|
||||
return resultCodeError
|
||||
}
|
||||
|
||||
if ctx.err = prx.Resolve(d); ctx.err != nil {
|
||||
// request was not filtered so let it be processed further
|
||||
if ctx.err = s.dnsProxy.Resolve(d); ctx.err != nil {
|
||||
return resultCodeError
|
||||
}
|
||||
|
||||
|
||||
@@ -551,21 +551,6 @@ func (s *Server) IsRunning() bool {
|
||||
return s.isRunning
|
||||
}
|
||||
|
||||
// srvClosedErr is returned when the method can't complete without unacessible
|
||||
// data from the closing server.
|
||||
const srvClosedErr errors.Error = "server is closed"
|
||||
|
||||
// proxy returns a pointer to the current DNS proxy instance. If p is nil, the
|
||||
// server is closing.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3655.
|
||||
func (s *Server) proxy() (p *proxy.Proxy) {
|
||||
s.serverLock.RLock()
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
return s.dnsProxy
|
||||
}
|
||||
|
||||
// Reconfigure applies the new configuration to the DNS server.
|
||||
func (s *Server) Reconfigure(config *ServerConfig) error {
|
||||
s.serverLock.Lock()
|
||||
@@ -596,8 +581,17 @@ func (s *Server) Reconfigure(config *ServerConfig) error {
|
||||
|
||||
// ServeHTTP is a HTTP handler method we use to provide DNS-over-HTTPS.
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if prx := s.proxy(); prx != nil {
|
||||
prx.ServeHTTP(w, r)
|
||||
var p *proxy.Proxy
|
||||
|
||||
func() {
|
||||
s.serverLock.RLock()
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
p = s.dnsProxy
|
||||
}()
|
||||
|
||||
if p != nil {
|
||||
p.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -108,7 +107,7 @@ func createServerTLSConfig(t *testing.T) (*tls.Config, []byte, []byte) {
|
||||
require.NoErrorf(t, err, "failed to generate serial number: %s", err)
|
||||
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(5 * 365 * timeutil.Day)
|
||||
notAfter := notBefore.Add(5 * 365 * time.Hour * 24)
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
|
||||
@@ -249,17 +249,9 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
|
||||
Req: &replReq,
|
||||
}
|
||||
|
||||
prx := s.proxy()
|
||||
if prx == nil {
|
||||
log.Debug("dns: %s", srvClosedErr)
|
||||
|
||||
return s.genServerFailure(request)
|
||||
}
|
||||
|
||||
err := prx.Resolve(newContext)
|
||||
err := s.dnsProxy.Resolve(newContext)
|
||||
if err != nil {
|
||||
log.Printf("couldn't look up replacement host %q: %s", newAddr, err)
|
||||
|
||||
log.Printf("Couldn't look up replacement host %q: %s", newAddr, err)
|
||||
return s.genServerFailure(request)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,12 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"go.etcd.io/bbolt"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// cookieTTL is the time-to-live of the session cookie.
|
||||
const cookieTTL = 365 * timeutil.Day
|
||||
const cookieTTL = 365 * 24 * time.Hour
|
||||
|
||||
// sessionCookieName is the name of the session cookie.
|
||||
const sessionCookieName = "agh_session"
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
@@ -16,7 +18,6 @@ import (
|
||||
"github.com/AdguardTeam/dnsproxy/fastip"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/google/renameio/maybe"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
@@ -107,9 +108,9 @@ type dnsConfig struct {
|
||||
QueryLogEnabled bool `yaml:"querylog_enabled"` // if true, query log is enabled
|
||||
QueryLogFileEnabled bool `yaml:"querylog_file_enabled"` // if true, query log will be written to a file
|
||||
// QueryLogInterval is the interval for query log's files rotation.
|
||||
QueryLogInterval timeutil.Duration `yaml:"querylog_interval"`
|
||||
QueryLogMemSize uint32 `yaml:"querylog_size_memory"` // number of entries kept in memory before they are flushed to disk
|
||||
AnonymizeClientIP bool `yaml:"anonymize_client_ip"` // anonymize clients' IP addresses in logs and stats
|
||||
QueryLogInterval aghtime.Duration `yaml:"querylog_interval"`
|
||||
QueryLogMemSize uint32 `yaml:"querylog_size_memory"` // number of entries kept in memory before they are flushed to disk
|
||||
AnonymizeClientIP bool `yaml:"anonymize_client_ip"` // anonymize clients' IP addresses in logs and stats
|
||||
|
||||
dnsforward.FilteringConfig `yaml:",inline"`
|
||||
|
||||
@@ -118,7 +119,7 @@ type dnsConfig struct {
|
||||
DnsfilterConf filtering.Config `yaml:",inline"`
|
||||
|
||||
// UpstreamTimeout is the timeout for querying upstream servers.
|
||||
UpstreamTimeout timeutil.Duration `yaml:"upstream_timeout"`
|
||||
UpstreamTimeout aghtime.Duration `yaml:"upstream_timeout"`
|
||||
|
||||
// LocalDomainName is the domain name used for known internal hosts.
|
||||
// For example, a machine called "myhost" can be addressed as
|
||||
@@ -181,7 +182,7 @@ var config = &configuration{
|
||||
Ratelimit: 20,
|
||||
RefuseAny: true,
|
||||
AllServers: false,
|
||||
FastestTimeout: timeutil.Duration{
|
||||
FastestTimeout: aghtime.Duration{
|
||||
Duration: fastip.DefaultPingWaitTimeout,
|
||||
},
|
||||
|
||||
@@ -195,7 +196,7 @@ var config = &configuration{
|
||||
},
|
||||
FilteringEnabled: true, // whether or not use filter lists
|
||||
FiltersUpdateIntervalHours: 24,
|
||||
UpstreamTimeout: timeutil.Duration{Duration: dnsforward.DefaultTimeout},
|
||||
UpstreamTimeout: aghtime.Duration{Duration: dnsforward.DefaultTimeout},
|
||||
LocalDomainName: "lan",
|
||||
ResolveClients: true,
|
||||
UsePrivateRDNS: true,
|
||||
@@ -222,7 +223,7 @@ func initConfig() {
|
||||
|
||||
config.DNS.QueryLogEnabled = true
|
||||
config.DNS.QueryLogFileEnabled = true
|
||||
config.DNS.QueryLogInterval = timeutil.Duration{Duration: 90 * timeutil.Day}
|
||||
config.DNS.QueryLogInterval = aghtime.Duration{Duration: 90 * 24 * time.Hour}
|
||||
config.DNS.QueryLogMemSize = 1000
|
||||
|
||||
config.DNS.CacheSize = 4 * 1024 * 1024
|
||||
@@ -291,7 +292,7 @@ func parseConfig() error {
|
||||
}
|
||||
|
||||
if config.DNS.UpstreamTimeout.Duration == 0 {
|
||||
config.DNS.UpstreamTimeout = timeutil.Duration{Duration: dnsforward.DefaultTimeout}
|
||||
config.DNS.UpstreamTimeout = aghtime.Duration{Duration: dnsforward.DefaultTimeout}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -338,7 +339,7 @@ func (c *configuration) write() error {
|
||||
Context.queryLog.WriteDiskConfig(&dc)
|
||||
config.DNS.QueryLogEnabled = dc.Enabled
|
||||
config.DNS.QueryLogFileEnabled = dc.FileEnabled
|
||||
config.DNS.QueryLogInterval = timeutil.Duration{Duration: dc.RotationIvl}
|
||||
config.DNS.QueryLogInterval = aghtime.Duration{Duration: dc.RotationIvl}
|
||||
config.DNS.QueryLogMemSize = dc.MemSize
|
||||
config.DNS.AnonymizeClientIP = dc.AnonymizeClientIP
|
||||
}
|
||||
|
||||
@@ -32,10 +32,6 @@ type dnsSettings struct {
|
||||
ServerName string `plist:",omitempty"`
|
||||
|
||||
// ServerAddresses is a list IP addresses of the server.
|
||||
//
|
||||
// TODO(a.garipov): Allow users to set this.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3607.
|
||||
ServerAddresses []net.IP `plist:",omitempty"`
|
||||
}
|
||||
|
||||
@@ -161,9 +157,19 @@ func handleMobileConfig(w http.ResponseWriter, r *http.Request, dnsp string) {
|
||||
}
|
||||
}
|
||||
|
||||
dnsIPs, err := collectDNSIPs()
|
||||
if err != nil {
|
||||
// Don't add a lot of formatting, since the error is already
|
||||
// wrapped by collectDNSIPs.
|
||||
respondJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d := &dnsSettings{
|
||||
DNSProtocol: dnsp,
|
||||
ServerName: host,
|
||||
DNSProtocol: dnsp,
|
||||
ServerName: host,
|
||||
ServerAddresses: dnsIPs,
|
||||
}
|
||||
|
||||
mobileconfig, err := encodeMobileConfig(d, clientID)
|
||||
@@ -197,3 +203,25 @@ func handleMobileConfigDoH(w http.ResponseWriter, r *http.Request) {
|
||||
func handleMobileConfigDoT(w http.ResponseWriter, r *http.Request) {
|
||||
handleMobileConfig(w, r, dnsProtoTLS)
|
||||
}
|
||||
|
||||
// collectDNSIPs returns a slice of IP addresses the server is listening
|
||||
// on, including the addresses on all interfaces in cases of unspecified IPs but
|
||||
// excluding loopback addresses.
|
||||
func collectDNSIPs() (ips []net.IP, err error) {
|
||||
// TODO(a.garipov): This really shouldn't be a function that parses
|
||||
// a list of strings. Instead, we need a function that returns this
|
||||
// data as []net.IP or []*netutil.IPPort. Maybe someday.
|
||||
addrs, err := collectDNSAddresses()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip != nil && !ip.IsLoopback() {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ func TestHandleMobileConfigDoH(t *testing.T) {
|
||||
s := mc.PayloadContent[0].DNSSettings
|
||||
require.NotNil(t, s)
|
||||
|
||||
assert.NotEmpty(t, s.ServerAddresses)
|
||||
assert.Empty(t, s.ServerName)
|
||||
assert.Equal(t, "https://example.org/dns-query", s.ServerURL)
|
||||
})
|
||||
@@ -103,6 +104,7 @@ func TestHandleMobileConfigDoH(t *testing.T) {
|
||||
s := mc.PayloadContent[0].DNSSettings
|
||||
require.NotNil(t, s)
|
||||
|
||||
assert.NotEmpty(t, s.ServerAddresses)
|
||||
assert.Empty(t, s.ServerName)
|
||||
assert.Equal(t, "https://example.org/dns-query/cli42", s.ServerURL)
|
||||
})
|
||||
@@ -130,6 +132,7 @@ func TestHandleMobileConfigDoT(t *testing.T) {
|
||||
s := mc.PayloadContent[0].DNSSettings
|
||||
require.NotNil(t, s)
|
||||
|
||||
assert.NotEmpty(t, s.ServerAddresses)
|
||||
assert.Equal(t, "example.org", s.ServerName)
|
||||
assert.Empty(t, s.ServerURL)
|
||||
})
|
||||
@@ -176,6 +179,7 @@ func TestHandleMobileConfigDoT(t *testing.T) {
|
||||
s := mc.PayloadContent[0].DNSSettings
|
||||
require.NotNil(t, s)
|
||||
|
||||
assert.NotEmpty(t, s.ServerAddresses)
|
||||
assert.Equal(t, "cli42.example.org", s.ServerName)
|
||||
assert.Empty(t, s.ServerURL)
|
||||
})
|
||||
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/google/renameio/maybe"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
@@ -685,7 +685,7 @@ func upgradeSchema11to12(diskConf yobj) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
dns[field] = timeutil.Duration{Duration: time.Duration(qlogIvl) * timeutil.Day}
|
||||
dns[field] = aghtime.Duration{Duration: time.Duration(qlogIvl) * 24 * time.Hour}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -426,7 +426,7 @@ func TestUpgradeSchema11to12(t *testing.T) {
|
||||
name string
|
||||
}{{
|
||||
ivl: 1,
|
||||
want: timeutil.Duration{Duration: timeutil.Day},
|
||||
want: aghtime.Duration{Duration: 24 * time.Hour},
|
||||
wantErr: "",
|
||||
name: "success",
|
||||
}, {
|
||||
@@ -463,8 +463,8 @@ func TestUpgradeSchema11to12(t *testing.T) {
|
||||
newDNSConf, ok = dnsVal.(yobj)
|
||||
require.True(t, ok)
|
||||
|
||||
var newIvl timeutil.Duration
|
||||
newIvl, ok = newDNSConf["querylog_interval"].(timeutil.Duration)
|
||||
var newIvl aghtime.Duration
|
||||
newIvl, ok = newDNSConf["querylog_interval"].(aghtime.Duration)
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Equal(t, tc.want, newIvl)
|
||||
@@ -505,8 +505,8 @@ func TestUpgradeSchema11to12(t *testing.T) {
|
||||
ivl, ok = dnsVal["querylog_interval"]
|
||||
require.True(t, ok)
|
||||
|
||||
var ivlVal timeutil.Duration
|
||||
ivlVal, ok = ivl.(timeutil.Duration)
|
||||
var ivlVal aghtime.Duration
|
||||
ivlVal, ok = ivl.(aghtime.Duration)
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Equal(t, 90*24*time.Hour, ivlVal.Duration)
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@@ -104,11 +103,11 @@ func (l *queryLog) Close() {
|
||||
func checkInterval(ivl time.Duration) (ok bool) {
|
||||
// The constants for possible values of query log's rotation interval.
|
||||
const (
|
||||
quarterDay = timeutil.Day / 4
|
||||
day = timeutil.Day
|
||||
week = timeutil.Day * 7
|
||||
month = timeutil.Day * 30
|
||||
threeMonths = timeutil.Day * 90
|
||||
quarterDay = 6 * time.Hour
|
||||
day = 24 * time.Hour
|
||||
week = day * 7
|
||||
month = day * 30
|
||||
threeMonths = day * 90
|
||||
)
|
||||
|
||||
return ivl == quarterDay || ivl == day || ivl == week || ivl == month || ivl == threeMonths
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/dnsproxy/proxyutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -27,7 +26,7 @@ func TestQueryLog(t *testing.T) {
|
||||
l := newQueryLog(Config{
|
||||
Enabled: true,
|
||||
FileEnabled: true,
|
||||
RotationIvl: timeutil.Day,
|
||||
RotationIvl: 24 * time.Hour,
|
||||
MemSize: 100,
|
||||
BaseDir: t.TempDir(),
|
||||
})
|
||||
@@ -129,7 +128,7 @@ func TestQueryLog(t *testing.T) {
|
||||
func TestQueryLogOffsetLimit(t *testing.T) {
|
||||
l := newQueryLog(Config{
|
||||
Enabled: true,
|
||||
RotationIvl: timeutil.Day,
|
||||
RotationIvl: 24 * time.Hour,
|
||||
MemSize: 100,
|
||||
BaseDir: t.TempDir(),
|
||||
})
|
||||
@@ -204,7 +203,7 @@ func TestQueryLogMaxFileScanEntries(t *testing.T) {
|
||||
l := newQueryLog(Config{
|
||||
Enabled: true,
|
||||
FileEnabled: true,
|
||||
RotationIvl: timeutil.Day,
|
||||
RotationIvl: 24 * time.Hour,
|
||||
MemSize: 100,
|
||||
BaseDir: t.TempDir(),
|
||||
})
|
||||
@@ -232,7 +231,7 @@ func TestQueryLogFileDisabled(t *testing.T) {
|
||||
l := newQueryLog(Config{
|
||||
Enabled: true,
|
||||
FileEnabled: false,
|
||||
RotationIvl: timeutil.Day,
|
||||
RotationIvl: 24 * time.Hour,
|
||||
MemSize: 2,
|
||||
BaseDir: t.TempDir(),
|
||||
})
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@@ -126,7 +125,7 @@ func newQueryLog(conf Config) (l *queryLog) {
|
||||
"querylog: warning: unsupported rotation interval %d, setting to 1 day",
|
||||
conf.RotationIvl,
|
||||
)
|
||||
l.conf.RotationIvl = timeutil.Day
|
||||
l.conf.RotationIvl = 24 * time.Hour
|
||||
}
|
||||
|
||||
return l
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
)
|
||||
|
||||
// flushLogBuffer flushes the current buffer to file and resets the current buffer
|
||||
@@ -151,6 +150,6 @@ func (l *queryLog) periodicRotate() {
|
||||
}
|
||||
|
||||
// What?
|
||||
time.Sleep(timeutil.Day)
|
||||
time.Sleep(24 * time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -38,7 +37,7 @@ func TestQueryLog_Search_findClient(t *testing.T) {
|
||||
l := newQueryLog(Config{
|
||||
FindClient: findClient,
|
||||
BaseDir: t.TempDir(),
|
||||
RotationIvl: timeutil.Day,
|
||||
RotationIvl: 24 * time.Hour,
|
||||
MemSize: 100,
|
||||
Enabled: true,
|
||||
FileEnabled: true,
|
||||
|
||||
Reference in New Issue
Block a user