Compare commits

..

2 Commits

Author SHA1 Message Date
Ildar Kamalov
02834e6b7b client: nowrap 2021-09-29 16:47:14 +03:00
Ildar Kamalov
b45162a0f2 fix: stats table layout 2021-09-29 16:28:05 +03:00
36 changed files with 651 additions and 653 deletions

View File

@@ -15,6 +15,8 @@ and this project adheres to
### Added ### 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 - Setting the timeout for IP address pinging in the "Fastest IP address" mode
through the new `fastest_timeout` field in the configuration file ([#1992]). through the new `fastest_timeout` field in the configuration file ([#1992]).
- Static IP address detection on FreeBSD ([#3289]). - Static IP address detection on FreeBSD ([#3289]).
@@ -114,9 +116,7 @@ In this release, the schema version has changed from 10 to 12.
### Fixed ### Fixed
- Occasional panic during shutdown ([#3655]). - Adding an IP into only one of the matching ipsets on Linux ([#3638]).
- Addition of IPs into only one as opposed to all matching ipsets on Linux
([#3638]).
- Removal of temporary filter files ([#3567]). - Removal of temporary filter files ([#3567]).
- Panic when an upstream server responds with an empty question section - Panic when an upstream server responds with an empty question section
([#3551]). ([#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 [#3579]: https://github.com/AdguardTeam/AdGuardHome/issues/3579
[#3607]: https://github.com/AdguardTeam/AdGuardHome/issues/3607 [#3607]: https://github.com/AdguardTeam/AdGuardHome/issues/3607
[#3638]: https://github.com/AdguardTeam/AdGuardHome/issues/3638 [#3638]: https://github.com/AdguardTeam/AdGuardHome/issues/3638
[#3655]: https://github.com/AdguardTeam/AdGuardHome/issues/3655

View File

@@ -7,7 +7,7 @@
# Make sure to sync any changes with the branch overrides below. # Make sure to sync any changes with the branch overrides below.
'variables': 'variables':
'channel': 'edge' 'channel': 'edge'
'dockerGo': 'adguard/golang-ubuntu:3.6' 'dockerGo': 'adguard/golang-ubuntu:3.3'
'stages': 'stages':
- 'Make release': - 'Make release':
@@ -266,7 +266,7 @@
# need to build a few of these. # need to build a few of these.
'variables': 'variables':
'channel': 'beta' '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 # release-vX.Y.Z branches are the branches from which the actual final release
# is built. # is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+': - '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -276,4 +276,4 @@
# are the ones that actually get released. # are the ones that actually get released.
'variables': 'variables':
'channel': 'release' 'channel': 'release'
'dockerGo': 'adguard/golang-ubuntu:3.6' 'dockerGo': 'adguard/golang-ubuntu:3.3'

View File

@@ -5,7 +5,7 @@
'key': 'AHBRTSPECS' 'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests' 'name': 'AdGuard Home - Build and run tests'
'variables': 'variables':
'dockerGo': 'adguard/golang-ubuntu:3.6' 'dockerGo': 'adguard/golang-ubuntu:3.3'
'stages': 'stages':
- 'Tests': - 'Tests':

View File

@@ -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_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)", "example_upstream_tcp": "zadani DNS (putem TCP)",
"all_lists_up_to_date_toast": "Svi popisi su ažurirani", "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_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", "dns_test_not_ok_toast": "\"{{key}}\" poslužitelja: ne može se upotrijebiti, provjerite jeste li to ispravno napisali",
"unblock": "Odblokiraj", "unblock": "Odblokiraj",
@@ -235,7 +235,7 @@
"loading_table_status": "Učitavanje...", "loading_table_status": "Učitavanje...",
"page_table_footer_text": "Stranica", "page_table_footer_text": "Stranica",
"rows_table_footer_text": "redova", "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_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}}", "rule_added_to_custom_filtering_toast": "Pravilo je dodano u prilagođena pravila filtriranja: {{rule}}",
"query_log_response_status": "Status: {{value}}", "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_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_settings_all_interfaces": "Sva sučelja",
"install_auth_title": "Autentikacija", "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_username": "Korisničko ime",
"install_auth_password": "Lozinka", "install_auth_password": "Lozinka",
"install_auth_confirm": "Potvrdi lozinku", "install_auth_confirm": "Potvrdi lozinku",
@@ -503,7 +503,6 @@
"statistics_clear_confirm": "Jeste li sigurni da želite poništiti statistiku?", "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_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_cleared": "Statistika je uspješno uklonjenja",
"statistics_enable": "Omogući statistiku",
"interval_hours": "{{count}} sata/i", "interval_hours": "{{count}} sata/i",
"interval_hours_plural": "{{count}} sata/i", "interval_hours_plural": "{{count}} sata/i",
"filters_configuration": "Postavke filtara", "filters_configuration": "Postavke filtara",
@@ -613,8 +612,6 @@
"click_to_view_queries": "Kliknite za pregled upita", "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.", "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.", "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.", "client_not_in_allowed_clients": "Klijent nije dopušten jer nije na popisu \"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"
"experimental": "Eksperimentalno",
"use_saved_key": "Korištenje prethodno spremljenog ključa"
} }

View File

@@ -112,8 +112,6 @@
"for_last_24_hours": "în ultimele 24 ore", "for_last_24_hours": "în ultimele 24 ore",
"for_last_days": "în ultima {{count}} zi", "for_last_days": "în ultima {{count}} zi",
"for_last_days_plural": "pentru ultimele {{count}} zile", "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", "no_domains_found": "Nu s-au găsit domenii",
"requests_count": "Cont interogări", "requests_count": "Cont interogări",
"top_blocked_domains": "Domeniile blocate cel mai des", "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_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)", "example_upstream_tcp": "DNS clasic (over TCP)",
"all_lists_up_to_date_toast": "Toate listele sunt deja la zi", "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_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", "dns_test_not_ok_toast": "Serverul \"{{key}}\": nu a putut fi utilizat, verificați dacă l-ați scris corect",
"unblock": "Deblocați", "unblock": "Deblocați",
@@ -235,7 +233,7 @@
"loading_table_status": "Se încarcă...", "loading_table_status": "Se încarcă...",
"page_table_footer_text": "Pagina", "page_table_footer_text": "Pagina",
"rows_table_footer_text": "linii", "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_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}}", "rule_added_to_custom_filtering_toast": "Regulă adăugată la regulile de filtrare personalizate: {{rule}}",
"query_log_response_status": "Statut: {{value}}", "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_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_settings_all_interfaces": "Toate interfețele",
"install_auth_title": "Autentificare", "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_username": "Nume utilizator",
"install_auth_password": "Parola", "install_auth_password": "Parola",
"install_auth_confirm": "Confirmați 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_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_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_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_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_2": "Clicați pe Network.",
"install_devices_macos_list_3": "Selectați prima conexiune din listă și clicați pe Avansat.", "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_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_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_cleared": "Statisticile au fost șterse cu succes",
"statistics_enable": "Activați statisticile",
"interval_hours": "{{count}} oră", "interval_hours": "{{count}} oră",
"interval_hours_plural": "{{count}} ore", "interval_hours_plural": "{{count}} ore",
"filters_configuration": "Configurația filtrelor", "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_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", "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ă", "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_general": "General",
"filter_category_security": "Securitate", "filter_category_security": "Securitate",
"filter_category_regional": "Regional", "filter_category_regional": "Regional",
@@ -613,8 +608,6 @@
"click_to_view_queries": "Clicați pentru a vizualiza interogări", "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.", "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.", "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.", "client_not_in_allowed_clients": "Clientul nu este permis deoarece nu este în 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"
"experimental": "Experimental",
"use_saved_key": "Folosiți cheia salvată anterior"
} }

8
go.mod
View File

@@ -4,7 +4,7 @@ go 1.16
require ( require (
github.com/AdguardTeam/dnsproxy v0.39.8 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/AdguardTeam/urlfilter v0.14.6
github.com/NYTimes/gziphandler v1.1.1 github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.2.2 github.com/ameshkov/dnscrypt/v2 v2.2.2
@@ -12,14 +12,12 @@ require (
github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify v1.4.9
github.com/go-ping/ping v0.0.0-20210506233800-ff8be3320020 github.com/go-ping/ping v0.0.0-20210506233800-ff8be3320020
github.com/google/go-cmp v0.5.5 github.com/google/go-cmp v0.5.5
github.com/google/gopacket v1.1.19
github.com/google/renameio v1.0.1 github.com/google/renameio v1.0.1
github.com/insomniacslk/dhcp v0.0.0-20210310193751-cfd4d47082c2 github.com/insomniacslk/dhcp v0.0.0-20210310193751-cfd4d47082c2
github.com/kardianos/service v1.2.0 github.com/kardianos/service v1.2.0
github.com/lucas-clemente/quic-go v0.21.1 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/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/miekg/dns v1.1.43
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/objx v0.1.1 // indirect
@@ -27,7 +25,7 @@ require (
github.com/ti-mo/netfilter v0.4.0 github.com/ti-mo/netfilter v0.4.0
go.etcd.io/bbolt v1.3.6 go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 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 golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0

15
go.sum
View File

@@ -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.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
github.com/AdguardTeam/golibs v0.4.2/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.9.2/go.mod h1:fCAMwPBJ8S7YMYbTWvYS+eeTLblP5E04IDtNAo7y7IY=
github.com/AdguardTeam/golibs v0.10.0 h1:A7MXRfZ+ItpOyS9tWKtqrLj3vZtE9FJFC+dOVY/LcWs= github.com/AdguardTeam/golibs v0.9.3 h1:noeKHJEzrSwxzX0Zi3USM3cXf1qQV99SO772jet/uEY=
github.com/AdguardTeam/golibs v0.10.0/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw= 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/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 h1:emqoKZElooHACYehRBYENeKVN1a/rspxiqTIMYLuoIo=
github.com/AdguardTeam/urlfilter v0.14.6/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U= 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-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-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/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/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/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU= 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-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-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-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.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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= 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-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-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-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6 h1:Z04ewVs7JhXaYkmDhBERPi41gnltfQpMWDnTnQbaCqk= golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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-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-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/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.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.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.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.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-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/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= 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-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-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-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/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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View 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
}

View 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`)))
})
}

View File

@@ -5,17 +5,33 @@ package dhcpd
import ( import (
"net" "net"
"github.com/AdguardTeam/golibs/log"
"github.com/insomniacslk/dhcp/dhcpv4"
) )
// broadcast sends resp to the broadcast address specific for network interface. // 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 // Despite the fact that server4.NewIPv4UDPConn explicitly sets socket
// options to allow broadcasting, it also binds the connection to a // options to allow broadcasting, it also binds the connection to a
// specific interface. On FreeBSD and OpenBSD net.UDPConn.WriteTo // specific interface. On FreeBSD and OpenBSD conn.WriteTo causes
// causes errors while writing to the addresses that belong to another // errors while writing to the addresses that belong to another
// interface. So, use the broadcast address specific for the interface // interface. So, use the broadcast address specific for the binded
// bound. // interface.
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(resp.ToBytes(), peer); err != nil {
log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
}
} }

View File

@@ -8,16 +8,17 @@ import (
"net" "net"
"testing" "testing"
"github.com/AdguardTeam/golibs/netutil"
"github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestDHCPConn_Broadcast(t *testing.T) { func TestV4Server_Send_broadcast(t *testing.T) {
b := &bytes.Buffer{} b := &bytes.Buffer{}
var peer *net.UDPAddr var peer *net.UDPAddr
udpConn := &fakePacketConn{ conn := &fakePacketConn{
writeTo: func(p []byte, addr net.Addr) (n int, err error) { writeTo: func(p []byte, addr net.Addr) (n int, err error) {
udpPeer, ok := addr.(*net.UDPAddr) udpPeer, ok := addr.(*net.UDPAddr)
require.True(t, ok) require.True(t, ok)
@@ -30,22 +31,57 @@ func TestDHCPConn_Broadcast(t *testing.T) {
return n, nil return n, nil
}, },
} }
conn := &dhcpConn{
udpConn: udpConn,
bcastIP: net.IP{1, 2, 3, 255},
}
defaultPeer := &net.UDPAddr{ defaultPeer := &net.UDPAddr{
IP: net.IP{1, 2, 3, 4}, IP: net.IP{1, 2, 3, 4},
// Use neither client nor server port. // Use neither client nor server port.
Port: 1234, 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()) for _, tc := range testCases {
assert.Equal(t, &net.UDPAddr{ t.Run(tc.name, func(t *testing.T) {
IP: conn.bcastIP, s.send(cloneUDPAddr(defaultPeer), conn, tc.req, tc.resp)
Port: defaultPeer.Port, assert.EqualValues(t, tc.resp.ToBytes(), b.Bytes())
}, peer) assert.Equal(t, &net.UDPAddr{
IP: s.conf.broadcastIP,
Port: defaultPeer.Port,
Zone: defaultPeer.Zone,
}, peer)
})
b.Reset()
peer = nil
}
} }

View File

@@ -5,10 +5,17 @@ package dhcpd
import ( import (
"net" "net"
"github.com/AdguardTeam/golibs/log"
"github.com/insomniacslk/dhcp/dhcpv4"
) )
// broadcast sends resp to the broadcast address specific for network interface. // 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 // This write to 0xffffffff reverts some behavior changes made in
// https://github.com/AdguardTeam/AdGuardHome/issues/3289. The DHCP // https://github.com/AdguardTeam/AdGuardHome/issues/3289. The DHCP
// server should broadcast the message to 0xffffffff but it's // 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. // https://github.com/AdguardTeam/AdGuardHome/issues/3366.
// //
// See also https://github.com/AdguardTeam/AdGuardHome/issues/3539. // See also https://github.com/AdguardTeam/AdGuardHome/issues/3539.
if n, err = c.udpConn.WriteTo(respData, peer); err != nil { if _, err := conn.WriteTo(respData, peer); err != nil {
return n, err 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 the message one more time using the interface-specific
// broadcast address. // 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)
}
} }

View File

@@ -8,16 +8,17 @@ import (
"net" "net"
"testing" "testing"
"github.com/AdguardTeam/golibs/netutil"
"github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestDHCPConn_Broadcast(t *testing.T) { func TestV4Server_Send_broadcast(t *testing.T) {
b := &bytes.Buffer{} b := &bytes.Buffer{}
var peers []*net.UDPAddr var peers []*net.UDPAddr
udpConn := &fakePacketConn{ conn := &fakePacketConn{
writeTo: func(p []byte, addr net.Addr) (n int, err error) { writeTo: func(p []byte, addr net.Addr) (n int, err error) {
udpPeer, ok := addr.(*net.UDPAddr) udpPeer, ok := addr.(*net.UDPAddr)
require.True(t, ok) require.True(t, ok)
@@ -30,27 +31,66 @@ func TestDHCPConn_Broadcast(t *testing.T) {
return n, nil return n, nil
}, },
} }
conn := &dhcpConn{
udpConn: udpConn,
bcastIP: net.IP{1, 2, 3, 255},
}
defaultPeer := &net.UDPAddr{ defaultPeer := &net.UDPAddr{
IP: net.IP{1, 2, 3, 4}, IP: net.IP{1, 2, 3, 4},
// Use neither client nor server port. // Use neither client nor server port.
Port: 1234, 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. for _, tc := range testCases {
assert.EqualValues(t, append(respData, respData...), b.Bytes()) 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]) require.Len(t, peers, 2)
assert.Equal(t, &net.UDPAddr{
IP: conn.bcastIP, assert.Equal(t, &net.UDPAddr{
Port: defaultPeer.Port, IP: defaultPeer.IP,
}, peers[1]) Port: defaultPeer.Port,
}, peers[0])
assert.Equal(t, &net.UDPAddr{
IP: s.conf.broadcastIP,
Port: defaultPeer.Port,
}, peers[1])
})
b.Reset()
peers = nil
}
} }

View File

@@ -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
}

View File

@@ -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)
})
}

View File

@@ -139,7 +139,7 @@ func TestNormalizeLeases(t *testing.T) {
} }
// cloneUDPAddr returns a deep copy of a. // 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{ return &net.UDPAddr{
IP: netutil.CloneIP(a.IP), IP: netutil.CloneIP(a.IP),
Port: a.Port, Port: a.Port,

View File

@@ -157,8 +157,8 @@ func prepareOptions(conf V4ServerConf) (opts dhcpv4.Options) {
dhcpv4.OptionPerformRouterDiscovery.Code(): []byte{1}, dhcpv4.OptionPerformRouterDiscovery.Code(): []byte{1},
// The all-routers address is preferred wherever possible, see // The all-routers address is preferred wherever possible, see
// https://datatracker.ietf.org/doc/html/rfc1256#section-5.1. // https://datatracker.ietf.org/doc/html/rfc1256#section-5.1.
dhcpv4.OptionRouterSolicitationAddress.Code(): netutil.IPv4allrouter(), dhcpv4.OptionRouterSolicitationAddress.Code(): net.IPv4allrouter.To4(),
dhcpv4.OptionBroadcastAddress.Code(): netutil.IPv4bcast(), dhcpv4.OptionBroadcastAddress.Code(): net.IPv4bcast.To4(),
// Link-Layer Per Interface // Link-Layer Per Interface

View File

@@ -16,11 +16,9 @@ import (
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil" "github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/go-ping/ping" "github.com/go-ping/ping"
"github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/server4" "github.com/insomniacslk/dhcp/dhcpv4/server4"
"github.com/mdlayher/raw"
) )
// v4Server is a DHCPv4 server. // 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. // 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) { func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) {
switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); { var unicast bool
case giaddr != nil && !giaddr.IsUnspecified(): if giaddr := req.GatewayIPAddr; giaddr != nil && !giaddr.IsUnspecified() {
// Send any return messages to the server port on the BOOTP // Send any return messages to the server port on the BOOTP
// relay agent whose address appears in giaddr. // relay agent whose address appears in giaddr.
peer = &net.UDPAddr{ peer = &net.UDPAddr{
IP: giaddr, IP: giaddr,
Port: dhcpv4.ServerPort, Port: dhcpv4.ServerPort,
} }
if mtype == dhcpv4.MessageTypeNak { unicast = true
// Set the broadcast bit in the DHCPNAK, so that the } else if mtype := resp.MessageType(); mtype == dhcpv4.MessageTypeNak {
// 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:
// Broadcast any DHCPNAK messages to 0xffffffff. // 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 // Unicast DHCPOFFER and DHCPACK messages to the address in
// ciaddr. // ciaddr.
peer = &net.UDPAddr{ peer = &net.UDPAddr{
IP: ciaddr, IP: ciaddr,
Port: dhcpv4.ClientPort, Port: dhcpv4.ClientPort,
} }
case !req.IsBroadcast() && req.ClientHWAddr != nil: unicast = true
// Unicast DHCPOFFER and DHCPACK messages to the client's }
// hardware address and yiaddr.
peer = &dhcpUnicastAddr{ // TODO(e.burkov): Unicast the message to the actual link-layer address
Addr: raw.Addr{HardwareAddr: req.ClientHWAddr}, // of the client if broadcast bit is not set. Perhaps, use custom
yiaddr: resp.YourIPAddr, // connection when creating the server.
} //
default: // See https://github.com/AdguardTeam/AdGuardHome/issues/3443.
// Go on since peer is already set to broadcast.
if !unicast {
s.broadcast(peer, conn, resp)
return
} }
log.Debug("dhcpv4: sending to %s: %s", peer, resp.Summary()) 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) 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 s.conf.dnsIPAddrs = dnsIPAddrs
var c net.PacketConn laddr := &net.UDPAddr{
if c, err = s.newDHCPConn(iface); err != nil { IP: net.IP{0, 0, 0, 0},
return err Port: dhcpv4.ServerPort,
} }
s.srv, err = server4.NewServer(iface.Name, laddr, s.packetHandler, server4.WithDebugLogger())
s.srv, err = server4.NewServer(
iface.Name,
nil,
s.packetHandler,
server4.WithConn(c),
server4.WithDebugLogger(),
)
if err != nil { if err != nil {
return err return err
} }
@@ -1127,7 +1117,7 @@ func v4Create(conf V4ServerConf) (srv DHCPServer, err error) {
s.leasedOffsets = newBitSet() s.leasedOffsets = newBitSet()
if conf.LeaseDuration == 0 { if conf.LeaseDuration == 0 {
s.conf.leaseTime = timeutil.Day s.conf.leaseTime = time.Hour * 24
s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds()) s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds())
} else { } else {
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration) s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)

View File

@@ -4,11 +4,12 @@
package dhcpd package dhcpd
import ( import (
"bytes"
"net" "net"
"testing" "testing"
"github.com/AdguardTeam/golibs/netutil"
"github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/mdlayher/raw"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "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) return fc.writeTo(p, addr)
} }
func TestV4Server_Send(t *testing.T) { func TestV4Server_Send_unicast(t *testing.T) {
s := &v4Server{} b := &bytes.Buffer{}
var peer *net.UDPAddr
var ( conn := &fakePacketConn{
defaultIP = net.IP{99, 99, 99, 99} writeTo: func(p []byte, addr net.Addr) (n int, err error) {
knownIP = net.IP{4, 2, 4, 2} udpPeer, ok := addr.(*net.UDPAddr)
knownMAC = net.HardwareAddr{6, 5, 4, 3, 2, 1} require.True(t, ok)
)
peer = cloneUDPAddr(udpPeer)
n, err = b.Write(p)
require.NoError(t, err)
return n, nil
},
}
defaultPeer := &net.UDPAddr{ defaultPeer := &net.UDPAddr{
IP: defaultIP, IP: net.IP{1, 2, 3, 4},
// Use neither client nor server port to check it actually // Use neither client nor server port.
// changed. Port: 1234,
Port: dhcpv4.ClientPort + dhcpv4.ServerPort,
} }
defaultResp := &dhcpv4.DHCPv4{} defaultResp := &dhcpv4.DHCPv4{
OpCode: dhcpv4.OpcodeBootReply,
}
s := &v4Server{}
testCases := []struct { testCases := []struct {
want net.Addr name string
req *dhcpv4.DHCPv4 req *dhcpv4.DHCPv4
resp *dhcpv4.DHCPv4 wantPeer net.Addr
name string
}{{ }{{
name: "giaddr", name: "relay_agent",
req: &dhcpv4.DHCPv4{GatewayIPAddr: knownIP}, req: &dhcpv4.DHCPv4{
resp: defaultResp, GatewayIPAddr: defaultPeer.IP,
want: &net.UDPAddr{ },
IP: knownIP, wantPeer: &net.UDPAddr{
IP: defaultPeer.IP,
Port: dhcpv4.ServerPort, Port: dhcpv4.ServerPort,
}, },
}, { }, {
name: "nak", name: "known_client",
req: &dhcpv4.DHCPv4{}, req: &dhcpv4.DHCPv4{
resp: &dhcpv4.DHCPv4{ GatewayIPAddr: netutil.IPv4Zero(),
Options: dhcpv4.OptionsFromList( ClientIPAddr: net.IP{2, 3, 4, 5},
dhcpv4.OptMessageType(dhcpv4.MessageTypeNak),
),
}, },
want: defaultPeer, wantPeer: &net.UDPAddr{
}, { IP: net.IP{2, 3, 4, 5},
name: "ciaddr",
req: &dhcpv4.DHCPv4{ClientIPAddr: knownIP},
resp: &dhcpv4.DHCPv4{},
want: &net.UDPAddr{
IP: knownIP,
Port: dhcpv4.ClientPort, 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 { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
conn := &fakePacketConn{ s.send(defaultPeer, conn, tc.req, defaultResp)
writeTo: func(_ []byte, addr net.Addr) (_ int, _ error) { assert.EqualValues(t, defaultResp.ToBytes(), b.Bytes())
assert.Equal(t, tc.want, addr) assert.Equal(t, tc.wantPeer, peer)
return 0, nil
},
}
s.send(cloneUDPAddr(defaultPeer), conn, tc.req, tc.resp)
}) })
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())
})
} }

View File

@@ -14,7 +14,6 @@ import (
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/insomniacslk/dhcp/dhcpv6" "github.com/insomniacslk/dhcp/dhcpv6"
"github.com/insomniacslk/dhcp/dhcpv6/server6" "github.com/insomniacslk/dhcp/dhcpv6/server6"
"github.com/insomniacslk/dhcp/iana" "github.com/insomniacslk/dhcp/iana"
@@ -708,7 +707,7 @@ func v6Create(conf V6ServerConf) (DHCPServer, error) {
} }
if conf.LeaseDuration == 0 { if conf.LeaseDuration == 0 {
s.conf.leaseTime = timeutil.Day s.conf.leaseTime = time.Hour * 24
s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds()) s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds())
} else { } else {
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration) s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)

View File

@@ -11,6 +11,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/dnsproxy/upstream"
@@ -18,7 +19,6 @@ import (
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil" "github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/ameshkov/dnscrypt/v2" "github.com/ameshkov/dnscrypt/v2"
) )
@@ -90,7 +90,7 @@ type FilteringConfig struct {
FastestAddr bool `yaml:"fastest_addr"` // use Fastest Address algorithm FastestAddr bool `yaml:"fastest_addr"` // use Fastest Address algorithm
// FastestTimeout replaces the default timeout for dialing IP addresses // FastestTimeout replaces the default timeout for dialing IP addresses
// when FastestAddr is true. // when FastestAddr is true.
FastestTimeout timeutil.Duration `yaml:"fastest_timeout"` FastestTimeout aghtime.Duration `yaml:"fastest_timeout"`
// Access settings // Access settings
// -- // --

View File

@@ -540,16 +540,8 @@ func (s *Server) processUpstream(ctx *dnsContext) (rc resultCode) {
} }
} }
// Process the request further since it wasn't filtered. // request was not filtered so let it be processed further
if ctx.err = s.dnsProxy.Resolve(d); ctx.err != nil {
prx := s.proxy()
if prx == nil {
ctx.err = srvClosedErr
return resultCodeError
}
if ctx.err = prx.Resolve(d); ctx.err != nil {
return resultCodeError return resultCodeError
} }

View File

@@ -551,21 +551,6 @@ func (s *Server) IsRunning() bool {
return s.isRunning 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. // Reconfigure applies the new configuration to the DNS server.
func (s *Server) Reconfigure(config *ServerConfig) error { func (s *Server) Reconfigure(config *ServerConfig) error {
s.serverLock.Lock() 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. // ServeHTTP is a HTTP handler method we use to provide DNS-over-HTTPS.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if prx := s.proxy(); prx != nil { var p *proxy.Proxy
prx.ServeHTTP(w, r)
func() {
s.serverLock.RLock()
defer s.serverLock.RUnlock()
p = s.dnsProxy
}()
if p != nil {
p.ServeHTTP(w, r)
} }
} }

View File

@@ -23,7 +23,6 @@ import (
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "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) require.NoErrorf(t, err, "failed to generate serial number: %s", err)
notBefore := time.Now() notBefore := time.Now()
notAfter := notBefore.Add(5 * 365 * timeutil.Day) notAfter := notBefore.Add(5 * 365 * time.Hour * 24)
template := x509.Certificate{ template := x509.Certificate{
SerialNumber: serialNumber, SerialNumber: serialNumber,

View File

@@ -249,17 +249,9 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
Req: &replReq, Req: &replReq,
} }
prx := s.proxy() err := s.dnsProxy.Resolve(newContext)
if prx == nil {
log.Debug("dns: %s", srvClosedErr)
return s.genServerFailure(request)
}
err := prx.Resolve(newContext)
if err != nil { 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) return s.genServerFailure(request)
} }

View File

@@ -15,13 +15,12 @@ import (
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
"go.etcd.io/bbolt" "go.etcd.io/bbolt"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
// cookieTTL is the time-to-live of the session cookie. // 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. // sessionCookieName is the name of the session cookie.
const sessionCookieName = "agh_session" const sessionCookieName = "agh_session"

View File

@@ -6,7 +6,9 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"sync" "sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd" "github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward" "github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
@@ -16,7 +18,6 @@ import (
"github.com/AdguardTeam/dnsproxy/fastip" "github.com/AdguardTeam/dnsproxy/fastip"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/google/renameio/maybe" "github.com/google/renameio/maybe"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
) )
@@ -107,9 +108,9 @@ type dnsConfig struct {
QueryLogEnabled bool `yaml:"querylog_enabled"` // if true, query log is enabled 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 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 is the interval for query log's files rotation.
QueryLogInterval timeutil.Duration `yaml:"querylog_interval"` QueryLogInterval aghtime.Duration `yaml:"querylog_interval"`
QueryLogMemSize uint32 `yaml:"querylog_size_memory"` // number of entries kept in memory before they are flushed to disk 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 AnonymizeClientIP bool `yaml:"anonymize_client_ip"` // anonymize clients' IP addresses in logs and stats
dnsforward.FilteringConfig `yaml:",inline"` dnsforward.FilteringConfig `yaml:",inline"`
@@ -118,7 +119,7 @@ type dnsConfig struct {
DnsfilterConf filtering.Config `yaml:",inline"` DnsfilterConf filtering.Config `yaml:",inline"`
// UpstreamTimeout is the timeout for querying upstream servers. // 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. // LocalDomainName is the domain name used for known internal hosts.
// For example, a machine called "myhost" can be addressed as // For example, a machine called "myhost" can be addressed as
@@ -181,7 +182,7 @@ var config = &configuration{
Ratelimit: 20, Ratelimit: 20,
RefuseAny: true, RefuseAny: true,
AllServers: false, AllServers: false,
FastestTimeout: timeutil.Duration{ FastestTimeout: aghtime.Duration{
Duration: fastip.DefaultPingWaitTimeout, Duration: fastip.DefaultPingWaitTimeout,
}, },
@@ -195,7 +196,7 @@ var config = &configuration{
}, },
FilteringEnabled: true, // whether or not use filter lists FilteringEnabled: true, // whether or not use filter lists
FiltersUpdateIntervalHours: 24, FiltersUpdateIntervalHours: 24,
UpstreamTimeout: timeutil.Duration{Duration: dnsforward.DefaultTimeout}, UpstreamTimeout: aghtime.Duration{Duration: dnsforward.DefaultTimeout},
LocalDomainName: "lan", LocalDomainName: "lan",
ResolveClients: true, ResolveClients: true,
UsePrivateRDNS: true, UsePrivateRDNS: true,
@@ -222,7 +223,7 @@ func initConfig() {
config.DNS.QueryLogEnabled = true config.DNS.QueryLogEnabled = true
config.DNS.QueryLogFileEnabled = 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.QueryLogMemSize = 1000
config.DNS.CacheSize = 4 * 1024 * 1024 config.DNS.CacheSize = 4 * 1024 * 1024
@@ -291,7 +292,7 @@ func parseConfig() error {
} }
if config.DNS.UpstreamTimeout.Duration == 0 { if config.DNS.UpstreamTimeout.Duration == 0 {
config.DNS.UpstreamTimeout = timeutil.Duration{Duration: dnsforward.DefaultTimeout} config.DNS.UpstreamTimeout = aghtime.Duration{Duration: dnsforward.DefaultTimeout}
} }
return nil return nil
@@ -338,7 +339,7 @@ func (c *configuration) write() error {
Context.queryLog.WriteDiskConfig(&dc) Context.queryLog.WriteDiskConfig(&dc)
config.DNS.QueryLogEnabled = dc.Enabled config.DNS.QueryLogEnabled = dc.Enabled
config.DNS.QueryLogFileEnabled = dc.FileEnabled 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.QueryLogMemSize = dc.MemSize
config.DNS.AnonymizeClientIP = dc.AnonymizeClientIP config.DNS.AnonymizeClientIP = dc.AnonymizeClientIP
} }

View File

@@ -32,10 +32,6 @@ type dnsSettings struct {
ServerName string `plist:",omitempty"` ServerName string `plist:",omitempty"`
// ServerAddresses is a list IP addresses of the server. // 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"` 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{ d := &dnsSettings{
DNSProtocol: dnsp, DNSProtocol: dnsp,
ServerName: host, ServerName: host,
ServerAddresses: dnsIPs,
} }
mobileconfig, err := encodeMobileConfig(d, clientID) 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) { func handleMobileConfigDoT(w http.ResponseWriter, r *http.Request) {
handleMobileConfig(w, r, dnsProtoTLS) 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
}

View File

@@ -58,6 +58,7 @@ func TestHandleMobileConfigDoH(t *testing.T) {
s := mc.PayloadContent[0].DNSSettings s := mc.PayloadContent[0].DNSSettings
require.NotNil(t, s) require.NotNil(t, s)
assert.NotEmpty(t, s.ServerAddresses)
assert.Empty(t, s.ServerName) assert.Empty(t, s.ServerName)
assert.Equal(t, "https://example.org/dns-query", s.ServerURL) assert.Equal(t, "https://example.org/dns-query", s.ServerURL)
}) })
@@ -103,6 +104,7 @@ func TestHandleMobileConfigDoH(t *testing.T) {
s := mc.PayloadContent[0].DNSSettings s := mc.PayloadContent[0].DNSSettings
require.NotNil(t, s) require.NotNil(t, s)
assert.NotEmpty(t, s.ServerAddresses)
assert.Empty(t, s.ServerName) assert.Empty(t, s.ServerName)
assert.Equal(t, "https://example.org/dns-query/cli42", s.ServerURL) 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 s := mc.PayloadContent[0].DNSSettings
require.NotNil(t, s) require.NotNil(t, s)
assert.NotEmpty(t, s.ServerAddresses)
assert.Equal(t, "example.org", s.ServerName) assert.Equal(t, "example.org", s.ServerName)
assert.Empty(t, s.ServerURL) assert.Empty(t, s.ServerURL)
}) })
@@ -176,6 +179,7 @@ func TestHandleMobileConfigDoT(t *testing.T) {
s := mc.PayloadContent[0].DNSSettings s := mc.PayloadContent[0].DNSSettings
require.NotNil(t, s) require.NotNil(t, s)
assert.NotEmpty(t, s.ServerAddresses)
assert.Equal(t, "cli42.example.org", s.ServerName) assert.Equal(t, "cli42.example.org", s.ServerName)
assert.Empty(t, s.ServerURL) assert.Empty(t, s.ServerURL)
}) })

View File

@@ -11,10 +11,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/google/renameio/maybe" "github.com/google/renameio/maybe"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
yaml "gopkg.in/yaml.v2" 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 return nil
} }

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/AdguardTeam/golibs/timeutil" "github.com/AdguardTeam/AdGuardHome/internal/aghtime"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -426,7 +426,7 @@ func TestUpgradeSchema11to12(t *testing.T) {
name string name string
}{{ }{{
ivl: 1, ivl: 1,
want: timeutil.Duration{Duration: timeutil.Day}, want: aghtime.Duration{Duration: 24 * time.Hour},
wantErr: "", wantErr: "",
name: "success", name: "success",
}, { }, {
@@ -463,8 +463,8 @@ func TestUpgradeSchema11to12(t *testing.T) {
newDNSConf, ok = dnsVal.(yobj) newDNSConf, ok = dnsVal.(yobj)
require.True(t, ok) require.True(t, ok)
var newIvl timeutil.Duration var newIvl aghtime.Duration
newIvl, ok = newDNSConf["querylog_interval"].(timeutil.Duration) newIvl, ok = newDNSConf["querylog_interval"].(aghtime.Duration)
require.True(t, ok) require.True(t, ok)
assert.Equal(t, tc.want, newIvl) assert.Equal(t, tc.want, newIvl)
@@ -505,8 +505,8 @@ func TestUpgradeSchema11to12(t *testing.T) {
ivl, ok = dnsVal["querylog_interval"] ivl, ok = dnsVal["querylog_interval"]
require.True(t, ok) require.True(t, ok)
var ivlVal timeutil.Duration var ivlVal aghtime.Duration
ivlVal, ok = ivl.(timeutil.Duration) ivlVal, ok = ivl.(aghtime.Duration)
require.True(t, ok) require.True(t, ok)
assert.Equal(t, 90*24*time.Hour, ivlVal.Duration) assert.Equal(t, 90*24*time.Hour, ivlVal.Duration)

View File

@@ -12,7 +12,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -104,11 +103,11 @@ func (l *queryLog) Close() {
func checkInterval(ivl time.Duration) (ok bool) { func checkInterval(ivl time.Duration) (ok bool) {
// The constants for possible values of query log's rotation interval. // The constants for possible values of query log's rotation interval.
const ( const (
quarterDay = timeutil.Day / 4 quarterDay = 6 * time.Hour
day = timeutil.Day day = 24 * time.Hour
week = timeutil.Day * 7 week = day * 7
month = timeutil.Day * 30 month = day * 30
threeMonths = timeutil.Day * 90 threeMonths = day * 90
) )
return ivl == quarterDay || ivl == day || ivl == week || ivl == month || ivl == threeMonths return ivl == quarterDay || ivl == day || ivl == week || ivl == month || ivl == threeMonths

View File

@@ -11,7 +11,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest" "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxyutil" "github.com/AdguardTeam/dnsproxy/proxyutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -27,7 +26,7 @@ func TestQueryLog(t *testing.T) {
l := newQueryLog(Config{ l := newQueryLog(Config{
Enabled: true, Enabled: true,
FileEnabled: true, FileEnabled: true,
RotationIvl: timeutil.Day, RotationIvl: 24 * time.Hour,
MemSize: 100, MemSize: 100,
BaseDir: t.TempDir(), BaseDir: t.TempDir(),
}) })
@@ -129,7 +128,7 @@ func TestQueryLog(t *testing.T) {
func TestQueryLogOffsetLimit(t *testing.T) { func TestQueryLogOffsetLimit(t *testing.T) {
l := newQueryLog(Config{ l := newQueryLog(Config{
Enabled: true, Enabled: true,
RotationIvl: timeutil.Day, RotationIvl: 24 * time.Hour,
MemSize: 100, MemSize: 100,
BaseDir: t.TempDir(), BaseDir: t.TempDir(),
}) })
@@ -204,7 +203,7 @@ func TestQueryLogMaxFileScanEntries(t *testing.T) {
l := newQueryLog(Config{ l := newQueryLog(Config{
Enabled: true, Enabled: true,
FileEnabled: true, FileEnabled: true,
RotationIvl: timeutil.Day, RotationIvl: 24 * time.Hour,
MemSize: 100, MemSize: 100,
BaseDir: t.TempDir(), BaseDir: t.TempDir(),
}) })
@@ -232,7 +231,7 @@ func TestQueryLogFileDisabled(t *testing.T) {
l := newQueryLog(Config{ l := newQueryLog(Config{
Enabled: true, Enabled: true,
FileEnabled: false, FileEnabled: false,
RotationIvl: timeutil.Day, RotationIvl: 24 * time.Hour,
MemSize: 2, MemSize: 2,
BaseDir: t.TempDir(), BaseDir: t.TempDir(),
}) })

View File

@@ -9,7 +9,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -126,7 +125,7 @@ func newQueryLog(conf Config) (l *queryLog) {
"querylog: warning: unsupported rotation interval %d, setting to 1 day", "querylog: warning: unsupported rotation interval %d, setting to 1 day",
conf.RotationIvl, conf.RotationIvl,
) )
l.conf.RotationIvl = timeutil.Day l.conf.RotationIvl = 24 * time.Hour
} }
return l return l

View File

@@ -8,7 +8,6 @@ import (
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/timeutil"
) )
// flushLogBuffer flushes the current buffer to file and resets the current buffer // flushLogBuffer flushes the current buffer to file and resets the current buffer
@@ -151,6 +150,6 @@ func (l *queryLog) periodicRotate() {
} }
// What? // What?
time.Sleep(timeutil.Day) time.Sleep(24 * time.Hour)
} }
} }

View File

@@ -5,7 +5,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -38,7 +37,7 @@ func TestQueryLog_Search_findClient(t *testing.T) {
l := newQueryLog(Config{ l := newQueryLog(Config{
FindClient: findClient, FindClient: findClient,
BaseDir: t.TempDir(), BaseDir: t.TempDir(),
RotationIvl: timeutil.Day, RotationIvl: 24 * time.Hour,
MemSize: 100, MemSize: 100,
Enabled: true, Enabled: true,
FileEnabled: true, FileEnabled: true,