Compare commits
13 Commits
release-v0
...
beta-v0.10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dda513fe82 | ||
|
|
13e0124dec | ||
|
|
f716b2b3f5 | ||
|
|
031e61305d | ||
|
|
b1b2f4a233 | ||
|
|
4921a325e2 | ||
|
|
d36f95a2b0 | ||
|
|
1d61f50fc8 | ||
|
|
d747ae36ca | ||
|
|
4e31610d7b | ||
|
|
3953ca50e6 | ||
|
|
f7e4e998cf | ||
|
|
e301480401 |
41
CHANGELOG.md
41
CHANGELOG.md
@@ -10,12 +10,39 @@ and this project adheres to
|
||||
## [Unreleased]
|
||||
|
||||
<!--
|
||||
## [v0.107.0] - 2021-06-21 (APPROX.)
|
||||
## [v0.107.0] - 2021-06-28 (APPROX.)
|
||||
-->
|
||||
|
||||
<!--
|
||||
## [v0.106.3] - 2021-05-17 (APPROX.)
|
||||
-->
|
||||
## [v0.106.3] - 2021-05-19
|
||||
|
||||
### Added
|
||||
|
||||
- Support for reinstall (`-r`) and uninstall (`-u`) flags in the installation
|
||||
script ([#2462]).
|
||||
- Support for DHCP `DECLINE` and `RELEASE` message types ([#3053]).
|
||||
|
||||
### Changed
|
||||
|
||||
- Add microseconds to log output.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Intermittent "Warning: ID mismatch" errors ([#3087]).
|
||||
- Error when using installation script on some ARMv7 devices ([#2542]).
|
||||
- DHCP leases validation ([#3107], [#3127]).
|
||||
- Local PTR request recursion in Docker containers ([#3064]).
|
||||
- Ignoring client-specific filtering settings when filtering is disabled in
|
||||
general settings ([#2875]).
|
||||
- Disallowed domains are now case-insensitive ([#3115]).
|
||||
|
||||
[#2462]: https://github.com/AdguardTeam/AdGuardHome/issues/2462
|
||||
[#2542]: https://github.com/AdguardTeam/AdGuardHome/issues/2542
|
||||
[#2875]: https://github.com/AdguardTeam/AdGuardHome/issues/2875
|
||||
[#3053]: https://github.com/AdguardTeam/AdGuardHome/issues/3053
|
||||
[#3064]: https://github.com/AdguardTeam/AdGuardHome/issues/3064
|
||||
[#3107]: https://github.com/AdguardTeam/AdGuardHome/issues/3107
|
||||
[#3115]: https://github.com/AdguardTeam/AdGuardHome/issues/3115
|
||||
[#3127]: https://github.com/AdguardTeam/AdGuardHome/issues/3127
|
||||
|
||||
|
||||
|
||||
@@ -346,12 +373,12 @@ and this project adheres to
|
||||
|
||||
|
||||
<!--
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.106.3...HEAD
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.0...HEAD
|
||||
[v0.107.0]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.106.3...v0.107.0
|
||||
[v0.106.3]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.106.2...v0.106.3
|
||||
-->
|
||||
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.106.2...HEAD
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.106.3...HEAD
|
||||
[v0.106.3]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.106.2...v0.106.3
|
||||
[v0.106.2]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.106.1...v0.106.2
|
||||
[v0.106.1]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.106.0...v0.106.1
|
||||
[v0.106.0]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.105.2...v0.106.0
|
||||
|
||||
@@ -309,6 +309,8 @@ on GitHub and most other Markdown renderers. -->
|
||||
|
||||
* Avoid bashisms and GNUisms, prefer POSIX features only.
|
||||
|
||||
* Avoid spaces between patterns of the same `case` condition.
|
||||
|
||||
* Prefer `'raw strings'` to `"double quoted strings"` whenever possible.
|
||||
|
||||
* Put spaces within `$( cmd )`, `$(( expr ))`, and `{ cmd; }`.
|
||||
@@ -324,10 +326,10 @@ on GitHub and most other Markdown renderers. -->
|
||||
* UPPERCASE names for external exported variables, lowercase for local,
|
||||
unexported ones.
|
||||
|
||||
* Use `set -e -f -u` and also `set -x` in verbose mode.
|
||||
|
||||
* Use `readonly` liberally.
|
||||
|
||||
* Use `set -e -f -u` and also `set -x` in verbose mode.
|
||||
|
||||
* Use the `"$var"` form instead of the `$var` form, unless word splitting is
|
||||
required.
|
||||
|
||||
@@ -352,6 +354,7 @@ on GitHub and most other Markdown renderers. -->
|
||||
* When using `test` (aka `[`), spell compound conditions with `&&`, `||`, and
|
||||
`!` **outside** of `test` instead of `-a`, `-o`, and `!` inside of `test`
|
||||
correspondingly. The latter ones are pretty much deprecated in POSIX.
|
||||
Also, prefer `!= ''` form instead of `-n` to check if string is empty.
|
||||
|
||||
See also: “[Problems With the `test` Builtin: What Does `-a` Mean?]”.
|
||||
|
||||
|
||||
12
README.md
12
README.md
@@ -68,9 +68,17 @@ It operates as a DNS server that re-routes tracking domains to a "black hole", t
|
||||
### Automated install (Linux and Mac)
|
||||
Run the following command in your terminal:
|
||||
```
|
||||
curl -sSL https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh
|
||||
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
|
||||
```
|
||||
|
||||
The script also accepts some options:
|
||||
* `-c <channel>` to use specified channel.
|
||||
* `-r` to reinstall AdGuard Home;
|
||||
* `-u` to uninstall AdGuard Home;
|
||||
* `-v` for verbose output;
|
||||
|
||||
Note that options `-r` and `-u` are mutually exclusive.
|
||||
|
||||
### Alternative methods
|
||||
|
||||
#### Manual installation
|
||||
@@ -137,7 +145,7 @@ AdGuard Home provides a lot of features out-of-the-box with no need to install a
|
||||
| Blocking ads and trackers | ✅ | ✅ |
|
||||
| Customizing blocklists | ✅ | ✅ |
|
||||
| Built-in DHCP server | ✅ | ✅ |
|
||||
| HTTPS for the Admin interface | ✅ | Kind of, but you'll need to manually configure lighthttpd |
|
||||
| HTTPS for the Admin interface | ✅ | Kind of, but you'll need to manually configure lighttpd |
|
||||
| Encrypted DNS upstream servers (DNS-over-HTTPS, DNS-over-TLS, DNSCrypt) | ✅ | ❌ (requires additional software) |
|
||||
| Cross-platform | ✅ | ❌ (not natively, only via Docker) |
|
||||
| Running as a DNS-over-HTTPS or DNS-over-TLS server | ✅ | ❌ (requires additional software) |
|
||||
|
||||
@@ -229,7 +229,9 @@
|
||||
- 'adg-docker': 'true'
|
||||
|
||||
'triggers':
|
||||
- 'cron': '0 30 14 ? * MON-FRI *'
|
||||
# Don't use minute values that end with a zero or a five as these are often used
|
||||
# in CI and so resources during these minutes can be quite busy.
|
||||
- 'cron': '0 42 13 ? * MON-FRI *'
|
||||
'branches':
|
||||
'create': 'manually'
|
||||
'delete':
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"bootstrap_dns": "Bootstrap DNS-серверы",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS-серверы выкарыстоўваюцца для пошуку IP-адрасоў DoH/DoT сервераў, якія вы паказалі.",
|
||||
"local_ptr_title": "Прыватныя DNS-серверы",
|
||||
"local_ptr_desc": "DNS-сервер ці серверы, якія AdGuard Home будзе выкарыстоўваць для запытаў на лакальныя рэсурсы. Напрыклад, гэтыя серверы будуць выкарыстоўвацца, каб атрымаць даменавыя імёны кліентаў у прыватных сетках. Калі спіс пусты, AdGuard Home будзе выкарыстоўваць сістэмны DNS-сервер па змаўчанні.",
|
||||
"local_ptr_desc": "DNS-серверы, якія AdGuard Home выкарыстоўвае для лакальных PTR-запытаў. Гэтыя серверы выкарыстоўваюцца, каб атрымаць даменавыя імёны кліентаў з прыватнымі IP-адрасамі, напрыклад «192.168.12.34», з дапамогай rDNS. Калі спіс пусты, AdGuard Home выкарыстоўвае прадвызначаныя DNS-серверы вашай АС.",
|
||||
"local_ptr_placeholder": "Увядзіце па адным адрасе на радок",
|
||||
"resolve_clients_title": "Уключыць запытванне даменавых імёнаў для кліентаў",
|
||||
"resolve_clients_desc": "AdGuard Home будзе спрабаваць аўтаматычна вызначыць даменавыя імёны кліентаў праз PTR-запыты да адпаведных сервераў (прыватны DNS-сервер для лакальных кліентаў, upstream-серверы для кліентаў з публічным IP-адрасам).",
|
||||
|
||||
@@ -193,10 +193,10 @@
|
||||
"example_comment_hash": "# Také komentář",
|
||||
"example_regex_meaning": "blokuje přístup doménám, které vyhovují regulárnímu výrazu",
|
||||
"example_upstream_regular": "obyčejný DNS (přes UDP)",
|
||||
"example_upstream_dot": "šifrovaný <0>DNS přes TLS</0>",
|
||||
"example_upstream_doh": "šifrovaný <0>DNS přes HTTPS</0>",
|
||||
"example_upstream_doq": "šifrovaný <0>DNS přes QUIC</0>",
|
||||
"example_upstream_sdns": "můžete použít <0>DNS razítka</0> pro <1>DNSCrypt</1> nebo <2>DNS přes HTTPS</2> řešitele",
|
||||
"example_upstream_dot": "šifrovaný <0>DNS skrze TLS</0>",
|
||||
"example_upstream_doh": "šifrovaný <0>DNS skrze HTTPS</0>",
|
||||
"example_upstream_doq": "šifrovaný <0>DNS skrze QUIC</0>",
|
||||
"example_upstream_sdns": "můžete použít <0>DNS razítka</0> pro <1>DNSCrypt</1> nebo <2>DNS skrze HTTPS</2> řešitele",
|
||||
"example_upstream_tcp": "obyčejný DNS (přes TCP)",
|
||||
"all_lists_up_to_date_toast": "Všechny seznamy jsou již aktuální",
|
||||
"updated_upstream_dns_toast": "Aktualizované upstream DNS servery",
|
||||
@@ -255,8 +255,8 @@
|
||||
"blocking_ipv4": "Blokování IPv4",
|
||||
"blocking_ipv6": "Blokování IPv6",
|
||||
"dnscrypt": "DNSCrypt",
|
||||
"dns_over_https": "DNS přes HTTPS",
|
||||
"dns_over_tls": "DNS přes TLS",
|
||||
"dns_over_https": "DNS skrze HTTPS",
|
||||
"dns_over_tls": "DNS skrze TLS",
|
||||
"dns_over_quic": "DNS skrze QUIC",
|
||||
"client_id": "ID klienta",
|
||||
"client_id_placeholder": "Zadejte ID klienta",
|
||||
@@ -347,11 +347,11 @@
|
||||
"encryption_redirect": "Automaticky přesměrovat na HTTPS",
|
||||
"encryption_redirect_desc": "Pokud je zaškrtnuto, AdGuard Home vás automaticky přesměruje z adres HTTP na HTTPS.",
|
||||
"encryption_https": "HTTPS port",
|
||||
"encryption_https_desc": "Pokud je nakonfigurován port HTTPS, AdGuard Home administrátorské rozhraní bude přístupné přes HTTPS a bude také poskytovat DNS přes HTTPS na '/dns-query'.",
|
||||
"encryption_dot": "DNS přes TLS port",
|
||||
"encryption_dot_desc": "Pokud je tento port nakonfigurován, AdGuard Home bude na tomto portu spouštět DNS přes TLS server.",
|
||||
"encryption_doq": "DNS přes QUIC port",
|
||||
"encryption_doq_desc": "Pokud je tento port nakonfigurován, AdGuard Home spustí na tomto portu server DNS přes QUIC. Je to experimentální a nemusí být spolehlivé. V současnosti také není příliš mnoho klientů, kteří to podporují.",
|
||||
"encryption_https_desc": "Pokud je nakonfigurován port HTTPS, AdGuard Home administrátorské rozhraní bude přístupné přes HTTPS a bude také poskytovat DNS skrze HTTPS na '/dns-query'.",
|
||||
"encryption_dot": "DNS skrze TLS port",
|
||||
"encryption_dot_desc": "Pokud je tento port nakonfigurován, AdGuard Home bude na tomto portu spouštět DNS skrze TLS server.",
|
||||
"encryption_doq": "DNS skrze QUIC port",
|
||||
"encryption_doq_desc": "Pokud je tento port nakonfigurován, AdGuard Home spustí na tomto portu server DNS skrze QUIC. Je to experimentální a nemusí být spolehlivé. V současnosti také není příliš mnoho klientů, kteří to podporují.",
|
||||
"encryption_certificates": "Certifikáty",
|
||||
"encryption_certificates_desc": "Chcete-li používat šifrování, musíte pro svou doménu poskytnout platný řetězec certifikátů SSL. Certifikát můžete získat bezplatně na adrese <0>{{link}}</ 0>, nebo jej můžete zakoupit od jednoho z důvěryhodných certifikačních úřadů.",
|
||||
"encryption_certificates_input": "Zde můžete nakopírovat/vložit certifikáty PEM.",
|
||||
@@ -359,8 +359,8 @@
|
||||
"encryption_expire": "Vyprší",
|
||||
"encryption_key": "Osobní kód",
|
||||
"encryption_key_input": "Zde můžete nakopírovat/vložit soukromý klíč k certifikátu PEM.",
|
||||
"encryption_enable": "Povolit šifrování (HTTPS, DNS přes HTTPS a DNS přes TLS)",
|
||||
"encryption_enable_desc": "Pokud je šifrování zapnuto, administrátorské rozhraní AdGuard Home bude pracovat přes HTTPS a DNS server bude naslouchat požadavky přes DNS přes HTTPS a DNS přes TLS.",
|
||||
"encryption_enable": "Povolit šifrování (HTTPS, DNS skrze HTTPS a DNS skrze TLS)",
|
||||
"encryption_enable_desc": "Pokud je šifrování zapnuto, administrátorské rozhraní AdGuard Home bude pracovat skrze HTTPS a DNS server bude naslouchat požadavky přes DNS skrze HTTPS a DNS skrze TLS.",
|
||||
"encryption_chain_valid": "Certifikační řetězec je platný",
|
||||
"encryption_chain_invalid": "Certifikační řetězec je neplatný",
|
||||
"encryption_key_valid": "Toto je platný {{type}} osobní klíč",
|
||||
@@ -429,23 +429,23 @@
|
||||
"updates_version_equal": "AdGuard Home je aktuální",
|
||||
"check_updates_now": "Zkontrolovat aktualizace nyní",
|
||||
"dns_privacy": "Soukromí DNS",
|
||||
"setup_dns_privacy_1": "<0>DNS-přes-TLS:</0> Použít <1>{{address}}</1> řetězec.",
|
||||
"setup_dns_privacy_2": "<0>DNS-přes-HTTPS:</0> Použít <1>{{address}}</1> řetězec.",
|
||||
"setup_dns_privacy_1": "<0>DNS skrze TLS:</0> Použít <1>{{address}}</1> řetězec.",
|
||||
"setup_dns_privacy_2": "<0>DNS skrze HTTPS:</0> Použít <1>{{address}}</1> řetězec.",
|
||||
"setup_dns_privacy_3": "<0>Zde je seznam softwaru, který můžete použít.</0>",
|
||||
"setup_dns_privacy_4": "Na zařízení se systémem iOS 14 nebo macOS Big Sur si můžete stáhnout speciální soubor '.mobileconfig', který do nastavení DNS přidává servery <highlight>DNS skrze HTTPS</highlight> nebo <highlight> DNS skrze TLS</highlight>.",
|
||||
"setup_dns_privacy_android_1": "Android 9 podporuje DNS-přes-TLS nativně. Pokud ho chcete konfigurovat, přejděte na Nastavení → Síť & internet → Pokročilé → Soukromé DNS a tam zadejte název vaší domény.",
|
||||
"setup_dns_privacy_android_2": "<0>AdGuard pro Android</0> podporuje <1>DNS-přes-HTTPS</1> a <1>DNS-přes-TLS</1>.",
|
||||
"setup_dns_privacy_android_3": "<0>Intra</0> přidává podporu <1>DNS-přes-HTTPS</1> pro Android.",
|
||||
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> podporuje funkci <1>DNS-přes-HTTPS</1>, ale abyste ji mohli nakonfigurovat pro používání vlastního serveru, musíte vygenerovat značku <2>DNS Stamp</2>.",
|
||||
"setup_dns_privacy_ios_2": "<0>AdGuard pro iOS</0> podporuje nastavení <1>DNS-přes-HTTPS</1> a <1>DNS-přes-TLS</1>.",
|
||||
"setup_dns_privacy_android_1": "Android 9 podporuje DNS skrze TLS nativně. Pokud ho chcete konfigurovat, přejděte na Nastavení → Síť & internet → Pokročilé → Soukromé DNS a tam zadejte název vaší domény.",
|
||||
"setup_dns_privacy_android_2": "<0>AdGuard pro Android</0> podporuje <1>DNS skrze HTTPS</1> a <1>DNS skrze LS</1>.",
|
||||
"setup_dns_privacy_android_3": "<0>Intra</0> přidává podporu <1>DNS skrze HTTPS</1> pro Android.",
|
||||
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> podporuje funkci <1>DNS skrze HTTPS</1>, ale abyste ji mohli nakonfigurovat pro používání vlastního serveru, musíte vygenerovat značku <2>DNS Stamp</2>.",
|
||||
"setup_dns_privacy_ios_2": "<0>AdGuard pro iOS</0> podporuje nastavení <1>DNS skrze HTTPS</1> a <1>DNS skrze TLS</1>.",
|
||||
"setup_dns_privacy_other_title": "Další implementace",
|
||||
"setup_dns_privacy_other_1": "Samotný AdGuard Home může být bezpečným klientem DNS na jakékoli platformě.",
|
||||
"setup_dns_privacy_other_2": "<0>dnsproxy</0> podporuje všechny známé bezpečné DNS protokoly.",
|
||||
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> podporuje <1>DNS-přes-HTTPS</1>.",
|
||||
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> podporuje <1>DNS-přes-HTTPS</1>.",
|
||||
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> podporuje <1>DNS skrze HTTPS</1>.",
|
||||
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> podporuje <1>DNS skrze HTTPS</1>.",
|
||||
"setup_dns_privacy_other_5": "Další implementace naleznete <0>zde</0> a <1>zde</1>.",
|
||||
"setup_dns_privacy_ioc_mac": "Konfigurace pro iOS a macOS",
|
||||
"setup_dns_notice": "Pro použití <1>DNS-přes-HTTPS</1> nebo <1>DNS-přes-TLS</1> potřebujete v nastaveních AdGuard Home <0>nakonfigurovat šifrování</0>.",
|
||||
"setup_dns_notice": "Pro použití <1>DNS skrze HTTPS</1> nebo <1>DNS skrze TLS</1> potřebujete v nastaveních AdGuard Home <0>nakonfigurovat šifrování</0>.",
|
||||
"rewrite_added": "Přesměrování DNS pro „{{key}}“ úspěšně přidáno",
|
||||
"rewrite_deleted": "Přesměrování DNS pro „{{key}}“ úspěšně smazáno",
|
||||
"rewrite_add": "Přidat přesměrování DNS",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"bootstrap_dns": "Bootstrap DNS 服务器",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS 服务器用于解析您指定为上游的 DoH / DoT 解析器的 IP 地址。",
|
||||
"local_ptr_title": "私人 DNS 服务器",
|
||||
"local_ptr_desc": "AdGuard Home 用于查询本地服务资源的 DNS 服务器。例如,该服务器将被用于解析具有私人 IP 地址的客户机的主机名,比如 \"192.168.12.34\"。如果没有设置,AdGuard Home 将自动使用您的默认 DNS 解析器。",
|
||||
"local_ptr_desc": "AdGuard Home 用于本地 PTR 查询的 DNS 服务器。例如,该服务器将被用于解析具有私人 IP 地址的客户机的主机名,比如 \"192.168.12.34\",使用 rDNS 。如果没有设置,AdGuard Home 将自动使用您的默认 DNS 解析器。",
|
||||
"local_ptr_placeholder": "每行输入一个服务器地址",
|
||||
"resolve_clients_title": "启用客户端的 IP 地址的反向解析",
|
||||
"resolve_clients_desc": "如果启用,AdGuard Home 将尝试通过发送 PTR 查询到对应的解析器 (本地客户端的私人 DNS 服务器,公有 IP 客户端的上游服务器) 将 IP 地址反向解析成其客户端主机名。",
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
"load_balancing_desc": "一次只查詢一個伺服器。AdGuard Home 會使用加權隨機取樣來選擇使用的查詢結果,以確保速度最快的伺服器能被充分運用。",
|
||||
"bootstrap_dns": "引導(Boostrap) DNS 伺服器",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS 伺服器用於解析您所設定的上游 DoH/DoT 解析器的 IP 地址",
|
||||
"local_ptr_title": "私人 DNS 伺服器",
|
||||
"local_ptr_desc": "AdGuard Home 用於區域 PTR 查詢的 DNS 伺服器。該伺服器將被用於解析具有私人 IP 位址的用戶端的主機名稱,比如 \"192.168.12.34\",使用 rDNS。如果沒有設定,AdGuard Home 將自動使用您的預設 DNS 解析器。",
|
||||
"local_ptr_placeholder": "每行輸入一個伺服器位址",
|
||||
"resolve_clients_title": "啟用用戶端的 IP 位址的反向解析",
|
||||
"resolve_clients_desc": "如果啟用,AdGuard Home 將嘗試透過傳送 PTR 查詢到對應的解析器 (本機用戶端的私人 DNS 伺服器,公有 IP 用戶端的上遊伺服器) ,將 IP 位址反向解析成其用戶端的主機名稱。",
|
||||
"check_dhcp_servers": "檢查 DHCP 伺服器",
|
||||
"save_config": "儲存設定",
|
||||
"enabled_dhcp": "DHCP 伺服器已啟動",
|
||||
@@ -33,6 +38,7 @@
|
||||
"form_error_mac_format": "無效的 「MAC 位址」格式",
|
||||
"form_error_client_id_format": "無效的「客戶端 ID」格式",
|
||||
"form_error_server_name": "無效伺服器名稱",
|
||||
"form_error_subnet": "子網路 \"{{cidr}}\" 不包含 IP 位址 \"{{ip}}\"",
|
||||
"form_error_positive": "數值必須大於 0",
|
||||
"form_error_negative": "數值必須大於等於 0",
|
||||
"range_end_error": "必須大於起始值",
|
||||
@@ -395,6 +401,7 @@
|
||||
"ip_address": "IP 位址",
|
||||
"client_identifier_desc": "可通過 IP 地址、CIDR、MAC 地址來辨識使用者裝置。注意:必須使用 AdGuard Home 內建 <0>DHCP 伺服器</0> 才能偵測 MAC 地址。",
|
||||
"form_enter_ip": "輸入 IP",
|
||||
"form_enter_subnet_ip": "在子網路 \"{{cidr}}\" 中輸入一個 IP 位址",
|
||||
"form_enter_mac": "輸入 MAC 地址",
|
||||
"form_enter_id": "輸入識別碼",
|
||||
"form_add_id": "新增識別碼",
|
||||
|
||||
@@ -209,7 +209,6 @@ ClientCell.propTypes = {
|
||||
client: propTypes.string.isRequired,
|
||||
client_id: propTypes.string,
|
||||
client_info: propTypes.shape({
|
||||
ids: propTypes.arrayOf(propTypes.string).isRequired,
|
||||
name: propTypes.string.isRequired,
|
||||
whois: propTypes.shape({
|
||||
country: propTypes.string,
|
||||
|
||||
@@ -231,7 +231,6 @@ Row.propTypes = {
|
||||
client_proto: propTypes.string.isRequired,
|
||||
client_id: propTypes.string,
|
||||
client_info: propTypes.shape({
|
||||
ids: propTypes.arrayOf(propTypes.string).isRequired,
|
||||
name: propTypes.string.isRequired,
|
||||
whois: propTypes.shape({
|
||||
country: propTypes.string,
|
||||
|
||||
@@ -63,8 +63,8 @@ const Logs = () => {
|
||||
const history = useHistory();
|
||||
|
||||
const {
|
||||
response_status: response_status_url_param = '',
|
||||
search: search_url_param = '',
|
||||
response_status: response_status_url_param,
|
||||
search: search_url_param,
|
||||
} = queryString.parse(history.location.search);
|
||||
|
||||
const {
|
||||
@@ -76,8 +76,8 @@ const Logs = () => {
|
||||
const filter = useSelector((state) => state.queryLogs.filter, shallowEqual);
|
||||
const logs = useSelector((state) => state.queryLogs.logs, shallowEqual);
|
||||
|
||||
const search = filter?.search || search_url_param;
|
||||
const response_status = filter?.response_status || response_status_url_param;
|
||||
const search = filter?.search || search_url_param || '';
|
||||
const response_status = filter?.response_status || response_status_url_param || '';
|
||||
|
||||
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth < SMALL_SCREEN_SIZE);
|
||||
const [detailedDataCurrent, setDetailedDataCurrent] = useState({});
|
||||
|
||||
6
go.mod
6
go.mod
@@ -3,8 +3,8 @@ module github.com/AdguardTeam/AdGuardHome
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.37.2
|
||||
github.com/AdguardTeam/golibs v0.4.5
|
||||
github.com/AdguardTeam/dnsproxy v0.37.4
|
||||
github.com/AdguardTeam/golibs v0.5.0
|
||||
github.com/AdguardTeam/urlfilter v0.14.5
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/ameshkov/dnscrypt/v2 v2.1.3
|
||||
@@ -40,4 +40,4 @@ require (
|
||||
howett.net/plist v0.0.0-20201203080718-1454fab16a06
|
||||
)
|
||||
|
||||
replace github.com/insomniacslk/dhcp => github.com/AdguardTeam/dhcp v0.0.0-20210420175708-50b0efd52063
|
||||
replace github.com/insomniacslk/dhcp => github.com/AdguardTeam/dhcp v0.0.0-20210517101438-550ef4cd8c6e
|
||||
|
||||
12
go.sum
12
go.sum
@@ -18,15 +18,15 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AdguardTeam/dhcp v0.0.0-20210420175708-50b0efd52063 h1:RBsQppxEJEqHApY6WDBkM2H0UG5wt57RcT0El2WGdp8=
|
||||
github.com/AdguardTeam/dhcp v0.0.0-20210420175708-50b0efd52063/go.mod h1:TKl4jN3Voofo4UJIicyNhWGp/nlQqQkFxmwIFTvBkKI=
|
||||
github.com/AdguardTeam/dnsproxy v0.37.2 h1:3lgizD+lZI6uqxFiQykd1/hV7Ji4vSJBMejl1rbFAXU=
|
||||
github.com/AdguardTeam/dnsproxy v0.37.2/go.mod h1:xkJWEuTr550gPDmB9azsciKZzSXjf9wMn+Ji54PQ4gE=
|
||||
github.com/AdguardTeam/dhcp v0.0.0-20210517101438-550ef4cd8c6e h1:M6YnFP12o0/SjBEPt6b2r8ZkIy/wsV14TK8X9Tb6DEE=
|
||||
github.com/AdguardTeam/dhcp v0.0.0-20210517101438-550ef4cd8c6e/go.mod h1:TKl4jN3Voofo4UJIicyNhWGp/nlQqQkFxmwIFTvBkKI=
|
||||
github.com/AdguardTeam/dnsproxy v0.37.4 h1:YIoJkIp828LKmmmgxXvZHUKfGLsqTQAK8g+4DXbDbyU=
|
||||
github.com/AdguardTeam/dnsproxy v0.37.4/go.mod h1:xkJWEuTr550gPDmB9azsciKZzSXjf9wMn+Ji54PQ4gE=
|
||||
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.4/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.5 h1:RRA9ZsmbJEN4OllAx0BcfvSbRBxxpWluJijBYmtp13U=
|
||||
github.com/AdguardTeam/golibs v0.4.5/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.5.0 h1:qwhEKjDrT0UcwDnHrNU2Yg/DLR9b/GsUncnXYW6VzAU=
|
||||
github.com/AdguardTeam/golibs v0.5.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/AdguardTeam/urlfilter v0.14.5 h1:WyF0hg0MwKevsqNPkoaZFH8f5WRi/yuy/7qePtYt5Ts=
|
||||
github.com/AdguardTeam/urlfilter v0.14.5/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
|
||||
|
||||
@@ -82,6 +82,12 @@ func validateDialedHost(host string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// dockerEmbeddedDNS is the address of Docker's embedded DNS server.
|
||||
//
|
||||
// See
|
||||
// https://github.com/moby/moby/blob/v1.12.0/docs/userguide/networking/dockernetworks.md.
|
||||
const dockerEmbeddedDNS = "127.0.0.11"
|
||||
|
||||
// dialFunc gets the resolver's address and puts it into internal cache.
|
||||
func (sr *systemResolvers) dialFunc(_ context.Context, _, address string) (_ net.Conn, err error) {
|
||||
// Just validate the passed address is a valid IP.
|
||||
@@ -93,6 +99,17 @@ func (sr *systemResolvers) dialFunc(_ context.Context, _, address string) (_ net
|
||||
return nil, fmt.Errorf("%s: %w", err, errBadAddrPassed)
|
||||
}
|
||||
|
||||
// Exclude Docker's embedded DNS server, as it may cause recursion if
|
||||
// the container is set as the host system's default DNS server.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3064.
|
||||
//
|
||||
// TODO(a.garipov): Perhaps only do this when we are in the container?
|
||||
// Maybe use an environment variable?
|
||||
if host == dockerEmbeddedDNS {
|
||||
return nil, errFakeDial
|
||||
}
|
||||
|
||||
err = validateDialedHost(host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validating dialed host: %w", err)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build darwin
|
||||
// +build darwin netbsd openbsd
|
||||
|
||||
package aghos
|
||||
|
||||
@@ -73,42 +73,26 @@ func normalizeHostname(hostname string) (norm string, err error) {
|
||||
return norm, nil
|
||||
}
|
||||
|
||||
// validHostnameForClient accepts the hostname sent by the client and returns
|
||||
// either a normalized version of that hostname or a new hostname generated from
|
||||
// the client's IP address. If this new hostname is different from the provided
|
||||
// previous hostname, additional uniqueness check is performed.
|
||||
//
|
||||
// hostname is always a non-empty valid hostname. If err is not nil, it
|
||||
// describes the issues encountered when normalizing cliHostname.
|
||||
func (s *v4Server) validHostnameForClient(
|
||||
cliHostname string,
|
||||
prevHostname string,
|
||||
ip net.IP,
|
||||
) (hostname string, err error) {
|
||||
hostname, err = normalizeHostname(cliHostname)
|
||||
if err == nil && hostname != "" {
|
||||
err = aghnet.ValidateDomainName(hostname)
|
||||
if err != nil {
|
||||
// Go on and assign a hostname made from the IP below,
|
||||
// returning the error that we've got.
|
||||
hostname = ""
|
||||
} else if hostname != prevHostname && s.leaseHosts.Has(hostname) {
|
||||
// Go on and assign a unique hostname made from the IP
|
||||
// below, returning the error about uniqueness.
|
||||
err = agherr.Error("hostname exists")
|
||||
hostname = ""
|
||||
}
|
||||
// validHostnameForClient accepts the hostname sent by the client and its IP and
|
||||
// returns either a normalized version of that hostname, or a new hostname
|
||||
// generated from the IP address, or an empty string.
|
||||
func (s *v4Server) validHostnameForClient(cliHostname string, ip net.IP) (hostname string) {
|
||||
hostname, err := normalizeHostname(cliHostname)
|
||||
if err != nil {
|
||||
log.Info("dhcpv4: %s", err)
|
||||
}
|
||||
|
||||
if hostname == "" {
|
||||
hostname = aghnet.GenerateHostname(ip)
|
||||
}
|
||||
|
||||
if hostname != cliHostname {
|
||||
log.Info("dhcpv4: normalized hostname %q into %q", cliHostname, hostname)
|
||||
err = aghnet.ValidateDomainName(hostname)
|
||||
if err != nil {
|
||||
log.Info("dhcpv4: %s", err)
|
||||
hostname = ""
|
||||
}
|
||||
|
||||
return hostname, err
|
||||
return hostname
|
||||
}
|
||||
|
||||
// ResetLeases - reset leases
|
||||
@@ -124,15 +108,7 @@ func (s *v4Server) ResetLeases(leases []*Lease) {
|
||||
s.leases = nil
|
||||
|
||||
for _, l := range leases {
|
||||
l.Hostname, err = s.validHostnameForClient(l.Hostname, l.Hostname, l.IP)
|
||||
if err != nil {
|
||||
log.Info(
|
||||
"dhcpv4: warning: previous hostname %q is invalid: %s",
|
||||
l.Hostname,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
l.Hostname = s.validHostnameForClient(l.Hostname, l.IP)
|
||||
err = s.addLease(l)
|
||||
if err != nil {
|
||||
// TODO(a.garipov): Wrap and bubble up the error.
|
||||
@@ -287,6 +263,15 @@ func (s *v4Server) rmDynamicLease(lease *Lease) (err error) {
|
||||
}
|
||||
|
||||
s.rmLeaseByIndex(i)
|
||||
if i == len(s.leases) {
|
||||
break
|
||||
}
|
||||
|
||||
l = s.leases[i]
|
||||
}
|
||||
|
||||
if l.Hostname == lease.Hostname {
|
||||
l.Hostname = ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,22 +337,25 @@ func (s *v4Server) AddStaticLease(l Lease) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
var hostname string
|
||||
hostname, err = normalizeHostname(l.Hostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if l.Hostname != "" {
|
||||
var hostname string
|
||||
hostname, err = normalizeHostname(l.Hostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = aghnet.ValidateDomainName(hostname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("validating hostname: %w", err)
|
||||
}
|
||||
err = aghnet.ValidateDomainName(hostname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("validating hostname: %w", err)
|
||||
}
|
||||
|
||||
if s.leaseHosts.Has(hostname) {
|
||||
return agherr.Error("hostname exists")
|
||||
}
|
||||
// Don't check for hostname uniqueness, since we try to emulate
|
||||
// dnsmasq here, which means that rmDynamicLease below will
|
||||
// simply empty the hostname of the dynamic lease if there even
|
||||
// is one.
|
||||
|
||||
l.Hostname = hostname
|
||||
l.Hostname = hostname
|
||||
}
|
||||
|
||||
// Perform the following actions in an anonymous function to make sure
|
||||
// that the lock gets unlocked before the notification step.
|
||||
@@ -553,20 +541,39 @@ func (s *v4Server) commitLease(l *Lease) {
|
||||
defer s.leasesLock.Unlock()
|
||||
|
||||
s.conf.notify(LeaseChangedDBStore)
|
||||
s.leaseHosts.Add(l.Hostname)
|
||||
|
||||
if l.Hostname != "" {
|
||||
s.leaseHosts.Add(l.Hostname)
|
||||
}
|
||||
}()
|
||||
|
||||
s.conf.notify(LeaseChangedAdded)
|
||||
}
|
||||
|
||||
// allocateLease allocates a new lease for the MAC address. If there are no IP
|
||||
// addresses left, both l and err are nil.
|
||||
func (s *v4Server) allocateLease(mac net.HardwareAddr) (l *Lease, err error) {
|
||||
for {
|
||||
l, err = s.reserveLease(mac)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reserving a lease: %w", err)
|
||||
} else if l == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if s.addrAvailable(l.IP) {
|
||||
return l, nil
|
||||
}
|
||||
|
||||
s.blocklistLease(l)
|
||||
}
|
||||
}
|
||||
|
||||
// processDiscover is the handler for the DHCP Discover request.
|
||||
func (s *v4Server) processDiscover(req, resp *dhcpv4.DHCPv4) (l *Lease, err error) {
|
||||
mac := req.ClientHWAddr
|
||||
|
||||
err = aghnet.ValidateHardwareAddress(mac)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.conf.notify(LeaseChangedDBStore)
|
||||
|
||||
s.leasesLock.Lock()
|
||||
defer s.leasesLock.Unlock()
|
||||
@@ -583,34 +590,13 @@ func (s *v4Server) processDiscover(req, resp *dhcpv4.DHCPv4) (l *Lease, err erro
|
||||
return l, nil
|
||||
}
|
||||
|
||||
needsUpdate := false
|
||||
defer func() {
|
||||
if needsUpdate {
|
||||
s.conf.notify(LeaseChangedDBStore)
|
||||
}
|
||||
}()
|
||||
l, err = s.allocateLease(mac)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if l == nil {
|
||||
log.Debug("dhcpv4: no more ip addresses")
|
||||
|
||||
leaseReady := false
|
||||
for !leaseReady {
|
||||
l, err = s.reserveLease(mac)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reserving a lease: %w", err)
|
||||
}
|
||||
|
||||
if l == nil {
|
||||
log.Debug("dhcpv4: no more ip addresses")
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
needsUpdate = true
|
||||
|
||||
if s.addrAvailable(l.IP) {
|
||||
leaseReady = true
|
||||
} else {
|
||||
s.blocklistLease(l)
|
||||
l = nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeOffer))
|
||||
@@ -647,14 +633,9 @@ func (o *optFQDN) ToBytes() []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
// processDiscover is the handler for the DHCP Request request.
|
||||
// processRequest is the handler for the DHCP Request request.
|
||||
func (s *v4Server) processRequest(req, resp *dhcpv4.DHCPv4) (lease *Lease, needsReply bool) {
|
||||
mac := req.ClientHWAddr
|
||||
err := aghnet.ValidateHardwareAddress(mac)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
reqIP := req.RequestedIPAddress()
|
||||
if reqIP == nil {
|
||||
reqIP = req.ClientIPAddr
|
||||
@@ -662,13 +643,13 @@ func (s *v4Server) processRequest(req, resp *dhcpv4.DHCPv4) (lease *Lease, needs
|
||||
|
||||
sid := req.ServerIdentifier()
|
||||
if len(sid) != 0 && !sid.Equal(s.conf.dnsIPAddrs[0]) {
|
||||
log.Debug("dhcpv4: bad OptionServerIdentifier in request message for %s", mac)
|
||||
log.Debug("dhcpv4: bad OptionServerIdentifier in req msg for %s", mac)
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if ip4 := reqIP.To4(); ip4 == nil {
|
||||
log.Debug("dhcpv4: bad OptionRequestedIPAddress in request message for %s", mac)
|
||||
log.Debug("dhcpv4: bad OptionRequestedIPAddress in req msg for %s", mac)
|
||||
|
||||
return nil, false
|
||||
}
|
||||
@@ -685,15 +666,17 @@ func (s *v4Server) processRequest(req, resp *dhcpv4.DHCPv4) (lease *Lease, needs
|
||||
|
||||
if l.IP.Equal(reqIP) {
|
||||
lease = l
|
||||
} else {
|
||||
log.Debug(
|
||||
`dhcpv4: mismatched OptionRequestedIPAddress `+
|
||||
`in request message for %s`,
|
||||
mac,
|
||||
)
|
||||
mismatch = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug(
|
||||
`dhcpv4: mismatched OptionRequestedIPAddress in req msg for %s`,
|
||||
mac,
|
||||
)
|
||||
|
||||
mismatch = true
|
||||
|
||||
return
|
||||
}
|
||||
}()
|
||||
@@ -709,17 +692,16 @@ func (s *v4Server) processRequest(req, resp *dhcpv4.DHCPv4) (lease *Lease, needs
|
||||
|
||||
if !lease.IsStatic() {
|
||||
cliHostname := req.HostName()
|
||||
lease.Hostname, err = s.validHostnameForClient(cliHostname, lease.Hostname, reqIP)
|
||||
if err != nil {
|
||||
log.Info(
|
||||
"dhcpv4: warning: client hostname %q is invalid: %s",
|
||||
cliHostname,
|
||||
err,
|
||||
)
|
||||
hostname := s.validHostnameForClient(cliHostname, reqIP)
|
||||
if hostname != lease.Hostname && s.leaseHosts.Has(hostname) {
|
||||
log.Info("dhcpv4: hostname %q already exists", hostname)
|
||||
lease.Hostname = ""
|
||||
} else {
|
||||
lease.Hostname = hostname
|
||||
}
|
||||
|
||||
s.commitLease(lease)
|
||||
} else if len(lease.Hostname) != 0 {
|
||||
} else if lease.Hostname != "" {
|
||||
o := &optFQDN{
|
||||
name: lease.Hostname,
|
||||
}
|
||||
@@ -736,6 +718,107 @@ func (s *v4Server) processRequest(req, resp *dhcpv4.DHCPv4) (lease *Lease, needs
|
||||
return lease, true
|
||||
}
|
||||
|
||||
// processRequest is the handler for the DHCP Decline request.
|
||||
func (s *v4Server) processDecline(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||
s.conf.notify(LeaseChangedDBStore)
|
||||
|
||||
s.leasesLock.Lock()
|
||||
defer s.leasesLock.Unlock()
|
||||
|
||||
mac := req.ClientHWAddr
|
||||
reqIP := req.RequestedIPAddress()
|
||||
if reqIP == nil {
|
||||
reqIP = req.ClientIPAddr
|
||||
}
|
||||
|
||||
var oldLease *Lease
|
||||
for _, l := range s.leases {
|
||||
if bytes.Equal(l.HWAddr, mac) && l.IP.Equal(reqIP) {
|
||||
oldLease = l
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if oldLease == nil {
|
||||
log.Info("dhcpv4: lease with ip %s for %s not found", reqIP, mac)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err = s.rmDynamicLease(oldLease)
|
||||
if err != nil {
|
||||
return fmt.Errorf("removing old lease for %s: %w", mac, err)
|
||||
}
|
||||
|
||||
newLease, err := s.allocateLease(mac)
|
||||
if err != nil {
|
||||
return fmt.Errorf("allocating new lease for %s: %w", mac, err)
|
||||
} else if newLease == nil {
|
||||
log.Info("dhcpv4: allocating new lease for %s: no more ip addresses", mac)
|
||||
|
||||
resp.YourIPAddr = make([]byte, 4)
|
||||
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
newLease.Hostname = oldLease.Hostname
|
||||
newLease.Expiry = time.Now().Add(s.conf.leaseTime)
|
||||
|
||||
err = s.addLease(newLease)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adding new lease for %s: %w", mac, err)
|
||||
}
|
||||
|
||||
log.Info("dhcpv4: changed ip from %s to %s for %s", reqIP, newLease.IP, mac)
|
||||
|
||||
resp.YourIPAddr = make([]byte, 4)
|
||||
copy(resp.YourIPAddr, newLease.IP)
|
||||
|
||||
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processRelease is the handler for the DHCP Release request.
|
||||
func (s *v4Server) processRelease(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||
mac := req.ClientHWAddr
|
||||
reqIP := req.RequestedIPAddress()
|
||||
if reqIP == nil {
|
||||
reqIP = req.ClientIPAddr
|
||||
}
|
||||
|
||||
// TODO(a.garipov): Add a separate notification type for dynamic lease
|
||||
// removal?
|
||||
defer s.conf.notify(LeaseChangedDBStore)
|
||||
|
||||
n := 0
|
||||
s.leasesLock.Lock()
|
||||
defer s.leasesLock.Unlock()
|
||||
|
||||
for _, l := range s.leases {
|
||||
if !bytes.Equal(l.HWAddr, mac) || !l.IP.Equal(reqIP) {
|
||||
continue
|
||||
}
|
||||
|
||||
err = s.rmDynamicLease(l)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("removing dynamic lease for %s: %w", mac, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
n++
|
||||
}
|
||||
|
||||
log.Info("dhcpv4: released %d dynamic leases for %s", n, mac)
|
||||
|
||||
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find a lease associated with MAC and prepare response
|
||||
// Return 1: OK
|
||||
// Return 0: error; reply with Nak
|
||||
@@ -745,6 +828,7 @@ func (s *v4Server) process(req, resp *dhcpv4.DHCPv4) int {
|
||||
|
||||
resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0]))
|
||||
|
||||
// TODO(a.garipov): Refactor this into handlers.
|
||||
var l *Lease
|
||||
switch req.MessageType() {
|
||||
case dhcpv4.MessageTypeDiscover:
|
||||
@@ -767,10 +851,26 @@ func (s *v4Server) process(req, resp *dhcpv4.DHCPv4) int {
|
||||
}
|
||||
return -1 // drop packet
|
||||
}
|
||||
case dhcpv4.MessageTypeDecline:
|
||||
err = s.processDecline(req, resp)
|
||||
if err != nil {
|
||||
log.Error("dhcpv4: processing decline: %s", err)
|
||||
|
||||
return 0
|
||||
}
|
||||
case dhcpv4.MessageTypeRelease:
|
||||
err = s.processRelease(req, resp)
|
||||
if err != nil {
|
||||
log.Error("dhcpv4: processing release: %s", err)
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
resp.YourIPAddr = make([]byte, 4)
|
||||
copy(resp.YourIPAddr, l.IP)
|
||||
if l != nil {
|
||||
resp.YourIPAddr = make([]byte, 4)
|
||||
copy(resp.YourIPAddr, l.IP)
|
||||
}
|
||||
|
||||
resp.UpdateOption(dhcpv4.OptIPAddressLeaseTime(s.conf.leaseTime))
|
||||
resp.UpdateOption(dhcpv4.OptRouter(s.conf.subnet.IP))
|
||||
@@ -793,11 +893,13 @@ func (s *v4Server) packetHandler(conn net.PacketConn, peer net.Addr, req *dhcpv4
|
||||
|
||||
switch req.MessageType() {
|
||||
case dhcpv4.MessageTypeDiscover,
|
||||
dhcpv4.MessageTypeRequest:
|
||||
//
|
||||
|
||||
dhcpv4.MessageTypeRequest,
|
||||
dhcpv4.MessageTypeDecline,
|
||||
dhcpv4.MessageTypeRelease:
|
||||
// Go on.
|
||||
default:
|
||||
log.Debug("dhcpv4: unsupported message type %d", req.MessageType())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ func newAccessCtx(allowedClients, disallowedClients, blockedHosts []string) (a *
|
||||
|
||||
b := &strings.Builder{}
|
||||
for _, s := range blockedHosts {
|
||||
aghstrings.WriteToBuilder(b, s, "\n")
|
||||
aghstrings.WriteToBuilder(b, strings.ToLower(s), "\n")
|
||||
}
|
||||
|
||||
listArray := []filterlist.RuleList{}
|
||||
|
||||
@@ -370,9 +370,7 @@ func (s *Server) setupResolvers(localAddrs []string) (err error) {
|
||||
// really applicable here since in case of listening on all network
|
||||
// interfaces we should check the whole interface's network to cut off
|
||||
// all the loopback addresses as well.
|
||||
localAddrs = aghstrings.FilterOut(localAddrs, func(s string) (ok bool) {
|
||||
return ourAddrsSet.Has(s)
|
||||
})
|
||||
localAddrs = aghstrings.FilterOut(localAddrs, ourAddrsSet.Has)
|
||||
|
||||
var upsConfig proxy.UpstreamConfig
|
||||
upsConfig, err = proxy.ParseUpstreamsConfig(localAddrs, upstream.Options{
|
||||
|
||||
@@ -20,7 +20,11 @@ func (s *Server) beforeRequestHandler(_ *proxy.Proxy, d *proxy.DNSContext) (bool
|
||||
}
|
||||
|
||||
if len(d.Req.Question) == 1 {
|
||||
host := strings.TrimSuffix(d.Req.Question[0].Name, ".")
|
||||
// It's lowercased here since this handler is called before any
|
||||
// other one.
|
||||
name := strings.ToLower(d.Req.Question[0].Name)
|
||||
d.Req.Question[0].Name = name
|
||||
host := strings.TrimSuffix(name, ".")
|
||||
if s.access.IsBlockedDomain(host) {
|
||||
log.Tracef("Domain %s is blocked by settings", host)
|
||||
return false, nil
|
||||
@@ -46,8 +50,6 @@ func (s *Server) getClientRequestFilteringSettings(ctx *dnsContext) *dnsfilter.F
|
||||
// filtered.
|
||||
func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||
d := ctx.proxyCtx
|
||||
// TODO(e.burkov): Consistently use req instead of d.Req since it is
|
||||
// declared.
|
||||
req := d.Req
|
||||
host := strings.TrimSuffix(req.Question[0].Name, ".")
|
||||
res, err := s.dnsFilter.CheckHost(host, req.Question[0].Qtype, ctx.setts)
|
||||
|
||||
@@ -133,14 +133,21 @@ func handleStatus(w http.ResponseWriter, _ *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
resp := statusResponse{
|
||||
DNSAddrs: dnsAddrs,
|
||||
DNSPort: config.DNS.Port,
|
||||
HTTPPort: config.BindPort,
|
||||
IsRunning: isRunning(),
|
||||
Version: version.Version(),
|
||||
Language: config.Language,
|
||||
}
|
||||
var resp statusResponse
|
||||
|
||||
func() {
|
||||
config.RLock()
|
||||
defer config.RUnlock()
|
||||
|
||||
resp = statusResponse{
|
||||
DNSAddrs: dnsAddrs,
|
||||
DNSPort: config.DNS.Port,
|
||||
HTTPPort: config.BindPort,
|
||||
IsRunning: isRunning(),
|
||||
Version: version.Version(),
|
||||
Language: config.Language,
|
||||
}
|
||||
}()
|
||||
|
||||
var c *dnsforward.FilteringConfig
|
||||
if Context.dnsServer != nil {
|
||||
|
||||
@@ -319,6 +319,9 @@ func applyAdditionalFiltering(clientAddr net.IP, clientID string, setts *dnsfilt
|
||||
}
|
||||
|
||||
func startDNSServer() error {
|
||||
config.Lock()
|
||||
defer config.Unlock()
|
||||
|
||||
if isRunning() {
|
||||
return fmt.Errorf("unable to start forwarding DNS server: Already running")
|
||||
}
|
||||
|
||||
@@ -76,16 +76,6 @@ type filter struct {
|
||||
dnsfilter.Filter `yaml:",inline"`
|
||||
}
|
||||
|
||||
// Creates a helper object for working with the user rules
|
||||
func userFilter() filter {
|
||||
f := filter{
|
||||
// User filter always has constant ID=0
|
||||
Enabled: true,
|
||||
}
|
||||
f.Filter.Data = []byte(strings.Join(config.UserRules, "\n"))
|
||||
return f
|
||||
}
|
||||
|
||||
const (
|
||||
statusFound = 1
|
||||
statusEnabledChanged = 2
|
||||
@@ -689,41 +679,33 @@ func (filter *filter) LastTimeUpdated() time.Time {
|
||||
}
|
||||
|
||||
func enableFilters(async bool) {
|
||||
var filters []dnsfilter.Filter
|
||||
var whiteFilters []dnsfilter.Filter
|
||||
if config.DNS.FilteringEnabled {
|
||||
// convert array of filters
|
||||
filters := []dnsfilter.Filter{{
|
||||
Data: []byte(strings.Join(config.UserRules, "\n")),
|
||||
}}
|
||||
|
||||
userFilter := userFilter()
|
||||
f := dnsfilter.Filter{
|
||||
ID: userFilter.ID,
|
||||
Data: userFilter.Data,
|
||||
for _, filter := range config.Filters {
|
||||
if !filter.Enabled {
|
||||
continue
|
||||
}
|
||||
filters = append(filters, f)
|
||||
|
||||
for _, filter := range config.Filters {
|
||||
if !filter.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
f = dnsfilter.Filter{
|
||||
ID: filter.ID,
|
||||
FilePath: filter.Path(),
|
||||
}
|
||||
filters = append(filters, f)
|
||||
filters = append(filters, dnsfilter.Filter{
|
||||
ID: filter.ID,
|
||||
FilePath: filter.Path(),
|
||||
})
|
||||
}
|
||||
for _, filter := range config.WhitelistFilters {
|
||||
if !filter.Enabled {
|
||||
continue
|
||||
}
|
||||
for _, filter := range config.WhitelistFilters {
|
||||
if !filter.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
f = dnsfilter.Filter{
|
||||
ID: filter.ID,
|
||||
FilePath: filter.Path(),
|
||||
}
|
||||
whiteFilters = append(whiteFilters, f)
|
||||
}
|
||||
whiteFilters = append(whiteFilters, dnsfilter.Filter{
|
||||
ID: filter.ID,
|
||||
FilePath: filter.Path(),
|
||||
})
|
||||
}
|
||||
|
||||
_ = Context.dnsFilter.SetFilters(filters, whiteFilters, async)
|
||||
if err := Context.dnsFilter.SetFilters(filters, whiteFilters, async); err != nil {
|
||||
log.Debug("enabling filters: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,6 +493,10 @@ func configureLogger(args options) {
|
||||
log.SetLevel(log.DEBUG)
|
||||
}
|
||||
|
||||
// Make sure that we see the microseconds in logs, as networking stuff
|
||||
// can happen pretty quickly.
|
||||
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
|
||||
|
||||
if args.runningAsService && ls.LogFile == "" && runtime.GOOS == "windows" {
|
||||
// When running as a Windows service, use eventlog by default if nothing else is configured
|
||||
// Otherwise, we'll simply loose the log output
|
||||
|
||||
@@ -86,7 +86,13 @@ func handleI18nChangeLanguage(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
config.Language = language
|
||||
func() {
|
||||
config.Lock()
|
||||
defer config.Unlock()
|
||||
|
||||
config.Language = language
|
||||
}()
|
||||
|
||||
onConfigModified()
|
||||
returnOK(w)
|
||||
}
|
||||
|
||||
@@ -1,249 +1,507 @@
|
||||
#!/bin/sh
|
||||
|
||||
# AdGuard Home Installation Script
|
||||
#
|
||||
# 1. Download the package
|
||||
# 2. Unpack it
|
||||
# 3. Install as a service
|
||||
#
|
||||
# Requirements:
|
||||
# . bash
|
||||
# . which
|
||||
# . printf
|
||||
# . uname
|
||||
# . id
|
||||
# . head, tail
|
||||
# . curl
|
||||
# . tar or unzip
|
||||
# . rm
|
||||
|
||||
set -e
|
||||
|
||||
log_info()
|
||||
{
|
||||
printf "[info] %s\\n" "$1"
|
||||
}
|
||||
|
||||
log_error()
|
||||
{
|
||||
printf "[error] %s\\n" "$1"
|
||||
}
|
||||
|
||||
# Get OS
|
||||
# Return: darwin, linux, freebsd
|
||||
detect_os()
|
||||
{
|
||||
UNAME_S="$(uname -s)"
|
||||
OS=
|
||||
case "$UNAME_S" in
|
||||
Linux)
|
||||
OS=linux
|
||||
;;
|
||||
|
||||
FreeBSD)
|
||||
OS=freebsd
|
||||
;;
|
||||
|
||||
Darwin)
|
||||
OS=darwin
|
||||
;;
|
||||
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
echo $OS
|
||||
}
|
||||
|
||||
# Get CPU endianness
|
||||
# Return: le, ""
|
||||
cpu_little_endian()
|
||||
{
|
||||
ENDIAN_FLAG="$(head -c 6 /bin/bash | tail -c 1)"
|
||||
if [ "$ENDIAN_FLAG" = "$(printf '\001')" ]; then
|
||||
echo 'le'
|
||||
return 0
|
||||
# Function log is an echo wrapper that writes to stderr if the caller
|
||||
# requested verbosity level greater than 0. Otherwise, it does nothing.
|
||||
log() {
|
||||
if [ "$verbose" -gt '0' ]
|
||||
then
|
||||
echo "$1" 1>&2
|
||||
fi
|
||||
}
|
||||
|
||||
# Get CPU
|
||||
# Return: amd64, 386, armv5, armv6, armv7, arm64, mips_softfloat, mipsle_softfloat, mips64_softfloat, mips64le_softfloat
|
||||
detect_cpu()
|
||||
{
|
||||
UNAME_M="$(uname -m)"
|
||||
CPU=
|
||||
# Function error_exit is an echo wrapper that writes to stderr and stops the
|
||||
# script execution with code 1.
|
||||
error_exit() {
|
||||
echo "$1" 1>&2
|
||||
|
||||
case "$UNAME_M" in
|
||||
|
||||
x86_64 | x86-64 | x64 | amd64)
|
||||
CPU=amd64
|
||||
;;
|
||||
|
||||
i386 | i486 | i686 | i786 | x86)
|
||||
CPU=386
|
||||
;;
|
||||
|
||||
armv5l)
|
||||
CPU=armv5
|
||||
;;
|
||||
|
||||
armv6l)
|
||||
CPU=armv6
|
||||
;;
|
||||
|
||||
armv7l | armv8l)
|
||||
CPU=armv7
|
||||
;;
|
||||
|
||||
aarch64 | arm64)
|
||||
CPU=arm64
|
||||
;;
|
||||
|
||||
mips)
|
||||
LE=$(cpu_little_endian)
|
||||
CPU=mips${LE}_softfloat
|
||||
;;
|
||||
|
||||
mips64)
|
||||
LE=$(cpu_little_endian)
|
||||
CPU=mips64${LE}_softfloat
|
||||
;;
|
||||
|
||||
*)
|
||||
return 1
|
||||
|
||||
esac
|
||||
|
||||
echo "${CPU}"
|
||||
}
|
||||
|
||||
# Get package file name extension
|
||||
# Return: tar.gz, zip
|
||||
package_extension()
|
||||
{
|
||||
if [ "$OS" = "darwin" ]; then
|
||||
echo "zip"
|
||||
return 0
|
||||
fi
|
||||
echo "tar.gz"
|
||||
}
|
||||
|
||||
# Download data to a file
|
||||
# Use: download URL OUTPUT
|
||||
download()
|
||||
{
|
||||
log_info "Downloading package from $1 -> $2"
|
||||
if is_command curl ; then
|
||||
curl -s "$1" --output "$2" || error_exit "Failed to download $1"
|
||||
else
|
||||
error_exit "curl is necessary to install AdGuard Home"
|
||||
fi
|
||||
}
|
||||
|
||||
# Unpack package to a directory
|
||||
# Use: unpack INPUT OUTPUT_DIR PKG_EXT
|
||||
unpack()
|
||||
{
|
||||
log_info "Unpacking package from $1 -> $2"
|
||||
mkdir -p "$2"
|
||||
if [ "$3" = "zip" ]; then
|
||||
unzip -qq "$1" -d "$2" || return 1
|
||||
elif [ "$3" = "tar.gz" ]; then
|
||||
tar xzf "$1" -C "$2" || return 1
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Print error message and exit
|
||||
# Use: error_exit MESSAGE
|
||||
error_exit()
|
||||
{
|
||||
log_error "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if command exists
|
||||
# Use: is_command COMMAND
|
||||
# Function usage prints the note about how to use the script.
|
||||
#
|
||||
# TODO(e.burkov): Document each option.
|
||||
usage() {
|
||||
echo 'install.sh: usage: [-c channel] [-C cpu_type] [-h] [-O os] [-o output_dir]'\
|
||||
'[-r|-R] [-u|-U] [-v|-V]' 1>&2
|
||||
|
||||
exit 2
|
||||
}
|
||||
|
||||
# Function is_command checks if the command exists on the machine.
|
||||
is_command() {
|
||||
check_command="$1"
|
||||
command -v "${check_command}" >/dev/null 2>&1
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Entry point
|
||||
main() {
|
||||
log_info "Starting AdGuard Home installation script"
|
||||
|
||||
CHANNEL=${1}
|
||||
if [ "${CHANNEL}" != "beta" ] && [ "${CHANNEL}" != "edge" ]; then
|
||||
CHANNEL=release
|
||||
fi
|
||||
log_info "Channel ${CHANNEL}"
|
||||
|
||||
OS=$(detect_os) || error_exit "Cannot detect your OS"
|
||||
CPU=$(detect_cpu) || error_exit "Cannot detect your CPU"
|
||||
|
||||
# TODO: Remove when Mac M1 native support is added
|
||||
if [ "${OS}" = "darwin" ] && [ "${CPU}" = "arm64" ]; then
|
||||
CPU="amd64"
|
||||
log_info "Use ${CPU} build on Mac M1 until the native ARM support is added"
|
||||
fi
|
||||
|
||||
PKG_EXT=$(package_extension)
|
||||
PKG_NAME=AdGuardHome_${OS}_${CPU}.${PKG_EXT}
|
||||
|
||||
SCRIPT_URL="https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh"
|
||||
URL="https://static.adguard.com/adguardhome/${CHANNEL}/${PKG_NAME}"
|
||||
OUT_DIR="/opt"
|
||||
if [ "${OS}" = "darwin" ]; then
|
||||
# It may be important to install AdGuard Home to /Applications on MacOS
|
||||
# Otherwise, it may not grant enough privileges to it
|
||||
OUT_DIR="/Applications"
|
||||
fi
|
||||
|
||||
AGH_DIR="${OUT_DIR}/AdGuardHome"
|
||||
|
||||
# Root check
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
log_info "Script called with root privileges"
|
||||
else
|
||||
if is_command sudo ; then
|
||||
log_info "Please note, that AdGuard Home requires root privileges to install using this script."
|
||||
log_info "Restarting with root privileges"
|
||||
|
||||
exec curl -sSL ${SCRIPT_URL} | sudo sh -s "$@"
|
||||
exit $?
|
||||
else
|
||||
log_info "Root privileges are required to install AdGuard Home using this installer."
|
||||
log_info "Please, re-run this script as root."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "AdGuard Home will be installed to ${AGH_DIR}"
|
||||
|
||||
[ -d "${AGH_DIR}" ] && [ -n "$(ls -1 -A -q ${AGH_DIR})" ] && error_exit "Directory ${AGH_DIR} is not empty, abort installation"
|
||||
|
||||
download "${URL}" "${PKG_NAME}" || error_exit "Cannot download the package"
|
||||
|
||||
if [ "${OS}" = "darwin" ]; then
|
||||
# TODO: remove this after v0.106.0 release
|
||||
mkdir "${AGH_DIR}"
|
||||
unpack "${PKG_NAME}" "${AGH_DIR}" "${PKG_EXT}" || error_exit "Cannot unpack the package"
|
||||
else
|
||||
unpack "${PKG_NAME}" "${OUT_DIR}" "${PKG_EXT}" || error_exit "Cannot unpack the package"
|
||||
fi
|
||||
|
||||
# Install AdGuard Home service and run it.
|
||||
( cd "${AGH_DIR}" && ./AdGuardHome -s install || error_exit "Cannot install AdGuardHome as a service" )
|
||||
|
||||
rm "${PKG_NAME}"
|
||||
|
||||
log_info "AdGuard Home is now installed and running."
|
||||
log_info "You can control the service status with the following commands:"
|
||||
log_info " sudo ${AGH_DIR}/AdGuardHome -s start|stop|restart|status|install|uninstall"
|
||||
# Function is_little_endian checks if the CPU is little-endian.
|
||||
is_little_endian() {
|
||||
[ "$( head -c 6 /bin/sh | tail -c 1 )" = "$( printf '\001' )" ]
|
||||
}
|
||||
|
||||
main "$@"
|
||||
# Function check_required checks if the required software is available on the
|
||||
# machine. The required software:
|
||||
#
|
||||
# curl
|
||||
# unzip (macOS) / tar (other unices)
|
||||
#
|
||||
check_required() {
|
||||
readonly required_darwin="unzip"
|
||||
readonly required_unix="tar"
|
||||
|
||||
# Split with space.
|
||||
required="curl"
|
||||
if [ "$os" = 'linux' ] || [ "$os" = 'freebsd' ]
|
||||
then
|
||||
required="$required $required_unix"
|
||||
elif [ "$os" = 'darwin' ]
|
||||
then
|
||||
required="$required $required_darwin"
|
||||
fi
|
||||
|
||||
# Don't use quotes to get word splitting.
|
||||
for cmd in ${required}
|
||||
do
|
||||
echo "checking $cmd"
|
||||
if ! is_command "$cmd"
|
||||
then
|
||||
log "the full list of required software: [$required]"
|
||||
|
||||
error_exit "$cmd is required to install AdGuard Home via this script"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Function check_out_dir requires the output directory to be set and exist.
|
||||
check_out_dir() {
|
||||
if [ "$out_dir" = '' ]
|
||||
then
|
||||
error_exit 'output directory should be presented'
|
||||
fi
|
||||
|
||||
if ! [ -d "$out_dir" ]
|
||||
then
|
||||
log "$out_dir directory will be created"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function parse_opts parses the options list and validates it's combinations.
|
||||
parse_opts() {
|
||||
while getopts "C:c:hO:o:rRuUvV" opt $*
|
||||
do
|
||||
case "$opt"
|
||||
in
|
||||
(C)
|
||||
cpu="$OPTARG"
|
||||
;;
|
||||
(c)
|
||||
channel="$OPTARG"
|
||||
;;
|
||||
(h)
|
||||
usage
|
||||
;;
|
||||
(O)
|
||||
os="$OPTARG"
|
||||
;;
|
||||
(o)
|
||||
out_dir="$OPTARG"
|
||||
;;
|
||||
(R)
|
||||
reinstall='0'
|
||||
;;
|
||||
(U)
|
||||
uninstall='0'
|
||||
;;
|
||||
(r)
|
||||
reinstall='1'
|
||||
;;
|
||||
(u)
|
||||
uninstall='1'
|
||||
;;
|
||||
(V)
|
||||
verbose='0'
|
||||
;;
|
||||
(v)
|
||||
verbose='1'
|
||||
;;
|
||||
(*)
|
||||
log "bad option $OPTARG"
|
||||
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$uninstall" = '1' ] && [ "$reinstall" = '1' ]
|
||||
then
|
||||
error_exit 'the -r and -u options are mutually exclusive'
|
||||
fi
|
||||
}
|
||||
|
||||
# Function set_channel sets the channel if needed and validates the value.
|
||||
set_channel() {
|
||||
# Validate.
|
||||
case "$channel"
|
||||
in
|
||||
('development'|'edge'|'beta'|'release')
|
||||
# All is well, go on.
|
||||
;;
|
||||
(*)
|
||||
error_exit \
|
||||
"invalid channel '$channel'
|
||||
supported values are 'development', 'edge', 'beta', and 'release'"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Log.
|
||||
log "channel: $channel"
|
||||
}
|
||||
|
||||
# Function set_os sets the os if needed and validates the value.
|
||||
set_os() {
|
||||
# Set if needed.
|
||||
if [ "$os" = '' ]
|
||||
then
|
||||
os="$( uname -s )"
|
||||
case "$os"
|
||||
in
|
||||
('Linux')
|
||||
os='linux'
|
||||
;;
|
||||
('FreeBSD')
|
||||
os='freebsd'
|
||||
;;
|
||||
('Darwin')
|
||||
os='darwin'
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Validate.
|
||||
case "$os"
|
||||
in
|
||||
('linux'|'freebsd'|'darwin')
|
||||
# All right, go on.
|
||||
;;
|
||||
(*)
|
||||
error_exit "unsupported operating system: $os"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Log.
|
||||
log "operating system: $os"
|
||||
}
|
||||
|
||||
# Function set_cpu sets the cpu if needed and validates the value.
|
||||
set_cpu() {
|
||||
# Set if needed.
|
||||
if [ "$cpu" = '' ]
|
||||
then
|
||||
cpu="$( uname -m )"
|
||||
case "$cpu"
|
||||
in
|
||||
('x86_64'|'x86-64'|'x64'|'amd64')
|
||||
cpu='amd64'
|
||||
;;
|
||||
('i386'|'i486'|'i686'|'i786'|'x86')
|
||||
cpu='386'
|
||||
;;
|
||||
('armv5l')
|
||||
cpu='armv5'
|
||||
;;
|
||||
('armv6l')
|
||||
cpu='armv6'
|
||||
;;
|
||||
('armv7l' | 'armv8l')
|
||||
cpu='armv7'
|
||||
;;
|
||||
('aarch64'|'arm64')
|
||||
cpu='arm64'
|
||||
;;
|
||||
('mips'|'mips64')
|
||||
if is_little_endian
|
||||
then
|
||||
cpu="${cpu}le"
|
||||
fi
|
||||
cpu="${cpu}_softfloat"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Validate.
|
||||
case "$cpu"
|
||||
in
|
||||
('amd64'|'386'|'armv5'|'armv6'|'armv7'|'arm64')
|
||||
# All right, go on.
|
||||
;;
|
||||
('mips64le_softfloat'|'mips64_softfloat'|'mipsle_softfloat'|'mips_softfloat')
|
||||
# That's right too.
|
||||
;;
|
||||
(*)
|
||||
error_exit "unsupported cpu type: $cpu"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Log.
|
||||
log "cpu type: $cpu"
|
||||
}
|
||||
|
||||
# Function fix_darwin performs some configuration changes for macOS if
|
||||
# needed.
|
||||
fix_darwin() {
|
||||
if ! [ "$os" = 'darwin' ]
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# TODO: Remove when Mac M1 native support is added.
|
||||
if [ "$cpu" = 'arm64' ]
|
||||
then
|
||||
cpu='amd64'
|
||||
log "use $cpu build on Mac M1 until the native ARM support is added."
|
||||
fi
|
||||
|
||||
# Set the package extension.
|
||||
pkg_ext='zip'
|
||||
|
||||
# It is important to install AdGuard Home into the /Applications
|
||||
# directory on MacOS. Otherwise, it may not grant enough privileges to
|
||||
# the AdGuard Home.
|
||||
out_dir='/Applications'
|
||||
}
|
||||
|
||||
# Function fix_freebsd performs some fixes to make it work on FreeBSD.
|
||||
fix_freebsd() {
|
||||
if ! [ "$os" = 'freebsd' ]
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
readonly rcd='/usr/local/etc/rc.d'
|
||||
if ! [ -d "$rcd" ]
|
||||
then
|
||||
mkdir "$rcd"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function configure sets the script's configuration.
|
||||
configure() {
|
||||
set_channel
|
||||
set_os
|
||||
set_cpu
|
||||
fix_darwin
|
||||
check_out_dir
|
||||
|
||||
readonly pkg_name="AdGuardHome_${os}_${cpu}.${pkg_ext}"
|
||||
readonly url="https://static.adguard.com/adguardhome/${channel}/${pkg_name}"
|
||||
readonly agh_dir="${out_dir}/AdGuardHome"
|
||||
|
||||
log "AdGuard Home will be installed into $agh_dir"
|
||||
}
|
||||
|
||||
# Function is_root checks for root privileges to be granted.
|
||||
is_root() {
|
||||
if [ "$( id -u )" = '0' ]
|
||||
then
|
||||
log 'script is executed with root privileges'
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
if is_command sudo
|
||||
then
|
||||
log 'note that AdGuard Home requires root privileges to install using this script'
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
error_exit \
|
||||
'root privileges are required to install AdGuard Home using this script
|
||||
please, restart it with root privileges'
|
||||
}
|
||||
|
||||
# Function rerun_with_root downloads the script and tries to run it with root
|
||||
# privileges. It also uses the configuration that already set.
|
||||
#
|
||||
# TODO(e.burkov): Try to avoid restarting.
|
||||
rerun_with_root() {
|
||||
readonly script_url=\
|
||||
'https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh'
|
||||
|
||||
flags=''
|
||||
if [ "$reinstall" = '1' ]
|
||||
then
|
||||
flags="${flags} -r"
|
||||
fi
|
||||
if [ "$uninstall" = '1' ]
|
||||
then
|
||||
flags="${flags} -u"
|
||||
fi
|
||||
if [ "$verbose" = '1' ]
|
||||
then
|
||||
flags="${flags} -v"
|
||||
fi
|
||||
|
||||
readonly opts="-c $channel -C $cpu -O $os -o $out_dir $flags"
|
||||
|
||||
log 'restarting with root privileges'
|
||||
|
||||
curl -L -S -s "$script_url" | sudo sh -s -- $opts
|
||||
exit $?
|
||||
}
|
||||
|
||||
# Function download downloads the file from the URL and saves it to the
|
||||
# specified filepath.
|
||||
download() {
|
||||
log "downloading package from $url -> $pkg_name"
|
||||
|
||||
if ! curl -s "$url" --output "$pkg_name"
|
||||
then
|
||||
error_exit "cannot download the package from $url into $pkg_name"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function unpack unpacks the passed archive depending on it's extension.
|
||||
unpack() {
|
||||
log "unpacking package from $pkg_name into $out_dir"
|
||||
if ! mkdir -p "$out_dir"
|
||||
then
|
||||
error_exit "cannot create directory at the $out_dir"
|
||||
fi
|
||||
|
||||
case "$pkg_ext"
|
||||
in
|
||||
('zip')
|
||||
unzip "$pkg_name" -d "$out_dir"
|
||||
;;
|
||||
('tar.gz')
|
||||
tar -C "$out_dir" -f "$pkg_name" -x -z
|
||||
;;
|
||||
(*)
|
||||
error_exit "unexpected package extension: '$pkg_ext'"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$?" != '0' ]
|
||||
then
|
||||
error_exit "cannot unpack the package into $out_dir"
|
||||
fi
|
||||
|
||||
rm "$pkg_name"
|
||||
}
|
||||
|
||||
# Function handle_existing detects the existing AGH installation and takes care
|
||||
# of removing it if needed.
|
||||
handle_existing() {
|
||||
if ! [ -d "$agh_dir" ]
|
||||
then
|
||||
log 'no need to uninstall'
|
||||
|
||||
if [ "$uninstall" = '1' ]
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$( ls -1 -A -q $agh_dir )" != '' ]
|
||||
then
|
||||
log 'the existing AdGuard Home installation is detected'
|
||||
|
||||
if [ "$reinstall" != '1' ] && [ "$uninstall" != '1' ]
|
||||
then
|
||||
error_exit \
|
||||
"to reinstall/uninstall the AdGuard Home using\this script specify one of the '-r' or '-u' flags"
|
||||
fi
|
||||
|
||||
if ( cd "$agh_dir" && ! ./AdGuardHome -s uninstall )
|
||||
then
|
||||
# It doesn't terminate the script since it is possible
|
||||
# that AGH just not installed as service but appearing
|
||||
# in the directory.
|
||||
log "cannot uninstall AdGuard Home from $agh_dir"
|
||||
fi
|
||||
|
||||
rm -r "$agh_dir"
|
||||
|
||||
log 'AdGuard Home was successfully uninstalled'
|
||||
fi
|
||||
|
||||
if [ "$uninstall" = '1' ]
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Function install_service tries to install AGH as service.
|
||||
install_service() {
|
||||
# TODO(e.burkov): Think about AGH's output suppressing with no verbose
|
||||
# flag.
|
||||
if ( cd "$agh_dir" && ./AdGuardHome -s install )
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
rm -r "$agh_dir"
|
||||
|
||||
# Some devices detected to have armv7 CPU face the compatibility
|
||||
# issues with actual armv7 builds. We should try to install the
|
||||
# armv5 binary instead.
|
||||
#
|
||||
# See https://github.com/AdguardTeam/AdGuardHome/issues/2542.
|
||||
if [ "$cpu" = 'armv7' ]
|
||||
then
|
||||
cpu='armv5'
|
||||
reinstall='1'
|
||||
|
||||
log "trying to use $cpu cpu"
|
||||
|
||||
rerun_with_root
|
||||
fi
|
||||
|
||||
error_exit 'cannot install AdGuardHome as a service'
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Entrypoint
|
||||
|
||||
# Exit the script if a pipeline fails (-e), prevent accidental filename
|
||||
# expansion (-f), and consider undefined variables as errors (-u).
|
||||
set -e -f -u
|
||||
|
||||
# Set default values of configuration variables.
|
||||
channel='release'
|
||||
reinstall='0'
|
||||
uninstall='0'
|
||||
verbose='0'
|
||||
cpu=''
|
||||
os=''
|
||||
out_dir='/opt'
|
||||
pkg_ext='tar.gz'
|
||||
parse_opts $*
|
||||
|
||||
echo 'starting AdGuard Home installation script'
|
||||
|
||||
configure
|
||||
check_required
|
||||
|
||||
if ! is_root
|
||||
then
|
||||
rerun_with_root
|
||||
fi
|
||||
# Needs rights.
|
||||
fix_freebsd
|
||||
|
||||
handle_existing
|
||||
|
||||
download
|
||||
unpack
|
||||
|
||||
install_service
|
||||
|
||||
echo "\
|
||||
AdGuard Home is now installed and running
|
||||
you can control the service status with the following commands:
|
||||
sudo ${agh_dir}/AdGuardHome -s start|stop|restart|status|install|uninstall"
|
||||
|
||||
@@ -37,13 +37,14 @@ RUN setcap 'cap_net_bind_service=+eip' /opt/adguardhome/AdGuardHome
|
||||
# 67, 68 : DHCP
|
||||
# 80 : HTTP
|
||||
# 443 : HTTPS, DNS-over-HTTPS, DNSCrypt
|
||||
# 784 : DNS-over-QUIC
|
||||
# 853 : DNS-over-TLS
|
||||
# 3000 : HTTP alt
|
||||
# 3001 : HTTP beta
|
||||
# 5443 : DNSCrypt alt
|
||||
EXPOSE 53/tcp 53/udp 67/udp 68/udp 80/tcp 443/tcp 443/udp 784/udp\
|
||||
853/tcp 3000/tcp 3001/tcp 5443/tcp 5443/udp
|
||||
# 6060 : HTTP pprof
|
||||
# 8853 : DNS-over-QUIC
|
||||
EXPOSE 53/tcp 53/udp 67/udp 68/udp 80/tcp 443/tcp 443/udp 853/tcp\
|
||||
3000/tcp 3001/tcp 5443/tcp 5443/udp 6060/tcp 8853/udp
|
||||
|
||||
WORKDIR /opt/adguardhome/work
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ underscores() {
|
||||
git ls-files '*_*.go' | {
|
||||
grep -F\
|
||||
-e '_big.go'\
|
||||
-e '_bsd.go'\
|
||||
-e '_darwin.go'\
|
||||
-e '_freebsd.go'\
|
||||
-e '_linux.go'\
|
||||
|
||||
Reference in New Issue
Block a user