From c9314610d45190aac953bdb5832811413dd1cfba Mon Sep 17 00:00:00 2001
From: Ainar Garipov
Date: Wed, 2 Nov 2022 16:18:02 +0300
Subject: [PATCH] all: sync with master
---
.github/ISSUE_TEMPLATE/config.yml | 5 +
.github/workflows/build.yml | 2 +-
.github/workflows/lint.yml | 2 +-
CHANGELOG.md | 88 +-
README.md | 555 +-
bamboo-specs/release.yaml | 6 +-
bamboo-specs/test.yaml | 2 +-
client/src/__locales/be.json | 7 +-
client/src/__locales/cs.json | 3 +-
client/src/__locales/da.json | 3 +-
client/src/__locales/de.json | 5 +-
client/src/__locales/en.json | 3 +-
client/src/__locales/es.json | 3 +-
client/src/__locales/fi.json | 13 +-
client/src/__locales/fr.json | 3 +-
client/src/__locales/hr.json | 3 +-
client/src/__locales/hu.json | 3 +-
client/src/__locales/id.json | 3 +-
client/src/__locales/it.json | 3 +-
client/src/__locales/ja.json | 3 +-
client/src/__locales/ko.json | 3 +-
client/src/__locales/nl.json | 3 +-
client/src/__locales/no.json | 2 +-
client/src/__locales/pl.json | 3 +-
client/src/__locales/pt-br.json | 3 +-
client/src/__locales/pt-pt.json | 3 +-
client/src/__locales/ro.json | 3 +-
client/src/__locales/ru.json | 3 +-
client/src/__locales/si-lk.json | 4 +-
client/src/__locales/sk.json | 3 +-
client/src/__locales/sl.json | 3 +-
client/src/__locales/sr-cs.json | 3 +-
client/src/__locales/sv.json | 3 +-
client/src/__locales/tr.json | 25 +-
client/src/__locales/uk.json | 3 +-
client/src/__locales/vi.json | 3 +-
client/src/__locales/zh-cn.json | 3 +-
client/src/__locales/zh-tw.json | 5 +-
client/src/actions/services.js | 15 +
client/src/api/Api.js | 7 +
client/src/components/Filters/DnsBlocklist.js | 2 +-
client/src/components/Filters/Form.js | 3 +-
.../src/components/Filters/Services/Form.js | 12 +-
.../src/components/Filters/Services/index.js | 4 +-
.../src/components/Logs/Cells/ClientCell.js | 2 +-
.../src/components/Logs/Cells/IconTooltip.css | 27 +-
.../src/components/Logs/Cells/ResponseCell.js | 9 +-
client/src/components/Logs/Cells/index.js | 11 +-
client/src/components/Logs/Logs.css | 4 +-
client/src/components/Logs/index.js | 21 +-
.../{ => ClientsTable}/ClientsTable.js | 263 +-
.../Settings/Clients/ClientsTable/index.js | 1 +
.../src/components/Settings/Clients/Form.js | 41 +-
.../components/Settings/Clients/Service.css | 8 +-
.../src/components/Settings/Clients/index.js | 2 +-
.../Settings/Dns/Upstream/Examples.js | 16 +
client/src/components/ui/Icons.js | 207 -
client/src/components/ui/Tabler.css | 1 +
client/src/helpers/constants.js | 152 -
.../filters/{filters.json => filters.js} | 322 +-
client/src/helpers/form.js | 48 +-
client/src/helpers/helpers.js | 20 +-
client/src/helpers/trackers/adguard.json | 77 +-
client/src/helpers/trackers/whotracksme.json | 9643 +++++++++++------
client/src/reducers/services.js | 10 +
go.mod | 42 +-
go.sum | 103 +-
internal/aghchan/aghchan.go | 33 +
internal/aghhttp/aghhttp.go | 11 +-
internal/aghhttp/header.go | 3 +
internal/aghnet/arpdb.go | 9 +-
internal/aghnet/arpdb_bsd.go | 15 +-
internal/aghnet/arpdb_bsd_test.go | 7 +-
internal/aghnet/arpdb_linux.go | 32 +-
internal/aghnet/arpdb_linux_test.go | 5 +-
internal/aghnet/arpdb_openbsd.go | 11 +-
internal/aghnet/arpdb_openbsd_test.go | 5 +-
internal/aghnet/arpdb_test.go | 3 +-
internal/aghnet/arpdb_windows.go | 7 +-
internal/aghnet/arpdb_windows_test.go | 5 +-
internal/aghnet/dhcp_unix.go | 31 +-
internal/aghnet/hostscontainer.go | 138 +-
internal/aghnet/hostscontainer_test.go | 38 +-
internal/aghnet/net.go | 165 +-
internal/aghnet/net_linux.go | 14 +-
internal/aghnet/net_test.go | 134 +-
.../aghnet/systemresolvers_others_test.go | 4 +-
internal/aghos/aghos_test.go | 4 +-
internal/aghos/filewalker_internal_test.go | 6 +-
internal/aghos/os.go | 10 +
internal/aghos/os_unix.go | 8 +
internal/aghos/os_windows.go | 12 +-
internal/aghtest/aghtest.go | 10 -
internal/aghtest/interface.go | 62 +-
internal/aghtest/interface_test.go | 8 +-
internal/aghtest/upstream.go | 162 +-
internal/aghtls/aghtls.go | 61 +-
internal/aghtls/aghtls_test.go | 56 +
internal/aghtls/root.go | 14 +
internal/aghtls/root_linux.go | 56 +
internal/aghtls/root_others.go | 9 +
internal/dhcpd/config.go | 82 +-
internal/dhcpd/conn_unix.go | 4 +-
internal/dhcpd/dhcpd.go | 8 +-
internal/dhcpd/dhcpd_unix_test.go | 42 +-
internal/dhcpd/http_unix.go | 92 +-
internal/dhcpd/http_windows.go | 11 +-
internal/dhcpd/iprange.go | 2 +
internal/dhcpd/options_unix.go | 7 +-
internal/dhcpd/options_unix_test.go | 2 -
internal/dhcpd/v4_unix.go | 27 +-
internal/dhcpd/v4_unix_test.go | 82 +-
internal/dnsforward/access.go | 69 +-
internal/dnsforward/access_test.go | 12 +-
internal/dnsforward/clientid.go | 81 +-
internal/dnsforward/clientid_test.go | 111 +-
internal/dnsforward/config.go | 78 +-
internal/dnsforward/dns.go | 180 +-
internal/dnsforward/dns_test.go | 108 +-
internal/dnsforward/dnsforward.go | 88 +-
internal/dnsforward/dnsforward_test.go | 113 +-
internal/dnsforward/dnsrewrite.go | 12 +-
internal/dnsforward/http.go | 18 +-
internal/dnsforward/http_test.go | 14 +-
internal/dnsforward/stats.go | 3 +-
internal/filtering/blocked.go | 418 +-
internal/filtering/filter.go | 134 +-
internal/filtering/filter_test.go | 50 +-
internal/filtering/filtering.go | 16 +-
internal/filtering/filtering_test.go | 3 +-
internal/filtering/http.go | 58 +-
internal/filtering/http_test.go | 143 +
internal/filtering/rewrites.go | 12 +-
internal/filtering/safebrowsing.go | 19 +-
internal/filtering/safesearch.go | 15 +-
internal/filtering/servicelist.go | 471 +
internal/home/auth_test.go | 5 -
internal/home/clients.go | 159 +-
internal/home/clients_test.go | 57 +-
internal/home/clientshttp.go | 19 +-
internal/home/config.go | 85 +-
internal/home/control.go | 39 +-
internal/home/controlinstall.go | 44 +-
internal/home/dns.go | 39 +-
internal/home/home.go | 172 +-
internal/home/home_test.go | 12 +
internal/home/mobileconfig_test.go | 11 +-
internal/home/options.go | 677 +-
internal/home/options_test.go | 70 +-
internal/home/rdns.go | 2 +-
internal/home/rdns_test.go | 17 +-
internal/home/service.go | 2 +-
internal/home/tls.go | 714 +-
.../{control_test.go => tls_internal_test.go} | 57 +-
internal/home/web.go | 16 +-
internal/home/whois.go | 2 +-
internal/querylog/http.go | 96 +-
internal/querylog/json.go | 10 +-
internal/querylog/qlog_test.go | 4 +-
internal/stats/http.go | 15 +-
internal/stats/stats_test.go | 3 +-
internal/tools/go.mod | 33 +-
internal/tools/go.sum | 86 +-
internal/updater/updater_test.go | 3 +-
internal/version/version.go | 16 +-
openapi/CHANGELOG.md | 13 +
openapi/openapi.yaml | 52 +
scripts/README.md | 43 +-
scripts/blocked-services/main.go | 117 +
scripts/companiesdb/download.sh | 4 +-
scripts/make/go-lint.sh | 6 +-
scripts/translations/package.json | 4 +-
scripts/vetted-filters/main.go | 164 +
173 files changed, 11539 insertions(+), 6928 deletions(-)
rename client/src/components/Settings/Clients/{ => ClientsTable}/ClientsTable.js (56%)
create mode 100644 client/src/components/Settings/Clients/ClientsTable/index.js
rename client/src/helpers/filters/{filters.json => filters.js} (59%)
create mode 100644 internal/aghchan/aghchan.go
create mode 100644 internal/aghtls/aghtls_test.go
create mode 100644 internal/aghtls/root.go
create mode 100644 internal/aghtls/root_linux.go
create mode 100644 internal/aghtls/root_others.go
create mode 100644 internal/filtering/http_test.go
create mode 100644 internal/filtering/servicelist.go
create mode 100644 internal/home/home_test.go
rename internal/home/{control_test.go => tls_internal_test.go} (59%)
create mode 100644 scripts/blocked-services/main.go
create mode 100644 scripts/vetted-filters/main.go
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 921e07cc..c80d6d66 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -6,6 +6,11 @@
website
'name': 'AdGuard filters issues'
'url': 'https://link.adtidy.org/forward.html?action=report&app=home&from=github'
+ - 'about': >
+ Please send requests for addition to the vetted filtering lists to the
+ Hostlists Registry repository.
+ 'name': 'AdGuard Hostlists Registry'
+ 'url': 'https://github.com/AdguardTeam/HostlistsRegistry'
- 'about': >
Please use GitHub Discussions for questions
'name': 'Q&A Discussions'
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bc2caa31..224fc42c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,7 +1,7 @@
'name': 'build'
'env':
- 'GO_VERSION': '1.18.7'
+ 'GO_VERSION': '1.18.8'
'NODE_VERSION': '14'
'on':
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 1028b6b1..d1dc8163 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -1,7 +1,7 @@
'name': 'lint'
'env':
- 'GO_VERSION': '1.18.7'
+ 'GO_VERSION': '1.18.8'
'on':
'push':
diff --git a/CHANGELOG.md b/CHANGELOG.md
index abe6d2c3..0265fc5f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,65 @@ and this project adheres to
## [v0.108.0] - TBA (APPROX.)
-->
+## Security
+
+- Go version has been updated to prevent the possibility of exploiting the
+ CVE-2022-41716 Go vulnerability fixed in [Go 1.18.8][go-1.18.8].
+
+[go-1.18.8]: https://groups.google.com/g/golang-announce/c/mbHY1UY3BaM
+
+## Added
+
+- The warning message when adding a certificate having no IP addresses
+ ([#4898]).
+- Several new blockable services ([#3972]). Those will now be more in sync with
+ the services that are already blockable in AdGuard DNS.
+- A new HTTP API, `GET /control/blocked_services/all`, that lists all available
+ blocked services and their data, such as SVG icons ([#3972]).
+- The new optional `tls.override_tls_ciphers` property, which allows
+ overriding TLS ciphers used by AdGuard Home ([#4925], [#4990]).
+- The ability to serve DNS on link-local IPv6 addresses ([#2926]).
+- The ability to put [ClientIDs][clientid] into DNS-over-HTTPS hostnames as
+ opposed to URL paths ([#3418]). Note that AdGuard Home checks the server name
+ only if the URL does not contain a ClientID.
+
+### Changed
+
+- DNS-over-TLS resolvers aren't returned anymore when the configured TLS
+ certificate contains no IP addresses ([#4927]).
+- Responses with `SERVFAIL` code are now cached for at least 30 seconds.
+
+### Deprecated
+
+- The `GET /control/blocked_services/services` HTTP API; use the new
+ `GET /control/blocked_services/all` API instead ([#3972]).
+
+### Fixed
+
+- ClientIDs not working when using DNS-over-HTTPS with HTTP/3.
+- Editing an enabled rule list's URL now also includes validation of the filter
+ contents preventing from saving a bad one ([#4916]).
+- The default value of `dns.cache_size` accidentally set to 0 has now been
+ reverted to 4 MiB ([#5010]).
+- Responses for which the DNSSEC validation had explicitly been omitted aren't
+ cached now ([#4942]).
+- Web UI not switching to HTTP/3 ([#4986], [#4993]).
+
+[#2926]: https://github.com/AdguardTeam/AdGuardHome/issues/2926
+[#3418]: https://github.com/AdguardTeam/AdGuardHome/issues/3418
+[#3972]: https://github.com/AdguardTeam/AdGuardHome/issues/3972
+[#4898]: https://github.com/AdguardTeam/AdGuardHome/issues/4898
+[#4916]: https://github.com/AdguardTeam/AdGuardHome/issues/4916
+[#4925]: https://github.com/AdguardTeam/AdGuardHome/issues/4925
+[#4927]: https://github.com/AdguardTeam/AdGuardHome/issues/4927
+[#4942]: https://github.com/AdguardTeam/AdGuardHome/issues/4942
+[#4986]: https://github.com/AdguardTeam/AdGuardHome/issues/4986
+[#4990]: https://github.com/AdguardTeam/AdGuardHome/issues/4990
+[#4993]: https://github.com/AdguardTeam/AdGuardHome/issues/4993
+[#5010]: https://github.com/AdguardTeam/AdGuardHome/issues/5010
+
+[clientid]: https://github.com/AdguardTeam/AdGuardHome/wiki/Clients#clientid
+
@@ -64,7 +123,7 @@ experimental and may break or change in the future.
explicitly enabled by setting the new property `dns.serve_http3` in the
configuration file to `true`.
- DNS-over-HTTP upstreams can now upgrade to HTTP/3 if the new configuration
- file property `use_http3_upstreams` is set to `true`.
+ file property `dns.use_http3_upstreams` is set to `true`.
- Upstreams with forced DNS-over-HTTP/3 and no fallback to prior HTTP versions
using the `h3://` scheme.
@@ -80,7 +139,7 @@ experimental and may break or change in the future.
[#4982]: https://github.com/AdguardTeam/AdGuardHome/issues/4982
[#4983]: https://github.com/AdguardTeam/AdGuardHome/issues/4983
-[ms-v0.107.15]: https://github.com/AdguardTeam/AdGuardHome/milestone/51?closed=1
+[ms-v0.107.15]: https://github.com/AdguardTeam/AdGuardHome/milestone/51?closed=1
@@ -90,9 +149,9 @@ See also the [v0.107.14 GitHub milestone][ms-v0.107.14].
### Security
-A Cross-Site Request Forgery (CSRF) vulnerability has been discovered. The CVE
-number is to be assigned. We thank Daniel Elkabes from Mend.io for reporting
-this vulnerability to us.
+A Cross-Site Request Forgery (CSRF) vulnerability has been discovered. We thank
+Daniel Elkabes from Mend.io for reporting this vulnerability to us. This is
+[CVE-2022-32175].
#### `SameSite` Policy
@@ -141,6 +200,7 @@ All JSON APIs that expect a body now check if the request actually has
[#4927]: https://github.com/AdguardTeam/AdGuardHome/issues/4927
[#4930]: https://github.com/AdguardTeam/AdGuardHome/issues/4930
+[CVE-2022-32175]: https://www.cvedetails.com/cve/CVE-2022-32175
[ms-v0.107.14]: https://github.com/AdguardTeam/AdGuardHome/milestone/50?closed=1
@@ -168,7 +228,7 @@ See also the [v0.107.13 GitHub milestone][ms-v0.107.13].
[#4722]: https://github.com/AdguardTeam/AdGuardHome/issues/4722
[#4904]: https://github.com/AdguardTeam/AdGuardHome/issues/4904
-[ms-v0.107.13]: https://github.com/AdguardTeam/AdGuardHome/milestone/49?closed=1
+[ms-v0.107.13]: https://github.com/AdguardTeam/AdGuardHome/milestone/49?closed=1
@@ -178,7 +238,7 @@ See also the [v0.107.12 GitHub milestone][ms-v0.107.12].
### Security
-- Go version was updated to prevent the possibility of exploiting the
+- Go version has been updated to prevent the possibility of exploiting the
CVE-2022-27664 and CVE-2022-32190 Go vulnerabilities fixed in
[Go 1.18.6][go-1.18.6].
@@ -299,7 +359,7 @@ See also the [v0.107.9 GitHub milestone][ms-v0.107.9].
### Security
-- Go version was updated to prevent the possibility of exploiting the
+- Go version has been updated to prevent the possibility of exploiting the
CVE-2022-32189 Go vulnerability fixed in [Go 1.18.5][go-1.18.5]. Go 1.17
support has also been removed, as it has reached end of life and will not
receive security updates.
@@ -342,7 +402,7 @@ See also the [v0.107.8 GitHub milestone][ms-v0.107.8].
### Security
-- Go version was updated to prevent the possibility of exploiting the
+- Go version has been updated to prevent the possibility of exploiting the
CVE-2022-1705, CVE-2022-32148, CVE-2022-30631, and other Go vulnerabilities
fixed in [Go 1.17.12][go-1.17.12].
@@ -378,7 +438,7 @@ See also the [v0.107.7 GitHub milestone][ms-v0.107.7].
### Security
-- Go version was updated to prevent the possibility of exploiting the
+- Go version has been updated to prevent the possibility of exploiting the
[CVE-2022-29526], [CVE-2022-30634], [CVE-2022-30629], [CVE-2022-30580], and
[CVE-2022-29804] Go vulnerabilities.
- Enforced password strength policy ([#3503]).
@@ -535,7 +595,7 @@ See also the [v0.107.6 GitHub milestone][ms-v0.107.6].
### Security
- `User-Agent` HTTP header removed from outgoing DNS-over-HTTPS requests.
-- Go version was updated to prevent the possibility of exploiting the
+- Go version has been updated to prevent the possibility of exploiting the
[CVE-2022-24675], [CVE-2022-27536], and [CVE-2022-28327] Go vulnerabilities.
### Added
@@ -590,7 +650,7 @@ were resolved.
### Security
-- Go version was updated to prevent the possibility of exploiting the
+- Go version has been updated to prevent the possibility of exploiting the
[CVE-2022-24921] Go vulnerability.
[CVE-2022-24921]: https://www.cvedetails.com/cve/CVE-2022-24921
@@ -603,7 +663,7 @@ See also the [v0.107.4 GitHub milestone][ms-v0.107.4].
### Security
-- Go version was updated to prevent the possibility of exploiting the
+- Go version has been updated to prevent the possibility of exploiting the
[CVE-2022-23806], [CVE-2022-23772], and [CVE-2022-23773] Go vulnerabilities.
### Fixed
diff --git a/README.md b/README.md
index 43c9db89..1f2bc7a3 100644
--- a/README.md
+++ b/README.md
@@ -10,68 +10,76 @@
Free and open source, powerful network-wide ads & trackers blocking DNS
server.
-
AdGuard.com |
Wiki |
Reddit |
Twitter |
Telegram
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
-
+
+
-
-
-AdGuard Home is a network-wide software for blocking ads & tracking. After you set it up, it'll cover ALL your home devices, and you don't need any client-side software for that.
+AdGuard Home is a network-wide software for blocking ads and tracking. After you
+set it up, it'll cover ALL your home devices, and you don't need any client-side
+software for that.
It operates as a DNS server that re-routes tracking domains to a “black hole”,
thus preventing your devices from connecting to those servers. It's based on
-software we use for our public [AdGuard DNS](https://adguard-dns.io/) servers,
-and both share a lot of code.
+software we use for our public [AdGuard DNS] servers, and both share a lot of
+code.
+
+[AdGuard DNS]: https://adguard-dns.io/
-* [Getting Started](#getting-started)
-* [Comparing AdGuard Home to other solutions](#comparison)
- * [How is this different from public AdGuard DNS servers?](#comparison-adguard-dns)
- * [How does AdGuard Home compare to Pi-Hole](#comparison-pi-hole)
- * [How does AdGuard Home compare to traditional ad blockers](#comparison-adblock)
-* [How to build from source](#how-to-build)
-* [Contributing](#contributing)
- * [Test unstable versions](#test-unstable-versions)
- * [Reporting issues](#reporting-issues)
- * [Help with translations](#translate)
- * [Other](#help-other)
-* [Projects that use AdGuard Home](#uses)
-* [Acknowledgments](#acknowledgments)
-* [Privacy](#privacy)
+ * [Getting Started](#getting-started)
+ * [Automated install (Unix)](#automated-install-linux-and-mac)
+ * [Alternative methods](#alternative-methods)
+ * [Guides](#guides)
+ * [API](#api)
+ * [Comparing AdGuard Home to other solutions](#comparison)
+ * [How is this different from public AdGuard DNS servers?](#comparison-adguard-dns)
+ * [How does AdGuard Home compare to Pi-Hole](#comparison-pi-hole)
+ * [How does AdGuard Home compare to traditional ad blockers](#comparison-adblock)
+ * [Known limitations](#comparison-limitations)
+ * [How to build from source](#how-to-build)
+ * [Prerequisites](#prerequisites)
+ * [Building](#building)
+ * [Contributing](#contributing)
+ * [Test unstable versions](#test-unstable-versions)
+ * [Reporting issues](#reporting-issues)
+ * [Help with translations](#translate)
+ * [Other](#help-other)
+ * [Projects that use AdGuard Home](#uses)
+ * [Acknowledgments](#acknowledgments)
+ * [Privacy](#privacy)
-
-## Getting Started
-### Automated install (Linux and Mac)
+
+## Getting Started
+
+ ### Automated install (Unix)
Run the following command in your terminal:
@@ -80,73 +88,96 @@ curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/s
```
The script also accepts some options:
-* `-c ` to use specified channel.
-* `-r` to reinstall AdGuard Home;
-* `-u` to uninstall AdGuard Home;
-* `-v` for verbose output;
+
+ * `-c ` 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
-Please read the **[Getting Started](https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started)** article on our Wiki to learn how to install AdGuard Home manually, and how to configure your devices to use it.
+ ### Alternative methods
-#### Docker
+ #### Manual installation
-You can use our [official Docker image](https://hub.docker.com/r/adguard/adguardhome).
+Please read the **[Getting Started][wiki-start]** article on our Wiki to learn
+how to install AdGuard Home manually, and how to configure your devices to use
+it.
-#### Snap Store
+ #### Docker
-If you're running **Linux**, there's a secure and easy way to install AdGuard Home - you can get it from the [Snap Store](https://snapcraft.io/adguard-home).
+You can use our official Docker image on [Docker Hub].
-### Guides
+ #### Snap Store
-* [Getting Started](https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started)
- * [FAQ](https://github.com/AdguardTeam/AdGuardHome/wiki/FAQ)
- * [How to Write Hosts Blocklists](https://github.com/AdguardTeam/AdGuardHome/wiki/Hosts-Blocklists)
- * [Comparing AdGuard Home to Other Solutions](https://github.com/AdguardTeam/AdGuardHome/wiki/Comparison)
-* Configuring AdGuard
- * [Configuration](https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration)
- * [Configuring AdGuard Home Clients](https://github.com/AdguardTeam/AdGuardHome/wiki/Clients)
- * [AdGuard Home as a DoH, DoT, or DoQ Server](https://github.com/AdguardTeam/AdGuardHome/wiki/Encryption)
- * [AdGuard Home as a DNSCrypt Server](https://github.com/AdguardTeam/AdGuardHome/wiki/DNSCrypt)
- * [AdGuard Home as a DHCP Server](https://github.com/AdguardTeam/AdGuardHome/wiki/DHCP)
-* Installing AdGuard Home
- * [Docker](https://github.com/AdguardTeam/AdGuardHome/wiki/Docker)
- * [How to Install and Run AdGuard Home on a Raspberry Pi](https://github.com/AdguardTeam/AdGuardHome/wiki/Raspberry-Pi)
- * [How to Install and Run AdGuard Home on a Virtual Private Server](https://github.com/AdguardTeam/AdGuardHome/wiki/VPS)
-* [Verifying Releases](https://github.com/AdguardTeam/AdGuardHome/wiki/Verify-Releases)
+If you're running **Linux,** there's a secure and easy way to install AdGuard
+Home: get it from the [Snap Store].
-### API
+[Docker Hub]: https://hub.docker.com/r/adguard/adguardhome
+[Snap Store]: https://snapcraft.io/adguard-home
+[wiki-start]: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started
-If you want to integrate with AdGuard Home, you can use our [REST API](https://github.com/AdguardTeam/AdGuardHome/tree/master/openapi).
-Alternatively, you can use this [python client](https://pypi.org/project/adguardhome/), which is used to build the [AdGuard Home Hass.io Add-on](https://www.home-assistant.io/integrations/adguard/).
-
-## Comparing AdGuard Home to other solutions
-
-### How is this different from public AdGuard DNS servers?
+ ### Guides
-Running your own AdGuard Home server allows you to do much more than using a public DNS server. It's a completely different level. See for yourself:
+See our [Wiki][wiki].
-* Choose what exactly the server blocks and permits.
-* Monitor your network activity.
-* Add your own custom filtering rules.
-* **Most importantly, this is your own server, and you are the only one who's in control.**
+[wiki]: https://github.com/AdguardTeam/AdGuardHome/wiki
-
-### How does AdGuard Home compare to Pi-Hole
-At this point, AdGuard Home has a lot in common with Pi-Hole. Both block ads and trackers using "DNS sinkholing" method, and both allow customizing what's blocked.
-> We're not going to stop here. DNS sinkholing is not a bad starting point, but this is just the beginning.
+ ### API
-AdGuard Home provides a lot of features out-of-the-box with no need to install and configure additional software. We want it to be simple to the point when even casual users can set it up with minimal effort.
+If you want to integrate with AdGuard Home, you can use our [REST API][openapi].
+Alternatively, you can use this [python client][pyclient], which is used to
+build the [AdGuard Home Hass.io Add-on][hassio].
-> Disclaimer: some of the listed features can be added to Pi-Hole by installing additional software or by manually using SSH terminal and reconfiguring one of the utilities Pi-Hole consists of. However, in our opinion, this cannot be legitimately counted as a Pi-Hole's feature.
+[hassio]: https://www.home-assistant.io/integrations/adguard/
+[openapi]: https://github.com/AdguardTeam/AdGuardHome/tree/master/openapi
+[pyclient]: https://pypi.org/project/adguardhome/
+
+
+
+## Comparing AdGuard Home to other solutions
+
+ ### How is this different from public AdGuard DNS servers?
+
+Running your own AdGuard Home server allows you to do much more than using a
+public DNS server. It's a completely different level. See for yourself:
+
+ * Choose what exactly the server blocks and permits.
+
+ * Monitor your network activity.
+
+ * Add your own custom filtering rules.
+
+ * **Most importantly, it's your own server, and you are the only one who's in
+ control.**
+
+
+
+ ### How does AdGuard Home compare to Pi-Hole
+
+At this point, AdGuard Home has a lot in common with Pi-Hole. Both block ads
+and trackers using the so-called “DNS sinkholing” method and both allow
+customizing what's blocked.
+
+
+We're not going to stop here. DNS sinkholing is not a bad starting point, but
+this is just the beginning.
+
+
+AdGuard Home provides a lot of features out-of-the-box with no need to install
+and configure additional software. We want it to be simple to the point when
+even casual users can set it up with minimal effort.
+
+**Disclaimer:** some of the listed features can be added to Pi-Hole by
+installing additional software or by manually using SSH terminal and
+reconfiguring one of the utilities Pi-Hole consists of. However, in our
+opinion, this cannot be legitimately counted as a Pi-Hole's feature.
| Feature | AdGuard Home | Pi-Hole |
|-------------------------------------------------------------------------|-------------------|-----------------------------------------------------------|
@@ -162,53 +193,72 @@ AdGuard Home provides a lot of features out-of-the-box with no need to install a
| Force Safe search on search engines | ✅ | ❌ |
| Per-client (device) configuration | ✅ | ✅ |
| Access settings (choose who can use AGH DNS) | ✅ | ❌ |
-| Running [without root privileges](https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#running-without-superuser) | ✅ | ❌ |
+| Running [without root privileges][wiki-noroot] | ✅ | ❌ |
-
-### How does AdGuard Home compare to traditional ad blockers
+[wiki-noroot]: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#running-without-superuser
+
+
+
+ ### How does AdGuard Home compare to traditional ad blockers
It depends.
-“DNS sinkholing” is capable of blocking a big percentage of ads, but it lacks
-flexibility and power of traditional ad blockers. You can get a good impression
-about the difference between these methods by reading
-[this article](https://adguard.com/en/blog/adguard-vs-adaway-dns66/). It
-compares AdGuard for Android (a traditional ad blocker) to hosts-level ad
-blockers (which are almost identical to DNS-based blockers in their
-capabilities). This level of protection is enough for some users.
+DNS sinkholing is capable of blocking a big percentage of ads, but it lacks
+the flexibility and the power of traditional ad blockers. You can get a good
+impression about the difference between these methods by reading [this
+article][blog-adaway], which compares AdGuard for Android (a traditional ad
+blocker) to hosts-level ad blockers (which are almost identical to DNS-based
+blockers in their capabilities). This level of protection is enough for some
+users.
+
+Additionally, using a DNS-based blocker can help to block ads, tracking and
+analytics requests on other types of devices, such as SmartTVs, smart speakers
+or other kinds of IoT devices (on which you can't install traditional ad
+blockers).
-Additionally, using a DNS-based blocker can help to block ads, tracking and analytics requests on other types of devices, such as SmartTVs, smart speakers or other kinds of IoT devices (on which you can't install traditional ad blockers).
-
-**Known limitations**
+ ### Known limitations
Here are some examples of what cannot be blocked by a DNS-level blocker:
-* YouTube, Twitch ads
-* Facebook, Twitter, Instagram sponsored posts
+ * YouTube, Twitch ads;
-Essentially, any advertising that shares a domain with content cannot be blocked by a DNS-level blocker.
+ * Facebook, Twitter, Instagram sponsored posts.
-Is there a chance to handle this in the future? DNS will never be enough to do this. Our only option is to use a content blocking proxy like what we do in the standalone AdGuard applications. We're [going to bring](https://github.com/AdguardTeam/AdGuardHome/issues/1228) this feature support to AdGuard Home in the future. Unfortunately, even in this case, there still will be cases when this won't be enough or would require quite a complicated configuration.
+Essentially, any advertising that shares a domain with content cannot be blocked
+by a DNS-level blocker.
-
-## How to build from source
+Is there a chance to handle this in the future? DNS will never be enough to do
+this. Our only option is to use a content blocking proxy like what we do in the
+standalone AdGuard applications. We're [going to bring][issue-1228] this
+feature support to AdGuard Home in the future. Unfortunately, even in this
+case, there still will be cases when this won't be enough or would require quite
+a complicated configuration.
-### Prerequisites
+[blog-adaway]: https://adguard.com/blog/adguard-vs-adaway-dns66.html
+[issue-1228]: https://github.com/AdguardTeam/AdGuardHome/issues/1228
+
+
+
+## How to build from source
+
+ ### Prerequisites
Run `make init` to prepare the development environment.
You will need this to build AdGuard Home:
- * [go](https://golang.org/dl/) v1.18 or later.
- * [node.js](https://nodejs.org/en/download/) v10.16.2 or later.
- * [npm](https://www.npmjs.com/) v6.14 or later (temporary requirement, TODO: remove when redesign is finished).
- * [yarn](https://yarnpkg.com/) v1.22.5 or later.
+ * [Go](https://golang.org/dl/) v1.18 or later;
+ * [Node.js](https://nodejs.org/en/download/) v10.16.2 or later;
+ * [npm](https://www.npmjs.com/) v6.14 or later;
+ * [yarn](https://yarnpkg.com/) v1.22.5 or later.
-### Building
-Open Terminal and execute these commands:
+
+ ### Building
+
+Open your terminal and execute these commands:
```sh
git clone https://github.com/AdguardTeam/AdGuardHome
@@ -216,16 +266,18 @@ cd AdGuardHome
make
```
-Please note, that the non-standard `-j` flag is currently not supported, so
-building with `make -j 4` or setting your `MAKEFLAGS` to include, for example,
-`-j 4` is likely to break the build. If you do have your `MAKEFLAGS` set to
-that, and you don't want to change it, you can override it by running
-`make -j 1`.
+**NOTE:** The non-standard `-j` flag is currently not supported, so building
+with `make -j 4` or setting your `MAKEFLAGS` to include, for example, `-j 4` is
+likely to break the build. If you do have your `MAKEFLAGS` set to that, and you
+don't want to change it, you can override it by running `make -j 1`.
-Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
+Check the [`Makefile`][src-makefile] to learn about other commands.
-**Building for a different platform.** You can build AdGuard for any OS/ARCH just like any other Go project.
-In order to do this, specify `GOOS` and `GOARCH` env variables before running make.
+ #### Building for a different platform
+
+You can build AdGuard Home for any OS/ARCH that Go supports. In order to do
+this, specify `GOOS` and `GOARCH` environment variables as macros when running
+`make`.
For example:
@@ -239,168 +291,223 @@ or:
make GOOS='linux' GOARCH='arm64'
```
-#### Preparing release
+ #### Preparing releases
-You'll need this to prepare a release build:
-
-* [snapcraft](https://snapcraft.io/)
-
-Commands:
+You'll need [`snapcraft`] to prepare a release build. Once installed, run the
+following command:
```sh
make build-release CHANNEL='...' VERSION='...'
```
-#### Docker image
+See the [`build-release` target documentation][targ-release].
-* Run `make build-docker` to build the Docker image locally (the one that we publish to DockerHub).
+ #### Docker image
-Please note, that we're using [Docker Buildx](https://docs.docker.com/buildx/working-with-buildx/) to build our official image.
+Run `make build-docker` to build the Docker image locally (the one that we
+publish to DockerHub). Please note, that we're using [Docker Buildx][buildx] to
+build our official image.
You may need to prepare before using these builds:
-* (Linux-only) Install Qemu: `docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes`
-* Prepare builder: `docker buildx create --name buildx-builder --driver docker-container --use`
+ * (Linux-only) Install Qemu:
+
+ ```sh
+ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes
+ ```
+
+ * Prepare the builder:
+
+ ```sh
+ docker buildx create --name buildx-builder --driver docker-container --use
+ ```
+
+See the [`build-docker` target documentation][targ-docker].
+
+ #### Debugging the frontend
+
+When you need to debug the frontend without recompiling the production version
+every time, for example to check how your labels would look on a form, you can
+run the frontend build a development environment.
+
+1. In a separate terminal, run:
+
+ ```sh
+ ( cd ./client/ && env NODE_ENV='development' npm run watch )
+ ```
+
+2. Run your `AdGuardHome` binary with the `--local-frontend` flag, which
+ instructs AdGuard Home to ignore the built-in frontend files and use those
+ from the `./build/` directory.
+
+3. Now any changes you make in the `./client/` directory should be recompiled
+ and become available on the web UI. Make sure that you disable the browser
+ cache to make sure that you actually get the recompiled version.
+
+[`snapcraft`]: https://snapcraft.io/
+[buildx]: https://docs.docker.com/buildx/working-with-buildx/
+[src-makefile]: https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile
+[targ-docker]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-dockersh-build-a-multi-architecture-docker-image
+[targ-release]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-releasesh-build-a-release-for-all-platforms
-### Resources that we update periodically
-* `scripts/translations`
-* `scripts/whotracksme`
+## Contributing
-
-## Contributing
+You are welcome to fork this repository, make your changes and [submit a pull
+request][pr]. Please make sure you follow our [code guidelines][guide] though.
-You are welcome to fork this repository, make your changes and submit a pull request — https://github.com/AdguardTeam/AdGuardHome/pulls
+Please note that we don't expect people to contribute to both UI and backend
+parts of the program simultaneously. Ideally, the backend part is implemented
+first, i.e. configuration, API, and the functionality itself. The UI part can
+be implemented later in a different pull request by a different person.
-Please note that we don't expect people to contribute to both UI and golang parts of the program simultaneously. Ideally, the golang part is implemented first, i.e. configuration, API, and the functionality itself. The UI part can be implemented later in a different pull request by a different person.
+[guide]: https://github.com/AdguardTeam/CodeGuidelines/
+[pr]: https://github.com/AdguardTeam/AdGuardHome/pulls
-
-### Test unstable versions
+
+
+ ### Test unstable versions
There are two update channels that you can use:
-* `beta` - beta version of AdGuard Home. More or less stable versions.
-* `edge` - the newest version of AdGuard Home. New updates are pushed to this channel daily and it is the closest to the master branch you can get.
+ * `beta`: beta versions of AdGuard Home. More or less stable versions,
+ usually released every two weeks or more often.
+
+ * `edge`: the newest version of AdGuard Home from the development branch. New
+ updates are pushed to this channel daily.
There are three options how you can install an unstable version:
-1. [Snap Store](https://snapcraft.io/adguard-home) -- look for "beta" and "edge" channels there.
-2. [Docker Hub](https://hub.docker.com/r/adguard/adguardhome) -- look for "beta" and "edge" tags there.
-3. Standalone builds. Use the automated installation script or look for the available builds below.
+1. [Snap Store]: look for the `beta` and `edge` channels.
-Beta:
+2. [Docker Hub]: look for the `beta` and `edge` tags.
-```sh
-curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c beta
-```
+3. Standalone builds. Use the automated installation script or look for the
+ available builds [on the Wiki][wiki-platf].
-Edge:
+ Script to install a beta version:
-```sh
-curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c edge
-```
+ ```sh
+ curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c beta
+ ```
- * Beta channel builds
- * Linux: [64-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_amd64.tar.gz), [32-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_386.tar.gz)
- * Linux ARM: [32-bit ARMv6](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_armv6.tar.gz) (recommended for Raspberry Pi OS stable), [64-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_arm64.tar.gz), [32-bit ARMv5](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_armv5.tar.gz), [32-bit ARMv7](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_armv7.tar.gz)
- * Linux MIPS: [32-bit MIPS](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_mips_softfloat.tar.gz), [32-bit MIPSLE](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_mipsle_softfloat.tar.gz), [64-bit MIPS](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_mips64_softfloat.tar.gz), [64-bit MIPSLE](https://static.adtidy.org/adguardhome/beta/AdGuardHome_linux_mips64le_softfloat.tar.gz)
- * Windows: [64-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_windows_amd64.zip), [32-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_windows_386.zip)
- * macOS: [64-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_darwin_amd64.zip), [32-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_darwin_386.zip)
- * macOS ARM: [64-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_darwin_arm64.zip)
- * FreeBSD: [64-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_freebsd_amd64.tar.gz), [32-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_freebsd_386.tar.gz)
- * FreeBSD ARM: [64-bit](https://static.adtidy.org/adguardhome/beta/AdGuardHome_freebsd_arm64.tar.gz), [32-bit ARMv5](https://static.adtidy.org/adguardhome/beta/AdGuardHome_freebsd_armv5.tar.gz), [32-bit ARMv6](https://static.adtidy.org/adguardhome/beta/AdGuardHome_freebsd_armv6.tar.gz), [32-bit ARMv7](https://static.adtidy.org/adguardhome/beta/AdGuardHome_freebsd_armv7.tar.gz)
- * OpenBSD: (coming soon)
- * OpenBSD ARM: (coming soon)
+ Script to install an edge version:
- * Edge channel builds
- * Linux: [64-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_amd64.tar.gz), [32-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_386.tar.gz)
- * Linux ARM: [32-bit ARMv6](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_armv6.tar.gz) (recommended for Raspberry Pi OS stable), [64-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_arm64.tar.gz), [32-bit ARMv5](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_armv5.tar.gz), [32-bit ARMv7](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_armv7.tar.gz)
- * Linux MIPS: [32-bit MIPS](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_mips_softfloat.tar.gz), [32-bit MIPSLE](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_mipsle_softfloat.tar.gz), [64-bit MIPS](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_mips64_softfloat.tar.gz), [64-bit MIPSLE](https://static.adtidy.org/adguardhome/edge/AdGuardHome_linux_mips64le_softfloat.tar.gz)
- * Windows: [64-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_windows_amd64.zip), [32-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_windows_386.zip)
- * macOS: [64-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_darwin_amd64.zip), [32-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_darwin_386.zip)
- * macOS ARM: [64-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_darwin_arm64.zip)
- * FreeBSD: [64-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_freebsd_amd64.tar.gz), [32-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_freebsd_386.tar.gz)
- * FreeBSD ARM: [64-bit](https://static.adtidy.org/adguardhome/edge/AdGuardHome_freebsd_arm64.tar.gz), [32-bit ARMv5](https://static.adtidy.org/adguardhome/edge/AdGuardHome_freebsd_armv5.tar.gz), [32-bit ARMv6](https://static.adtidy.org/adguardhome/edge/AdGuardHome_freebsd_armv6.tar.gz), [32-bit ARMv7](https://static.adtidy.org/adguardhome/edge/AdGuardHome_freebsd_armv7.tar.gz)
- * OpenBSD: [64-bit (experimental)](https://static.adtidy.org/adguardhome/edge/AdGuardHome_openbsd_amd64.tar.gz)
- * OpenBSD ARM: [64-bit (experimental)](https://static.adtidy.org/adguardhome/edge/AdGuardHome_openbsd_arm64.tar.gz)
+ ```sh
+ curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c edge
+ ```
+[wiki-platf]: https://github.com/AdguardTeam/AdGuardHome/wiki/Platforms
-
-### Report issues
-If you run into any problem or have a suggestion, head to [this page](https://github.com/AdguardTeam/AdGuardHome/issues) and click on the `New issue` button.
+ ### Report issues
-
-### Help with translations
+If you run into any problem or have a suggestion, head to [this page][iss] and
+click on the “New issue” button.
+
+[iss]: https://github.com/AdguardTeam/AdGuardHome/issues
+
+
+
+ ### Help with translations
If you want to help with AdGuard Home translations, please learn more about
-translating AdGuard products
-[in our Knowledge Base](https://kb.adguard.com/en/general/adguard-translations).
+translating AdGuard products [in our Knowledge Base][kb-trans]. You can
+contribute to the [AdGuardHome project on CrowdIn][crowdin].
-Here is a link to AdGuard Home project:
-
+[crowdin]: https://crowdin.com/project/adguard-applications/en#/adguard-home
+[kb-trans]: https://kb.adguard.com/en/general/adguard-translations
-
-### Other
-Here's what you can also do to contribute:
-1. [Look for issues][helpissues] marked as "help wanted".
-2. Actualize the list of *Blocked services*. It can be found in
- [filtering/blocked.go][blocked.go].
-3. Actualize the list of known *trackers*. It it can be found in [this repo]
- [companiesdb].
-4. Actualize the list of vetted *blocklists*. It it can be found in
- [client/src/helpers/filters/filters.json][filters.json].
+ ### Other
-[helpissues]: https://github.com/AdguardTeam/AdGuardHome/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22+
-[blocked.go]: https://github.com/AdguardTeam/AdGuardHome/blob/master/internal/filtering/blocked.go
-[companiesdb]: https://github.com/AdguardTeam/companiesdb
-[filters.json]: https://github.com/AdguardTeam/AdGuardHome/blob/master/client/src/helpers/filters/filters.json
+Another way you can contribute is by [looking for issues][iss-help] marked as
+`help wanted`, asking if the issue is up for grabs, and sending a PR fixing the
+bug or implementing the feature.
-
-## Projects that use AdGuard Home
+[iss-help]: https://github.com/AdguardTeam/AdGuardHome/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22
-* [AdGuard Home Remote](https://apps.apple.com/app/apple-store/id1543143740) - iOS app by [Joost](https://rocketscience-it.nl/)
-* [Python library](https://github.com/frenck/python-adguardhome) by [@frenck](https://github.com/frenck)
-* [Home Assistant add-on](https://github.com/hassio-addons/addon-adguard-home) by [@frenck](https://github.com/frenck)
-* [OpenWrt LUCI app](https://github.com/kongfl888/luci-app-adguardhome) by [@kongfl888](https://github.com/kongfl888) (originally by [@rufengsuixing](https://github.com/rufengsuixing))
-* [Prometheus exporter for AdGuard Home](https://github.com/ebrianne/adguard-exporter) by [@ebrianne](https://github.com/ebrianne)
-* [AdGuard Home on GLInet routers](https://forum.gl-inet.com/t/adguardhome-on-gl-routers/10664) by [Gl-Inet](https://gl-inet.com/)
-* [Cloudron app](https://git.cloudron.io/cloudron/adguard-home-app) by [@gramakri](https://github.com/gramakri)
-* [Asuswrt-Merlin-AdGuardHome-Installer](https://github.com/jumpsmm7/Asuswrt-Merlin-AdGuardHome-Installer) by [@jumpsmm7](https://github.com/jumpsmm7) aka [@SomeWhereOverTheRainBow](https://www.snbforums.com/members/somewhereovertherainbow.64179/)
-* [Node.js library](https://github.com/Andrea055/AdguardHomeAPI) by [@Andrea055](https://github.com/Andrea055/)
-
-## Acknowledgments
+
+## Projects that use AdGuard Home
+
+
+
+ * [AdGuard Home Remote](https://apps.apple.com/app/apple-store/id1543143740):
+ iOS app by [Joost](https://rocketscience-it.nl/).
+
+ * [Python library](https://github.com/frenck/python-adguardhome) by
+ [@frenck](https://github.com/frenck).
+
+ * [Home Assistant add-on](https://github.com/hassio-addons/addon-adguard-home)
+ by [@frenck](https://github.com/frenck).
+
+ * [OpenWrt LUCI app](https://github.com/kongfl888/luci-app-adguardhome) by
+ [@kongfl888](https://github.com/kongfl888) (originally by
+ [@rufengsuixing](https://github.com/rufengsuixing)).
+
+ * [Prometheus exporter for AdGuard
+ Home](https://github.com/ebrianne/adguard-exporter) by
+ [@ebrianne](https://github.com/ebrianne).
+
+ * [AdGuard Home on GLInet
+ routers](https://forum.gl-inet.com/t/adguardhome-on-gl-routers/10664) by
+ [Gl-Inet](https://gl-inet.com/).
+
+ * [Cloudron app](https://git.cloudron.io/cloudron/adguard-home-app) by
+ [@gramakri](https://github.com/gramakri).
+
+ * [Asuswrt-Merlin-AdGuardHome-Installer](https://github.com/jumpsmm7/Asuswrt-Merlin-AdGuardHome-Installer)
+ by [@jumpsmm7](https://github.com/jumpsmm7) aka
+ [@SomeWhereOverTheRainBow](https://www.snbforums.com/members/somewhereovertherainbow.64179/).
+
+ * [Node.js library](https://github.com/Andrea055/AdguardHomeAPI) by
+ [@Andrea055](https://github.com/Andrea055/).
+
+
+
+## Acknowledgments
+
+
This software wouldn't have been possible without:
- * [Go](https://golang.org/dl/) and its libraries:
- * [gcache](https://github.com/bluele/gcache)
- * [miekg's dns](https://github.com/miekg/dns)
- * [go-yaml](https://github.com/go-yaml/yaml)
- * [service](https://godoc.org/github.com/kardianos/service)
- * [dnsproxy](https://github.com/AdguardTeam/dnsproxy)
- * [urlfilter](https://github.com/AdguardTeam/urlfilter)
- * [Node.js](https://nodejs.org/) and its libraries:
- * [React.js](https://reactjs.org)
- * [Tabler](https://github.com/tabler/tabler)
- * And many more node.js packages.
- * [whotracks.me data](https://github.com/cliqz-oss/whotracks.me)
+ * [Go](https://golang.org/dl/) and its libraries:
+ * [gcache](https://github.com/bluele/gcache)
+ * [miekg's dns](https://github.com/miekg/dns)
+ * [go-yaml](https://github.com/go-yaml/yaml)
+ * [service](https://godoc.org/github.com/kardianos/service)
+ * [dnsproxy](https://github.com/AdguardTeam/dnsproxy)
+ * [urlfilter](https://github.com/AdguardTeam/urlfilter)
+ * [Node.js](https://nodejs.org/) and its libraries:
+ * And many more Node.js packages.
+ * [React.js](https://reactjs.org)
+ * [Tabler](https://github.com/tabler/tabler)
+ * [whotracks.me data](https://github.com/cliqz-oss/whotracks.me)
-You might have seen that [CoreDNS](https://coredns.io) was mentioned here
-before, but we've stopped using it in AdGuard Home.
+You might have seen that [CoreDNS] was mentioned here before, but we've stopped
+using it in AdGuard Home.
-For a full list of all node.js packages in use, please take a look at [client/package.json](https://github.com/AdguardTeam/AdGuardHome/blob/master/client/package.json) file.
+For the full list of all Node.js packages in use, please take a look at
+[`client/package.json`][src-packagejson] file.
-
-## Privacy
+[CoreDNS]: https://coredns.io
+[src-packagejson]: https://github.com/AdguardTeam/AdGuardHome/blob/master/client/package.json
+
+## Privacy
+
Our main idea is that you are the one, who should be in control of your data.
So it is only natural, that AdGuard Home does not collect any usage statistics,
-and does not use any web services unless you configure it to do so. Full policy
-with every bit that *could in theory be* sent by AdGuard Home is available
-[here](https://adguard.com/en/privacy/home.html)
+and does not use any web services unless you configure it to do so. See also
+the [full privacy policy][privacy] with every bit that *could in theory be sent*
+by AdGuard Home is available.
+
+[privacy]: https://adguard.com/en/privacy/home.html
diff --git a/bamboo-specs/release.yaml b/bamboo-specs/release.yaml
index 4232b734..86cc16ec 100644
--- a/bamboo-specs/release.yaml
+++ b/bamboo-specs/release.yaml
@@ -7,7 +7,7 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
- 'dockerGo': 'adguard/golang-ubuntu:5.2'
+ 'dockerGo': 'adguard/golang-ubuntu:5.3'
'stages':
- 'Build frontend':
@@ -322,7 +322,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
- 'dockerGo': 'adguard/golang-ubuntu:5.2'
+ 'dockerGo': 'adguard/golang-ubuntu:5.3'
# release-vX.Y.Z branches are the branches from which the actual final release
# is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -337,4 +337,4 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
- 'dockerGo': 'adguard/golang-ubuntu:5.2'
+ 'dockerGo': 'adguard/golang-ubuntu:5.3'
diff --git a/bamboo-specs/test.yaml b/bamboo-specs/test.yaml
index 81796e1f..ac8c67fd 100644
--- a/bamboo-specs/test.yaml
+++ b/bamboo-specs/test.yaml
@@ -5,7 +5,7 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
- 'dockerGo': 'adguard/golang-ubuntu:5.2'
+ 'dockerGo': 'adguard/golang-ubuntu:5.3'
'stages':
- 'Tests':
diff --git a/client/src/__locales/be.json b/client/src/__locales/be.json
index c666daab..af646119 100644
--- a/client/src/__locales/be.json
+++ b/client/src/__locales/be.json
@@ -139,7 +139,7 @@
"average_processing_time": "Сярэдні час апрацоўкі запыту",
"average_processing_time_hint": "Сярэдні час для апрацоўкі запыту DNS у мілісекундах",
"block_domain_use_filters_and_hosts": "Блакаваць дамены з выкарыстаннем фільтраў і файлаў хастоў",
- "filters_block_toggle_hint": "Вы можаце наладзіць правілы блакавання ў «Фільтрах» .",
+ "filters_block_toggle_hint": "Вы можаце наладзіць правілы блакавання ў «Фільтрах ».",
"use_adguard_browsing_sec": "Выкарыстаць Бяспечную навігацыю AdGuard",
"use_adguard_browsing_sec_hint": "AdGuard Home праверыць, ці ўлучаны дамен у ўэб-службу бяспекі браўзара. Ён будзе выкарыстоўваць API, каб выканаць праверку: на сервер адсылаецца толькі кароткі прэфікс імя дамена SHA256.",
"use_adguard_parental": "Ужывайце модуль Бацькоўскага кантролю AdGuard ",
@@ -215,6 +215,7 @@
"example_upstream_udp": "звычайны DNS (праз UDP, імя хаста);",
"example_upstream_dot": "зашыфраваны <0>DNS-over-TLS0>;",
"example_upstream_doh": "зашыфраваны <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "зашыфраваны DNS-над-HTTPS з прымусовым <0>HTTP/30> і без вяртання да HTTP/2 або ніжэй;",
"example_upstream_doq": "зашыфраваны <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS Stamps0> для <1>DNSCrypt1> ці <2>DNS-over-HTTPS2> рэзалвераў;",
"example_upstream_tcp": "звычайны DNS (наўзверх TCP);",
@@ -339,7 +340,7 @@
"install_devices_router_list_3": "Увядзіце туды адрас вашага AdGuard Home.",
"install_devices_router_list_4": "Вы не можаце ўсталяваць уласны DNS-сервер на некаторых тыпах маршрутызатараў. У гэтым выпадку можа дапамагчы налада AdGuard Home у якасці DHCP-сервера . У адваротным выпадку вам трэба звярнуцца да кіраўніцтва па наладзе DNS-сервераў для вашай пэўнай мадэлі маршрутызатара.",
"install_devices_windows_list_1": "Адкрыйце Панэль кіравання праз меню «Пуск» ці праз пошук Windows.",
- "install_devices_windows_list_2": "Перайдзіце ў «Сеціва і інтэрнэт», а потым у «Цэнтр кіравання сеціва і агульным доступам»",
+ "install_devices_windows_list_2": "Перайдзіце ў «Сеціва і інтэрнэт», а потым у «Цэнтр кіравання сеціва і агульным доступам».",
"install_devices_windows_list_3": "У левым боку экрана клікніце «Змена параметраў адаптара».",
"install_devices_windows_list_4": "Пстрыкніце правай кнопкай мышы ваша актыўнае злучэнне і абярыце Уласцівасці.",
"install_devices_windows_list_5": "Знайдзіце ў спісе пункт «IP версіі 4 (TCP/IPv4)», вылучыце яго і потым ізноў націсніце «Уласцівасці».",
@@ -605,7 +606,7 @@
"blocklist": "Чорны спіс",
"milliseconds_abbreviation": "мс",
"cache_size": "Памер кэша",
- "cache_size_desc": "Памер кэша DNS (у байтах).",
+ "cache_size_desc": "Памер кэша DNS (у байтах). Каб адключыць кэшаванне, пакіньце пустым.",
"cache_ttl_min_override": "Перавызначыць мінімальны TTL",
"cache_ttl_max_override": "Перавызначыць максімальны TTL",
"enter_cache_size": "Увядзіце памер кэша (байты)",
diff --git a/client/src/__locales/cs.json b/client/src/__locales/cs.json
index 8c879b09..160b1046 100644
--- a/client/src/__locales/cs.json
+++ b/client/src/__locales/cs.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "obvyklý DNS (skrze UDP, název hostitele);",
"example_upstream_dot": "šifrovaný <0>DNS skrze TLS0>;",
"example_upstream_doh": "šifrovaný <0>DNS skrze HTTPS0>;",
+ "example_upstream_doh3": "šifrovaný DNS skrze HTTPS s vynuceným <0>HTTP/30> a bez možnosti zpětného přechodu na HTTP/2 nebo nižší;",
"example_upstream_doq": "šifrovaný <0>DNS skrze QUIC0>;",
"example_upstream_sdns": "<0>DNS razítka0> pro <1>DNSCrypt1> nebo <2>DNS skrze HTTPS2> řešitele;",
"example_upstream_tcp": "obvyklý DNS (přes TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Zakázaný",
"milliseconds_abbreviation": "ms",
"cache_size": "Velikost mezipaměti",
- "cache_size_desc": "Velikost mezipaměti DNS (v bajtech).",
+ "cache_size_desc": "Velikost mezipaměti DNS (v bajtech). Chcete-li ukládání do mezipaměti zakázat, ponechte prázdné.",
"cache_ttl_min_override": "Přepsat minimální hodnotu TTL",
"cache_ttl_max_override": "Přepsat maximální hodnotu TTL",
"enter_cache_size": "Zadejte velikost mezipaměti (v bajtech)",
diff --git a/client/src/__locales/da.json b/client/src/__locales/da.json
index 0df5ff72..941645d8 100644
--- a/client/src/__locales/da.json
+++ b/client/src/__locales/da.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "almindelig DNS (over UDP, værtsnavn);",
"example_upstream_dot": "krypteret <0>DNS-over-TLS0>",
"example_upstream_doh": "krypteret <0>DNS-over-HTTPS0>",
+ "example_upstream_doh3": "krypteret DNS-over-HTTPS med tvungen <0>HTTP/30> uden fallback til HTTP/2 eller lavere;",
"example_upstream_doq": "krypteret <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS Stamps0> til <1>DNSCrypt1> eller <2>DNS-over-HTTPS2>-opløsere;",
"example_upstream_tcp": "almindelig DNS (over TCP)",
@@ -605,7 +606,7 @@
"blocklist": "Sortliste",
"milliseconds_abbreviation": "ms",
"cache_size": "Cache-størrelse",
- "cache_size_desc": "DNS cache-størrelse (i bytes).",
+ "cache_size_desc": "DNS cache-størrelse (i bytes). Lad stå tomt for at deaktivere cache.",
"cache_ttl_min_override": "Tilsidesæt minimum TTL",
"cache_ttl_max_override": "Tilsidesæt maksimal TTL",
"enter_cache_size": "Angiv cache-størrelse (bytes)",
diff --git a/client/src/__locales/de.json b/client/src/__locales/de.json
index cff3d43d..25c4bd9e 100644
--- a/client/src/__locales/de.json
+++ b/client/src/__locales/de.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "normales DNS (über UDP, Hostname);",
"example_upstream_dot": "verschlüsseltes <0>DNS-over-TLS0>;",
"example_upstream_doh": "verschlüsseltes <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "verschlüsseltes DNS-over-HTTPS mit erzwungenem <0>HTTP/30> und keinem Fallback zu HTTP/2 oder darunter;",
"example_upstream_doq": "verschlüsseltes <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS-Stempel0> für <1>DNSCrypt1> oder <2>DNS-over-HTTPS2> Resolver;",
"example_upstream_tcp": "reguläres DNS (over TCP);",
@@ -447,7 +448,7 @@
"access_disallowed_title": "Nicht zugelassene Clients",
"access_disallowed_desc": "Eine Liste von CIDRs, IP-Adressen oder ClientIDs . Wenn diese Liste gefüllt ist, weist AdGuard Home Anfragen von diesen Clients zurück. Dieses Feld wird ignoriert, wenn es Einträge in der Liste „Zugelassene Clients“ gibt.",
"access_blocked_title": "Nicht zugelassene Domains",
- "access_blocked_desc": "Verwechseln Sie dies nicht mit Filtern. AdGuard Home verwirft DNS-Abfragen, die mit diesen Domänen übereinstimmen, und diese Abfragen erscheinen nicht einmal im Abfrageprotokoll. Hier können Sie die genauen Domain-Namen, Wildcards und URL-Filter-Regeln angeben, z.B. „example.org“, „*.example.org“ oder „||example.org^“.",
+ "access_blocked_desc": "Nicht zu verwechseln mit Filtern. AdGuard Home verwirft DNS-Anfragen, die mit diesen Domains übereinstimmen, und diese Abfragen werden nicht einmal im Abfrageprotokoll angezeigt. Sie können exakte Domainnamen, Wildcards oder URL-Filterregeln angeben, z. B. „example.org“, „*.example.org“ oder „||example.org^“.",
"access_settings_saved": "Zugriffseinstellungen erfolgreich gespeichert",
"updates_checked": "Neue Version von AdGuard Home ist jetzt verfügbar",
"updates_version_equal": "AdGuard Home ist aktuell",
@@ -605,7 +606,7 @@
"blocklist": "Sperrliste",
"milliseconds_abbreviation": "ms",
"cache_size": "Größe des Cache",
- "cache_size_desc": "Größe des DNS-Zwischenspeichers (in Bytes).",
+ "cache_size_desc": "Größe des DNS-Zwischenspeichers (in Bytes)",
"cache_ttl_min_override": "TTL-Minimalwert überschreiben",
"cache_ttl_max_override": "TTL-Höchstwert überschreiben",
"enter_cache_size": "Größe des Cache (Bytes) eingeben",
diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json
index e059c9f4..b986dea1 100644
--- a/client/src/__locales/en.json
+++ b/client/src/__locales/en.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "regular DNS (over UDP, hostname);",
"example_upstream_dot": "encrypted <0>DNS-over-TLS0>;",
"example_upstream_doh": "encrypted <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "encrypted DNS-over-HTTPS with forced <0>HTTP/30> and no fallback to HTTP/2 or below;",
"example_upstream_doq": "encrypted <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS Stamps0> for <1>DNSCrypt1> or <2>DNS-over-HTTPS2> resolvers;",
"example_upstream_tcp": "regular DNS (over TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Blocklist",
"milliseconds_abbreviation": "ms",
"cache_size": "Cache size",
- "cache_size_desc": "DNS cache size (in bytes).",
+ "cache_size_desc": "DNS cache size (in bytes). To disable caching, leave empty.",
"cache_ttl_min_override": "Override minimum TTL",
"cache_ttl_max_override": "Override maximum TTL",
"enter_cache_size": "Enter cache size (bytes)",
diff --git a/client/src/__locales/es.json b/client/src/__locales/es.json
index 506f98d7..8f8eaf11 100644
--- a/client/src/__locales/es.json
+++ b/client/src/__locales/es.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "DNS regular (mediante UDP, nombre del host).",
"example_upstream_dot": "cifrado <0>DNS mediante TLS0>.",
"example_upstream_doh": "cifrado <0>DNS mediante HTTPS0>.",
+ "example_upstream_doh3": "cifrado DNS mediante HTTPS con <0>HTTP/30> forzado y sin alternativa a HTTP/2 o inferior.",
"example_upstream_doq": "cifrado <0>DNS mediante QUIC0>.",
"example_upstream_sdns": "<0>DNS Stamps0> para <1>DNSCrypt1> o resolutores <2>DNS mediante HTTPS2>.",
"example_upstream_tcp": "DNS regular (mediante TCP).",
@@ -605,7 +606,7 @@
"blocklist": "Lista de bloqueo",
"milliseconds_abbreviation": "ms",
"cache_size": "Tamaño de la caché",
- "cache_size_desc": "Tamaño de la caché DNS (en bytes).",
+ "cache_size_desc": "Tamaño de la caché DNS (en bytes). Para deshabilitar el almacenamiento en caché, déjalo vacío.",
"cache_ttl_min_override": "Anular TTL mínimo",
"cache_ttl_max_override": "Anular TTL máximo",
"enter_cache_size": "Ingresa el tamaño de la caché (bytes)",
diff --git a/client/src/__locales/fi.json b/client/src/__locales/fi.json
index 3231d087..831b1cc6 100644
--- a/client/src/__locales/fi.json
+++ b/client/src/__locales/fi.json
@@ -88,7 +88,7 @@
"response_details": "Vastauksen tiedot",
"request_details": "Pyynnön tiedot",
"client_details": "Päätelaitteen tiedot",
- "details": "Tiedot",
+ "details": "Yksityiskohdat",
"back": "Takaisin",
"dashboard": "Tila",
"settings": "Asetukset",
@@ -215,6 +215,7 @@
"example_upstream_udp": "tavallinen DNS (UDP, isäntänimi);",
"example_upstream_dot": "salattu <0>DNS-over-TLS0>;",
"example_upstream_doh": "salattu <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "salattu DNS-over-HTTPS <0>HTTP/30>-pakotuksella, ilman HTTP/2 (tai alempi) -varmistusta;",
"example_upstream_doq": "salattu <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS Stamp0> -merkinnät <1>DNSCrypt1> tai <2>DNS-over-HTTPS2> -resolvereille;",
"example_upstream_tcp": "tavallinen DNS (TCP);",
@@ -274,7 +275,7 @@
"nxdomain": "NXDOMAIN",
"refused": "REFUSED",
"null_ip": "Tyhjä IP",
- "custom_ip": "Oma IP",
+ "custom_ip": "Oma IP-osoite",
"blocking_ipv4": "IPv4-esto",
"blocking_ipv6": "IPv6-esto",
"dnscrypt": "DNSCrypt",
@@ -414,8 +415,8 @@
"clients_title": "Pysyvät päätelaitteet",
"clients_desc": "Määritä pysyvät AdGuard Homeen yhdistetyt päätelaitetiedot.",
"settings_global": "Yleinen",
- "settings_custom": "Oma",
- "table_client": "Päätelaite",
+ "settings_custom": "Muut aiheet",
+ "table_client": "Asiakas",
"table_name": "Nimi",
"save_btn": "Tallenna",
"client_add": "Lisää päätelaite",
@@ -596,7 +597,7 @@
"show_whitelisted_responses": "Sallitut",
"show_processed_responses": "Käsitelty",
"blocked_safebrowsing": "Turvallisen selauksen estämät",
- "blocked_adult_websites": "Lapsilukon estämät",
+ "blocked_adult_websites": "Estetty lapsilukolla",
"blocked_threats": "Estetyt uhat",
"allowed": "Sallitut",
"filtered": "Suodatetut",
@@ -605,7 +606,7 @@
"blocklist": "Estolista",
"milliseconds_abbreviation": "ms",
"cache_size": "Välimuistin koko",
- "cache_size_desc": "DNS-välimuistin koko (tavuina).",
+ "cache_size_desc": "DNS-välimuistin koko (tavuina). Jätä tyhjäksi poistaaksesi välimuistin käytöstä.",
"cache_ttl_min_override": "Korvaa vähimmäis-TTL",
"cache_ttl_max_override": "Korvaa enimmäis-TTL",
"enter_cache_size": "Syötä välimuistin koko (tavuina)",
diff --git a/client/src/__locales/fr.json b/client/src/__locales/fr.json
index f9528e82..3f840948 100644
--- a/client/src/__locales/fr.json
+++ b/client/src/__locales/fr.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "DNS normal (sur UDP, nom d’hôte) ;",
"example_upstream_dot": "<0>DNS-over-TLS0> chiffré ;",
"example_upstream_doh": "<0>DNS-over-HTTPS0> chiffré ;",
+ "example_upstream_doh3": "DNS-over-HTTPS chiffré avec <0>HTTP/30> forcé sans repli sur HTTP/2 ou inférieur ;",
"example_upstream_doq": "<0>DNS-over-QUIC0> chiffré;",
"example_upstream_sdns": "vous pouvez utiliser <0>DNS Stamps0> pour <1>DNSCrypt1> ou les résolveurs <2>DNS_over_HTTPS2> ;",
"example_upstream_tcp": "DNS classique (au-dessus de TCP) ;",
@@ -605,7 +606,7 @@
"blocklist": "Liste de blocage",
"milliseconds_abbreviation": "ms",
"cache_size": "Taille du cache",
- "cache_size_desc": "Taille du cache DNS (en bytes) .",
+ "cache_size_desc": "Taille du cache DNS (en octets). Pour désactiver la mise en cache, laissez vide.",
"cache_ttl_min_override": "Remplacer le TTL minimum",
"cache_ttl_max_override": "Remplacer le TTL maximum",
"enter_cache_size": "Entrer la taille du cache (octets)",
diff --git a/client/src/__locales/hr.json b/client/src/__locales/hr.json
index 8a64f6c8..e3ec2290 100644
--- a/client/src/__locales/hr.json
+++ b/client/src/__locales/hr.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "obični DNS (preko UDP-a, ime hosta);",
"example_upstream_dot": "šifrirano <0>DNS-over-TLS0>;",
"example_upstream_doh": "šifrirano <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "šifrirani DNS-over-HTTPS s prisilnim <0>HTTP/30> i nema povratka na HTTP/2 ili niže;",
"example_upstream_doq": "šifrirano <0>DNS-over-QUIC0> (eksperimentalno);",
"example_upstream_sdns": "<0>DNS Stamps0> za <1>DNSCrypt1> ili <2>DNS-over-HTTPS2> rezolvere;",
"example_upstream_tcp": "zadani DNS (putem TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Popis nedopuštenih",
"milliseconds_abbreviation": "ms",
"cache_size": "Veličina predmemorije",
- "cache_size_desc": "Veličina DNS predmemorije (u bajtovima).",
+ "cache_size_desc": "Veličina DNS predmemorije (u bajtovima). Da biste onemogućili predmemoriju, ostavite prazno.",
"cache_ttl_min_override": "Nadjačaj minimum TTL-a",
"cache_ttl_max_override": "Nadjačaj maksimum TTL-a",
"enter_cache_size": "Unesite veličinu predmemorije (u bajtovima)",
diff --git a/client/src/__locales/hu.json b/client/src/__locales/hu.json
index 7bc43b33..1c1cd839 100644
--- a/client/src/__locales/hu.json
+++ b/client/src/__locales/hu.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "normál DNS (UDP felett, hostnév);",
"example_upstream_dot": "titkosított <0>DNS-over-TLS0>;",
"example_upstream_doh": "titkosított <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "titkosított DNS-over-HTTPS kényszerített <0>HTTP/3-mal0> és nincs visszalépés a HTTP/2-re vagy az alább;",
"example_upstream_doq": "titkosított <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS Stamps0> a <1>DNSCrypt1> vagy <2>DNS-over-HTTPS2> feloldókhoz;",
"example_upstream_tcp": "hagyományos DNS (TCP felett);",
@@ -605,7 +606,7 @@
"blocklist": "Tiltólista",
"milliseconds_abbreviation": "ms",
"cache_size": "Gyorsítótár mérete",
- "cache_size_desc": "DNS gyorsítótár mérete (bájtokban).",
+ "cache_size_desc": "DNS-gyorsítótár mérete (byte-ban). A gyorsítótárazás letiltásához hagyja üresen.",
"cache_ttl_min_override": "A minimális TTL felülírása",
"cache_ttl_max_override": "A maximális TTL felülírása",
"enter_cache_size": "Adja meg a gyorsítótár méretét",
diff --git a/client/src/__locales/id.json b/client/src/__locales/id.json
index beb011f0..93526063 100644
--- a/client/src/__locales/id.json
+++ b/client/src/__locales/id.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "DNS biasa (lebih dari UDP, nama host);",
"example_upstream_dot": "terenkripsi <0>DNS-over-TLS0>;",
"example_upstream_doh": "terenkripsi <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "DNS-over-HTTPS terenkripsi dengan paksa <0>HTTP/30> dan tidak ada fallback ke HTTP/2 atau lebih rendah;",
"example_upstream_doq": "terenkripsi <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>Stempel DNS0> untuk <1>DNSCrypt1> atau pengarah <2>DNS-over-HTTPS2>;",
"example_upstream_tcp": "DNS reguler (melalui TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Daftar blokir",
"milliseconds_abbreviation": "ms",
"cache_size": "Ukuran cache",
- "cache_size_desc": "Ukuran DNS cache (dalam bit).",
+ "cache_size_desc": "Ukuran cache DNS (dalam byte). Untuk menonaktifkan caching, biarkan kosong.",
"cache_ttl_min_override": "Tumpuk TTL minimum",
"cache_ttl_max_override": "Tumpuk TTL maksimum",
"enter_cache_size": "Masukkan ukuran cache (bytes)",
diff --git a/client/src/__locales/it.json b/client/src/__locales/it.json
index 74b109ff..499a8d81 100644
--- a/client/src/__locales/it.json
+++ b/client/src/__locales/it.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "DNS regolare (over UDP, nome host);",
"example_upstream_dot": "<0>DNS su TLS0> crittografato;",
"example_upstream_doh": "<0>DNS su HTTPS0> crittografato;",
+ "example_upstream_doh3": "DNS-over-HTTPS crittografato con <0>HTTP/3 forzato0> e nessun fallback su HTTP/2 o inferiore;",
"example_upstream_doq": "<0>DNS su QUIC0> crittografato;",
"example_upstream_sdns": "<0>DNS Stamps0> per <1>DNSCrypt1> oppure i risolutori <2>DNS su HTTPS2>;",
"example_upstream_tcp": "DNS regolare (over TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Lista nera",
"milliseconds_abbreviation": "ms",
"cache_size": "Dimensioni cache",
- "cache_size_desc": "Dimensioni cache DNS (in byte).",
+ "cache_size_desc": "Dimensione della cache DNS (in byte). Per disabilitare la memorizzazione nella cache, lascia vuoto.",
"cache_ttl_min_override": "Sovrascrivi TTL minimo",
"cache_ttl_max_override": "Sovrascrivi TTL massimo",
"enter_cache_size": "Immetti dimensioni cache (in byte)",
diff --git a/client/src/__locales/ja.json b/client/src/__locales/ja.json
index 9fc9ac6f..e4faaa06 100644
--- a/client/src/__locales/ja.json
+++ b/client/src/__locales/ja.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "通常のDNS(over UDP, ホスト名)。",
"example_upstream_dot": "暗号化されている <0>DNS-over-TLS0>。",
"example_upstream_doh": "暗号化されている <0>DNS-over-HTTPS0>。",
+ "example_upstream_doh3": "暗号化されたDNS-over-HTTPS(<0>HTTP/30>の強制、HTTP/2 以下へのフォールバックなし)",
"example_upstream_doq": "暗号化 <0>DNS-over-QUIC0>。",
"example_upstream_sdns": "<1>DNSCrypt1> または <2>DNS-over-HTTPS2> リゾルバのための <0>DNS Stamps0>。",
"example_upstream_tcp": "通常のDNS(over TCP)。",
@@ -605,7 +606,7 @@
"blocklist": "ブロックリスト",
"milliseconds_abbreviation": "ms",
"cache_size": "キャッシュサイズ",
- "cache_size_desc": "DNSキャッシュサイズ(バイト単位)",
+ "cache_size_desc": "DNSキャッシュサイズ(バイト単位)。※キャッシュを無効化するには、この欄を空してください。",
"cache_ttl_min_override": "最小TTLの上書き(秒単位)",
"cache_ttl_max_override": "最大TTLの上書き(秒単位)",
"enter_cache_size": "キャッシュサイズ(バイト単位)を入力してください",
diff --git a/client/src/__locales/ko.json b/client/src/__locales/ko.json
index 96da066c..779f2f98 100644
--- a/client/src/__locales/ko.json
+++ b/client/src/__locales/ko.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "일반 DNS (UDP를 통한, 호스트명);",
"example_upstream_dot": "암호화된 <0>DNS-over-TLS0>;",
"example_upstream_doh": "암호화된 <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "암호화된 DNS-over-HTTPS가 강제로 <0>HTTP/30>를 사용하며 HTTP/2 이하로 폴백하지 않습니다.",
"example_upstream_doq": "암호화된 <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<1>DNSCrypt1> 또는 <2>DNS-over-HTTPS2> 리졸버를 위한 <0>DNS 스탬프0>;",
"example_upstream_tcp": "일반 DNS (TCP를 통한 접속);",
@@ -605,7 +606,7 @@
"blocklist": "차단 목록",
"milliseconds_abbreviation": "ms",
"cache_size": "캐시 크기",
- "cache_size_desc": "DNS 캐시 크기 (바이트).",
+ "cache_size_desc": "DNS 캐시 크기(바이트). 캐싱을 비활성화하려면 비워 둡니다.",
"cache_ttl_min_override": "최소 TTL (초) 무시",
"cache_ttl_max_override": "최대 TTL (초) 무시",
"enter_cache_size": "캐시 크기를 입력하세요",
diff --git a/client/src/__locales/nl.json b/client/src/__locales/nl.json
index 6a2c2ac4..c21addcf 100644
--- a/client/src/__locales/nl.json
+++ b/client/src/__locales/nl.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "standaard DNS (via UDP, hostnaam);",
"example_upstream_dot": "versleutelde <0>DNS-via-TLS0>;",
"example_upstream_doh": "versleutelde <0>DNS-via-HTTPS0>;",
+ "example_upstream_doh3": "versleutelde DNS-over-HTTPS met geforceerde <0>HTTP/30> en geen terugval naar HTTP/2 of lager;",
"example_upstream_doq": "versleutelde <0>DNS-via-QUIC0>;",
"example_upstream_sdns": "<0>DNS Stamps0> voor <1>DNSCrypt1> of <2>DNS-via-HTTPS2> oplossingen;",
"example_upstream_tcp": "standaard DNS (over TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Blokkeerlijst",
"milliseconds_abbreviation": "ms",
"cache_size": "Cache grootte",
- "cache_size_desc": "DNS-cache grootte (in bytes).",
+ "cache_size_desc": "DNS-cachegrootte (in bytes). Leeg laten om caching uit te schakelen.",
"cache_ttl_min_override": "Minimale TTL overschrijven",
"cache_ttl_max_override": "Maximale TTL overschrijven",
"enter_cache_size": "Cache grootte invoeren (bytes)",
diff --git a/client/src/__locales/no.json b/client/src/__locales/no.json
index 7ff4664f..ca9baf02 100644
--- a/client/src/__locales/no.json
+++ b/client/src/__locales/no.json
@@ -584,7 +584,7 @@
"blocklist": "Blokkeringsliste",
"milliseconds_abbreviation": "ms",
"cache_size": "Mellomlagerstørrelse",
- "cache_size_desc": "DNS-mellomlagerstørrelse (i bytes)",
+ "cache_size_desc": "DNS-bufferstørrelse (i byte). For å deaktivere caching, la stå tomt.",
"cache_ttl_min_override": "Overstyr minimumslevetiden",
"cache_ttl_max_override": "Overstyr maksimallevetiden",
"enter_cache_size": "Skriv inn mellomlagerstørrelse (i bytes)",
diff --git a/client/src/__locales/pl.json b/client/src/__locales/pl.json
index 48e75a4f..8e5ccb4b 100644
--- a/client/src/__locales/pl.json
+++ b/client/src/__locales/pl.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "zwykły DNS (przez UDP, nazwa hosta);",
"example_upstream_dot": "zaszyfrowany <0>DNS-over-TLS0>;",
"example_upstream_doh": "zaszyfrowany <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "szyfrowany DNS-over-HTTPS z wymuszonym <0>HTTP/30> i nie ma powrotu do HTTP/2 lub niższego;",
"example_upstream_doq": "zaszyfrowany <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>Stempel DNS0> dla resolwerów <1>DNSCrypt1> lub <2>DNS-over-HTTPS2>;",
"example_upstream_tcp": "zwykły DNS (przez TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Lista zablokowanych",
"milliseconds_abbreviation": "ms",
"cache_size": "Rozmiar pamięci podręcznej",
- "cache_size_desc": "Rozmiar pamięci podręcznej DNS (w bajtach).",
+ "cache_size_desc": "Rozmiar pamięci podręcznej DNS (w bajtach). Aby wyłączyć buforowanie, pozostaw puste.",
"cache_ttl_min_override": "Nadpisz minimalną wartość TTL",
"cache_ttl_max_override": "Nadpisz maksymalną wartość TTL",
"enter_cache_size": "Wpisz rozmiar pamięci podręcznej (w bajtach)",
diff --git a/client/src/__locales/pt-br.json b/client/src/__locales/pt-br.json
index 1a1bbecb..ee8b5c1e 100644
--- a/client/src/__locales/pt-br.json
+++ b/client/src/__locales/pt-br.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "DNS normal (através do UDP, nome do servidor);",
"example_upstream_dot": "<0>DNS-sobre-TLS0> criptografado;",
"example_upstream_doh": "<0>DNS-sobre-HTTPS0> criptografado;",
+ "example_upstream_doh3": "DNS-over-HTTPS criptografado com <0>HTTP/3 forçado0> e sem fallback para HTTP/2 ou inferior;",
"example_upstream_doq": "<0>DNS-sobre-QUIC0> criptografado;",
"example_upstream_sdns": "<0>DNS Stamps0> para o <1>DNSCrypt1> ou usar os resolvedores <2>DNS-sobre-HTTPS2>;",
"example_upstream_tcp": "DNS regular (através do TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Lista de bloqueio",
"milliseconds_abbreviation": "ms",
"cache_size": "Tamanho do cache",
- "cache_size_desc": "Tamanho do cache do DNS (em bytes).",
+ "cache_size_desc": "Tamanho do cache do DNS (em bytes). Para desativar o cache, deixe em branco.",
"cache_ttl_min_override": "Sobrepor o TTL mínimo",
"cache_ttl_max_override": "Sobrepor o TTL máximo",
"enter_cache_size": "Digite o tamanho do cache (bytes)",
diff --git a/client/src/__locales/pt-pt.json b/client/src/__locales/pt-pt.json
index 8be5b304..b143afae 100644
--- a/client/src/__locales/pt-pt.json
+++ b/client/src/__locales/pt-pt.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "DNS normal (através do UDP, nome do servidor);",
"example_upstream_dot": "<0>DNS-sobre-TLS0> criptografado;",
"example_upstream_doh": "<0>DNS-sobre-HTTPS0> criptografado;",
+ "example_upstream_doh3": "DNS-over-HTTPS encriptado com <0>HTTP/30> forçado e sem retorno para HTTP/2 ou inferior;",
"example_upstream_doq": "<0>DNS-sobre-QUIC0> criptografado;",
"example_upstream_sdns": "<0>DNS Stamps0> para o <1>DNSCrypt1> ou usar os resolvedores <2>DNS-sobre-HTTPS2>;",
"example_upstream_tcp": "DNS regular (através do TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Lista de bloqueio",
"milliseconds_abbreviation": "ms",
"cache_size": "Tamanho do cache",
- "cache_size_desc": "Tamanho do cache do DNS (em bytes).",
+ "cache_size_desc": "Tamanho do cache DNS (em bytes). Para desativar o cache, deixar o campo vazio.",
"cache_ttl_min_override": "Sobrepor o TTL mínimo",
"cache_ttl_max_override": "Sobrepor o TTL máximo",
"enter_cache_size": "Digite o tamanho do cache (bytes)",
diff --git a/client/src/__locales/ro.json b/client/src/__locales/ro.json
index cbc458e7..a8f94d39 100644
--- a/client/src/__locales/ro.json
+++ b/client/src/__locales/ro.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "DNS obișnuit (over UDP, nume de gazdă);",
"example_upstream_dot": "<0>DNS-over-TLS0> criptat;",
"example_upstream_doh": "<0>DNS-over-HTTPS0> criptat;",
+ "example_upstream_doh3": "DNS-over-HTTPS criptat cu <0>HTTP/30> forțat și fără revenire la HTTP/2 sau inferior;",
"example_upstream_doq": "criptat <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS Stamps0> pentru <1>DNSCrypt1> sau rezolvatori <2>DNS-over-HTTPS2>;",
"example_upstream_tcp": "DNS clasic (over TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Lista de blocări",
"milliseconds_abbreviation": "ms",
"cache_size": "Mărime cache",
- "cache_size_desc": "Mărime cache DNS (în octeți).",
+ "cache_size_desc": "Mărimea cache-ului DNS (în bytes). Pentru a dezactiva caching-ul, lăsați gol.",
"cache_ttl_min_override": "Suprascrieți minimum TTL",
"cache_ttl_max_override": "Suprascrieți maximum TTL",
"enter_cache_size": "Introduceți mărimea cache-ului (bytes)",
diff --git a/client/src/__locales/ru.json b/client/src/__locales/ru.json
index fd605df7..7cf3732f 100644
--- a/client/src/__locales/ru.json
+++ b/client/src/__locales/ru.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "обычный DNS (поверх UDP, с именем хоста);",
"example_upstream_dot": "зашифрованный <0>DNS-over-TLS0>;",
"example_upstream_doh": "зашифрованный <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "зашифрованный DNS-over-HTTPS с принудительным <0>HTTP/30> без отката к HTTP/2 или ниже;",
"example_upstream_doq": "зашифрован <0>DNS-over-QUIC0>",
"example_upstream_sdns": "<0>DNS Stamps0> для <1>DNSCrypt1> или <2>DNS-over-HTTPS2> серверов;",
"example_upstream_tcp": "обычный DNS (поверх TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Чёрный список",
"milliseconds_abbreviation": "мс",
"cache_size": "Размер кеша",
- "cache_size_desc": "Размера кеша DNS (в байтах).",
+ "cache_size_desc": "Размера кеша DNS (в байтах). Чтобы отключить кэширование, оставьте поле пустым.",
"cache_ttl_min_override": "Переопределить минимальный TTL",
"cache_ttl_max_override": "Переопределить максимальный TTL",
"enter_cache_size": "Введите размер кеша (в байтах)",
diff --git a/client/src/__locales/si-lk.json b/client/src/__locales/si-lk.json
index 8d4c335a..a9c8d378 100644
--- a/client/src/__locales/si-lk.json
+++ b/client/src/__locales/si-lk.json
@@ -126,7 +126,7 @@
"number_of_dns_query_to_safe_search": "ආරක්ෂිත සෙවීම බලාත්මක කළ සෙවුම් යන්ත්ර සඳහා ව.නා.ප. ඉල්ලීම් ගණන",
"average_processing_time": "සාමාන්ය සැකසුම් කාලය",
"average_processing_time_hint": "ව.නා.ප. ඉල්ලීමක් සැකසීමේ සාමාන්ය කාලය මිලි තත්පර වලින්",
- "block_domain_use_filters_and_hosts": "පෙරහන් සහ ධාරක ගොනු භාවිතා කරමින් වසම් අවහිර කරන්න",
+ "block_domain_use_filters_and_hosts": "පෙරහන් හා සත්කාරක ගොනු භාවිතයෙන් වසම් අවහිර කරන්න",
"filters_block_toggle_hint": "ඔබට අවහිර කිරීමේ නීති පෙරහන් තුළ පිහිටුවිය හැකිය.",
"use_adguard_browsing_sec": "ඇඩ්ගාර්ඩ් පිරික්සුම් ආරක්ෂණ වියමන සේවාව භාවිතා කරන්න",
"use_adguard_browsing_sec_hint": "ඇඩ්ගාර්ඩ් හෝම් විසින් පිරික්සුම් ආරක්ෂණ වියමන සේවාව මගින් වසම අවහිර කර ඇත්දැයි පරීක්ෂා කරයි. එය සිදු කිරීමට රහස්යතා-හිතකාමී බැලීමේ යෙ.ක්ර.මු. භාවිතා කෙරේ: වසමේ කෙටි උපසර්ගයක SHA256 පූරකයක් පමණක් සේවාදායකය වෙත යවනු ලැබේ.",
@@ -575,7 +575,7 @@
"blocklist": "අවහිර කිරීමේ ලැයිස්තුව",
"milliseconds_abbreviation": "මිලි තත්.",
"cache_size": "නිහිතයෙහි ප්රමාණය",
- "cache_size_desc": "ව.නා.ප. නිහිතයෙහි ප්රමාණය (බයිට)",
+ "cache_size_desc": "ව.නා.ප. නිහිතයෙහි ප්රමාණය (බයිට). නිහිතය අබල කිරීමට, හිස්ව තබන්න.",
"cache_ttl_min_override": "අවම පව. කා. අභිබවන්න",
"cache_ttl_max_override": "උපරිම පව. කා. අභිබවන්න",
"enter_cache_size": "ව.නා.ප. නිහිතයෙහි ප්රමාණය යොදන්න (බයිට)",
diff --git a/client/src/__locales/sk.json b/client/src/__locales/sk.json
index 631d2457..243ed299 100644
--- a/client/src/__locales/sk.json
+++ b/client/src/__locales/sk.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "štandardné DNS (cez UDP, hostname);",
"example_upstream_dot": "šifrované <0>DNS-over-TLS0>;",
"example_upstream_doh": "šifrované <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "šifrované DNS-over-HTTPS s vynúteným <0>HTTP/30> a bez spätného prechodu na HTTP/2 alebo nižšie;",
"example_upstream_doq": "šifrované <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS pečiatky0> pre <1>DNSCrypt1> alebo <2>DNS-over-HTTPS2> rezolvery;",
"example_upstream_tcp": "obyčajná DNS (cez TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Zoznam blokovaní",
"milliseconds_abbreviation": "ms",
"cache_size": "Veľkosť cache",
- "cache_size_desc": "Veľkosť DNS cache (v bajtoch)",
+ "cache_size_desc": "Veľkosť vyrovnávacej pamäte DNS (v bajtoch). Ak chcete zakázať ukladanie do vyrovnávacej pamäte, ponechajte pole prázdne.",
"cache_ttl_min_override": "Prepísať minimálne TTL",
"cache_ttl_max_override": "Prepísať maximálne TTL",
"enter_cache_size": "Zadať veľkosť cache (v bajtoch)",
diff --git a/client/src/__locales/sl.json b/client/src/__locales/sl.json
index b779868d..2c8436ae 100644
--- a/client/src/__locales/sl.json
+++ b/client/src/__locales/sl.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "redni DNS (nad UDP, ime gostitelja);",
"example_upstream_dot": "šifriran <0>DNS-prek-TLS0>;",
"example_upstream_doh": "šifriran <0>DNS-prek-HTTPS0>;",
+ "example_upstream_doh3": "šifriran DNS prek HTTPS s prisilnim <0>HTTP/30> in brez povratne možnosti za HTTP/2 ali nižjim;",
"example_upstream_doq": "šifriran <0>DNS-prek-QUIC0>;",
"example_upstream_sdns": "lahko uporabite <0>DNS Žige0> za reševalce <1>DNSCrypt1> ali <2>DNS-prek-HTTPS2>;",
"example_upstream_tcp": "redni DNS (nad TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Seznam nedovoljenih",
"milliseconds_abbreviation": "ms",
"cache_size": "Velikost predpomnilnika",
- "cache_size_desc": "Velikost predpomnilnika DNS (v bajtih).",
+ "cache_size_desc": "Velikost predpomnilnika DNS (v bajtih). Če želite onemogočiti predpomnjenje, pustite prazno.",
"cache_ttl_min_override": "Preglasi najmanjši TTL",
"cache_ttl_max_override": "Preglasi največji TTL",
"enter_cache_size": "Vnesite velikost predpomnilnika (v bajtih)",
diff --git a/client/src/__locales/sr-cs.json b/client/src/__locales/sr-cs.json
index 23f5647b..4bb3f011 100644
--- a/client/src/__locales/sr-cs.json
+++ b/client/src/__locales/sr-cs.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "uobičajen DNS (preko UDP, imena domaćina);",
"example_upstream_dot": "šifrovano <0>DNS-over-TLS0>;",
"example_upstream_doh": "šifrovano <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "šifrovani DNS-over-HTTPS sa prinudnim <0>HTTP/30> bez povratka na HTTP/2 ili ispod;",
"example_upstream_doq": "šifrovano <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS brojeve0> za <1>DNSCrypt1> ili <2>DNS-over-HTTPS2> razrešivače;",
"example_upstream_tcp": "uobičajeni DNS (preko TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Lista blokiranih",
"milliseconds_abbreviation": "ms",
"cache_size": "Veličina predmemorije",
- "cache_size_desc": "Veličina DNS predmemorije (u bitovima).",
+ "cache_size_desc": "Veličina DNS keša (u bajtovima). Da biste onemogućili keširanje, ostavite prazno.",
"cache_ttl_min_override": "Prepiši najmanji TTL",
"cache_ttl_max_override": "Prepiši najveći TTL",
"enter_cache_size": "Unesite veličinu predmemorije",
diff --git a/client/src/__locales/sv.json b/client/src/__locales/sv.json
index 8818f9d7..2d3cbcaa 100644
--- a/client/src/__locales/sv.json
+++ b/client/src/__locales/sv.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "vanlig DNS (över UDP, värdnamn);",
"example_upstream_dot": "krypterat <0>DNS-over-TLS0>",
"example_upstream_doh": "krypterat <0>DNS-over-HTTPS0>",
+ "example_upstream_doh3": "krypterad DNS-över-HTTPS med påtvingad <0>HTTP/30> och ingen reserv till HTTP/2 eller lägre;",
"example_upstream_doq": "krypterat <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "Du kan använda <0>DNS-stamps0> för <1>DNSCrypt1> eller <2>DNS-over-HTTPS2>-resolvers",
"example_upstream_tcp": "vanlig DNS (över UDP)",
@@ -605,7 +606,7 @@
"blocklist": "Blocklista",
"milliseconds_abbreviation": "ms",
"cache_size": "Cachestorlek",
- "cache_size_desc": "DNS cachestorlek (i byte).",
+ "cache_size_desc": "DNS-cachestorlek (i byte). Lämna tomt om du vill inaktivera cachelagring.",
"cache_ttl_min_override": "Åsidosätt minsta TTL",
"cache_ttl_max_override": "Åsidosätt maximal TTL",
"enter_cache_size": "Ange cachestorlek (byte)",
diff --git a/client/src/__locales/tr.json b/client/src/__locales/tr.json
index bcd06813..791c397d 100644
--- a/client/src/__locales/tr.json
+++ b/client/src/__locales/tr.json
@@ -59,7 +59,7 @@
"dhcp_form_range_title": "IP adresi aralığı",
"dhcp_form_range_start": "Başlangıç aralığı",
"dhcp_form_range_end": "Bitiş aralığı",
- "dhcp_form_lease_title": "DHCP kira süresi (saniye olarak)",
+ "dhcp_form_lease_title": "DHCP kiralama süresi (saniye cinsinden)",
"dhcp_form_lease_input": "Kira süresi",
"dhcp_interface_select": "DHCP arayüzünü seç",
"dhcp_hardware_address": "Donanım adresi",
@@ -215,6 +215,7 @@
"example_upstream_udp": "normal DNS (UDP üzerinden, ana makine adı);",
"example_upstream_dot": "şifrelenmiş <0>DNS-over-TLS0>;",
"example_upstream_doh": "şifrelenmiş <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "<0>HTTP/30> uygulanmış ve HTTP/2 veya aşağısı için yedek olmayan şifrelenmiş DNS-over-HTTPS;",
"example_upstream_doq": "şifrelenmiş <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<1>DNSCrypt1> veya <2>DNS-over-HTTPS2> çözümleyicileri için <0>DNS Damgaları0>;",
"example_upstream_tcp": "normal DNS (TCP üzerinden);",
@@ -317,7 +318,7 @@
"install_settings_interface_link": "AdGuard Home yönetici web arayüzünüz aşağıdaki adreslerde bulunacaktır:",
"form_error_port": "Geçerli bir bağlantı noktası değeri girin",
"install_settings_dns": "DNS sunucusu",
- "install_settings_dns_desc": "Cihazlarınızı veya yönlendiricinizi şu adresteki DNS sunucusunu kullanması için ayarlamanız gerekecek:",
+ "install_settings_dns_desc": "Aşağıdaki adreslerde DNS sunucusunu kullanmak için cihazlarınızı veya yönlendiricinizi yapılandırmanız gerekir:",
"install_settings_all_interfaces": "Tüm arayüzler",
"install_auth_title": "Kimlik Doğrulama",
"install_auth_desc": "AdGuard Home yönetim web arayüzü için şifre doğrulaması yapılandırılmalıdır. AdGuard Home'a yalnızca yerel ağınızdan erişilebilir olsa bile, onu sınırsız erişimden korumak yine de önemlidir.",
@@ -351,7 +352,7 @@
"install_devices_android_list_1": "Android Menüsü ana ekranından Ayarlar'a dokunun.",
"install_devices_android_list_2": "Menüde bulunan Wi-Fi öğesine dokunun. Mevcut tüm ağlar listelenecektir (mobil ağlar için özel DNS sunucusu ayarlanamaz).",
"install_devices_android_list_3": "Bağlı olduğunuz ağın üzerine basılı tutun ve Ağı Değiştir'e dokunun.",
- "install_devices_android_list_4": "Bazı cihazlarda, diğer ayarları görmek için \"Gelişmiş\" seçeneğini seçmeniz gerekebilir. Android DNS ayarlarınızı yapmak için IP ayarlarını DHCP modundan Statik moda almanız gerekecektir.",
+ "install_devices_android_list_4": "Bazı cihazlarda, diğer ayarları görmek için \"Gelişmiş\" seçeneğini seçmeniz gerekebilir. Android DNS ayarlarınızı yapmak için IP ayarlarını DHCP modundan Statik moda değiştirmeniz gerekir.",
"install_devices_android_list_5": "DNS 1 ve DNS 2 değerlerini AdGuard Home sunucunuzun adresleriyle değiştirin.",
"install_devices_ios_list_1": "Ana ekrandan Ayarlar'a dokunun.",
"install_devices_ios_list_2": "Sol menüde bulunan Wi-Fi bölümüne girin (mobil ağlar için özel DNS sunucusu ayarlanamaz).",
@@ -368,13 +369,13 @@
"encryption_server_enter": "Alan adınızı girin",
"encryption_server_desc": "Ayarlanırsa, AdGuard Home ClientID'leri algılar, DDR sorgularına yanıt verir ve ek bağlantı doğrulamaları gerçekleştirir. Ayarlanmazsa, bu özellikler devre dışı bırakılır. Sertifikadaki DNS Adlarından biriyle eşleşmelidir.",
"encryption_redirect": "Otomatik olarak HTTPS'e yönlendir",
- "encryption_redirect_desc": "İşaretlenirse, AdGuard Home sizi otomatik olarak HTTP adresinden HTTPS adreslerine yönlendirecektir.",
+ "encryption_redirect_desc": "İşaretlenirse, AdGuard Home sizi otomatik olarak HTTP adresinden HTTPS adreslerine yönlendirir.",
"encryption_https": "HTTPS bağlantı noktası",
- "encryption_https_desc": "HTTPS bağlantı noktası yapılandırılırsa, AdGuard Home yönetici arayüzüne HTTPS aracılığıyla erişilebilir olacak ve ayrıca '/dns-query' üzerinden DNS-over-HTTPS bağlantısı sağlayacaktır.",
+ "encryption_https_desc": "HTTPS bağlantı noktası yapılandırılırsa, AdGuard Home yönetici arayüzüne HTTPS aracılığıyla erişilebilir olacak ve ayrıca '/dns-query' üzerinden DNS-over-HTTPS bağlantısı sağlar.",
"encryption_dot": "DNS-over-TLS bağlantı noktası",
- "encryption_dot_desc": "Bu bağlantı noktası yapılandırılırsa, AdGuard Home, DNS-over-TLS sunucusunu bu bağlantı noktası üzerinden çalıştıracaktır.",
+ "encryption_dot_desc": "Bu bağlantı noktası yapılandırılırsa, AdGuard Home, DNS-over-TLS sunucusunu bu bağlantı noktası üzerinden çalıştırır.",
"encryption_doq": "DNS-over-QUIC bağlantı noktası",
- "encryption_doq_desc": "Bu bağlantı noktası yapılandırılırsa, AdGuard Home, bu bağlantı noktasında bir DNS-over-QUIC sunucusu çalıştıracaktır.",
+ "encryption_doq_desc": "Bu bağlantı noktası yapılandırılırsa, AdGuard Home, bu bağlantı noktasında bir DNS-over-QUIC sunucusu çalıştırır.",
"encryption_certificates": "Sertifikalar",
"encryption_certificates_desc": "Şifrelemeyi kullanmak için alan adınıza geçerli bir SSL sertifika zinciri sağlamanız gerekir. <0>{{link}}0> adresinden ücretsiz bir sertifika alabilir veya güvenilir Sertifika Yetkililerinden satın alabilirsiniz.",
"encryption_certificates_input": "PEM biçimindeki sertifikalarınızı kopyalayıp buraya yapıştırın.",
@@ -467,7 +468,7 @@
"setup_dns_privacy_other_2": "<0>dnsproxy0>, bilinen tüm güvenli DNS protokollerini destekler.",
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy0>, <1>DNS-over-HTTPS1> protokolünü destekler.",
"setup_dns_privacy_other_4": "<0>Mozilla Firefox0>, <1>DNS-over-HTTPS1> protokolünü destekler.",
- "setup_dns_privacy_other_5": "<0>Burada0> ve <1>burada1> daha fazla kullanım alanı bulacaksınız.",
+ "setup_dns_privacy_other_5": "<0>Burada0> ve <1>burada1> daha fazla kullanım alanı bulabilirsiniz.",
"setup_dns_privacy_ioc_mac": "iOS ve macOS yapılandırması",
"setup_dns_notice": "<1>DNS-over-HTTPS1> veya <1>DNS-over-TLS1> protokolünü kullanmak için AdGuard Home üzerinde <0>Şifreleme ayarları0> bölümünden ayarları yapmanız gerekir.",
"rewrite_added": "\"{{key}}\" için DNS yeniden yazımı başarıyla eklendi",
@@ -556,7 +557,7 @@
"fastest_addr": "En hızlı IP adresi",
"fastest_addr_desc": "Tüm DNS sunucularını sorgulayın ve tüm yanıtlar arasından en hızlı olan IP adresini döndürün. AdGuard Home'un tüm DNS sunucularından yanıt beklemesi gerektiği için DNS sorgularını yavaşlatır, ancak genel bağlantıyı iyileştirir.",
"autofix_warning_text": "\"Düzelt\" seçeneğine tıklarsanız, AdGuard Home, sisteminizi AdGuard Home DNS sunucusunu kullanacak şekilde yapılandırır.",
- "autofix_warning_list": "Bu görevleri gerçekleştirecek: <0>Sistem DNSStubListener'ı devre dışı bırakın0> <0>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın0> <0>/etc/resolv.conf'un sembolik bağlantı hedefini /run/systemd/resolve/resolv.conf ile değiştirin<0> <0>DNSStubListener'ı durdurun (systemd çözümlenmiş hizmeti yeniden yükleyin)0>",
+ "autofix_warning_list": "Bu görevleri gerçekleştirir: <0>Sistem DNSStubListener'ı devre dışı bırakın0> <0>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın0> <0>/etc/resolv.conf'un sembolik bağlantı hedefini /run/systemd/resolve/resolv.conf ile değiştirin<0> <0>DNSStubListener'ı durdurun (systemd çözümlenmiş hizmeti yeniden yükleyin)0>",
"autofix_warning_result": "Sonuç olarak, sisteminizden gelen tüm DNS istekleri varsayılan olarak AdGuard Home tarafından işlenecektir.",
"tags_title": "Etiketler",
"tags_desc": "İstemciye karşılık gelen etiketleri seçebilirsiniz. Etiketleri daha kesin olarak uygulamak için filtreleme kurallarına dahil edin. <0>Daha fazla bilgi edinin0>.",
@@ -585,7 +586,7 @@
"install_static_ok": "Güzel haber! Sabit IP adresi zaten yapılandırılmış",
"install_static_error": "AdGuard Home, bu ağ arayüzü için otomatik olarak yapılandıramıyor. Lütfen bunu elle nasıl yapacağınızla ilgili talimatlara bakın.",
"install_static_configure": "AdGuard Home, <0>{{ip}}0> dinamik IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını ister misiniz?",
- "confirm_static_ip": "AdGuard Home, {{ip}} adresini sabit IP adresiniz olacak şekilde yapılandıracaktır. Devam etmek istiyor musunuz?",
+ "confirm_static_ip": "AdGuard Home, {{ip}} adresini sabit IP adresiniz olacak şekilde yapılandırır. Devam etmek istiyor musunuz?",
"list_updated": "{{count}} liste güncellendi",
"list_updated_plural": "{{count}} liste güncellendi",
"dnssec_enable": "DNSSEC'i etkinleştir",
@@ -605,7 +606,7 @@
"blocklist": "Engel listesi",
"milliseconds_abbreviation": "ms",
"cache_size": "Önbellek boyutu",
- "cache_size_desc": "DNS önbellek boyutu (bayt cinsinden).",
+ "cache_size_desc": "DNS önbellek boyutu (bayt cinsinden). Önbelleğe almayı devre dışı bırakmak için boş bırakın.",
"cache_ttl_min_override": "Minimum kullanım süresini geçersiz kıl",
"cache_ttl_max_override": "Maksimum kullanım süresini geçersiz kıl",
"enter_cache_size": "Önbellek boyutunu girin (bayt)",
@@ -629,7 +630,7 @@
"click_to_view_queries": "Sorguları görmek için tıklayın",
"port_53_faq_link": "53 numaralı bağlantı noktası genellikle \"DNSStubListener\" veya \"systemd-resolved\" hizmetleri tarafından kullanılır. Lütfen bu sorunun nasıl çözüleceğine ilişkin <0>bu talimatı0> okuyun.",
"adg_will_drop_dns_queries": "AdGuard Home, bu istemciden gelen tüm DNS sorgularını yok sayacaktır.",
- "filter_allowlist": "UYARI: Bu işlem ayrıca \"{{disallowed_rule}}\" kuralını izin verilen istemciler listesinden hariç tutacaktır.",
+ "filter_allowlist": "UYARI: Bu işlem ayrıca \"{{disallowed_rule}}\" kuralını izin verilen istemciler listesinden hariç tutar.",
"last_rule_in_allowlist": "\"{{disallowed_rule}}\" kuralı hariç tutulduğunda \"İzin verilen istemciler\" listesi DEVRE DIŞI bırakılacağı için bu istemciye izin verilemez.",
"use_saved_key": "Önceden kaydedilmiş anahtarı kullan",
"parental_control": "Ebeveyn Denetimi",
diff --git a/client/src/__locales/uk.json b/client/src/__locales/uk.json
index b882e856..d9f61146 100644
--- a/client/src/__locales/uk.json
+++ b/client/src/__locales/uk.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "звичайний DNS (поверх UDP, з назвою вузла);",
"example_upstream_dot": "зашифрований <0>DNS-over-TLS0>;",
"example_upstream_doh": "зашифрований <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "зашифрований DNS через HTTPS із примусовим <0>HTTP/30> і без повернення до HTTP/2 або нижче;",
"example_upstream_doq": "зашифрований <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "<0>DNS Stamps0> для <1>DNSCrypt-1> або <2>DNS-over-HTTPS-2>вирішувачів;",
"example_upstream_tcp": "звичайний DNS (через TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Список блокування",
"milliseconds_abbreviation": "мс",
"cache_size": "Розмір кешу",
- "cache_size_desc": "Розмір кешу DNS (у байтах).",
+ "cache_size_desc": "Розмір кешу DNS (у байтах). Щоб вимкнути кешування, залиште пустим.",
"cache_ttl_min_override": "Змінити мінімальний TTL",
"cache_ttl_max_override": "Змінити максимальний TTL",
"enter_cache_size": "Введіть розмір кешу (байт)",
diff --git a/client/src/__locales/vi.json b/client/src/__locales/vi.json
index 05c9cf46..52020d80 100644
--- a/client/src/__locales/vi.json
+++ b/client/src/__locales/vi.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "DNS thông thường (qua UDP, tên máy chủ);",
"example_upstream_dot": "được mã hoá <0>DNS-over-TLS0>;",
"example_upstream_doh": "được mã hoá <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "DNS-over-HTTPS được mã hóa với <0>HTTP/30> bắt buộc và không có dự phòng cho HTTP/2 trở xuống;",
"example_upstream_doq": "được mã hoá <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "bạn có thể sử dụng <0>DNS Stamps0> for <1>DNSCrypt1> hoặc <2>DNS-over-HTTPS2> ",
"example_upstream_tcp": "DNS thông thường(dùng TCP);",
@@ -605,7 +606,7 @@
"blocklist": "Danh sách chặn",
"milliseconds_abbreviation": "ms",
"cache_size": "Kích thước cache",
- "cache_size_desc": "Kích thước cache DNS (bytes).",
+ "cache_size_desc": "Kích thước bộ nhớ cache DNS (tính bằng byte). Để tắt bộ nhớ đệm, hãy để trống.",
"cache_ttl_min_override": "Ghi đè TTL tối thiểu",
"cache_ttl_max_override": "Ghi đè TTL tối đa",
"enter_cache_size": "Nhập kích thước bộ nhớ cache (byte)",
diff --git a/client/src/__locales/zh-cn.json b/client/src/__locales/zh-cn.json
index aa51902f..cc22c96b 100644
--- a/client/src/__locales/zh-cn.json
+++ b/client/src/__locales/zh-cn.json
@@ -215,6 +215,7 @@
"example_upstream_udp": "常规 DNS(通过 UDP、主机名);",
"example_upstream_dot": "加密 <0>DNS-over-TLS0>;",
"example_upstream_doh": "加密 <0>DNS-over-HTTPS0>;",
+ "example_upstream_doh3": "带有强制 <0>HTTP/30> 的加密 DNS-over-HTTPS,并且没有回退到 HTTP/2 或更低版本;",
"example_upstream_doq": "加密 <0>DNS-over-QUIC0>",
"example_upstream_sdns": "<1>DNSCrypt1> 的 <0>DNS Stamps0> 或者 <2>DNS-over-HTTPS2> 解析器;",
"example_upstream_tcp": "常规 DNS(基于 TCP );",
@@ -605,7 +606,7 @@
"blocklist": "拦截列表",
"milliseconds_abbreviation": "毫秒",
"cache_size": "缓存大小",
- "cache_size_desc": "DNS 缓存大小(单位:字节)。",
+ "cache_size_desc": "DNS 缓存大小(单位:字节)。要关闭缓存,请留空。",
"cache_ttl_min_override": "覆盖最小TTL值",
"cache_ttl_max_override": "覆盖最大TTL值",
"enter_cache_size": "输入缓存大小(字节)",
diff --git a/client/src/__locales/zh-tw.json b/client/src/__locales/zh-tw.json
index 9ca99a64..4a5913ba 100644
--- a/client/src/__locales/zh-tw.json
+++ b/client/src/__locales/zh-tw.json
@@ -215,7 +215,8 @@
"example_upstream_udp": "常規 DNS(透過 UDP,主機名稱);",
"example_upstream_dot": "加密的 <0>DNS-over-TLS0>;",
"example_upstream_doh": "加密的 <0>DNS-over-HTTPS0>;",
- "example_upstream_doq": "加密的 <0>DNS-over-QUIC0>;",
+ "example_upstream_doh3": "有強制的 <0>HTTP/30> 且無退回到 HTTP/2 或更低版本之加密的 DNS-over-HTTPS;",
+ "example_upstream_doq": "加密的 <0>DNS-over-QUIC0>;",
"example_upstream_sdns": "關於 <1>DNSCrypt1> 或 <2>DNS-over-HTTPS2> 解析器之 <0>DNS 戳記0>;",
"example_upstream_tcp": "常規 DNS(透過 TCP);",
"example_upstream_tcp_port": "常規 DNS(透過 TCP,含連接埠);",
@@ -605,7 +606,7 @@
"blocklist": "封鎖清單",
"milliseconds_abbreviation": "ms",
"cache_size": "快取大小",
- "cache_size_desc": "DNS 快取大小(以位元組)。",
+ "cache_size_desc": "DNS 快取大小(以位元組)。要禁用快取,留空。",
"cache_ttl_min_override": "覆寫最小的存活時間(TTL)",
"cache_ttl_max_override": "覆寫最大的存活時間(TTL)",
"enter_cache_size": "輸入快取大小(位元組)",
diff --git a/client/src/actions/services.js b/client/src/actions/services.js
index 650aa330..f360081e 100644
--- a/client/src/actions/services.js
+++ b/client/src/actions/services.js
@@ -32,6 +32,21 @@ export const getBlockedServices = () => async (dispatch) => {
}
};
+export const getAllBlockedServicesRequest = createAction('GET_ALL_BLOCKED_SERVICES_REQUEST');
+export const getAllBlockedServicesFailure = createAction('GET_ALL_BLOCKED_SERVICES_FAILURE');
+export const getAllBlockedServicesSuccess = createAction('GET_ALL_BLOCKED_SERVICES_SUCCESS');
+
+export const getAllBlockedServices = () => async (dispatch) => {
+ dispatch(getAllBlockedServicesRequest());
+ try {
+ const data = await apiClient.getAllBlockedServices();
+ dispatch(getAllBlockedServicesSuccess(data));
+ } catch (error) {
+ dispatch(addErrorToast({ error }));
+ dispatch(getAllBlockedServicesFailure());
+ }
+};
+
export const setBlockedServicesRequest = createAction('SET_BLOCKED_SERVICES_REQUEST');
export const setBlockedServicesFailure = createAction('SET_BLOCKED_SERVICES_FAILURE');
export const setBlockedServicesSuccess = createAction('SET_BLOCKED_SERVICES_SUCCESS');
diff --git a/client/src/api/Api.js b/client/src/api/Api.js
index 036f9050..bc030fa1 100644
--- a/client/src/api/Api.js
+++ b/client/src/api/Api.js
@@ -465,11 +465,18 @@ class Api {
BLOCKED_SERVICES_SET = { path: 'blocked_services/set', method: 'POST' };
+ BLOCKED_SERVICES_ALL = { path: 'blocked_services/all', method: 'GET' };
+
getBlockedServicesAvailableServices() {
const { path, method } = this.BLOCKED_SERVICES_SERVICES;
return this.makeRequest(path, method);
}
+ getAllBlockedServices() {
+ const { path, method } = this.BLOCKED_SERVICES_ALL;
+ return this.makeRequest(path, method);
+ }
+
getBlockedServices() {
const { path, method } = this.BLOCKED_SERVICES_LIST;
return this.makeRequest(path, method);
diff --git a/client/src/components/Filters/DnsBlocklist.js b/client/src/components/Filters/DnsBlocklist.js
index 4059b48f..c4facadc 100644
--- a/client/src/components/Filters/DnsBlocklist.js
+++ b/client/src/components/Filters/DnsBlocklist.js
@@ -15,7 +15,7 @@ import {
getObjDiff,
} from '../../helpers/helpers';
-const filtersCatalog = require('../../helpers/filters/filters.json');
+import filtersCatalog from '../../helpers/filters/filters';
class DnsBlocklist extends Component {
componentDidMount() {
diff --git a/client/src/components/Filters/Form.js b/client/src/components/Filters/Form.js
index 39791a5f..f4e902f5 100644
--- a/client/src/components/Filters/Form.js
+++ b/client/src/components/Filters/Form.js
@@ -7,8 +7,7 @@ import classNames from 'classnames';
import { validatePath, validateRequiredValue } from '../../helpers/validators';
import { CheckboxField, renderInputField } from '../../helpers/form';
import { MODAL_OPEN_TIMEOUT, MODAL_TYPE, FORM_NAME } from '../../helpers/constants';
-
-const filtersCatalog = require('../../helpers/filters/filters.json');
+import filtersCatalog from '../../helpers/filters/filters';
const getIconsData = (homepage, source) => ([
{
diff --git a/client/src/components/Filters/Services/Form.js b/client/src/components/Filters/Services/Form.js
index 4aed810d..1684d5ea 100644
--- a/client/src/components/Filters/Services/Form.js
+++ b/client/src/components/Filters/Services/Form.js
@@ -6,10 +6,11 @@ import flow from 'lodash/flow';
import { toggleAllServices } from '../../../helpers/helpers';
import { renderServiceField } from '../../../helpers/form';
-import { FORM_NAME, SERVICES } from '../../../helpers/constants';
+import { FORM_NAME } from '../../../helpers/constants';
const Form = (props) => {
const {
+ blockedServices,
handleSubmit,
change,
pristine,
@@ -27,7 +28,7 @@ const Form = (props) => {
type="button"
className="btn btn-secondary btn-block"
disabled={processing || processingSet}
- onClick={() => toggleAllServices(SERVICES, change, true)}
+ onClick={() => toggleAllServices(blockedServices, change, true)}
>
block_all
@@ -37,17 +38,17 @@ const Form = (props) => {
type="button"
className="btn btn-secondary btn-block"
disabled={processing || processingSet}
- onClick={() => toggleAllServices(SERVICES, change, false)}
+ onClick={() => toggleAllServices(blockedServices, change, false)}
>
unblock_all
- {SERVICES.map((service) => (
+ {blockedServices.map((service) => (
{
};
Form.propTypes = {
+ blockedServices: PropTypes.array.isRequired,
pristine: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
change: PropTypes.func.isRequired,
diff --git a/client/src/components/Filters/Services/index.js b/client/src/components/Filters/Services/index.js
index be5516bd..09cdd7c8 100644
--- a/client/src/components/Filters/Services/index.js
+++ b/client/src/components/Filters/Services/index.js
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import Form from './Form';
import Card from '../../ui/Card';
-import { getBlockedServices, setBlockedServices } from '../../../actions/services';
+import { getBlockedServices, getAllBlockedServices, setBlockedServices } from '../../../actions/services';
import PageTitle from '../../ui/PageTitle';
const getInitialDataForServices = (initial) => (initial ? initial.reduce(
@@ -21,6 +21,7 @@ const Services = () => {
useEffect(() => {
dispatch(getBlockedServices());
+ dispatch(getAllBlockedServices());
}, []);
const handleSubmit = (values) => {
@@ -49,6 +50,7 @@ const Services = () => {
-
- {SERVICES.map((service) => (
-
- ))}
-
+ {services.allServices.length > 0 && (
+
+ {services.allServices.map((service) => (
+
+ ))}
+
+ )}
,
},
diff --git a/client/src/components/Settings/Clients/Service.css b/client/src/components/Settings/Clients/Service.css
index 7c1890fe..153cf2d8 100644
--- a/client/src/components/Settings/Clients/Service.css
+++ b/client/src/components/Settings/Clients/Service.css
@@ -9,6 +9,12 @@
cursor: pointer;
}
+.service__text {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
@media screen and (min-width: 768px) {
.services {
display: flex;
@@ -33,7 +39,7 @@
margin-right: 30px;
margin-left: 0;
}
-
+
.service:nth-child(3n) {
margin-right: 0;
margin-left: auto;
diff --git a/client/src/components/Settings/Clients/index.js b/client/src/components/Settings/Clients/index.js
index a6dfc0b1..b7c50ae8 100644
--- a/client/src/components/Settings/Clients/index.js
+++ b/client/src/components/Settings/Clients/index.js
@@ -2,7 +2,7 @@ import React, { Component, Fragment } from 'react';
import { withTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
-import ClientsTable from './ClientsTable';
+import { ClientsTable } from './ClientsTable';
import AutoClients from './AutoClients';
import PageTitle from '../../ui/PageTitle';
import Loading from '../../ui/Loading';
diff --git a/client/src/components/Settings/Dns/Upstream/Examples.js b/client/src/components/Settings/Dns/Upstream/Examples.js
index c17e9456..a975e444 100644
--- a/client/src/components/Settings/Dns/Upstream/Examples.js
+++ b/client/src/components/Settings/Dns/Upstream/Examples.js
@@ -57,6 +57,22 @@ const Examples = (props) => (
example_upstream_doh
+
+ h3://unfiltered.adguard-dns.com/dns-query:
+ HTTP/3
+ ,
+ ]}
+ >
+ example_upstream_doh3
+
+
quic://unfiltered.adguard-dns.com: (
d="M15 3C10.57 3 6.701 5.419 4.623 9h2.39a10.063 10.063 0 0 1 4.05-3.19c-.524.89-.961 1.973-1.3 3.19h2.108c.79-2.459 1.998-4 3.129-4s2.339 1.541 3.129 4h2.107c-.338-1.217-.774-2.3-1.299-3.19A10.062 10.062 0 0 1 22.989 9h2.389C23.298 5.419 19.43 3 15 3zm7.035 9.129c-1.372 0-2.264.73-2.264 1.842 0 .896.538 1.463 1.579 1.66l.75.15c.65.13.898.3.898.615 0 .375-.37.635-.91.635-.6 0-1.014-.265-1.049-.68h-1.38c.023 1.097.93 1.776 2.37 1.776 1.491 0 2.399-.717 2.399-1.904 0-.903-.504-1.412-1.63-1.63l-.734-.142c-.6-.118-.851-.3-.851-.611 0-.378.336-.62.844-.62.509 0 .891.28.923.682h1.336c-.024-1.053-.948-1.773-2.28-1.773zm-16.185.148v5.696h2.39c1.712 0 2.662-1.033 2.662-2.903 0-1.779-.966-2.793-2.662-2.793H5.85zm6.933.004v5.692h1.373v-3.235h.076l2.377 3.235h1.149V12.28h-1.373v3.203h-.076l-2.372-3.203h-1.154zm-5.486 1.16h.682c.912 0 1.449.596 1.449 1.657 0 1.128-.51 1.713-1.45 1.713h-.681v-3.37zM4.623 21C6.701 24.581 10.57 27 15 27c4.43 0 8.299-2.419 10.377-6h-2.389a10.063 10.063 0 0 1-4.049 3.19c.524-.89.96-1.973 1.297-3.19H18.13c-.79 2.459-1.996 4-3.127 4-1.131 0-2.339-1.541-3.129-4h-2.11c.339 1.217.776 2.3 1.3 3.19A10.056 10.056 0 0 1 7.013 21h-2.39z">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -356,62 +205,6 @@ const Icons = () => (
d="M8.036 10.93l3.93 4.07 4.068-3.93" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
);
diff --git a/client/src/components/ui/Tabler.css b/client/src/components/ui/Tabler.css
index 34d37622..fac3d80a 100644
--- a/client/src/components/ui/Tabler.css
+++ b/client/src/components/ui/Tabler.css
@@ -15452,6 +15452,7 @@ a.tag-addon:hover {
}
.custom-switch-indicator {
+ flex-shrink: 0;
display: inline-block;
height: 1.25rem;
width: 2.25rem;
diff --git a/client/src/helpers/constants.js b/client/src/helpers/constants.js
index 943b28ac..c13ab952 100644
--- a/client/src/helpers/constants.js
+++ b/client/src/helpers/constants.js
@@ -202,158 +202,6 @@ export const FILTERS_URLS = {
blocked_services: '/blocked_services',
};
-export const SERVICES = [
- {
- id: '9gag',
- name: '9GAG',
- },
- {
- id: 'amazon',
- name: 'Amazon',
- },
- {
- id: 'bilibili',
- name: 'Bilibili',
- },
- {
- id: 'cloudflare',
- name: 'CloudFlare',
- },
- {
- id: 'dailymotion',
- name: 'Dailymotion',
- },
- {
- id: 'discord',
- name: 'Discord',
- },
- {
- id: 'disneyplus',
- name: 'Disney+',
- },
- {
- id: 'ebay',
- name: 'EBay',
- },
- {
- id: 'epic_games',
- name: 'Epic Games',
- },
- {
- id: 'facebook',
- name: 'Facebook',
- },
- {
- id: 'hulu',
- name: 'Hulu',
- },
- {
- id: 'imgur',
- name: 'Imgur',
- },
- {
- id: 'instagram',
- name: 'Instagram',
- },
- {
- id: 'mail_ru',
- name: 'Mail.ru',
- },
- {
- id: 'netflix',
- name: 'Netflix',
- },
- {
- id: 'ok',
- name: 'OK.ru',
- },
- {
- id: 'origin',
- name: 'Origin',
- },
- {
- id: 'pinterest',
- name: 'Pinterest',
- },
- {
- id: 'qq',
- name: 'QQ',
- },
- {
- id: 'reddit',
- name: 'Reddit',
- },
- {
- id: 'skype',
- name: 'Skype',
- },
- {
- id: 'snapchat',
- name: 'Snapchat',
- },
- {
- id: 'spotify',
- name: 'Spotify',
- },
- {
- id: 'steam',
- name: 'Steam',
- },
- {
- id: 'telegram',
- name: 'Telegram',
- },
- {
- id: 'tiktok',
- name: 'TikTok',
- },
- {
- id: 'tinder',
- name: 'Tinder',
- },
- {
- id: 'twitch',
- name: 'Twitch',
- },
- {
- id: 'twitter',
- name: 'Twitter',
- },
- {
- id: 'viber',
- name: 'Viber',
- },
- {
- id: 'vimeo',
- name: 'Vimeo',
- },
- {
- id: 'vk',
- name: 'VK.com',
- },
- {
- id: 'wechat',
- name: 'WeChat',
- },
- {
- id: 'weibo',
- name: 'Weibo',
- },
- {
- id: 'whatsapp',
- name: 'WhatsApp',
- },
- {
- id: 'youtube',
- name: 'YouTube',
- },
-];
-
-export const SERVICES_ID_NAME_MAP = SERVICES.reduce((acc, { id, name }) => {
- acc[id] = name;
- return acc;
-}, {});
-
export const ENCRYPTION_SOURCE = {
PATH: 'path',
CONTENT: 'content',
diff --git a/client/src/helpers/filters/filters.json b/client/src/helpers/filters/filters.js
similarity index 59%
rename from client/src/helpers/filters/filters.json
rename to client/src/helpers/filters/filters.js
index 63fe0995..8be075e9 100644
--- a/client/src/helpers/filters/filters.json
+++ b/client/src/helpers/filters/filters.js
@@ -1,162 +1,220 @@
-{
+// Code generated by go run ./scripts/vetted-filters/main.go; DO NOT EDIT.
+
+/* eslint quote-props: 'off', quotes: 'off', comma-dangle: 'off', semi: 'off' */
+
+export default {
"categories": {
"general": {
"name": "filter_category_general",
"description": "filter_category_general_desc"
},
- "security": {
- "name": "filter_category_security",
- "description": "filter_category_security_desc"
+ "other": {
+ "name": "filter_category_other",
+ "description": "filter_category_other_desc"
},
"regional": {
"name": "filter_category_regional",
"description": "filter_category_regional_desc"
},
- "other": {
- "name": "filter_category_other",
- "description": "filter_category_other_desc"
+ "security": {
+ "name": "filter_category_security",
+ "description": "filter_category_security_desc"
}
},
"filters": {
- "adguard-dns-filter": {
- "name": "AdGuard DNS filter",
+ "1hosts_lite": {
+ "name": "1Hosts (Lite)",
"categoryId": "general",
- "homepage": "https://github.com/AdguardTeam/AdGuardSDNSFilter",
- "source": "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt"
+ "homepage": "https://badmojr.github.io/1Hosts/",
+ "source": "https://badmojr.gitlab.io/1hosts/Lite/adblock.txt"
},
- "adaway-default-blocklist": {
- "name": "AdAway Default Blocklist",
- "categoryId": "general",
- "homepage": "https://github.com/AdAway/adaway.github.io/",
- "source": "https://adaway.org/hosts.txt"
- },
- "peter-lowe-list": {
- "name": "Peter Lowe's List",
- "categoryId": "general",
- "homepage": "https://pgl.yoyo.org/adservers/",
- "source": "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=adblockplus&showintro=1&mimetype=plaintext"
- },
- "dan-pollock-list": {
- "name": "Dan Pollock's List",
- "categoryId": "general",
- "homepage": "https://someonewhocares.org/",
- "source": "https://someonewhocares.org/hosts/zero/hosts"
- },
- "oisd": {
- "name": "OISD Blocklist Basic",
- "categoryId": "general",
- "homepage": "https://oisd.nl/",
- "source": "https://abp.oisd.nl/basic/"
- },
- "game-console-adblock-list": {
- "name": "Game Console Adblock List",
- "categoryId": "general",
- "homepage": "https://github.com/DandelionSprout/adfilt",
- "source": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/GameConsoleAdblockList.txt"
- },
- "perflyst-dandelion-sprout-smart-tv-blocklist-for-adguard-home": {
- "name": "Perflyst and Dandelion Sprout's Smart-TV Blocklist",
- "categoryId": "general",
- "homepage": "https://github.com/Perflyst/PiHoleBlocklist",
- "source": "https://raw.githubusercontent.com/Perflyst/PiHoleBlocklist/master/SmartTV-AGH.txt"
- },
- "nocoin-filter-list": {
- "name": "NoCoin Filter List",
- "categoryId": "security",
- "homepage": "https://github.com/hoshsadiq/adblock-nocoin-list/",
- "source": "https://raw.githubusercontent.com/hoshsadiq/adblock-nocoin-list/master/hosts.txt"
- },
- "the-big-list-of-hacked-malware-web-sites": {
- "name": "The Big List of Hacked Malware Web Sites",
- "categoryId": "security",
- "homepage": "https://github.com/mitchellkrogza/The-Big-List-of-Hacked-Malware-Web-Sites",
- "source": "https://raw.githubusercontent.com/mitchellkrogza/The-Big-List-of-Hacked-Malware-Web-Sites/master/hosts"
- },
- "scam-blocklist-by-durable-napkin": {
- "name": "Scam Blocklist by DurableNapkin",
- "categoryId": "security",
- "homepage": "https://github.com/durablenapkin/scamblocklist",
- "source": "https://raw.githubusercontent.com/durablenapkin/scamblocklist/master/adguard.txt"
- },
- "urlhaus-filter-online": {
- "name": "Online Malicious URL Blocklist",
- "categoryId": "security",
- "homepage": "https://gitlab.com/malware-filter/urlhaus-filter",
- "source": "https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-agh-online.txt"
- },
- "dandelion-sprouts-anti-malware-list": {
- "name": "Dandelion Sprout's Anti-Malware List",
- "categoryId": "security",
- "homepage": "https://github.com/DandelionSprout/adfilt",
- "source": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareAdGuardHome.txt"
- },
- "NOR-dandelion-sprouts-nordiske-filtre": {
- "name": "NOR: Dandelion Sprouts nordiske filtre",
+ "CHN_adrules": {
+ "name": "CHN: AdRules DNS List",
"categoryId": "regional",
- "homepage": "https://github.com/DandelionSprout/adfilt",
- "source": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianExperimentalList%20alternate%20versions/NordicFiltersAdGuardHome.txt"
+ "homepage": "https://github.com/Cats-Team/AdRules",
+ "source": "https://raw.githubusercontent.com/Cats-Team/AdRules/main/dns.txt"
},
- "POL-polish-filters-for-pihole": {
- "name": "POL: Polish filters for Pi hole",
- "categoryId": "regional",
- "homepage": "https://www.certyficate.it/",
- "source": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-pihole-filters/hostfile.txt"
- },
- "KOR-youslist": {
- "name": "KOR: YousList",
- "categoryId": "regional",
- "homepage": "https://github.com/yous/YousList",
- "source": "https://raw.githubusercontent.com/yous/YousList/master/hosts.txt"
- },
- "VNM-abpvn-list": {
- "name": "VNM: ABPVN List",
- "categoryId": "regional",
- "homepage": "http://abpvn.com/",
- "source": "https://abpvn.com/android/abpvn.txt"
- },
- "SWE-frellwit-swedish-hosts-file": {
- "name": "SWE: Frellwit's Swedish Hosts File",
- "categoryId": "regional",
- "homepage": "https://github.com/lassekongo83/Frellwits-filter-lists/",
- "source": "https://raw.githubusercontent.com/lassekongo83/Frellwits-filter-lists/master/Frellwits-Swedish-Hosts-File.txt"
- },
- "ITA-filtri-dns": {
- "name": "ITA: Filtri-DNS",
- "categoryId": "regional",
- "homepage": "https://filtri-dns.ga/",
- "source": "https://filtri-dns.ga/filtri.txt"
- },
- "IRN-unwanted-iranian-domains": {
- "name": "IRN: Unwanted Iranian domains",
- "categoryId": "regional",
- "homepage": "https://github.com/DRSDavidSoft/additional-hosts",
- "source": "https://raw.githubusercontent.com/DRSDavidSoft/additional-hosts/master/domains/blacklist/unwanted-iranian.txt"
- },
- "MKD-macedonian-pi-hole-blocklist": {
- "name": "MKD: Macedonian Pi-hole Blocklist",
- "categoryId": "regional",
- "homepage": "https://github.com/cchevy/macedonian-pi-hole-blocklist",
- "source": "https://raw.githubusercontent.com/cchevy/macedonian-pi-hole-blocklist/master/hosts.txt"
- },
- "CHN-anti-ad" : {
+ "CHN_anti_ad": {
"name": "CHN: anti-AD",
"categoryId": "regional",
"homepage": "https://anti-ad.net/",
"source": "https://anti-ad.net/easylist.txt"
},
- "IDN-abpindo": {
+ "IDN_abpindo": {
"name": "IDN: ABPindo",
"categoryId": "regional",
- "homepage": "https://github.com/ABPindo/indonesianadblockrules/",
- "source": "https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/abpindo.txt"
+ "homepage": "https://github.com/ABPindo/indonesianadblockrules",
+ "source": "https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/aghome.txt"
},
- "NLD-Easylist": {
- "name": "NLD: Easylist",
+ "IRN_unwanted_iranian_domains": {
+ "name": "IRN: PersianBlocker list",
"categoryId": "regional",
- "homepage": "https://forums.lanik.us/viewforum.php?f=100",
- "source": "https://easylist-downloads.adblockplus.org/easylistdutch.txt"
+ "homepage": "https://github.com/MasterKia/PersianBlocker",
+ "source": "https://raw.githubusercontent.com/MasterKia/PersianBlocker/main/PersianBlockerHosts.txt"
},
- "windows-spy-blocker" : {
+ "ITA_filtri_dns": {
+ "name": "ITA: Filtri-DNS",
+ "categoryId": "regional",
+ "homepage": "https://filtri-dns.ga/",
+ "source": "https://filtri-dns.ga/filtri.txt"
+ },
+ "KOR_list_kr": {
+ "name": "KOR: List-KR DNS",
+ "categoryId": "regional",
+ "homepage": "https://github.com/List-KR/List-KR",
+ "source": "https://github.com/List-KR/List-KR"
+ },
+ "KOR_youslist": {
+ "name": "KOR: YousList",
+ "categoryId": "regional",
+ "homepage": "https://github.com/yous/YousList",
+ "source": "https://raw.githubusercontent.com/yous/YousList/master/hosts.txt"
+ },
+ "MKD_macedonian_pi_hole_blocklist": {
+ "name": "MKD: Macedonian Pi-hole Blocklist",
+ "categoryId": "regional",
+ "homepage": "https://github.com/cchevy/macedonian-pi-hole-blocklist",
+ "source": "https://raw.githubusercontent.com/cchevy/macedonian-pi-hole-blocklist/master/hosts.txt"
+ },
+ "NOR_dandelion_sprouts_anti_malware_list": {
+ "name": "NOR: Dandelion Sprouts nordiske filtre",
+ "categoryId": "regional",
+ "homepage": "https://github.com/DandelionSprout/adfilt",
+ "source": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianExperimentalList%20alternate%20versions/NordicFiltersAdGuardHome.txt"
+ },
+ "POL_polish_filters_for_pi_hole": {
+ "name": "POL: Polish filters for Pi hole",
+ "categoryId": "regional",
+ "homepage": "https://www.certyficate.it/",
+ "source": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-pihole-filters/hostfile.txt"
+ },
+ "SWE_frellwit_swedish_hosts_file": {
+ "name": "SWE: Frellwit's Swedish Hosts File",
+ "categoryId": "regional",
+ "homepage": "https://github.com/lassekongo83/Frellwits-filter-lists/",
+ "source": "https://raw.githubusercontent.com/lassekongo83/Frellwits-filter-lists/master/Frellwits-Swedish-Hosts-File.txt"
+ },
+ "TUR_turk_adlist": {
+ "name": "TUR: turk-adlist",
+ "categoryId": "regional",
+ "homepage": "https://github.com/bkrucarci/turk-adlist",
+ "source": "https://raw.githubusercontent.com/bkrucarci/turk-adlist/master/hosts"
+ },
+ "VNM_abpvn": {
+ "name": "VNM: ABPVN List",
+ "categoryId": "regional",
+ "homepage": "http://abpvn.com/",
+ "source": "https://abpvn.com/android/abpvn.txt"
+ },
+ "adguard_dns_filter": {
+ "name": "AdGuard DNS filter",
+ "categoryId": "general",
+ "homepage": "https://github.com/AdguardTeam/AdGuardSDNSFilter",
+ "source": "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt"
+ },
+ "adway_default_blocklist": {
+ "name": "AdAway Default Blocklist",
+ "categoryId": "general",
+ "homepage": "https://github.com/AdAway/adaway.github.io/",
+ "source": "https://adaway.org/hosts.txt"
+ },
+ "curben_phishing_filter": {
+ "name": "Phishing URL Blocklist (PhishTank and OpenPhish)",
+ "categoryId": "security",
+ "homepage": "https://gitlab.com/malware-filter/phishing-filter",
+ "source": "https://malware-filter.gitlab.io/malware-filter/phishing-filter-agh.txt"
+ },
+ "dan_pollocks_list": {
+ "name": "Dan Pollock's List",
+ "categoryId": "general",
+ "homepage": "https://someonewhocares.org/",
+ "source": "https://someonewhocares.org/hosts/zero/hosts"
+ },
+ "dandelion_sprouts_anti_malware_list": {
+ "name": "Dandelion Sprout's Anti-Malware List",
+ "categoryId": "security",
+ "homepage": "https://github.com/DandelionSprout/adfilt",
+ "source": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareAdGuardHome.txt"
+ },
+ "dandelion_sprouts_game_console_adblock_list": {
+ "name": "Dandelion Sprout's Game Console Adblock List",
+ "categoryId": "other",
+ "homepage": "https://github.com/DandelionSprout/adfilt",
+ "source": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/GameConsoleAdblockList.txt"
+ },
+ "energized_spark": {
+ "name": "Energized Spark",
+ "categoryId": "general",
+ "homepage": "https://energized.pro/",
+ "source": "https://block.energized.pro/spark/formats/filter"
+ },
+ "nocoin_filter_list": {
+ "name": "NoCoin Filter List",
+ "categoryId": "security",
+ "homepage": "https://github.com/hoshsadiq/adblock-nocoin-list/",
+ "source": "https://raw.githubusercontent.com/hoshsadiq/adblock-nocoin-list/master/hosts.txt"
+ },
+ "notracking_hosts_blocklists": {
+ "name": "The NoTracking blocklist",
+ "categoryId": "general",
+ "homepage": "https://github.com/notracking/hosts-blocklists",
+ "source": "https://raw.githubusercontent.com/notracking/hosts-blocklists/master/adblock/adblock.txt"
+ },
+ "oisd_basic": {
+ "name": "OISD Blocklist Basic",
+ "categoryId": "general",
+ "homepage": "https://oisd.nl/",
+ "source": "https://abp.oisd.nl/basic/"
+ },
+ "oisd_full": {
+ "name": "OISD Blocklist Full",
+ "categoryId": "general",
+ "homepage": "https://oisd.nl/",
+ "source": "https://abp.oisd.nl/"
+ },
+ "perflyst_dandelion_sprout_smart_tv_blocklist_for_adguard_home": {
+ "name": "Perflyst and Dandelion Sprout's Smart-TV Blocklist",
+ "categoryId": "other",
+ "homepage": "https://github.com/Perflyst/PiHoleBlocklist",
+ "source": "https://raw.githubusercontent.com/Perflyst/PiHoleBlocklist/master/SmartTV-AGH.txt"
+ },
+ "peter_lowe_list": {
+ "name": "Peter Lowe's Blocklist",
+ "categoryId": "general",
+ "homepage": "https://pgl.yoyo.org/adservers/",
+ "source": "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=adblockplus\u0026showintro=1\u0026mimetype=plaintext"
+ },
+ "scam_blocklist_by_durablenapkin": {
+ "name": "Scam Blocklist by DurableNapkin",
+ "categoryId": "security",
+ "homepage": "https://github.com/durablenapkin/scamblocklist",
+ "source": "https://raw.githubusercontent.com/durablenapkin/scamblocklist/master/adguard.txt"
+ },
+ "staklerware_indicators_list": {
+ "name": "Stalkerware Indicators List",
+ "categoryId": "security",
+ "homepage": "https://github.com/AssoEchap/stalkerware-indicators",
+ "source": "https://raw.githubusercontent.com/AssoEchap/stalkerware-indicators/master/generated/hosts"
+ },
+ "steven_blacks_list": {
+ "name": "Steven Black's List",
+ "categoryId": "general",
+ "homepage": "https://github.com/StevenBlack/hosts",
+ "source": "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
+ },
+ "the_big_list_of_hacked_malware_web_sites": {
+ "name": "The Big List of Hacked Malware Web Sites",
+ "categoryId": "security",
+ "homepage": "https://github.com/mitchellkrogza/The-Big-List-of-Hacked-Malware-Web-Sites",
+ "source": "https://raw.githubusercontent.com/mitchellkrogza/The-Big-List-of-Hacked-Malware-Web-Sites/master/hosts"
+ },
+ "urlhaus_filter_online": {
+ "name": "Malicious URL Blocklist (URLHaus)",
+ "categoryId": "security",
+ "homepage": "https://gitlab.com/malware-filter/urlhaus-filter",
+ "source": "https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-agh.txt"
+ },
+ "windowsspyblocker_hosts_spy_rules": {
"name": "WindowsSpyBlocker - Hosts spy rules",
"categoryId": "other",
"homepage": "https://github.com/crazy-max/WindowsSpyBlocker",
diff --git a/client/src/helpers/form.js b/client/src/helpers/form.js
index c4f90722..f58aa830 100644
--- a/client/src/helpers/form.js
+++ b/client/src/helpers/form.js
@@ -1,6 +1,8 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Trans } from 'react-i18next';
+import cn from 'classnames';
+
import { createOnBlurHandler } from './helpers';
import { R_MAC_WITHOUT_COLON, R_UNIX_ABSOLUTE_PATH, R_WIN_ABSOLUTE_PATH } from './constants';
@@ -229,24 +231,34 @@ export const renderServiceField = ({
modifier,
icon,
meta: { touched, error },
-}) =>
-
-
-
- {placeholder}
-
-
-
-
- {!disabled && touched && error
- && {error} }
- ;
+}) => (
+ <>
+
+
+
+
+ {placeholder}
+
+ {icon && (
+
+ )}
+
+ {!disabled && touched && error && (
+
+ {error}
+
+ )}
+ >
+);
renderServiceField.propTypes = {
input: PropTypes.object.isRequired,
diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js
index a7bf9485..af65589d 100644
--- a/client/src/helpers/helpers.js
+++ b/client/src/helpers/helpers.js
@@ -21,7 +21,6 @@ import {
FILTERED,
FILTERED_STATUS,
R_CLIENT_ID,
- SERVICES_ID_NAME_MAP,
STANDARD_DNS_PORT,
STANDARD_HTTPS_PORT,
STANDARD_WEB_PORT,
@@ -991,7 +990,22 @@ export const filterOutComments = (lines) => lines
.filter((line) => !line.startsWith(COMMENT_LINE_DEFAULT_TOKEN));
/**
- * @param {string} serviceId
+ * @param {array} services
+ * @param {string} id
* @returns {string}
*/
-export const getServiceName = (serviceId) => SERVICES_ID_NAME_MAP[serviceId] || serviceId;
+export const getService = (services, id) => services.find((s) => s.id === id);
+
+/**
+ * @param {array} services
+ * @param {string} id
+ * @returns {string}
+ */
+export const getServiceName = (services, id) => getService(services, id)?.name;
+
+/**
+ * @param {array} services
+ * @param {string} id
+ * @returns {string}
+ */
+export const getServiceIcon = (services, id) => getService(services, id)?.icon_svg;
diff --git a/client/src/helpers/trackers/adguard.json b/client/src/helpers/trackers/adguard.json
index 82831aef..1559055c 100644
--- a/client/src/helpers/trackers/adguard.json
+++ b/client/src/helpers/trackers/adguard.json
@@ -21,87 +21,128 @@
"akamai_technologies": {
"name": "Akamai Technologies",
"categoryId": 9,
- "url": "https://www.akamai.com/"
+ "url": "https://www.akamai.com/",
+ "companyId": "akamai"
},
"apple": {
"name": "Apple",
"categoryId": 8,
- "url": "https://www.apple.com/"
+ "url": "https://www.apple.com/",
+ "companyId": "apple"
},
"apple_ads": {
"name": "Apple Search Ads",
"categoryId": 4,
- "url": "https://searchads.apple.com/"
+ "url": "https://searchads.apple.com/",
+ "companyId": "apple"
},
"facebook_audience": {
"name": "Facebook Audience Network",
"categoryId": 4,
- "url": "https://www.facebook.com/business/products/audience-network"
+ "url": "https://www.facebook.com/business/products/audience-network",
+ "companyId": "facebook"
},
"crashlytics": {
"name": "Crashlytics",
"categoryId": 101,
- "url": "https://crashlytics.com/"
+ "url": "https://crashlytics.com/",
+ "companyId": null
},
"flurry": {
"name": "Flurry",
"categoryId": 101,
- "url": "http://www.flurry.com/"
+ "url": "http://www.flurry.com/",
+ "companyId": "verizon"
},
"hockeyapp": {
"name": "HockeyApp",
"categoryId": 101,
- "url": "https://hockeyapp.net/"
+ "url": "https://hockeyapp.net/",
+ "companyId": null
},
"firebase": {
"name": "Firebase",
"categoryId": 101,
- "url": "https://firebase.google.com/"
+ "url": "https://firebase.google.com/",
+ "companyId": "google"
},
"appsflyer": {
"name": "AppsFlyer",
"categoryId": 101,
- "url": "https://www.appsflyer.com/"
+ "url": "https://www.appsflyer.com/",
+ "companyId": "appsflyer"
},
"yandex_appmetrica": {
"name": "Yandex AppMetrica",
"categoryId": 101,
- "url": "https://appmetrica.yandex.com/"
+ "url": "https://appmetrica.yandex.com/",
+ "companyId": "yandex"
},
"adjust": {
"name": "Adjust",
"categoryId": 101,
- "url": "https://www.adjust.com/"
+ "url": "https://www.adjust.com/",
+ "companyId": "adjust"
},
"branch": {
"name": "Branch.io",
"categoryId": 101,
- "url": "https://branch.io/"
+ "url": "https://branch.io/",
+ "companyId": "branch_metrics_inc"
},
"markmonitor": {
"name": "MarkMonitor",
"categoryId": 4,
- "url": "https://www.markmonitor.com/"
+ "url": "https://www.markmonitor.com/",
+ "companyId": "markmonitor"
},
"appcenter": {
"name": "Microsoft App Center",
"categoryId": 5,
- "url": "https://appcenter.ms/"
+ "url": "https://appcenter.ms/",
+ "companyId": null
},
"unity_ads": {
"name": "Unity Ads",
"categoryId": 4,
- "url": "https://unity.com/solutions/mobile-business/monetize-your-game"
+ "url": "https://unity.com/solutions/mobile-business/monetize-your-game",
+ "companyId": null
},
"azure": {
"name": "Microsoft Azure",
"categoryId": 10,
- "url": "https://azure.microsoft.com/"
+ "url": "https://azure.microsoft.com/",
+ "companyId": "microsoft"
},
"button": {
"name": "Button",
"categoryId": 4,
- "url": "https://www.usebutton.com/"
+ "url": "https://www.usebutton.com/",
+ "companyId": null
+ },
+ "netflix": {
+ "name": "Netflix",
+ "categoryId": 8,
+ "url": "https://www.netflix.com/",
+ "companyId": null
+ },
+ "mail.ru_banner": {
+ "name": "Mail.Ru Banner Network",
+ "categoryId": 4,
+ "url": "http://mail.ru/",
+ "companyId": "vk"
+ },
+ "mail.ru_counter": {
+ "name": "Mail.Ru Counter",
+ "categoryId": 2,
+ "url": "http://mail.ru/",
+ "companyId": "vk"
+ },
+ "mail.ru_group": {
+ "name": "Mail.Ru Group",
+ "categoryId": 7,
+ "url": "http://mail.ru/",
+ "companyId": "vk"
}
},
"trackerDomains": {
@@ -128,4 +169,4 @@
"azure.com": "azure",
"bttn.io": "button"
}
-}
\ No newline at end of file
+}
diff --git a/client/src/helpers/trackers/whotracksme.json b/client/src/helpers/trackers/whotracksme.json
index 26e4327d..bc8e7fed 100644
--- a/client/src/helpers/trackers/whotracksme.json
+++ b/client/src/helpers/trackers/whotracksme.json
@@ -1,5 +1,5 @@
{
- "timeUpdated": "2021-12-15T12:50:00.512Z",
+ "timeUpdated": "2022-10-15T00:14:03.765Z",
"categories": {
"0": "audio_video_player",
"1": "comments",
@@ -14,15963 +14,19192 @@
"10": "hosting",
"11": "unknown",
"12": "extensions",
- "13": "email"
+ "13": "email",
+ "14": "consent",
+ "15": "telemetry"
},
"trackers": {
"163": {
"name": "163",
"categoryId": 4,
- "url": "http://www.163.com/"
+ "url": "http://www.163.com/",
+ "companyId": "163"
},
"1000mercis": {
"name": "1000mercis",
"categoryId": 6,
- "url": "http://www.1000mercis.com/"
+ "url": "http://www.1000mercis.com/",
+ "companyId": "1000mercis"
},
"161media": {
"name": "Platform161",
"categoryId": 4,
- "url": "https://platform161.com/"
+ "url": "https://platform161.com/",
+ "companyId": "platform161"
},
"1822direkt.de": {
"name": "1822direkt.de",
"categoryId": 8,
- "url": "https://www.1822direkt.de/"
+ "url": "https://www.1822direkt.de/",
+ "companyId": null
},
"1dmp.io": {
"name": "1DMP",
"categoryId": 4,
- "url": "https://1dmp.io/"
+ "url": "https://1dmp.io/",
+ "companyId": "1dmp"
},
"1plusx": {
"name": "1plusX",
"categoryId": 6,
- "url": "https://www.1plusx.com/"
+ "url": "https://www.1plusx.com/",
+ "companyId": "1plusx"
},
"1sponsor": {
"name": "1sponsor",
"categoryId": 4,
- "url": "http://fr.1sponsor.com/"
+ "url": "http://fr.1sponsor.com/",
+ "companyId": "1sponsor"
},
"1tag": {
"name": "1tag",
"categoryId": 6,
- "url": "http://www.dentsuaegisnetwork.com/"
+ "url": "http://www.dentsuaegisnetwork.com/",
+ "companyId": "dentsu_aegis_network"
},
"1und1": {
"name": "1&1 Internet",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"24-ads.com": {
"name": "24-ADS GmbH",
"categoryId": 4,
- "url": "http://www.24-ads.com/"
+ "url": "http://www.24-ads.com/",
+ "companyId": null
},
"24_7": {
"name": "[24]7",
"categoryId": 2,
- "url": "http://www.247-inc.com/"
+ "url": "http://www.247-inc.com/",
+ "companyId": "24_7"
},
"24log": {
"name": "24log",
"categoryId": 6,
- "url": "http://24log.ru/"
+ "url": "http://24log.ru/",
+ "companyId": "24log"
},
"24smi": {
"name": "24СМИ",
"categoryId": 8,
- "url": "https://24smi.org/"
+ "url": "https://24smi.org/",
+ "companyId": null
},
"2leep": {
"name": "2leep",
"categoryId": 4,
- "url": "http://2leep.com/"
+ "url": "http://2leep.com/",
+ "companyId": "2leep"
},
"33across": {
"name": "33Across",
"categoryId": 4,
- "url": "http://33across.com/"
+ "url": "http://33across.com/",
+ "companyId": "33across"
},
"3dstats": {
"name": "3DStats",
"categoryId": 6,
- "url": "http://www.3dstats.com/"
+ "url": "http://www.3dstats.com/",
+ "companyId": "3dstats"
},
"4chan": {
"name": "4Chan",
"categoryId": 8,
- "url": "https://www.4chan.org/"
+ "url": "https://www.4chan.org/",
+ "companyId": null
},
"4finance_com": {
"name": "4finance.com",
"categoryId": 2,
- "url": "http://4finance.com/"
+ "url": "http://4finance.com/",
+ "companyId": null
},
"4w_marketplace": {
"name": "4w Marketplace",
"categoryId": 4,
- "url": "http://www.4wmarketplace.com/"
+ "url": "http://www.4wmarketplace.com/",
+ "companyId": "4w_marketplace"
},
"500friends": {
"name": "500friends",
"categoryId": 2,
- "url": "http://500friends.com/"
+ "url": "http://500friends.com/",
+ "companyId": "500friends"
},
"51.la": {
"name": "51.La",
"categoryId": 6,
- "url": "http://www.51.la/"
+ "url": "http://www.51.la/",
+ "companyId": "51.la"
},
"5min_media": {
"name": "5min Media",
"categoryId": 0,
- "url": "http://www.5min.com/"
+ "url": "http://www.5min.com/",
+ "companyId": "verizon"
},
"6sense": {
"name": "6Sense",
"categoryId": 6,
- "url": "http://home.grepdata.com"
+ "url": "http://home.grepdata.com",
+ "companyId": "6sense"
},
"77tracking": {
"name": "77Tracking",
"categoryId": 6,
- "url": "http://www.77agency.com/"
+ "url": "http://www.77agency.com/",
+ "companyId": "77agency"
},
"7tv.de": {
"name": "7tv.de",
"categoryId": 0,
- "url": "https://www.7tv.de/"
+ "url": "https://www.7tv.de/",
+ "companyId": null
},
"888media": {
"name": "888media",
"categoryId": 4,
- "url": "http://888media.net/"
+ "url": "http://888media.net/",
+ "companyId": "888_media"
},
"8digits": {
"name": "8digits",
"categoryId": 6,
- "url": "http://8digits.com/"
+ "url": "http://8digits.com/",
+ "companyId": "8digits"
},
"94j7afz2nr.xyz": {
"name": "94j7afz2nr.xyz",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"99stats": {
"name": "99stats",
"categoryId": 6,
- "url": "http://www.99stats.com/"
+ "url": "http://www.99stats.com/",
+ "companyId": "99stats"
},
"a3cloud_net": {
"name": "a3cloud.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"a8": {
"name": "A8",
"categoryId": 4,
- "url": "http://www.a8.net/"
+ "url": "http://www.a8.net/",
+ "companyId": "a8"
},
"aaxads.com": {
"name": "Acceptable Ads Exchange",
"categoryId": 4,
- "url": "https://aax.media/"
+ "url": "https://aax.media/",
+ "companyId": null
},
"ab_tasty": {
"name": "AB Tasty",
"categoryId": 6,
- "url": "https://en.abtasty.com"
+ "url": "https://en.abtasty.com",
+ "companyId": "ab_tasty"
},
"ablida": {
"name": "ablida",
"categoryId": 4,
- "url": "https://www.ablida.de/"
+ "url": "https://www.ablida.de/",
+ "companyId": null
},
"accelia": {
"name": "Accelia",
"categoryId": 4,
- "url": "http://www.durasite.net/"
+ "url": "http://www.durasite.net/",
+ "companyId": "accelia"
},
"accengage": {
"name": "Accengage",
"categoryId": 4,
- "url": "https://www.accengage.com/"
+ "url": "https://www.accengage.com/",
+ "companyId": "accengage"
},
"accessanalyzer": {
"name": "AccessAnalyzer",
"categoryId": 6,
- "url": "http://ax.xrea.com/"
+ "url": "http://ax.xrea.com/",
+ "companyId": "accessanalyzer"
},
"accesstrade": {
"name": "AccessTrade",
"categoryId": 4,
- "url": "http://accesstrade.net/"
+ "url": "http://accesstrade.net/",
+ "companyId": "accesstrade"
},
"accord_group": {
"name": "Accord Group",
"categoryId": 4,
- "url": "http://www.accordgroup.co.uk/"
+ "url": "http://www.accordgroup.co.uk/",
+ "companyId": "accord_group"
},
"accordant_media": {
"name": "Accordant Media",
"categoryId": 4,
- "url": "http://www.accordantmedia.com/"
+ "url": "http://www.accordantmedia.com/",
+ "companyId": "accordant_media"
},
"accuen_media": {
"name": "Accuen Media",
"categoryId": 4,
- "url": "http://www.accuenmedia.com/"
+ "url": "http://www.accuenmedia.com/",
+ "companyId": "accuen_media"
},
"acestream.net": {
"name": "ActStream",
"categoryId": 12,
- "url": "http://www.acestream.org/"
+ "url": "http://www.acestream.org/",
+ "companyId": null
},
"acint.net": {
"name": "Artificial Computation Intelligence",
"categoryId": 6,
- "url": "https://www.acint.net/"
+ "url": "https://www.acint.net/",
+ "companyId": "acint"
},
"acloudimages": {
"name": "Acloudimages",
"categoryId": 4,
- "url": "http://adsterra.com"
+ "url": "http://adsterra.com",
+ "companyId": "adsterra"
},
"acpm.fr": {
"name": "ACPM",
"categoryId": 6,
- "url": "http://www.acpm.fr/"
+ "url": "http://www.acpm.fr/",
+ "companyId": null
},
"acquia.com": {
"name": "Acquia",
"categoryId": 6,
- "url": "https://www.acquia.com/"
+ "url": "https://www.acquia.com/",
+ "companyId": null
},
"acrweb": {
"name": "ACRWEB",
"categoryId": 7,
- "url": "http://www.ziyu.net/"
+ "url": "http://www.ziyu.net/",
+ "companyId": "acrweb"
},
"actionpay": {
"name": "actionpay",
"categoryId": 4,
- "url": "http://actionpay.ru/"
+ "url": "http://actionpay.ru/",
+ "companyId": "actionpay"
},
"active_agent": {
"name": "Active Agent",
"categoryId": 4,
- "url": "http://www.active-agent.com/"
+ "url": "http://www.active-agent.com/",
+ "companyId": "active_agent"
},
"active_campaign": {
"name": "Active Campaign",
"categoryId": 6,
- "url": "https://www.activecampaign.com"
+ "url": "https://www.activecampaign.com",
+ "companyId": "active_campaign"
},
"active_performance": {
"name": "Active Performance",
"categoryId": 4,
- "url": "http://www.active-performance.de/"
+ "url": "http://www.active-performance.de/",
+ "companyId": "active_performance"
},
"activeconversion": {
"name": "ActiveConversion",
"categoryId": 4,
- "url": "http://www.activeconversion.com/"
+ "url": "http://www.activeconversion.com/",
+ "companyId": "activeconversion"
},
"activecore": {
"name": "activecore",
"categoryId": 6,
- "url": "http://activecore.jp/"
+ "url": "http://activecore.jp/",
+ "companyId": "activecore"
},
"activemeter": {
"name": "ActiveMeter",
"categoryId": 4,
- "url": "http://www.activemeter.com/"
+ "url": "http://www.activemeter.com/",
+ "companyId": "activeconversion"
},
"activengage": {
"name": "ActivEngage",
"categoryId": 2,
- "url": "http://www.activengage.com"
+ "url": "http://www.activengage.com",
+ "companyId": "activengage"
},
"acton": {
"name": "Act-On Beacon",
"categoryId": 4,
- "url": "http://www.actonsoftware.com/"
+ "url": "http://www.actonsoftware.com/",
+ "companyId": "act-on"
},
"acuity_ads": {
"name": "Acuity Ads",
"categoryId": 4,
- "url": "http://www.acuityads.com/"
+ "url": "http://www.acuityads.com/",
+ "companyId": "acuity_ads"
},
"acxiom": {
"name": "Acxiom",
"categoryId": 4,
- "url": "http://www.acxiom.com"
+ "url": "http://www.acxiom.com",
+ "companyId": "acxiom"
},
"ad-blocker.org": {
"name": "ad-blocker.org",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"ad-center": {
"name": "Ad-Center",
"categoryId": 6,
- "url": "http://www.ad-center.com"
+ "url": "http://www.ad-center.com",
+ "companyId": "ad-center"
},
"ad-delivery.net": {
"name": "ad-delivery.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"ad-sys": {
"name": "Ad-Sys",
"categoryId": 4,
- "url": "http://www.ad-sys.com/"
+ "url": "http://www.ad-sys.com/",
+ "companyId": "ad-sys"
},
"ad.agio": {
"name": "Ad.agio",
"categoryId": 4,
- "url": "http://neodatagroup.com/"
+ "url": "http://neodatagroup.com/",
+ "companyId": "neodata"
},
"ad2click": {
"name": "Ad2Click",
"categoryId": 4,
- "url": "http://www.ad2click.com/"
+ "url": "http://www.ad2click.com/",
+ "companyId": "ad2click_media"
},
"ad2games": {
"name": "ad2games",
"categoryId": 4,
- "url": "http://web.ad2games.com/"
+ "url": "http://web.ad2games.com/",
+ "companyId": "ad2games"
},
"ad360": {
"name": "Ad360",
"categoryId": 4,
- "url": "http://ad360.vn"
+ "url": "http://ad360.vn",
+ "companyId": "ad360"
},
"ad4game": {
"name": "ad4game",
"categoryId": 4,
- "url": "http://www.ad4game.com/"
+ "url": "http://www.ad4game.com/",
+ "companyId": "ad4game"
},
"ad4mat": {
"name": "ad4mat",
"categoryId": 4,
- "url": "http://ad4mat.info"
+ "url": "http://ad4mat.info",
+ "companyId": "ad4mat"
},
"ad6media": {
"name": "ad6media",
"categoryId": 4,
- "url": "https://www.ad6media.fr/"
+ "url": "https://www.ad6media.fr/",
+ "companyId": "ad6media"
},
"ad_decisive": {
"name": "Ad Decisive",
"categoryId": 4,
- "url": "http://www.lagardere-global-advertising.com/"
+ "url": "http://www.lagardere-global-advertising.com/",
+ "companyId": "lagardere_advertising"
},
"ad_dynamo": {
"name": "Ad Dynamo",
"categoryId": 4,
- "url": "http://www.addynamo.com/"
+ "url": "http://www.addynamo.com/",
+ "companyId": "ad_dynamo"
},
"ad_ebis": {
"name": "AD EBiS",
"categoryId": 4,
- "url": "http://www.ebis.ne.jp/en/"
+ "url": "http://www.ebis.ne.jp/en/",
+ "companyId": "ad_ebis"
},
"ad_lightning": {
"name": "Ad Lightning",
"categoryId": 4,
- "url": "https://www.adlightning.com/"
+ "url": "https://www.adlightning.com/",
+ "companyId": "ad_lightning"
},
"ad_magnet": {
"name": "Ad Magnet",
"categoryId": 4,
- "url": "http://www.admagnet.com/"
+ "url": "http://www.admagnet.com/",
+ "companyId": "ad_magnet"
},
"ad_spirit": {
"name": "Ad Spirit",
"categoryId": 4,
- "url": "http://www.adspirit.de"
+ "url": "http://www.adspirit.de",
+ "companyId": "adspirit"
},
"adac_de": {
"name": "adac.de",
"categoryId": 8,
- "url": "http://adac.de/"
+ "url": "http://adac.de/",
+ "companyId": null
},
"adacado": {
"name": "Adacado",
"categoryId": 4,
- "url": "http://www.adacado.com/"
+ "url": "http://www.adacado.com/",
+ "companyId": "adacado"
},
"adadyn": {
"name": "Adadyn",
"categoryId": 4,
- "url": "http://ozonemedia.com/index.html"
+ "url": "http://ozonemedia.com/index.html",
+ "companyId": "adadyn"
},
"adality_gmbh": {
"name": "adality GmbH",
"categoryId": 4,
- "url": "https://www.arvato.com/"
+ "url": "https://www.arvato.com/",
+ "companyId": "arvato"
},
"adalliance.io": {
"name": "Ad Alliance",
"categoryId": 4,
- "url": "https://www.ad-alliance.de/"
+ "url": "https://www.ad-alliance.de/",
+ "companyId": null
},
"adalyser.com": {
"name": "Adalyser",
"categoryId": 6,
- "url": "https://www.adalyser.com/"
+ "url": "https://www.adalyser.com/",
+ "companyId": "onesoon"
},
"adaos": {
"name": "ADAOS",
"categoryId": 4,
- "url": "http://www.24-interactive.com"
+ "url": "http://www.24-interactive.com",
+ "companyId": "24_interactive"
},
"adap.tv": {
"name": "Adap.tv",
"categoryId": 4,
- "url": "http://www.adap.tv/"
+ "url": "http://www.adap.tv/",
+ "companyId": "verizon"
},
"adaptiveblue_smartlinks": {
"name": "AdaptiveBlue SmartLinks",
"categoryId": 2,
- "url": "http://www.adaptiveblue.com/smartlinks.html"
+ "url": "http://www.adaptiveblue.com/smartlinks.html",
+ "companyId": "telfie"
},
"adara_analytics": {
"name": "ADARA Analytics",
"categoryId": 4,
- "url": "http://www.adaramedia.com/"
+ "url": "http://www.adaramedia.com/",
+ "companyId": "adara_analytics"
},
"adasia_holdings": {
"name": "AdAsia Holdings",
"categoryId": 4,
- "url": "https://adasiaholdings.com/"
+ "url": "https://adasiaholdings.com/",
+ "companyId": "adasia_holdings"
},
"adbetclickin.pink": {
"name": "adbetnet",
"categoryId": 4,
- "url": "http://adbetnet.com/"
+ "url": "http://adbetnet.com/",
+ "companyId": null
},
"adbetnet.com": {
"name": "adbetnet",
"categoryId": 4,
- "url": "https://adbetnet.com/"
+ "url": "https://adbetnet.com/",
+ "companyId": null
},
"adblade.com": {
"name": "Adblade",
"categoryId": 4,
- "url": "https://adblade.com/"
+ "url": "https://adblade.com/",
+ "companyId": "adblade"
},
"adbooth": {
"name": "Adbooth",
"categoryId": 4,
- "url": "http://www.adbooth.com/"
+ "url": "http://www.adbooth.com/",
+ "companyId": "adbooth_media_group"
},
"adbox": {
"name": "AdBox",
"categoryId": 4,
- "url": "http://www.adbox.lv/"
+ "url": "http://www.adbox.lv/",
+ "companyId": "adbox"
},
"adbrain": {
"name": "Adbrain",
"categoryId": 6,
- "url": "https://www.adbrain.com/"
+ "url": "https://www.adbrain.com/",
+ "companyId": "adbrain"
},
"adbrite": {
"name": "AdBrite",
"categoryId": 4,
- "url": "http://www.adbrite.com/"
+ "url": "http://www.adbrite.com/",
+ "companyId": "centro"
},
"adbull": {
"name": "AdBull",
"categoryId": 4,
- "url": "http://www.adbull.com/"
+ "url": "http://www.adbull.com/",
+ "companyId": "adbull"
},
"adbutler": {
"name": "AdButler",
"categoryId": 4,
- "url": "https://www.adbutler.com/d"
+ "url": "https://www.adbutler.com/d",
+ "companyId": "sparklit_networks"
},
"adc_media": {
"name": "ad:C media",
"categoryId": 4,
- "url": "http://www.adcmedia.de/en/"
+ "url": "http://www.adcmedia.de/en/",
+ "companyId": "ad:c_media"
},
"adcash": {
"name": "Adcash",
"categoryId": 4,
- "url": "http://www.adcash.com"
+ "url": "http://www.adcash.com",
+ "companyId": "adcash"
},
"adchakra": {
"name": "AdChakra",
"categoryId": 6,
- "url": "http://adchakra.com/"
+ "url": "http://adchakra.com/",
+ "companyId": "adchakra"
},
"adchina": {
"name": "AdChina",
"categoryId": 4,
- "url": "http://www.adchina.com/"
+ "url": "http://www.adchina.com/",
+ "companyId": "alibaba"
},
"adcito": {
"name": "Adcito",
"categoryId": 4,
- "url": "http://adcito.com/"
+ "url": "http://adcito.com/",
+ "companyId": "adcito"
},
"adclear": {
"name": "AdClear",
"categoryId": 4,
- "url": "http://www.adclear.de/en/home.html"
+ "url": "http://www.adclear.de/en/home.html",
+ "companyId": "adclear"
},
"adclerks": {
"name": "Adclerks",
"categoryId": 4,
- "url": "https://adclerks.com/"
+ "url": "https://adclerks.com/",
+ "companyId": "adclerks"
},
"adclickmedia": {
"name": "AdClickMedia",
"categoryId": 4,
- "url": "http://www.adclickmedia.com/"
+ "url": "http://www.adclickmedia.com/",
+ "companyId": "adclickmedia"
},
"adclickzone": {
"name": "AdClickZone",
"categoryId": 4,
- "url": "http://www.adclickzone.com/"
+ "url": "http://www.adclickzone.com/",
+ "companyId": "adclickzone"
},
"adcloud": {
"name": "adcloud",
"categoryId": 4,
- "url": "https://ad-cloud.jp"
+ "url": "https://ad-cloud.jp",
+ "companyId": "adcloud"
},
"adcolony": {
"name": "AdColony",
"categoryId": 4,
- "url": "thttp://www.admarvel.com/"
+ "url": "thttp://www.admarvel.com/",
+ "companyId": "adcolony"
},
"adconion": {
"name": "Adconion",
"categoryId": 4,
- "url": "http://www.adconion.com/"
+ "url": "http://www.adconion.com/",
+ "companyId": "singtel"
},
"adcrowd": {
"name": "Adcrowd",
"categoryId": 4,
- "url": "https://www.adcrowd.com"
+ "url": "https://www.adcrowd.com",
+ "companyId": "adcrowd"
},
"adcurve": {
"name": "AdCurve",
"categoryId": 4,
- "url": "http://www.shop2market.com/"
+ "url": "http://www.shop2market.com/",
+ "companyId": "adcurve"
},
"add_to_calendar": {
"name": "Add To Calendar",
"categoryId": 2,
- "url": "http://addtocalendar.com/"
+ "url": "http://addtocalendar.com/",
+ "companyId": "addtocalendar"
},
"addaptive": {
"name": "Addaptive",
"categoryId": 4,
- "url": "http://www.datapointmedia.com/"
+ "url": "http://www.datapointmedia.com/",
+ "companyId": "addaptive"
},
"addefend": {
"name": "AdDefend",
"categoryId": 4,
- "url": "https://www.addefend.com/"
+ "url": "https://www.addefend.com/",
+ "companyId": null
},
"addfreestats": {
"name": "AddFreeStats",
"categoryId": 6,
- "url": "http://www.addfreestats.com/"
+ "url": "http://www.addfreestats.com/",
+ "companyId": "3dstats"
},
"addinto": {
"name": "AddInto",
"categoryId": 2,
- "url": "http://www.addinto.com/"
+ "url": "http://www.addinto.com/",
+ "companyId": "addinto"
},
"addshoppers": {
"name": "AddShoppers",
"categoryId": 7,
- "url": "http://www.addshoppers.com/"
+ "url": "http://www.addshoppers.com/",
+ "companyId": "addshoppers"
},
"addthis": {
"name": "AddThis",
"categoryId": 4,
- "url": "http://www.addthis.com/"
+ "url": "http://www.addthis.com/",
+ "companyId": "oracle"
},
"addvalue": {
"name": "Addvalue",
"categoryId": 6,
- "url": "http://www.addvalue.de/en/"
+ "url": "http://www.addvalue.de/en/",
+ "companyId": "addvalue.de"
},
"addyon": {
"name": "AddyON",
"categoryId": 4,
- "url": "http://www.addyon.com/homepage.php"
+ "url": "http://www.addyon.com/homepage.php",
+ "companyId": "addyon"
},
"adeasy": {
"name": "AdEasy",
"categoryId": 4,
- "url": "http://www.adeasy.ru/"
+ "url": "http://www.adeasy.ru/",
+ "companyId": "adeasy"
},
"adelphic": {
"name": "Adelphic",
"categoryId": 6,
- "url": "http://www.adelphic.com/"
+ "url": "http://www.adelphic.com/",
+ "companyId": "adelphic"
},
"adengage": {
"name": "AdEngage",
"categoryId": 4,
- "url": "http://www.adengage.com"
+ "url": "http://www.adengage.com",
+ "companyId": "synacor"
},
"adespresso": {
"name": "AdEspresso",
"categoryId": 4,
- "url": "http://adespresso.com"
+ "url": "http://adespresso.com",
+ "companyId": "adespresso"
},
"adexcite": {
"name": "AdExcite",
"categoryId": 4,
- "url": "http://adexcite.com"
+ "url": "http://adexcite.com",
+ "companyId": "adexcite"
},
"adextent": {
"name": "AdExtent",
"categoryId": 4,
- "url": "http://www.adextent.com/"
+ "url": "http://www.adextent.com/",
+ "companyId": "adextent"
},
"adf.ly": {
"name": "AdF.ly",
"categoryId": 4,
- "url": "http://adf.ly/"
+ "url": "http://adf.ly/",
+ "companyId": "adf.ly"
},
"adfalcon": {
"name": "AdFalcon",
"categoryId": 4,
- "url": "http://www.adfalcon.com/"
+ "url": "http://www.adfalcon.com/",
+ "companyId": "adfalcon"
},
"adfocus": {
"name": "AdFocus",
"categoryId": 4,
- "url": "http://adfoc.us/"
+ "url": "http://adfoc.us/",
+ "companyId": "adfoc.us"
},
"adforgames": {
"name": "AdForGames",
"categoryId": 4,
- "url": "http://www.adforgames.com/"
+ "url": "http://www.adforgames.com/",
+ "companyId": "adforgames"
},
"adform": {
"name": "Adform",
"categoryId": 4,
- "url": "http://www.adform.com"
+ "url": "http://www.adform.com",
+ "companyId": "adform"
},
"adfox": {
"name": "AdFox",
"categoryId": 4,
- "url": "http://adfox.ru"
+ "url": "http://adfox.ru",
+ "companyId": "yandex"
},
"adfreestyle": {
"name": "adFreestyle",
"categoryId": 4,
- "url": "http://www.adfreestyle.pl/"
+ "url": "http://www.adfreestyle.pl/",
+ "companyId": "adfreestyle"
},
"adfront": {
"name": "AdFront",
"categoryId": 4,
- "url": "http://buysellads.com/"
+ "url": "http://buysellads.com/",
+ "companyId": "buysellads.com"
},
"adfrontiers": {
"name": "AdFrontiers",
"categoryId": 4,
- "url": "http://www.adfrontiers.com/"
+ "url": "http://www.adfrontiers.com/",
+ "companyId": "adfrontiers"
},
"adgear": {
"name": "AdGear",
"categoryId": 4,
- "url": "http://adgear.com/"
+ "url": "http://adgear.com/",
+ "companyId": "samsung"
},
"adgebra": {
"name": "Adgebra",
"categoryId": 4,
- "url": "https://adgebra.in/"
+ "url": "https://adgebra.in/",
+ "companyId": "adgebra"
},
"adgenie": {
"name": "adGENIE",
"categoryId": 4,
- "url": "http://www.adgenie.co.uk/"
+ "url": "http://www.adgenie.co.uk/",
+ "companyId": "ve"
},
"adgile": {
"name": "Adgile",
"categoryId": 4,
- "url": "http://www.adgile.com/"
+ "url": "http://www.adgile.com/",
+ "companyId": "adgile_media"
},
"adglare.net": {
"name": "Adglare",
"categoryId": 4,
- "url": "https://www.adglare.com/"
+ "url": "https://www.adglare.com/",
+ "companyId": null
},
"adglue": {
"name": "Adglue",
"categoryId": 4,
- "url": "http://admans.de/de.html"
+ "url": "http://admans.de/de.html",
+ "companyId": "admans"
},
"adgoal": {
"name": "adgoal",
"categoryId": 4,
- "url": "http://www.adgoal.de/"
+ "url": "http://www.adgoal.de/",
+ "companyId": "adgoal"
},
"adgorithms": {
"name": "Adgorithms",
"categoryId": 4,
- "url": "http://www.adgorithms.com/"
+ "url": "http://www.adgorithms.com/",
+ "companyId": "albert"
},
"adgoto": {
"name": "ADGoto",
"categoryId": 4,
- "url": "http://adgoto.com/"
+ "url": "http://adgoto.com/",
+ "companyId": "adgoto"
},
"adguard": {
"name": "Adguard",
"categoryId": 12,
- "url": "https://adguard.com/"
+ "url": "https://adguard.com/",
+ "companyId": null
},
"adhands": {
"name": "AdHands",
"categoryId": 4,
- "url": "http://promo.adhands.ru/"
+ "url": "http://promo.adhands.ru/",
+ "companyId": "adhands"
},
"adhese": {
"name": "Adhese",
"categoryId": 4,
- "url": "http://adhese.com"
+ "url": "http://adhese.com",
+ "companyId": "adhese"
},
"adhitz": {
"name": "AdHitz",
"categoryId": 4,
- "url": "http://www.adhitz.com/"
+ "url": "http://www.adhitz.com/",
+ "companyId": "adhitz"
},
"adhood": {
"name": "adhood",
"categoryId": 4,
- "url": "http://www.adhood.com/"
+ "url": "http://www.adhood.com/",
+ "companyId": "adhood"
},
"adify": {
"name": "Adify",
"categoryId": 4,
- "url": "http://www.adify.com/"
+ "url": "http://www.adify.com/",
+ "companyId": "cox_enterpries"
},
"adikteev": {
"name": "Adikteev",
"categoryId": 4,
- "url": "http://www.adikteev.com/"
+ "url": "http://www.adikteev.com/",
+ "companyId": "adikteev"
},
"adimpact": {
"name": "Adimpact",
"categoryId": 4,
- "url": "http://www.adimpact.com/"
+ "url": "http://www.adimpact.com/",
+ "companyId": "adimpact"
},
"adinch": {
"name": "Adinch",
"categoryId": 4,
- "url": "http://adinch.com/"
+ "url": "http://adinch.com/",
+ "companyId": "adinch"
},
"adition": {
"name": "Adition",
"categoryId": 4,
- "url": "http://en.adition.com/"
+ "url": "http://en.adition.com/",
+ "companyId": "prosieben_sat1"
},
"adjal": {
"name": "Adjal",
"categoryId": 4,
- "url": "http://adjal.com/"
+ "url": "http://adjal.com/",
+ "companyId": "marketing_adjal"
},
"adjs": {
"name": "ADJS",
"categoryId": 4,
- "url": "https://github.com/widgital/adjs"
+ "url": "https://github.com/widgital/adjs",
+ "companyId": "adjs"
},
"adjug": {
"name": "AdJug",
"categoryId": 4,
- "url": "http://www.adjug.com/"
+ "url": "http://www.adjug.com/",
+ "companyId": "adjug"
},
"adjust": {
"name": "Adjust",
"categoryId": 6,
- "url": "https://www.adjust.com/"
+ "url": "https://www.adjust.com/",
+ "companyId": "adjust"
},
"adk2": {
"name": "adk2",
"categoryId": 4,
- "url": "http://www.adk2.com/"
+ "url": "http://www.adk2.com/",
+ "companyId": "adk2_plymedia"
},
"adklip": {
"name": "adklip",
"categoryId": 4,
- "url": "http://adklip.com"
+ "url": "http://adklip.com",
+ "companyId": "adklip"
},
"adknowledge": {
"name": "Adknowledge",
"categoryId": 4,
- "url": "http://www.adknowledge.com/"
+ "url": "http://www.adknowledge.com/",
+ "companyId": "adknowledge"
},
"adkontekst": {
"name": "Adkontekst",
"categoryId": 4,
- "url": "http://www.en.adkontekst.pl/"
+ "url": "http://www.en.adkontekst.pl/",
+ "companyId": "adkontekst"
},
"adkontekst.pl": {
"name": "Adkontekst",
"categoryId": 4,
- "url": "http://netsprint.eu/"
+ "url": "http://netsprint.eu/",
+ "companyId": "netsprint"
},
"adlabs": {
"name": "AdLabs",
"categoryId": 4,
- "url": "https://www.adlabs.ru/"
+ "url": "https://www.adlabs.ru/",
+ "companyId": "adlabs"
},
"adlantic": {
"name": "AdLantic",
"categoryId": 4,
- "url": "http://www.adlantic.nl/"
+ "url": "http://www.adlantic.nl/",
+ "companyId": "adlantic_online_advertising"
},
"adlantis": {
"name": "AdLantis",
"categoryId": 4,
- "url": "http://www.adlantis.jp/"
+ "url": "http://www.adlantis.jp/",
+ "companyId": "adlantis"
},
"adless": {
"name": "Adless",
"categoryId": 4,
- "url": "https://www.adless.io/"
+ "url": "https://www.adless.io/",
+ "companyId": "adless"
},
"adlive_header_bidding": {
"name": "Adlive Header Bidding",
"categoryId": 4,
- "url": "http://adlive.io/"
+ "url": "http://adlive.io/",
+ "companyId": "adlive"
},
"adloox": {
"name": "Adloox",
"categoryId": 4,
- "url": "http://www.adloox.com"
+ "url": "http://www.adloox.com",
+ "companyId": "adloox"
},
"admachine": {
"name": "AdMachine",
"categoryId": 4,
- "url": "https://admachine.co/"
+ "url": "https://admachine.co/",
+ "companyId": null
},
"adman": {
"name": "ADMAN",
"categoryId": 4,
- "url": "http://www.adman.gr/"
+ "url": "http://www.adman.gr/",
+ "companyId": "adman"
},
"adman_media": {
"name": "ADman Media",
"categoryId": 4,
- "url": "http://www.admanmedia.com/"
+ "url": "http://www.admanmedia.com/",
+ "companyId": "ad_man_media"
},
"admantx.com": {
"name": "ADmantX",
"categoryId": 4,
- "url": "http://www.admantx.com/"
+ "url": "http://www.admantx.com/",
+ "companyId": "expert_system_spa"
},
"admaster": {
"name": "AdMaster",
"categoryId": 4,
- "url": "http://admaster.net"
+ "url": "http://admaster.net",
+ "companyId": "admaster"
},
"admaster.cn": {
"name": "AdMaster.cn",
"categoryId": 4,
- "url": "http://www.admaster.com.cn/"
+ "url": "http://www.admaster.com.cn/",
+ "companyId": "admaster"
},
"admatic": {
"name": "Admatic",
"categoryId": 4,
- "url": "http://www.admatic.com.tr/#1page"
+ "url": "http://www.admatic.com.tr/#1page",
+ "companyId": "admatic"
},
"admatrix": {
"name": "Admatrix",
"categoryId": 4,
- "url": "https://admatrix.jp/login#block01"
+ "url": "https://admatrix.jp/login#block01",
+ "companyId": "admatrix"
},
"admax": {
"name": "Admax",
"categoryId": 4,
- "url": "http://www.admaxnetwork.com/index.php"
+ "url": "http://www.admaxnetwork.com/index.php",
+ "companyId": "komli"
},
"admaxim": {
"name": "AdMaxim",
"categoryId": 4,
- "url": "http://admaxim.com/"
+ "url": "http://admaxim.com/",
+ "companyId": "admaxim"
},
"admaya": {
"name": "Admaya",
"categoryId": 4,
- "url": "http://www.admaya.in/"
+ "url": "http://www.admaya.in/",
+ "companyId": "admaya"
},
"admedia": {
"name": "AdMedia",
"categoryId": 4,
- "url": "http://admedia.com/"
+ "url": "http://admedia.com/",
+ "companyId": "admedia"
},
"admedo_com": {
"name": "Admedo",
"categoryId": 4,
- "url": "http://admedo.com/"
+ "url": "http://admedo.com/",
+ "companyId": "admedo"
},
"admeira.ch": {
"name": "AdMeira",
"categoryId": 4,
- "url": "http://admeira.ch/"
+ "url": "http://admeira.ch/",
+ "companyId": "admeira"
},
"admeld": {
"name": "AdMeld",
"categoryId": 4,
- "url": "http://www.admeld.com"
+ "url": "http://www.admeld.com",
+ "companyId": "google"
},
"admeo": {
"name": "Admeo",
"categoryId": 4,
- "url": "http://admeo.ru/"
+ "url": "http://admeo.ru/",
+ "companyId": "admeo.ru"
},
"admeta": {
"name": "Admeta",
"categoryId": 4,
- "url": "http://www.admeta.com/"
+ "url": "http://www.admeta.com/",
+ "companyId": "admeta"
},
"admicro": {
"name": "AdMicro",
"categoryId": 4,
- "url": "http://www.admicro.vn/"
+ "url": "http://www.admicro.vn/",
+ "companyId": "admicro"
},
"admitad.com": {
"name": "Admitad",
"categoryId": 4,
- "url": "https://www.admitad.com/en/#"
+ "url": "https://www.admitad.com/en/#",
+ "companyId": "admitad"
},
"admixer.net": {
"name": "Admixer",
"categoryId": 4,
- "url": "https://admixer.net/"
+ "url": "https://admixer.net/",
+ "companyId": "admixer"
},
"admized": {
"name": "ADMIZED",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"admo.tv": {
"name": "Admo.tv",
"categoryId": 4,
- "url": "https://admo.tv/"
+ "url": "https://admo.tv/",
+ "companyId": "admo.tv"
},
"admob": {
"name": "AdMob",
"categoryId": 4,
- "url": "http://www.admob.com/"
+ "url": "http://www.admob.com/",
+ "companyId": "google"
},
"admost": {
"name": "adMOST",
"categoryId": 4,
- "url": "http://www.admost.com/"
+ "url": "http://www.admost.com/",
+ "companyId": "admost"
},
"admotion": {
"name": "Admotion",
"categoryId": 4,
- "url": "http://www.admotionus.com/"
+ "url": "http://www.admotionus.com/",
+ "companyId": "admotion"
},
"admulti": {
"name": "ADmulti",
"categoryId": 4,
- "url": "http://admulti.com"
+ "url": "http://admulti.com",
+ "companyId": "admulti"
},
"adnegah": {
"name": "Adnegah",
"categoryId": 4,
- "url": "https://adnegah.net/"
+ "url": "https://adnegah.net/",
+ "companyId": "adnegah"
},
"adnet": {
"name": "Adnet",
"categoryId": 4,
- "url": "http://www.adnet.vn/"
+ "url": "http://www.adnet.vn/",
+ "companyId": "adnet"
},
"adnet.de": {
"name": "adNET.de",
"categoryId": 4,
- "url": "http://www.adnet.de"
+ "url": "http://www.adnet.de",
+ "companyId": "adnet.de"
},
"adnet_media": {
"name": "Adnet Media",
"categoryId": 4,
- "url": "http://www.adnetmedia.lt/"
+ "url": "http://www.adnetmedia.lt/",
+ "companyId": "adnet_media"
},
"adnetwork.net": {
"name": "AdNetwork.net",
"categoryId": 4,
- "url": "http://www.adnetwork.net/"
+ "url": "http://www.adnetwork.net/",
+ "companyId": "adnetwork.net"
},
"adnetworkperformance.com": {
"name": "adnetworkperformance.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"adnexio": {
"name": "AdNexio",
"categoryId": 4,
- "url": "http://adnexio.com/"
+ "url": "http://adnexio.com/",
+ "companyId": "adnexio"
},
"adnium.com": {
"name": "Adnium",
"categoryId": 4,
- "url": "https://adnium.com/"
+ "url": "https://adnium.com/",
+ "companyId": null
},
"adnologies": {
"name": "Adnologies",
"categoryId": 4,
- "url": "http://www.adnologies.com/"
+ "url": "http://www.adnologies.com/",
+ "companyId": "adnologies_gmbh"
},
"adnow": {
"name": "Adnow",
"categoryId": 4,
- "url": "http://adnow.com/"
+ "url": "http://adnow.com/",
+ "companyId": "adnow"
},
"adnymics": {
"name": "Adnymics",
"categoryId": 4,
- "url": "http://adnymics.com/en/"
+ "url": "http://adnymics.com/en/",
+ "companyId": "adnymics"
},
"adobe_audience_manager": {
"name": "Adobe Audience Manager",
"categoryId": 4,
- "url": "http://www.demdex.com/"
+ "url": "http://www.demdex.com/",
+ "companyId": "adobe"
},
"adobe_dynamic_media": {
"name": "Adobe Dynamic Media",
"categoryId": 4,
- "url": "http://www.adobe.com/"
+ "url": "http://www.adobe.com/",
+ "companyId": "adobe"
},
"adobe_dynamic_tag_management": {
"name": "Adobe Dynamic Tag Management",
"categoryId": 5,
- "url": "https://dtm.adobe.com/sign_in"
+ "url": "https://dtm.adobe.com/sign_in",
+ "companyId": "adobe"
},
"adobe_experience_cloud": {
"name": "Adobe Experience Cloud",
"categoryId": 6,
- "url": "https://www.adobe.com/experience-cloud.html"
+ "url": "https://www.adobe.com/experience-cloud.html",
+ "companyId": "adobe"
},
"adobe_login": {
"name": "Adobe Login",
"categoryId": 2,
- "url": "https://www.adobe.com/"
+ "url": "https://www.adobe.com/",
+ "companyId": "adobe"
},
"adobe_tagmanager": {
"name": "Adobe TagManager",
"categoryId": 4,
- "url": "https://www.adobe.com/"
+ "url": "https://www.adobe.com/",
+ "companyId": "adobe"
},
"adobe_test_and_target": {
"name": "Adobe Target",
"categoryId": 4,
- "url": "https://www.adobe.com/marketing/target.html"
+ "url": "https://www.adobe.com/marketing/target.html",
+ "companyId": "adobe"
},
"adobe_typekit": {
"name": "Adobe Typekit",
"categoryId": 5,
- "url": "https://www.adobe.com/"
+ "url": "https://www.adobe.com/",
+ "companyId": "adobe"
},
"adocean": {
"name": "AdOcean",
"categoryId": 4,
- "url": "http://adocean.cz/en"
+ "url": "http://adocean.cz/en",
+ "companyId": "adocean"
},
"adometry": {
"name": "Adometry",
"categoryId": 4,
- "url": "http://www.adometry.com/"
+ "url": "http://www.adometry.com/",
+ "companyId": "google"
},
"adomik": {
"name": "Adomik",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"adon_network": {
"name": "AdOn Network",
"categoryId": 4,
- "url": "http://www.adonnetwork.com/"
+ "url": "http://www.adonnetwork.com/",
+ "companyId": "adon_network"
},
"adonion": {
"name": "AdOnion",
"categoryId": 4,
- "url": "http://www.adonion.com/"
+ "url": "http://www.adonion.com/",
+ "companyId": "adonion"
},
"adonly": {
"name": "AdOnly",
"categoryId": 4,
- "url": "https://gloadmarket.com/"
+ "url": "https://gloadmarket.com/",
+ "companyId": "adonly"
},
"adoperator": {
"name": "AdOperator",
"categoryId": 4,
- "url": "http://www.adoperator.com/start/"
+ "url": "http://www.adoperator.com/start/",
+ "companyId": "adoperator"
},
"adoric": {
"name": "Adoric",
"categoryId": 6,
- "url": "https://adoric.com/"
+ "url": "https://adoric.com/",
+ "companyId": "adoric"
},
"adorika": {
"name": "Adorika",
"categoryId": 4,
- "url": "http://www.adorika.com/"
+ "url": "http://www.adorika.com/",
+ "companyId": "adorika"
},
"adosia": {
"name": "Adosia",
"categoryId": 4,
- "url": "https://adosia.com"
+ "url": "https://adosia.com",
+ "companyId": "adosia"
},
"adotmob.com": {
"name": "Adotmob",
"categoryId": 4,
- "url": "https://adotmob.com/"
+ "url": "https://adotmob.com/",
+ "companyId": "adotmob"
},
"adotube": {
"name": "AdoTube",
"categoryId": 4,
- "url": "http://www.adotube.com"
+ "url": "http://www.adotube.com",
+ "companyId": "exponential_interactive"
},
"adparlor": {
"name": "AdParlor",
"categoryId": 4,
- "url": "http://www.adparlor.com/"
+ "url": "http://www.adparlor.com/",
+ "companyId": "fluent"
},
"adpartner": {
"name": "adpartner",
"categoryId": 4,
- "url": "http://adpartner.pro/"
+ "url": "http://adpartner.pro/",
+ "companyId": "adpartner"
},
"adpeeps": {
"name": "Ad Peeps",
"categoryId": 4,
- "url": "http://www.adpeeps.com/"
+ "url": "http://www.adpeeps.com/",
+ "companyId": "ad_peeps"
},
"adperfect": {
"name": "AdPerfect",
"categoryId": 4,
- "url": "http://www.adperfect.com/"
+ "url": "http://www.adperfect.com/",
+ "companyId": "adperfect"
},
"adperium": {
"name": "AdPerium",
"categoryId": 4,
- "url": "http://www.adperium.com/"
+ "url": "http://www.adperium.com/",
+ "companyId": "adperium"
},
"adpilot": {
"name": "AdPilot",
"categoryId": 4,
- "url": "http://www.adpilotgroup.com/"
+ "url": "http://www.adpilotgroup.com/",
+ "companyId": "adpilot"
},
"adplan": {
"name": "AdPlan",
"categoryId": 4,
- "url": "http://www.adplan.ne.jp/"
+ "url": "http://www.adplan.ne.jp/",
+ "companyId": "adplan"
},
"adplus": {
"name": "ADPLUS",
"categoryId": 4,
- "url": "http://www.adplus.co.id/"
+ "url": "http://www.adplus.co.id/",
+ "companyId": "adplus"
},
"adprofy": {
"name": "AdProfy",
"categoryId": 4,
- "url": "http://adprofy.com/"
+ "url": "http://adprofy.com/",
+ "companyId": "adprofy"
},
"adpulse": {
"name": "AdPulse",
"categoryId": 4,
- "url": "http://adpulse.ir/"
+ "url": "http://adpulse.ir/",
+ "companyId": "adpulse.ir"
},
"adpv": {
"name": "Adpv",
"categoryId": 4,
- "url": "http://www.adpv.com/"
+ "url": "http://www.adpv.com/",
+ "companyId": "adpv"
},
"adreactor": {
"name": "AdReactor",
"categoryId": 4,
- "url": "http://www.adreactor.com/"
+ "url": "http://www.adreactor.com/",
+ "companyId": "adreactor"
},
"adrecord": {
"name": "Adrecord",
"categoryId": 4,
- "url": "http://www.adrecord.com/"
+ "url": "http://www.adrecord.com/",
+ "companyId": "adrecord"
},
"adrecover": {
"name": "AdRecover",
"categoryId": 4,
- "url": "https://www.adrecover.com/"
+ "url": "https://www.adrecover.com/",
+ "companyId": "adpushup"
},
"adresult": {
"name": "ADResult",
"categoryId": 4,
- "url": "http://www.adresult.jp/"
+ "url": "http://www.adresult.jp/",
+ "companyId": "adresult"
},
"adriver": {
"name": "AdRiver",
"categoryId": 4,
- "url": "http://www.adriver.ru/"
+ "url": "http://www.adriver.ru/",
+ "companyId": "ad_river"
},
"adroll": {
"name": "AdRoll",
"categoryId": 4,
- "url": "https://www.adroll.com/"
+ "url": "https://www.adroll.com/",
+ "companyId": "adroll"
},
"adroll_pixel": {
"name": "AdRoll Pixel",
"categoryId": 4,
- "url": "https://www.adroll.com/"
+ "url": "https://www.adroll.com/",
+ "companyId": "adroll"
},
"adroll_roundtrip": {
"name": "AdRoll Roundtrip",
"categoryId": 4,
- "url": "https://www.adroll.com/"
+ "url": "https://www.adroll.com/",
+ "companyId": "adroll"
},
"adrom": {
"name": "adRom",
"categoryId": 4,
- "url": "http://www.adrom.net/"
+ "url": "http://www.adrom.net/",
+ "companyId": null
},
"adru.net": {
"name": "adru.net",
"categoryId": 4,
- "url": "http://adru.net/"
+ "url": "http://adru.net/",
+ "companyId": "adru.net"
},
"adrunnr": {
"name": "AdRunnr",
"categoryId": 4,
- "url": "https://adrunnr.com/"
+ "url": "https://adrunnr.com/",
+ "companyId": "adrunnr"
},
"adsame": {
"name": "Adsame",
"categoryId": 4,
- "url": "http://adsame.com/"
+ "url": "http://adsame.com/",
+ "companyId": "adsame"
},
"adsbookie": {
"name": "AdsBookie",
"categoryId": 4,
- "url": "http://adsbookie.com/"
+ "url": "http://adsbookie.com/",
+ "companyId": null
},
"adscale": {
"name": "AdScale",
"categoryId": 4,
- "url": "http://www.adscale.de/"
+ "url": "http://www.adscale.de/",
+ "companyId": "stroer"
},
"adscience": {
"name": "Adscience",
"categoryId": 4,
- "url": "http://www.adscience.nl/"
+ "url": "http://www.adscience.nl/",
+ "companyId": "adscience"
},
"adsco.re": {
"name": "Adscore",
"categoryId": 4,
- "url": "https://www.adscore.com/"
+ "url": "https://www.adscore.com/",
+ "companyId": null
},
"adsensecamp": {
"name": "AdsenseCamp",
"categoryId": 4,
- "url": "http://adsensecamp.com"
+ "url": "http://adsensecamp.com",
+ "companyId": "adsensecamp"
},
"adserverpub": {
"name": "AdServerPub",
"categoryId": 4,
- "url": "http://www.adserverpub.com/"
+ "url": "http://www.adserverpub.com/",
+ "companyId": "adserverpub"
},
"adservice_media": {
"name": "Adservice Media",
"categoryId": 4,
- "url": "http://www.adservicemedia.com/"
+ "url": "http://www.adservicemedia.com/",
+ "companyId": "adservice_media"
},
"adsfactor": {
"name": "Adsfactor",
"categoryId": 4,
- "url": "http://www.adsfactor.com/"
+ "url": "http://www.adsfactor.com/",
+ "companyId": "pixels_asia"
},
"adside": {
"name": "AdSide",
"categoryId": 4,
- "url": "http://www.adside.com/"
+ "url": "http://www.adside.com/",
+ "companyId": "adside"
},
"adskeeper": {
"name": "AdsKeeper",
"categoryId": 4,
- "url": "http://adskeeper.co.uk/"
+ "url": "http://adskeeper.co.uk/",
+ "companyId": "adskeeper"
},
"adskom": {
"name": "ADSKOM",
"categoryId": 4,
- "url": "http://adskom.com/"
+ "url": "http://adskom.com/",
+ "companyId": "adskom"
},
"adslot": {
"name": "Adslot",
"categoryId": 4,
- "url": "http://www.adslot.com/"
+ "url": "http://www.adslot.com/",
+ "companyId": "adslot"
},
"adsnative": {
"name": "adsnative",
"categoryId": 4,
- "url": "http://www.adsnative.com/"
+ "url": "http://www.adsnative.com/",
+ "companyId": "adsnative"
},
"adsniper.ru": {
"name": "AdSniper",
"categoryId": 4,
- "url": "http://ad-sniper.com/"
+ "url": "http://ad-sniper.com/",
+ "companyId": "adsniper"
},
"adspeed": {
"name": "AdSpeed",
"categoryId": 4,
- "url": "http://www.adspeed.com/"
+ "url": "http://www.adspeed.com/",
+ "companyId": "adspeed"
},
"adspyglass": {
"name": "AdSpyglass",
"categoryId": 4,
- "url": "https://www.adspyglass.com/"
+ "url": "https://www.adspyglass.com/",
+ "companyId": "adspyglass"
},
"adstage": {
"name": "AdStage",
"categoryId": 4,
- "url": "http://www.adstage.io/"
+ "url": "http://www.adstage.io/",
+ "companyId": "adstage"
},
"adstanding": {
"name": "AdStanding",
"categoryId": 4,
- "url": "http://www.adstanding.com/en/"
+ "url": "http://www.adstanding.com/en/",
+ "companyId": "adstanding"
},
"adstars": {
"name": "Adstars",
"categoryId": 4,
- "url": "http://adstars.co.id"
+ "url": "http://adstars.co.id",
+ "companyId": "adstars"
},
"adstir": {
"name": "adstir",
"categoryId": 4,
- "url": "https://en.ad-stir.com/"
+ "url": "https://en.ad-stir.com/",
+ "companyId": "united_inc"
},
"adsupply": {
"name": "AdSupply",
"categoryId": 4,
- "url": "http://www.adsupply.com/"
+ "url": "http://www.adsupply.com/",
+ "companyId": "adsupply"
},
"adswizz": {
"name": "AdsWizz",
"categoryId": 4,
- "url": "http://www.adswizz.com/"
+ "url": "http://www.adswizz.com/",
+ "companyId": "adswizz"
},
"adtaily": {
"name": "AdTaily",
"categoryId": 4,
- "url": "http://www.adtaily.pl/"
+ "url": "http://www.adtaily.pl/",
+ "companyId": "adtaily"
},
"adtarget.me": {
"name": "Adtarget.me",
"categoryId": 4,
- "url": "http://www.adtarget.me/"
+ "url": "http://www.adtarget.me/",
+ "companyId": "adtarget.me"
},
"adtech": {
"name": "ADTECH",
"categoryId": 6,
- "url": "http://www.adtechus.com/"
+ "url": "http://www.adtechus.com/",
+ "companyId": "verizon"
},
"adtegrity": {
"name": "Adtegrity",
"categoryId": 4,
- "url": "http://www.adtegrity.com/"
+ "url": "http://www.adtegrity.com/",
+ "companyId": "adtegrity"
},
"adtelligence.de": {
"name": "Adtelligence",
"categoryId": 4,
- "url": "https://adtelligence.com/"
+ "url": "https://adtelligence.com/",
+ "companyId": null
},
"adtheorent": {
"name": "Adtheorent",
"categoryId": 4,
- "url": "http://adtheorent.com/"
+ "url": "http://adtheorent.com/",
+ "companyId": "adtheorant"
},
"adthink": {
"name": "Adthink",
"categoryId": 4,
- "url": "https://adthink.com/"
+ "url": "https://adthink.com/",
+ "companyId": "adthink"
},
"adtiger": {
"name": "AdTiger",
"categoryId": 4,
- "url": "http://www.adtiger.de/"
+ "url": "http://www.adtiger.de/",
+ "companyId": "adtiger"
},
"adtima": {
"name": "Adtima",
"categoryId": 4,
- "url": "http://adtima.vn/"
+ "url": "http://adtima.vn/",
+ "companyId": "adtima"
},
"adtng.com": {
"name": "adtng.com",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"adtoma": {
"name": "Adtoma",
"categoryId": 4,
- "url": "http://www.adtoma.com/"
+ "url": "http://www.adtoma.com/",
+ "companyId": "adtoma"
},
"adtr02.com": {
"name": "adtr02.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"adtraction": {
"name": "Adtraction",
"categoryId": 4,
- "url": "http://adtraction.com/"
+ "url": "http://adtraction.com/",
+ "companyId": "adtraction"
},
"adtraxx": {
"name": "AdTraxx",
"categoryId": 4,
- "url": "https://www1.adtraxx.de/"
+ "url": "https://www1.adtraxx.de/",
+ "companyId": "adtrax"
},
"adtriba.com": {
"name": "AdTriba",
"categoryId": 6,
- "url": "https://www.adtriba.com/"
+ "url": "https://www.adtriba.com/",
+ "companyId": null
},
"adtrue": {
"name": "Adtrue",
"categoryId": 4,
- "url": "http://adtrue.com/"
+ "url": "http://adtrue.com/",
+ "companyId": "adtrue"
},
"adtrustmedia": {
"name": "AdTrustMedia",
"categoryId": 4,
- "url": "https://adtrustmedia.com/"
+ "url": "https://adtrustmedia.com/",
+ "companyId": "adtrustmedia"
},
"adtube": {
"name": "AdTube",
"categoryId": 4,
- "url": "http://adtube.ir/"
+ "url": "http://adtube.ir/",
+ "companyId": "adtube"
},
"adult_webmaster_empire": {
"name": "Adult Webmaster Empire",
"categoryId": 3,
- "url": "http://www.awempire.com/"
+ "url": "http://www.awempire.com/",
+ "companyId": "adult_webmaster_empire"
},
"adultadworld": {
"name": "AdultAdWorld",
"categoryId": 3,
- "url": "http://adultadworld.com/"
+ "url": "http://adultadworld.com/",
+ "companyId": "adult_adworld"
},
"adup-tech.com": {
"name": "AdUp Technology",
"categoryId": 4,
- "url": "https://www.adup-tech.com/"
+ "url": "https://www.adup-tech.com/",
+ "companyId": "adup_technology"
},
"advaction": {
"name": "Advaction",
"categoryId": 4,
- "url": "http://advaction.ru/"
+ "url": "http://advaction.ru/",
+ "companyId": "advaction"
},
"advalo": {
"name": "Advalo",
"categoryId": 4,
- "url": "https://www.advalo.com"
+ "url": "https://www.advalo.com",
+ "companyId": "advalo"
},
"advanced_hosters": {
"name": "Advanced Hosters",
"categoryId": 9,
- "url": "https://advancedhosters.com/"
+ "url": "https://advancedhosters.com/",
+ "companyId": null
},
"advark": {
"name": "Advark",
"categoryId": 4,
- "url": "https://advarkads.com/"
+ "url": "https://advarkads.com/",
+ "companyId": "advark"
},
"adventori": {
"name": "ADventori",
"categoryId": 8,
- "url": "https://www.adventori.com/"
+ "url": "https://www.adventori.com/",
+ "companyId": "adventori"
},
"adverline": {
"name": "Adverline",
"categoryId": 4,
- "url": "http://www.adverline.com/"
+ "url": "http://www.adverline.com/",
+ "companyId": "adverline"
},
"adversal": {
"name": "Adversal",
"categoryId": 4,
- "url": "https://www.adversal.com/"
+ "url": "https://www.adversal.com/",
+ "companyId": "adversal"
},
"adverserve": {
"name": "adverServe",
"categoryId": 4,
- "url": "http://www.adverserve.com/"
+ "url": "http://www.adverserve.com/",
+ "companyId": "adverserve"
},
"adverteerdirect": {
"name": "Adverteerdirect",
"categoryId": 4,
- "url": "http://www.adverteerdirect.nl/"
+ "url": "http://www.adverteerdirect.nl/",
+ "companyId": "adverteerdirect"
},
"adverticum": {
"name": "Adverticum",
"categoryId": 4,
- "url": "https://adverticum.net/english/"
+ "url": "https://adverticum.net/english/",
+ "companyId": "adverticum"
},
"advertise.com": {
"name": "Advertise.com",
"categoryId": 4,
- "url": "http://advertise.com/"
+ "url": "http://advertise.com/",
+ "companyId": "advertise.com"
},
"advertisespace": {
"name": "AdvertiseSpace",
"categoryId": 4,
- "url": "http://www.advertisespace.com/"
+ "url": "http://www.advertisespace.com/",
+ "companyId": "advertisespace"
},
"advertising.com": {
"name": "Verizon Media",
"categoryId": 4,
- "url": "https://www.verizonmedia.com/"
+ "url": "https://www.verizonmedia.com/",
+ "companyId": "verizon"
},
"advertlets": {
"name": "Advertlets",
"categoryId": 4,
- "url": "http://www.advertlets.com/"
+ "url": "http://www.advertlets.com/",
+ "companyId": "advertlets"
},
"advertserve": {
"name": "AdvertServe",
"categoryId": 4,
- "url": "https://secure.advertserve.com/"
+ "url": "https://secure.advertserve.com/",
+ "companyId": "advertserve"
},
"advidi": {
"name": "Advidi",
"categoryId": 4,
- "url": "http://advidi.com/"
+ "url": "http://advidi.com/",
+ "companyId": "advidi"
},
"advmaker.ru": {
"name": "advmaker.ru",
"categoryId": 4,
- "url": "http://advmaker.ru/"
+ "url": "http://advmaker.ru/",
+ "companyId": "advmaker.ru"
},
"advolution": {
"name": "Advolution",
"categoryId": 4,
- "url": "http://www.advolution.de"
+ "url": "http://www.advolution.de",
+ "companyId": "advolution"
},
"adwebster": {
"name": "adwebster",
"categoryId": 4,
- "url": "http://adwebster.com"
+ "url": "http://adwebster.com",
+ "companyId": "adwebster"
},
"adwit": {
"name": "Adwit",
"categoryId": 4,
- "url": "http://www.adwitserver.com"
+ "url": "http://www.adwitserver.com",
+ "companyId": "adwit"
},
"adworx.at": {
"name": "ADworx",
"categoryId": 4,
- "url": "http://www.adworx.at/"
+ "url": "http://www.adworx.at/",
+ "companyId": "ors"
},
"adworxs.net": {
"name": "adworxs.net",
"categoryId": 4,
- "url": "http://www.adworxs.net/?lang=en"
+ "url": "http://www.adworxs.net/?lang=en",
+ "companyId": null
},
"adxion": {
"name": "adXion",
"categoryId": 4,
- "url": "http://www.adxion.com"
+ "url": "http://www.adxion.com",
+ "companyId": "adxion"
},
"adxpansion": {
"name": "AdXpansion",
"categoryId": 3,
- "url": "http://www.adxpansion.com/"
+ "url": "http://www.adxpansion.com/",
+ "companyId": "adxpansion"
},
"adxpose": {
"name": "AdXpose",
"categoryId": 4,
- "url": "http://www.adxpose.com/home.page"
+ "url": "http://www.adxpose.com/home.page",
+ "companyId": "comscore"
},
"adxprtz.com": {
"name": "adxprtz.com",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"adyoulike": {
"name": "Adyoulike",
"categoryId": 4,
- "url": "http://www.adyoulike.com/"
+ "url": "http://www.adyoulike.com/",
+ "companyId": "adyoulike"
},
"adzerk": {
"name": "Adzerk",
"categoryId": 4,
- "url": "http://adzerk.com/"
+ "url": "http://adzerk.com/",
+ "companyId": "adzerk"
},
"adzly": {
"name": "adzly",
"categoryId": 4,
- "url": "http://www.adzly.com/"
+ "url": "http://www.adzly.com/",
+ "companyId": "adzly"
},
"aemediatraffic": {
"name": "Aemediatraffic",
"categoryId": 6,
- "url": null
+ "url": null,
+ "companyId": null
},
"aerify_media": {
"name": "Aerify Media",
"categoryId": 4,
- "url": "http://aerifymedia.com/"
+ "url": "http://aerifymedia.com/",
+ "companyId": "aerify_media"
},
"aeris_weather": {
"name": "Aeris Weather",
"categoryId": 2,
- "url": "https://www.aerisweather.com/"
+ "url": "https://www.aerisweather.com/",
+ "companyId": "aerisweather"
},
"affectv": {
"name": "Hybrid Theory",
"categoryId": 4,
- "url": "https://hybridtheory.com/"
+ "url": "https://hybridtheory.com/",
+ "companyId": "affectv"
},
"affiliate-b": {
"name": "Affiliate-B",
"categoryId": 4,
- "url": "https://www.affiliate-b.com/"
+ "url": "https://www.affiliate-b.com/",
+ "companyId": "affiliate_b"
},
"affiliate4you": {
"name": "Affiliate4You",
"categoryId": 4,
- "url": "http://www.affiliate4you.nl/"
+ "url": "http://www.affiliate4you.nl/",
+ "companyId": "family_blend"
},
"affiliatebuzz": {
"name": "AffiliateBuzz",
"categoryId": 4,
- "url": "http://www.affiliatebuzz.com/"
+ "url": "http://www.affiliatebuzz.com/",
+ "companyId": "affiliatebuzz"
},
"affiliatefuture": {
"name": "AffiliateFuture",
"categoryId": 4,
- "url": "http://www.affiliatefuture.com"
+ "url": "http://www.affiliatefuture.com",
+ "companyId": "affiliatefuture"
},
"affiliatelounge": {
"name": "AffiliateLounge",
"categoryId": 4,
- "url": "http://www.affiliatelounge.com/"
+ "url": "http://www.affiliatelounge.com/",
+ "companyId": "betsson_group_affiliates"
},
"affiliation_france": {
"name": "Affiliation France",
"categoryId": 4,
- "url": "http://www.affiliation-france.com/"
+ "url": "http://www.affiliation-france.com/",
+ "companyId": "affiliation-france"
},
"affiliator": {
"name": "Affiliator",
"categoryId": 4,
- "url": "http://www.affiliator.com/"
+ "url": "http://www.affiliator.com/",
+ "companyId": "affiliator"
},
"affiliaweb": {
"name": "Affiliaweb",
"categoryId": 4,
- "url": "http://affiliaweb.fr/"
+ "url": "http://affiliaweb.fr/",
+ "companyId": "affiliaweb"
},
"affilinet": {
"name": "affilinet",
"categoryId": 4,
- "url": "https://www.affili.net/"
+ "url": "https://www.affili.net/",
+ "companyId": "axel_springer"
},
"affimax": {
"name": "AffiMax",
"categoryId": 4,
- "url": "https://www.affimax.de"
+ "url": "https://www.affimax.de",
+ "companyId": "affimax"
},
"affinity": {
"name": "Affinity",
"categoryId": 4,
- "url": "http://www.affinity.com/"
+ "url": "http://www.affinity.com/",
+ "companyId": "affinity"
},
"affinity.by": {
"name": "Affinity.by",
"categoryId": 4,
- "url": "http://affinity.by"
+ "url": "http://affinity.by",
+ "companyId": "affinity_digital_agency"
},
"affiz_cpm": {
"name": "Affiz CPM",
"categoryId": 4,
- "url": "http://cpm.affiz.com/home"
+ "url": "http://cpm.affiz.com/home",
+ "companyId": "affiz_cpm"
},
"afftrack": {
"name": "Afftrack",
"categoryId": 6,
- "url": "http://www.afftrack.com/"
+ "url": "http://www.afftrack.com/",
+ "companyId": "afftrack"
},
"afgr2.com": {
"name": "afgr2.com",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"afilio": {
"name": "Afilio",
"categoryId": 6,
- "url": "http://afilio.com.br/"
+ "url": "http://afilio.com.br/",
+ "companyId": "afilio"
},
"afs_analystics": {
"name": "AFS Analystics",
"categoryId": 6,
- "url": "https://www.afsanalytics.com/"
+ "url": "https://www.afsanalytics.com/",
+ "companyId": "afs_analytics"
},
"aftonbladet_ads": {
"name": "Aftonbladet Ads",
"categoryId": 4,
- "url": "http://annonswebb.aftonbladet.se/"
+ "url": "http://annonswebb.aftonbladet.se/",
+ "companyId": "aftonbladet"
},
"aftv-serving.bid": {
"name": "aftv-serving.bid",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"aggregate_knowledge": {
"name": "Aggregate Knowledge",
"categoryId": 4,
- "url": "http://www.aggregateknowledge.com/"
+ "url": "http://www.aggregateknowledge.com/",
+ "companyId": "neustar"
},
"agilone": {
"name": "AgilOne",
"categoryId": 6,
- "url": "http://www.agilone.com/"
+ "url": "http://www.agilone.com/",
+ "companyId": "agilone"
},
"agora": {
"name": "Agora",
"categoryId": 4,
- "url": "https://www.agora.pl/"
+ "url": "https://www.agora.pl/",
+ "companyId": "agora_sa"
},
"ahalogy": {
"name": "Ahalogy",
"categoryId": 7,
- "url": "http://www.ahalogy.com/"
+ "url": "http://www.ahalogy.com/",
+ "companyId": "ahalogy"
},
"ai_media_group": {
"name": "Ai Media Group",
"categoryId": 4,
- "url": "http://aimediagroup.com/"
+ "url": "http://aimediagroup.com/",
+ "companyId": "ai_media_group"
},
"aidata": {
"name": "Aidata",
"categoryId": 4,
- "url": "http://aidata.me/"
+ "url": "http://aidata.me/",
+ "companyId": "aidata"
},
"aim4media": {
"name": "Aim4Media",
"categoryId": 4,
- "url": "http://aim4media.com"
+ "url": "http://aim4media.com",
+ "companyId": "aim4media"
},
"airbnb": {
"name": "Airbnb",
"categoryId": 6,
- "url": "https://affiliate.withairbnb.com/"
+ "url": "https://affiliate.withairbnb.com/",
+ "companyId": null
},
"airbrake": {
"name": "Airbrake",
"categoryId": 4,
- "url": "https://airbrake.io/"
+ "url": "https://airbrake.io/",
+ "companyId": "airbrake"
},
"airpr.com": {
"name": "AirPR",
"categoryId": 6,
- "url": "https://airpr.com/"
+ "url": "https://airpr.com/",
+ "companyId": "airpr"
},
"airpush": {
"name": "Airpush",
"categoryId": 4,
- "url": "http://www.airpush.com/"
+ "url": "http://www.airpush.com/",
+ "companyId": "airpush"
},
"akamai_technologies": {
"name": "Akamai Technologies",
"categoryId": 9,
- "url": "https://www.akamai.com/"
+ "url": "https://www.akamai.com/",
+ "companyId": "akamai"
},
"akamoihd.net": {
"name": "akamoihd.net",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"akane": {
"name": "AkaNe",
"categoryId": 4,
- "url": "http://akane-ad.com/"
+ "url": "http://akane-ad.com/",
+ "companyId": "akane"
},
"akanoo": {
"name": "Akanoo",
"categoryId": 6,
- "url": "http://www.akanoo.com/"
+ "url": "http://www.akanoo.com/",
+ "companyId": "akanoo"
},
"akavita": {
"name": "Akavita",
"categoryId": 4,
- "url": "http://www.akavita.by/en"
+ "url": "http://www.akavita.by/en",
+ "companyId": "akavita"
},
"al_bawaba_advertising": {
"name": "Al Bawaba Advertising",
"categoryId": 4,
- "url": "http://www.albawaba.com/advertising"
+ "url": "http://www.albawaba.com/advertising",
+ "companyId": "al_bawaba"
},
"albacross": {
"name": "Albacross",
"categoryId": 4,
- "url": "https://albacross.com"
+ "url": "https://albacross.com",
+ "companyId": "albacross"
},
"aldi-international.com": {
"name": "aldi-international.com",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"alenty": {
"name": "Alenty",
"categoryId": 4,
- "url": "http://www.alenty.com/"
+ "url": "http://www.alenty.com/",
+ "companyId": "appnexus"
},
"alephd.com": {
"name": "alephd",
"categoryId": 4,
- "url": "https://www.alephd.com/"
+ "url": "https://www.alephd.com/",
+ "companyId": "verizon"
},
"alexa_metrics": {
"name": "Alexa Metrics",
"categoryId": 6,
- "url": "http://www.alexa.com/"
+ "url": "http://www.alexa.com/",
+ "companyId": "amazon_associates"
},
"alexa_traffic_rank": {
"name": "Alexa Traffic Rank",
"categoryId": 4,
- "url": "http://www.alexa.com/"
+ "url": "http://www.alexa.com/",
+ "companyId": "amazon_associates"
},
"algolia.net": {
"name": "algolia",
"categoryId": 4,
- "url": "https://www.algolia.com/"
+ "url": "https://www.algolia.com/",
+ "companyId": null
},
"algovid.com": {
"name": "algovid.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"alibaba.com": {
"name": "Alibaba",
"categoryId": 8,
- "url": "http://www.alibaba.com/"
+ "url": "http://www.alibaba.com/",
+ "companyId": "alibaba"
},
"alipay.com": {
"name": "Alipay",
"categoryId": 2,
- "url": "https://www.alipay.com/"
+ "url": "https://www.alipay.com/",
+ "companyId": "alibaba"
},
"alivechat": {
"name": "AliveChat",
"categoryId": 2,
- "url": "http://www.websitealive.com/"
+ "url": "http://www.websitealive.com/",
+ "companyId": "websitealive"
},
"allegro.pl": {
"name": "Allegro",
"categoryId": 8,
- "url": "https://allegro.pl"
+ "url": "https://allegro.pl",
+ "companyId": "allegro.pl"
},
"allin": {
"name": "Allin",
"categoryId": 6,
- "url": "http://allin.com.br/"
+ "url": "http://allin.com.br/",
+ "companyId": "allin"
},
"allo-pages.fr": {
"name": "Allo-Pages",
"categoryId": 2,
- "url": "http://www.allo-pages.fr/"
+ "url": "http://www.allo-pages.fr/",
+ "companyId": "links_lab"
},
"allotraffic": {
"name": "AlloTraffic",
"categoryId": 4,
- "url": "http://www.allotraffic.com/"
+ "url": "http://www.allotraffic.com/",
+ "companyId": "allotraffic"
},
"allure_media": {
"name": "Allure Media",
"categoryId": 4,
- "url": "http://www.alluremedia.com.au"
+ "url": "http://www.alluremedia.com.au",
+ "companyId": "allure_media"
},
"allyes": {
"name": "Allyes",
"categoryId": 4,
- "url": "http://www.allyes.com/"
+ "url": "http://www.allyes.com/",
+ "companyId": "allyes"
},
"alooma": {
"name": "Alooma",
"categoryId": 4,
- "url": "https://www.alooma.com/"
+ "url": "https://www.alooma.com/",
+ "companyId": "alooma"
},
"altitude_digital": {
"name": "Altitude Digital",
"categoryId": 4,
- "url": "http://www.altitudedigital.com/"
+ "url": "http://www.altitudedigital.com/",
+ "companyId": "altitude_digital"
},
"amadesa": {
"name": "Amadesa",
"categoryId": 4,
- "url": "http://www.amadesa.com/"
+ "url": "http://www.amadesa.com/",
+ "companyId": "amadesa"
},
"amazon": {
"name": "Amazon.com",
"categoryId": 8,
- "url": "https://www.amazon.com"
+ "url": "https://www.amazon.com",
+ "companyId": "amazon_associates"
},
"amazon_adsystem": {
"name": "Amazon Advertising",
"categoryId": 4,
- "url": "https://advertising.amazon.com/"
+ "url": "https://advertising.amazon.com/",
+ "companyId": "amazon_associates"
},
"amazon_associates": {
"name": "Amazon Associates",
"categoryId": 4,
- "url": "http://aws.amazon.com/associates/"
+ "url": "http://aws.amazon.com/associates/",
+ "companyId": "amazon_associates"
},
"amazon_cdn": {
"name": "Amazon CDN",
"categoryId": 9,
- "url": "https://www.amazon.com"
+ "url": "https://www.amazon.com",
+ "companyId": "amazon_associates"
},
"amazon_cloudfront": {
"name": "Amazon CloudFront",
"categoryId": 10,
- "url": "https://aws.amazon.com/cloudfront/?nc1=h_ls"
+ "url": "https://aws.amazon.com/cloudfront/?nc1=h_ls",
+ "companyId": "amazon_associates"
},
"amazon_mobile_ads": {
"name": "Amazon Mobile Ads",
"categoryId": 4,
- "url": "http://www.amazon.com/"
+ "url": "http://www.amazon.com/",
+ "companyId": "amazon_associates"
},
"amazon_payments": {
"name": "Amazon Payments",
"categoryId": 2,
- "url": "https://pay.amazon.com/"
+ "url": "https://pay.amazon.com/",
+ "companyId": "amazon_associates"
},
"amazon_video": {
"name": "Amazon Instant Video",
"categoryId": 0,
- "url": "https://www.amazon.com"
+ "url": "https://www.amazon.com",
+ "companyId": "amazon_associates"
},
"amazon_web_services": {
"name": "Amazon Web Services",
"categoryId": 10,
- "url": "https://aws.amazon.com/"
+ "url": "https://aws.amazon.com/",
+ "companyId": "amazon_associates"
},
"ambient_digital": {
"name": "Ambient Digital",
"categoryId": 4,
- "url": "http://www.adnetwork.vn/"
+ "url": "http://www.adnetwork.vn/",
+ "companyId": "ambient_digital"
},
"amgload.net": {
"name": "amgload.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"amoad": {
"name": "AMoAd",
"categoryId": 4,
- "url": "http://www.amoad.com/"
+ "url": "http://www.amoad.com/",
+ "companyId": "amoad"
},
"amobee": {
"name": "Amobee",
"categoryId": 4,
- "url": "https://www.amobee.com/"
+ "url": "https://www.amobee.com/",
+ "companyId": "singtel"
},
"amp_platform": {
"name": "AMP Platform",
"categoryId": 4,
- "url": "http://www.collective.com/"
+ "url": "http://www.collective.com/",
+ "companyId": "collective"
},
"amplitude": {
"name": "Amplitude",
"categoryId": 6,
- "url": "https://amplitude.com/"
+ "url": "https://amplitude.com/",
+ "companyId": "amplitude"
},
"ampproject.org": {
"name": "AMP Project",
"categoryId": 8,
- "url": "https://www.ampproject.org/"
+ "url": "https://www.ampproject.org/",
+ "companyId": "google"
},
"anametrix": {
"name": "Anametrix",
"categoryId": 6,
- "url": "http://anametrix.com/"
+ "url": "http://anametrix.com/",
+ "companyId": "anametrix"
},
"ancestry_cdn": {
"name": "Ancestry CDN",
"categoryId": 9,
- "url": "https://www.ancestry.com/"
+ "url": "https://www.ancestry.com/",
+ "companyId": "ancestry"
},
"ancora": {
"name": "Ancora",
"categoryId": 6,
- "url": "http://www.ancoramediasolutions.com/"
+ "url": "http://www.ancoramediasolutions.com/",
+ "companyId": "ancora"
},
"anetwork": {
"name": "Anetwork",
"categoryId": 4,
- "url": "http://anetwork.ir/"
+ "url": "http://anetwork.ir/",
+ "companyId": "anetwork"
},
"aniview.com": {
"name": "AniView",
"categoryId": 4,
- "url": "https://www.aniview.com/"
+ "url": "https://www.aniview.com/",
+ "companyId": null
},
"anonymousads": {
"name": "AnonymousAds",
"categoryId": 4,
- "url": "https://a-ads.com/"
+ "url": "https://a-ads.com/",
+ "companyId": "anonymousads"
},
"anormal_tracker": {
"name": "Anormal Tracker",
"categoryId": 6,
- "url": "http://anormal-tracker.de/"
+ "url": "http://anormal-tracker.de/",
+ "companyId": "anormal-tracker"
},
"answers_cloud_service": {
"name": "Answers Cloud Service",
"categoryId": 1,
- "url": "http://www.answers.com/"
+ "url": "http://www.answers.com/",
+ "companyId": "answers.com"
},
"ants": {
"name": "Ants",
"categoryId": 7,
- "url": "http://ants.vn/en/"
+ "url": "http://ants.vn/en/",
+ "companyId": "ants"
},
"anvato": {
"name": "Anvato",
"categoryId": 0,
- "url": "https://www.anvato.com/"
+ "url": "https://www.anvato.com/",
+ "companyId": "google"
},
"anyclip": {
"name": "AnyClip",
"categoryId": 0,
- "url": "https://anyclip.com"
+ "url": "https://anyclip.com",
+ "companyId": "anyclip"
},
"aol_be_on": {
"name": "AOL Be On",
"categoryId": 4,
- "url": "http://beon.aolnetworks.com/"
+ "url": "http://beon.aolnetworks.com/",
+ "companyId": "verizon"
},
"aol_cdn": {
"name": "AOL CDN",
"categoryId": 6,
- "url": "https://www.verizon.com/"
+ "url": "https://www.verizon.com/",
+ "companyId": "verizon"
},
"aol_images_cdn": {
"name": "AOL Images CDN",
"categoryId": 5,
- "url": "https://www.verizon.com/"
+ "url": "https://www.verizon.com/",
+ "companyId": "verizon"
},
"apa.at": {
"name": "Apa",
"categoryId": 8,
- "url": "http://www.apa.at/Site/index.de.html"
+ "url": "http://www.apa.at/Site/index.de.html",
+ "companyId": "apa"
},
"apester": {
"name": "Apester",
"categoryId": 4,
- "url": "http://apester.com/"
+ "url": "http://apester.com/",
+ "companyId": "apester"
},
"apicit.net": {
"name": "apicit.net",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"aplus_analytics": {
"name": "Aplus Analytics",
"categoryId": 6,
- "url": "https://ww.deluxe.com/"
+ "url": "https://ww.deluxe.com/",
+ "companyId": "deluxe"
},
"appcues": {
"name": "Appcues",
"categoryId": 2,
- "url": "https://www.appcues.com/"
+ "url": "https://www.appcues.com/",
+ "companyId": null
},
"appdynamics": {
"name": "AppDynamics",
"categoryId": 6,
- "url": "http://www.appdynamics.com"
+ "url": "http://www.appdynamics.com",
+ "companyId": "appdynamics"
},
"appier": {
"name": "Appier",
"categoryId": 4,
- "url": "http://www.appier.com/en/index.html"
+ "url": "http://www.appier.com/en/index.html",
+ "companyId": "appier"
},
"applifier": {
"name": "Applifier",
"categoryId": 4,
- "url": "http://www.applifier.com/"
+ "url": "http://www.applifier.com/",
+ "companyId": "applifier"
},
"applovin": {
"name": "AppLovin",
"categoryId": 4,
- "url": "https://www.applovin.com"
+ "url": "https://www.applovin.com",
+ "companyId": "applovin"
},
"appmetrx": {
"name": "AppMetrx",
"categoryId": 4,
- "url": "http://www.engago.com"
+ "url": "http://www.engago.com",
+ "companyId": "engago_technologies"
},
"appnexus": {
"name": "AppNexus",
"categoryId": 4,
- "url": "http://www.appnexus.com/"
+ "url": "http://www.appnexus.com/",
+ "companyId": "appnexus"
},
"appsflyer": {
"name": "AppsFlyer",
"categoryId": 6,
- "url": "https://www.appsflyer.com/"
+ "url": "https://www.appsflyer.com/",
+ "companyId": "appsflyer"
},
"apptv": {
"name": "appTV",
"categoryId": 4,
- "url": "http://www.apptv.com/"
+ "url": "http://www.apptv.com/",
+ "companyId": "apptv"
},
"apture": {
"name": "Apture",
"categoryId": 2,
- "url": "http://www.apture.com/"
+ "url": "http://www.apture.com/",
+ "companyId": "google"
},
"arcpublishing": {
"name": "Arc Publishing",
"categoryId": 6,
- "url": "https://www.arcpublishing.com/"
+ "url": "https://www.arcpublishing.com/",
+ "companyId": "arc_publishing"
},
"ard.de": {
"name": "ard.de",
"categoryId": 0,
- "url": null
+ "url": null,
+ "companyId": null
},
"are_you_a_human": {
"name": "Are You a Human",
"categoryId": 6,
- "url": "https://areyouahuman.com/"
+ "url": "https://areyouahuman.com/",
+ "companyId": "distil_networks"
},
"arkoselabs.com": {
"name": "Arkose Labs",
"categoryId": 6,
- "url": "https://www.arkoselabs.com/"
+ "url": "https://www.arkoselabs.com/",
+ "companyId": null
},
"art19": {
"name": "Art19",
"categoryId": 4,
- "url": "https://art19.com/"
+ "url": "https://art19.com/",
+ "companyId": "art19"
},
"artimedia": {
"name": "Artimedia",
"categoryId": 4,
- "url": "http://arti-media.net/en/"
+ "url": "http://arti-media.net/en/",
+ "companyId": "artimedia"
},
"artlebedev.ru": {
"name": "Art.Lebedev",
"categoryId": 8,
- "url": "https://www.artlebedev.ru/"
+ "url": "https://www.artlebedev.ru/",
+ "companyId": "art.lebedev_studio"
},
"aruba_media_marketing": {
"name": "Aruba Media Marketing",
"categoryId": 4,
- "url": "http://www.arubamediamarketing.it/"
+ "url": "http://www.arubamediamarketing.it/",
+ "companyId": "aruba_media_marketing"
},
"arvato_canvas_fp": {
"name": "Arvato Canvas FP",
"categoryId": 6,
- "url": "https://www.arvato.com/"
+ "url": "https://www.arvato.com/",
+ "companyId": "arvato"
},
"asambeauty.com": {
"name": "asambeauty.com",
"categoryId": 8,
- "url": "https://www.asambeauty.com/"
+ "url": "https://www.asambeauty.com/",
+ "companyId": null
},
"ask.com": {
"name": "Ask.com",
"categoryId": 7,
- "url": null
+ "url": null,
+ "companyId": null
},
"aspnetcdn": {
"name": "Microsoft Ajax CDN",
"categoryId": 9,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"astronomer": {
"name": "Astronomer",
"categoryId": 6,
- "url": "https://www.astronomer.io"
+ "url": "https://www.astronomer.io",
+ "companyId": "astronomer"
},
"at_internet": {
"name": "AT Internet",
"categoryId": 6,
- "url": "http://www.xiti.com/"
+ "url": "http://www.xiti.com/",
+ "companyId": "at_internet"
},
"atedra": {
"name": "Atedra",
"categoryId": 4,
- "url": "http://www.atedra.com/"
+ "url": "http://www.atedra.com/",
+ "companyId": "atedra"
},
"atg_group": {
"name": "ATG Ad Tech Group",
"categoryId": 4,
- "url": "https://ad-tech-group.com/"
+ "url": "https://ad-tech-group.com/",
+ "companyId": null
},
"atg_optimization": {
"name": "ATG Optimization",
"categoryId": 4,
- "url": "http://www.atg.com/en/products-services/optimization/"
+ "url": "http://www.atg.com/en/products-services/optimization/",
+ "companyId": "oracle"
},
"atg_recommendations": {
"name": "ATG Recommendations",
"categoryId": 4,
- "url": "http://www.atg.com/en/products-services/optimization/recommendations/"
+ "url": "http://www.atg.com/en/products-services/optimization/recommendations/",
+ "companyId": "oracle"
},
"atlas": {
"name": "Atlas",
"categoryId": 4,
- "url": "https://atlassolutions.com"
+ "url": "https://atlassolutions.com",
+ "companyId": "facebook"
},
"atlas_profitbuilder": {
"name": "Atlas ProfitBuilder",
"categoryId": 4,
- "url": "http://www.atlassolutions.com/"
+ "url": "http://www.atlassolutions.com/",
+ "companyId": "atlas"
},
"atlassian.net": {
"name": "Atlassian",
"categoryId": 2,
- "url": "https://www.atlassian.com/"
+ "url": "https://www.atlassian.com/",
+ "companyId": "atlassian"
},
"atlassian_marketplace": {
"name": "Atlassian Marketplace",
"categoryId": 9,
- "url": "https://marketplace.atlassian.com/"
+ "url": "https://marketplace.atlassian.com/",
+ "companyId": "atlassian"
},
"atomz_search": {
"name": "Atomz Search",
"categoryId": 2,
- "url": "http://atomz.com/"
+ "url": "http://atomz.com/",
+ "companyId": "atomz"
},
"atsfi_de": {
"name": "atsfi.de",
"categoryId": 11,
- "url": "http://www.axelspringer.de/en/index.html"
+ "url": "http://www.axelspringer.de/en/index.html",
+ "companyId": "axel_springer"
},
"attracta": {
"name": "Attracta",
"categoryId": 4,
- "url": "http://www.attracta.com/"
+ "url": "http://www.attracta.com/",
+ "companyId": "attracta"
},
"attraqt": {
"name": "Attraqt",
"categoryId": 6,
- "url": "http://www.locayta.com/"
+ "url": "http://www.locayta.com/",
+ "companyId": "attraqt"
},
"audience2media": {
"name": "Audience2Media",
"categoryId": 4,
- "url": "http://www.audience2media.com/"
+ "url": "http://www.audience2media.com/",
+ "companyId": "audience2media"
},
"audience_ad_network": {
"name": "Audience Ad Network",
"categoryId": 4,
- "url": "http://www.audienceadnetwork.com"
+ "url": "http://www.audienceadnetwork.com",
+ "companyId": "bridgeline_digital"
},
"audience_science": {
"name": "Audience Science",
"categoryId": 4,
- "url": "http://www.audiencescience.com/"
+ "url": "http://www.audiencescience.com/",
+ "companyId": "audiencescience"
},
"audiencerate": {
"name": "AudienceRate",
"categoryId": 4,
- "url": "http://www.audiencerate.com/"
+ "url": "http://www.audiencerate.com/",
+ "companyId": "audiencerate"
},
"audiencesquare.com": {
"name": "Audience Square",
"categoryId": 4,
- "url": "http://www.audiencesquare.fr/"
+ "url": "http://www.audiencesquare.fr/",
+ "companyId": "audience_square"
},
"auditude": {
"name": "Auditude",
"categoryId": 0,
- "url": "http://www.auditude.com/"
+ "url": "http://www.auditude.com/",
+ "companyId": "adobe"
},
"audtd.com": {
"name": "Auditorius",
"categoryId": 4,
- "url": "http://www.auditorius.ru/"
+ "url": "http://www.auditorius.ru/",
+ "companyId": "auditorius"
},
"augur": {
"name": "Augur",
"categoryId": 6,
- "url": "https://www.augur.io/"
+ "url": "https://www.augur.io/",
+ "companyId": "augur"
},
"aumago": {
"name": "Aumago",
"categoryId": 4,
- "url": "http://www.aumago.com/"
+ "url": "http://www.aumago.com/",
+ "companyId": "aumago"
},
"aurea_clicktracks": {
"name": "Aurea ClickTracks",
"categoryId": 4,
- "url": "http://www.clicktracks.com/"
+ "url": "http://www.clicktracks.com/",
+ "companyId": "aurea"
},
"ausgezeichnet_org": {
"name": "ausgezeichnet.org",
"categoryId": 2,
- "url": "http://ausgezeichnet.org/"
+ "url": "http://ausgezeichnet.org/",
+ "companyId": null
},
"australia.gov": {
"name": "Australia.gov",
"categoryId": 4,
- "url": "http://www.australia.gov.au/"
+ "url": "http://www.australia.gov.au/",
+ "companyId": "australian_government"
},
"auth0": {
"name": "Auth0 Inc.",
"categoryId": 6,
- "url": "https://auth0.com/"
+ "url": "https://auth0.com/",
+ "companyId": "auth0"
},
"autoid": {
"name": "AutoID",
"categoryId": 6,
- "url": "http://www.autoid.com/"
+ "url": "http://www.autoid.com/",
+ "companyId": "autoid"
},
"autonomy": {
"name": "Autonomy",
"categoryId": 4,
- "url": "http://www.optimost.com/"
+ "url": "http://www.optimost.com/",
+ "companyId": "hp"
},
"autonomy_campaign": {
"name": "Autonomy Campaign",
"categoryId": 4,
- "url": "http://www.autonomy.com/"
+ "url": "http://www.autonomy.com/",
+ "companyId": "hp"
},
"autopilothq": {
"name": "Auto Pilot",
"categoryId": 4,
- "url": "https://www.autopilothq.com/"
+ "url": "https://www.autopilothq.com/",
+ "companyId": "autopilothq"
},
"autoscout24.com": {
"name": "Autoscout24",
"categoryId": 8,
- "url": "http://www.scout24.com/"
+ "url": "http://www.scout24.com/",
+ "companyId": "scout24"
},
"avail": {
"name": "Avail",
"categoryId": 4,
- "url": "http://avail.com"
+ "url": "http://avail.com",
+ "companyId": "richrelevance"
},
"avanser": {
"name": "AVANSER",
"categoryId": 2,
- "url": "http://www.avanser.com.au/"
+ "url": "http://www.avanser.com.au/",
+ "companyId": "avanser"
},
"avant_metrics": {
"name": "Avant Metrics",
"categoryId": 6,
- "url": "http://www.avantlink.com/"
+ "url": "http://www.avantlink.com/",
+ "companyId": "avantlink"
},
"avantlink": {
"name": "AvantLink",
"categoryId": 4,
- "url": "http://www.avantlink.com/"
+ "url": "http://www.avantlink.com/",
+ "companyId": "avantlink"
},
"avazu_network": {
"name": "Avazu Network",
"categoryId": 4,
- "url": "http://www.avazudsp.net/"
+ "url": "http://www.avazudsp.net/",
+ "companyId": "avazu_network"
},
"avenseo": {
"name": "Avenseo",
"categoryId": 4,
- "url": "http://avenseo.com"
+ "url": "http://avenseo.com",
+ "companyId": "avenseo"
},
"avid_media": {
"name": "Avid Media",
"categoryId": 0,
- "url": "http://www.avidglobalmedia.com/"
+ "url": "http://www.avidglobalmedia.com/",
+ "companyId": "avid_media"
},
"avocet": {
"name": "Avocet",
"categoryId": 8,
- "url": "https://avocet.io/"
+ "url": "https://avocet.io/",
+ "companyId": "avocet"
},
"aweber": {
"name": "AWeber",
"categoryId": 4,
- "url": "http://www.aweber.com/"
+ "url": "http://www.aweber.com/",
+ "companyId": "aweber_communications"
},
"awin": {
"name": "AWIN",
"categoryId": 4,
- "url": "https://www.awin.com"
+ "url": "https://www.awin.com",
+ "companyId": "axel_springer"
},
"axill": {
"name": "Axill",
"categoryId": 4,
- "url": "http://www.axill.com/"
+ "url": "http://www.axill.com/",
+ "companyId": "axill"
},
"azadify": {
"name": "Azadify",
"categoryId": 4,
- "url": "http://azadify.com/engage/index.php"
+ "url": "http://azadify.com/engage/index.php",
+ "companyId": "azadify"
},
"azureedge.net": {
"name": "Azure CDN",
"categoryId": 9,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"b2bcontext": {
"name": "B2BContext",
"categoryId": 4,
- "url": "http://b2bcontext.ru/"
+ "url": "http://b2bcontext.ru/",
+ "companyId": "b2bcontext"
},
"b2bvideo": {
"name": "B2Bvideo",
"categoryId": 4,
- "url": "http://b2bvideo.ru/"
+ "url": "http://b2bvideo.ru/",
+ "companyId": "b2bvideo"
},
"babator.com": {
"name": "Babator",
"categoryId": 6,
- "url": "https://www.babator.com/"
+ "url": "https://www.babator.com/",
+ "companyId": null
},
"back_beat_media": {
"name": "Back Beat Media",
"categoryId": 4,
- "url": "http://www.backbeatmedia.com"
+ "url": "http://www.backbeatmedia.com",
+ "companyId": "backbeat_media"
},
"backtype_widgets": {
"name": "BackType Widgets",
"categoryId": 4,
- "url": "http://www.backtype.com/widgets"
+ "url": "http://www.backtype.com/widgets",
+ "companyId": "backtype"
},
"bahn_de": {
"name": "Deutsche Bahn",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"baidu_ads": {
"name": "Baidu Ads",
"categoryId": 4,
- "url": "http://www.baidu.com/"
+ "url": "http://www.baidu.com/",
+ "companyId": "baidu"
},
"baidu_static": {
"name": "Baidu Static",
"categoryId": 8,
- "url": "https://www.baidu.com/"
+ "url": "https://www.baidu.com/",
+ "companyId": "baidu"
},
"baletingo.com": {
"name": "baletingo.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"bangdom.com": {
"name": "BangBros",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"bankrate": {
"name": "Bankrate",
"categoryId": 4,
- "url": "https://www.bankrate.com/"
+ "url": "https://www.bankrate.com/",
+ "companyId": "bankrate"
},
"banner_connect": {
"name": "Banner Connect",
"categoryId": 4,
- "url": "http://www.bannerconnect.net/"
+ "url": "http://www.bannerconnect.net/",
+ "companyId": "bannerconnect"
},
"bannerflow.com": {
"name": "Bannerflow",
"categoryId": 4,
- "url": "https://www.bannerflow.com/"
+ "url": "https://www.bannerflow.com/",
+ "companyId": "bannerflow"
},
"bannerplay": {
"name": "BannerPlay",
"categoryId": 4,
- "url": "http://www.bannerplay.com/"
+ "url": "http://www.bannerplay.com/",
+ "companyId": "bannerplay"
},
"bannersnack": {
"name": "Bannersnack",
"categoryId": 4,
- "url": "http://www.bannersnack.com/"
+ "url": "http://www.bannersnack.com/",
+ "companyId": "bannersnack"
},
"barilliance": {
"name": "Barilliance",
"categoryId": 4,
- "url": "http://www.barilliance.com/"
+ "url": "http://www.barilliance.com/",
+ "companyId": "barilliance"
},
"barometer": {
"name": "Barometer",
"categoryId": 2,
- "url": "http://getbarometer.com/"
+ "url": "http://getbarometer.com/",
+ "companyId": "barometer"
},
"basilic.io": {
"name": "basilic.io",
"categoryId": 6,
- "url": "https://basilic.io/"
+ "url": "https://basilic.io/",
+ "companyId": null
},
"batanga_network": {
"name": "Batanga Network",
"categoryId": 4,
- "url": "http://www.batanganetwork.com/"
+ "url": "http://www.batanganetwork.com/",
+ "companyId": "batanga_network"
},
"batch_media": {
"name": "Batch Media",
"categoryId": 4,
- "url": "http://batch.ba/"
+ "url": "http://batch.ba/",
+ "companyId": "prosieben_sat1"
},
"bauer_media": {
"name": "Bauer Media",
"categoryId": 4,
- "url": "http://www.bauermedia.com"
+ "url": "http://www.bauermedia.com",
+ "companyId": "bauer_media"
},
"baur.de": {
"name": "baur.de",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"baynote_observer": {
"name": "Baynote Observer",
"categoryId": 4,
- "url": "http://www.baynote.com/"
+ "url": "http://www.baynote.com/",
+ "companyId": "baynote"
},
"bazaarvoice": {
"name": "Bazaarvoice",
"categoryId": 2,
- "url": "http://www.bazaarvoice.com/"
+ "url": "http://www.bazaarvoice.com/",
+ "companyId": "bazaarvoice"
},
"bbci": {
"name": "BBC",
"categoryId": 10,
- "url": "https://bbc.co.uk"
+ "url": "https://bbc.co.uk",
+ "companyId": null
},
"bd4travel": {
"name": "bd4travel",
"categoryId": 4,
- "url": "https://bd4travel.com/"
+ "url": "https://bd4travel.com/",
+ "companyId": "bd4travel"
},
"be_opinion": {
"name": "Be Opinion",
"categoryId": 2,
- "url": "http://beopinion.com/"
+ "url": "http://beopinion.com/",
+ "companyId": "be_opinion"
},
"beachfront": {
"name": "Beachfront Media",
"categoryId": 4,
- "url": "http://beachfrontmedia.com/"
+ "url": "http://beachfrontmedia.com/",
+ "companyId": null
},
"beacon_ad_network": {
"name": "Beacon Ad Network",
"categoryId": 4,
- "url": "http://beaconads.com/"
+ "url": "http://beaconads.com/",
+ "companyId": "beacon_ad_network"
},
"beampulse.com": {
"name": "BeamPulse",
"categoryId": 4,
- "url": "https://en.beampulse.com/"
+ "url": "https://en.beampulse.com/",
+ "companyId": null
},
"beanstalk_data": {
"name": "Beanstalk Data",
"categoryId": 4,
- "url": "http://www.beanstalkdata.com/"
+ "url": "http://www.beanstalkdata.com/",
+ "companyId": "beanstalk_data"
},
"bebi": {
"name": "Bebi Media",
"categoryId": 4,
- "url": "https://www.bebi.com/"
+ "url": "https://www.bebi.com/",
+ "companyId": "bebi_media"
},
"beeketing.com": {
"name": "Beeketing",
"categoryId": 4,
- "url": "https://beeketing.com/"
+ "url": "https://beeketing.com/",
+ "companyId": "beeketing"
},
"beeline.ru": {
"name": "Beeline",
"categoryId": 4,
- "url": "https://moskva.beeline.ru/"
+ "url": "https://moskva.beeline.ru/",
+ "companyId": null
},
"beeswax": {
"name": "Beeswax",
"categoryId": 4,
- "url": "http://beeswax.com/"
+ "url": "http://beeswax.com/",
+ "companyId": "beeswax"
},
"beezup": {
"name": "BeezUP",
"categoryId": 4,
- "url": "http://www.beezup.co.uk/"
+ "url": "http://www.beezup.co.uk/",
+ "companyId": "beezup"
},
"begun": {
"name": "Begun",
"categoryId": 4,
- "url": "http://begun.ru/"
+ "url": "http://begun.ru/",
+ "companyId": "begun"
},
"behavioralengine": {
"name": "BehavioralEngine",
"categoryId": 4,
- "url": "http://www.behavioralengine.com/"
+ "url": "http://www.behavioralengine.com/",
+ "companyId": "behavioralengine"
},
"belboon_gmbh": {
"name": "belboon GmbH",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"belco": {
"name": "Belco",
"categoryId": 2,
- "url": "https://www.belco.io/"
+ "url": "https://www.belco.io/",
+ "companyId": "belco"
},
"belstat": {
"name": "BelStat",
"categoryId": 6,
- "url": "http://www.belstat.com/"
+ "url": "http://www.belstat.com/",
+ "companyId": "belstat"
},
"bemobile.ua": {
"name": "Bemobile",
"categoryId": 10,
- "url": "http://bemobile.ua/en/"
+ "url": "http://bemobile.ua/en/",
+ "companyId": "bemobile"
},
"bench_platform": {
"name": "Bench Platform",
"categoryId": 4,
- "url": "https://benchplatform.com"
+ "url": "https://benchplatform.com",
+ "companyId": "bench_platform"
},
"betterttv": {
"name": "BetterTTV",
"categoryId": 7,
- "url": "https://nightdev.com/betterttv/"
+ "url": "https://nightdev.com/betterttv/",
+ "companyId": "nightdev"
},
"betweendigital.com": {
"name": "Between Digital",
"categoryId": 4,
- "url": "http://betweendigital.ru/ssp"
+ "url": "http://betweendigital.ru/ssp",
+ "companyId": "between_digital"
},
"bid.run": {
"name": "Bid Run",
"categoryId": 4,
- "url": "http://bid.run/"
+ "url": "http://bid.run/",
+ "companyId": "bid.run"
},
"bidgear": {
"name": "BidGear",
"categoryId": 6,
- "url": "https://bidgear.com/"
+ "url": "https://bidgear.com/",
+ "companyId": "bidgear"
},
"bidswitch": {
"name": "Bidswitch",
"categoryId": 4,
- "url": "http://www.iponweb.com/"
+ "url": "http://www.iponweb.com/",
+ "companyId": "iponweb"
},
"bidtellect": {
"name": "Bidtellect",
"categoryId": 4,
- "url": "https://www.bidtellect.com/"
+ "url": "https://www.bidtellect.com/",
+ "companyId": "bidtellect"
},
"bidtheatre": {
"name": "BidTheatre",
"categoryId": 4,
- "url": "http://www.bidtheatre.com/"
+ "url": "http://www.bidtheatre.com/",
+ "companyId": "bidtheatre"
},
"bidvertiser": {
"name": "BidVertiser",
"categoryId": 4,
- "url": "http://www.bidvertiser.com/"
+ "url": "http://www.bidvertiser.com/",
+ "companyId": "bidvertiser"
},
"big_mobile": {
"name": "Big Mobile",
"categoryId": 4,
- "url": "http://www.bigmobile.com/"
+ "url": "http://www.bigmobile.com/",
+ "companyId": "big_mobile"
},
"bigcommerce.com": {
"name": "BigCommerce",
"categoryId": 6,
- "url": "https://www.bigcommerce.com/"
+ "url": "https://www.bigcommerce.com/",
+ "companyId": "bigcommerce"
},
"bigmir.net": {
"name": "bigmir",
"categoryId": 6,
- "url": "https://www.bigmir.net/"
+ "url": "https://www.bigmir.net/",
+ "companyId": "bigmir-internet"
},
"bigpoint": {
"name": "Bigpoint",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"bild": {
"name": "Bild.de",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"bilgin_pro": {
"name": "Bilgin Pro",
"categoryId": 4,
- "url": "http://bilgin.pro/"
+ "url": "http://bilgin.pro/",
+ "companyId": "bilginpro"
},
"bilin": {
"name": "Bilin",
"categoryId": 4,
- "url": "http://www.bilintechnology.com/"
+ "url": "http://www.bilintechnology.com/",
+ "companyId": "bilin"
},
"bing_ads": {
"name": "Bing Ads",
"categoryId": 4,
- "url": "https://bingads.microsoft.com/"
+ "url": "https://bingads.microsoft.com/",
+ "companyId": "microsoft"
},
"bing_maps": {
"name": "Bing Maps",
"categoryId": 2,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"binlayer": {
"name": "BinLayer",
"categoryId": 4,
- "url": "http://binlayer.com/"
+ "url": "http://binlayer.com/",
+ "companyId": "binlayer"
},
"binotel": {
"name": "Binotel",
"categoryId": 4,
- "url": "http://www.binotel.ua/"
+ "url": "http://www.binotel.ua/",
+ "companyId": "binotel"
},
"bisnode": {
"name": "Bisnode",
"categoryId": 4,
- "url": "http://www.esendra.fi/"
+ "url": "http://www.esendra.fi/",
+ "companyId": "bisnode"
},
"bitcoin_miner": {
"name": "Bitcoin Miner",
"categoryId": 2,
- "url": "http://www.bitcoinplus.com/"
+ "url": "http://www.bitcoinplus.com/",
+ "companyId": "bitcoin_plus"
},
"bitly": {
"name": "Bitly",
"categoryId": 6,
- "url": "https://bitly.com/"
+ "url": "https://bitly.com/",
+ "companyId": null
},
"bitrix": {
"name": "Bitrix24",
"categoryId": 4,
- "url": "https://www.bitrix24.com/"
+ "url": "https://www.bitrix24.com/",
+ "companyId": "bitrix24"
},
"bizcn": {
"name": "Bizcn",
"categoryId": 4,
- "url": "http://www.bizcn.com/"
+ "url": "http://www.bizcn.com/",
+ "companyId": "bizcn"
},
"blackdragon": {
"name": "BlackDragon",
"categoryId": 4,
- "url": "http://www.jd.com/"
+ "url": "http://www.jd.com/",
+ "companyId": "jing_dong"
},
"blau.de": {
"name": "Blau",
"categoryId": 8,
- "url": "https://www.blau.de/"
+ "url": "https://www.blau.de/",
+ "companyId": null
},
"blink_new_media": {
"name": "Blink New Media",
"categoryId": 4,
- "url": "http://engagebdr.com/"
+ "url": "http://engagebdr.com/",
+ "companyId": "engage_bdr"
},
"blis": {
"name": "Blis",
"categoryId": 6,
- "url": "http://www.blis.com/index.php"
+ "url": "http://www.blis.com/index.php",
+ "companyId": "blis"
},
"blogad": {
"name": "BlogAD",
"categoryId": 4,
- "url": "http://www.blogad.com.tw/"
+ "url": "http://www.blogad.com.tw/",
+ "companyId": "blogad"
},
"blogbang": {
"name": "BlogBang",
"categoryId": 4,
- "url": "http://www.blogbang.com/"
+ "url": "http://www.blogbang.com/",
+ "companyId": "blogbang"
},
"blogcatalog": {
"name": "BlogCatalog",
"categoryId": 2,
- "url": "http://www.blogcatalog.com/"
+ "url": "http://www.blogcatalog.com/",
+ "companyId": "blogcatalog"
},
"blogcounter": {
"name": "BlogCounter",
"categoryId": 6,
- "url": "http://blogcounter.com/"
+ "url": "http://blogcounter.com/",
+ "companyId": "adfire_gmbh"
},
"blogfoster.com": {
"name": "Blogfoster",
"categoryId": 8,
- "url": "http://www.blogfoster.com/"
+ "url": "http://www.blogfoster.com/",
+ "companyId": "blogfoster"
},
"bloggerads": {
"name": "BloggerAds",
"categoryId": 4,
- "url": "http://www.bloggerads.net/"
+ "url": "http://www.bloggerads.net/",
+ "companyId": "bloggerads"
},
"blogher": {
"name": "BlogHer Ads",
"categoryId": 4,
- "url": "https://www.blogher.com/"
+ "url": "https://www.blogher.com/",
+ "companyId": "penske_media_corp"
},
"blogimg.jp": {
"name": "blogimg.jp",
"categoryId": 9,
- "url": "https://line.me/"
+ "url": "https://line.me/",
+ "companyId": "line"
},
"blogsmithmedia.com": {
"name": "blogsmithmedia.com",
"categoryId": 8,
- "url": "https://www.verizon.com/"
+ "url": "https://www.verizon.com/",
+ "companyId": "verizon"
},
"blogspot_com": {
"name": "blogspot.com",
"categoryId": 8,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"bloomreach": {
"name": "BloomReach",
"categoryId": 4,
- "url": "https://www.bloomreach.com/en"
+ "url": "https://www.bloomreach.com/en",
+ "companyId": "bloomreach"
},
"blue_cherry_group": {
"name": "Blue Cherry Group",
"categoryId": 4,
- "url": "http://www.bluecherrygroup.com"
+ "url": "http://www.bluecherrygroup.com",
+ "companyId": "blue_cherry_group"
},
"blue_seed": {
"name": "Blue Seed",
"categoryId": 4,
- "url": "http://blueseed.tv/#/en/platform"
+ "url": "http://blueseed.tv/#/en/platform",
+ "companyId": "blue_seed"
},
"blueconic.net": {
"name": "BlueConic Plugin",
"categoryId": 6,
- "url": "https://www.blueconic.com/"
+ "url": "https://www.blueconic.com/",
+ "companyId": "blueconic"
},
"bluecore": {
"name": "Bluecore",
"categoryId": 4,
- "url": "https://www.bluecore.com/"
+ "url": "https://www.bluecore.com/",
+ "companyId": "triggermail"
},
"bluekai": {
"name": "BlueKai",
"categoryId": 4,
- "url": "http://www.bluekai.com/"
+ "url": "http://www.bluekai.com/",
+ "companyId": "oracle"
},
"bluelithium": {
"name": "Bluelithium",
"categoryId": 4,
- "url": "http://www.bluelithium.com/"
+ "url": "http://www.bluelithium.com/",
+ "companyId": "verizon"
},
"bluemetrix": {
"name": "Bluemetrix",
"categoryId": 4,
- "url": "http://www.bluemetrix.ie/"
+ "url": "http://www.bluemetrix.ie/",
+ "companyId": "bluemetrix"
},
"bluenewsupdate.info": {
"name": "bluenewsupdate.info",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"bluestreak": {
"name": "BlueStreak",
"categoryId": 4,
- "url": "http://www.bluestreak.com/"
+ "url": "http://www.bluestreak.com/",
+ "companyId": "dentsu_aegis_network"
},
"bluetriangle": {
"name": "Blue Triangle",
"categoryId": 6,
- "url": "https://www.bluetriangle.com/"
+ "url": "https://www.bluetriangle.com/",
+ "companyId": "blue_triangle"
},
"bodelen.com": {
"name": "bodelen.com",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"bol_affiliate_program": {
"name": "BOL Affiliate Program",
"categoryId": 4,
- "url": "http://www.bol.com"
+ "url": "http://www.bol.com",
+ "companyId": "bol.com"
},
"bold": {
"name": "Bold",
"categoryId": 4,
- "url": "https://boldcommerce.com/"
+ "url": "https://boldcommerce.com/",
+ "companyId": "bold"
},
"boldchat": {
"name": "Boldchat",
"categoryId": 2,
- "url": "http://www.boldchat.com/"
+ "url": "http://www.boldchat.com/",
+ "companyId": "boldchat"
},
"boltdns.net": {
"name": "boltdns.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"bombora": {
"name": "Bombora",
"categoryId": 6,
- "url": "http://bombora.com/"
+ "url": "http://bombora.com/",
+ "companyId": "bombora"
},
"bongacams.com": {
"name": "bongacams.com",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"bonial": {
"name": "Bonial Connect",
"categoryId": 2,
- "url": "http://www.bonial.com/"
+ "url": "http://www.bonial.com/",
+ "companyId": null
},
"boo-box": {
"name": "boo-box",
"categoryId": 4,
- "url": "http://boo-box.com/"
+ "url": "http://boo-box.com/",
+ "companyId": "boo-box"
},
"booking.com": {
"name": "Booking.com",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"boost_box": {
"name": "Boost Box",
"categoryId": 6,
- "url": "http://www.boostbox.com.br/"
+ "url": "http://www.boostbox.com.br/",
+ "companyId": "boost_box"
},
"booster_video": {
"name": "Booster Video",
"categoryId": 0,
- "url": "https://boostervideo.ru/"
+ "url": "https://boostervideo.ru/",
+ "companyId": "booster_video"
},
"bootstrap": {
"name": "Bootstrap CDN",
"categoryId": 9,
- "url": "http://getbootstrap.com/"
+ "url": "http://getbootstrap.com/",
+ "companyId": "bootstrap_cdn"
},
"borrango.com": {
"name": "borrango.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"botscanner": {
"name": "BotScanner",
"categoryId": 6,
- "url": "http://botscanner.com"
+ "url": "http://botscanner.com",
+ "companyId": "botscanner"
},
"boudja.com": {
"name": "boudja.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"bounce_exchange": {
"name": "Bounce Exchange",
"categoryId": 4,
- "url": "http://bounceexchange.com"
+ "url": "http://bounceexchange.com",
+ "companyId": "bounce_exchange"
},
"bouncex": {
"name": "BounceX",
"categoryId": 4,
- "url": "https://www.bouncex.com/"
+ "url": "https://www.bouncex.com/",
+ "companyId": null
},
"box_uk": {
"name": "Box UK",
"categoryId": 6,
- "url": "http://www.clickdensity.com"
+ "url": "http://www.clickdensity.com",
+ "companyId": "box_uk"
},
"boxever": {
"name": "Boxever",
"categoryId": 4,
- "url": "https://www.boxever.com/"
+ "url": "https://www.boxever.com/",
+ "companyId": "boxever"
},
"brainient": {
"name": "Brainient",
"categoryId": 4,
- "url": "http://www.brainient.com/"
+ "url": "http://www.brainient.com/",
+ "companyId": "brainient"
},
"brainsins": {
"name": "BrainSINS",
"categoryId": 4,
- "url": "http://www.brainsins.com/"
+ "url": "http://www.brainsins.com/",
+ "companyId": "brainsins"
},
"branch_metrics": {
"name": "Branch",
"categoryId": 4,
- "url": "https://branch.io/"
+ "url": "https://branch.io/",
+ "companyId": "branch_metrics_inc"
},
"brand_affinity": {
"name": "Brand Affinity",
"categoryId": 4,
- "url": "http://brandaffinity.net/about"
+ "url": "http://brandaffinity.net/about",
+ "companyId": "yoonla"
},
"brand_networks": {
"name": "Brand Networks",
"categoryId": 4,
- "url": "http://www.xa.net/"
+ "url": "http://www.xa.net/",
+ "companyId": "brand_networks"
},
"brandmetrics.com": {
"name": "Brandmetrics.com",
"categoryId": 4,
- "url": "https://www.brandmetrics.com/"
+ "url": "https://www.brandmetrics.com/",
+ "companyId": null
},
"brandreach": {
"name": "BrandReach",
"categoryId": 4,
- "url": "http://www.brandreach.com/"
+ "url": "http://www.brandreach.com/",
+ "companyId": "brandreach"
},
"brandscreen": {
"name": "Brandscreen",
"categoryId": 4,
- "url": "http://www.brandscreen.com/"
+ "url": "http://www.brandscreen.com/",
+ "companyId": "zenovia"
},
"brandwire.tv": {
"name": "BrandWire",
"categoryId": 4,
- "url": "https://brandwire.tv/"
+ "url": "https://brandwire.tv/",
+ "companyId": null
},
"branica": {
"name": "Branica",
"categoryId": 4,
- "url": "http://www.branica.com/"
+ "url": "http://www.branica.com/",
+ "companyId": "branica"
},
"braze": {
"name": "Braze",
"categoryId": 6,
- "url": "https://www.braze.com/"
+ "url": "https://www.braze.com/",
+ "companyId": "braze_inc"
},
"brealtime": {
"name": "EMX Digital",
"categoryId": 4,
- "url": "https://emxdigital.com/"
+ "url": "https://emxdigital.com/",
+ "companyId": null
},
"bridgetrack": {
"name": "BridgeTrack",
"categoryId": 4,
- "url": "http://www.bridgetrack.com/"
+ "url": "http://www.bridgetrack.com/",
+ "companyId": "bridgetrack"
},
"brightcove": {
"name": "Brightcove",
"categoryId": 0,
- "url": "http://www.brightcove.com/en/"
+ "url": "http://www.brightcove.com/en/",
+ "companyId": "brightcove"
},
"brightcove_player": {
"name": "Brightcove Player",
"categoryId": 0,
- "url": "http://www.brightcove.com/en/"
+ "url": "http://www.brightcove.com/en/",
+ "companyId": "brightcove"
},
"brightedge": {
"name": "BrightEdge",
"categoryId": 4,
- "url": "http://www.brightedge.com/"
+ "url": "http://www.brightedge.com/",
+ "companyId": "brightedge"
},
"brightfunnel": {
"name": "BrightFunnel",
"categoryId": 6,
- "url": "http://www.brightfunnel.com/"
+ "url": "http://www.brightfunnel.com/",
+ "companyId": "brightfunnel"
},
"brightonclick.com": {
"name": "brightonclick.com",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"brightroll": {
"name": "BrightRoll",
"categoryId": 4,
- "url": "http://www.brightroll.com/"
+ "url": "http://www.brightroll.com/",
+ "companyId": "verizon"
},
"brilig": {
"name": "Brilig",
"categoryId": 4,
- "url": "http://www.brilig.com/"
+ "url": "http://www.brilig.com/",
+ "companyId": "dentsu_aegis_network"
},
"brillen.de": {
"name": "brillen.de",
"categoryId": 8,
- "url": "https://www.brillen.de/"
+ "url": "https://www.brillen.de/",
+ "companyId": null
},
"broadstreet": {
"name": "Broadstreet",
"categoryId": 4,
- "url": "http://broadstreetads.com/"
+ "url": "http://broadstreetads.com/",
+ "companyId": "broadstreet"
},
"bronto": {
"name": "Bronto",
"categoryId": 4,
- "url": "http://bronto.com/"
+ "url": "http://bronto.com/",
+ "companyId": "bronto"
},
"brow.si": {
"name": "Brow.si",
"categoryId": 4,
- "url": "https://brow.si/"
+ "url": "https://brow.si/",
+ "companyId": "brow.si"
},
"browser-statistik": {
"name": "Browser-Statistik",
"categoryId": 6,
- "url": "http://www.browser-statistik.de/"
+ "url": "http://www.browser-statistik.de/",
+ "companyId": "browser-statistik"
},
"browser_update": {
"name": "Browser Update",
"categoryId": 2,
- "url": "http://www.browser-update.org/"
+ "url": "http://www.browser-update.org/",
+ "companyId": "browser-update"
},
"btncdn.com": {
"name": "btncdn.com",
"categoryId": 9,
- "url": null
+ "url": null,
+ "companyId": null
},
"bubblestat": {
"name": "Bubblestat",
"categoryId": 4,
- "url": "http://www.bubblestat.com/"
+ "url": "http://www.bubblestat.com/",
+ "companyId": "bubblestat"
},
"buddy_media": {
"name": "Buddy Media",
"categoryId": 7,
- "url": "http://www.salesforce.com/"
+ "url": "http://www.salesforce.com/",
+ "companyId": "salesforce"
},
"buffer_button": {
"name": "Buffer Button",
"categoryId": 7,
- "url": "http://www.bufferapp.com/"
+ "url": "http://www.bufferapp.com/",
+ "companyId": "buffer"
},
"bugherd.com": {
"name": "BugHerd",
"categoryId": 2,
- "url": "https://bugherd.com"
+ "url": "https://bugherd.com",
+ "companyId": "bugherd"
},
"bugsnag": {
"name": "Bugsnag",
"categoryId": 6,
- "url": "https://bugsnag.com"
+ "url": "https://bugsnag.com",
+ "companyId": "bugsnag"
},
"bulkhentai.com": {
"name": "bulkhentai.com",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"bumlam.com": {
"name": "bumlam.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"bunchbox": {
"name": "Bunchbox",
"categoryId": 6,
- "url": "https://app.bunchbox.co/login"
+ "url": "https://app.bunchbox.co/login",
+ "companyId": "bunchbox"
},
"burda": {
"name": "BurdaForward",
"categoryId": 4,
- "url": "http://www.hubert-burda-media.com/"
+ "url": "http://www.hubert-burda-media.com/",
+ "companyId": "hubert_burda_media"
},
"burda_digital_systems": {
"name": "Burda Digital Systems",
"categoryId": 4,
- "url": "http://www.hubert-burda-media.com/"
+ "url": "http://www.hubert-burda-media.com/",
+ "companyId": "hubert_burda_media"
},
"burst_media": {
"name": "Burst Media",
"categoryId": 4,
- "url": "http://www.burstmedia.com/"
+ "url": "http://www.burstmedia.com/",
+ "companyId": "rhythmone"
},
"burt": {
"name": "Burt",
"categoryId": 4,
- "url": "http://www.burtcorp.com/"
+ "url": "http://www.burtcorp.com/",
+ "companyId": "burt"
},
"businessonline_analytics": {
"name": "BusinessOnLine Analytics",
"categoryId": 6,
- "url": "http://www.businessol.com/"
+ "url": "http://www.businessol.com/",
+ "companyId": "businessonline"
},
"buysellads": {
"name": "BuySellAds",
"categoryId": 4,
- "url": "http://buysellads.com/"
+ "url": "http://buysellads.com/",
+ "companyId": "buysellads.com"
},
"buzzadexchange.com": {
"name": "buzzadexchange.com",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"buzzador": {
"name": "Buzzador",
"categoryId": 7,
- "url": "http://www.buzzador.com"
+ "url": "http://www.buzzador.com",
+ "companyId": "buzzador"
},
"buzzfeed": {
"name": "BuzzFeed",
"categoryId": 2,
- "url": "http://www.buzzfeed.com"
+ "url": "http://www.buzzfeed.com",
+ "companyId": "buzzfeed"
},
"bwbx.io": {
"name": "Bloomberg CDN",
"categoryId": 9,
- "url": "https://www.bloomberg.com/"
+ "url": "https://www.bloomberg.com/",
+ "companyId": null
},
"bypass": {
"name": "Bypass",
"categoryId": 4,
- "url": "http://bypass.jp/"
+ "url": "http://bypass.jp/",
+ "companyId": "united_inc"
},
"c1_exchange": {
"name": "C1 Exchange",
"categoryId": 4,
- "url": "http://c1exchange.com/"
+ "url": "http://c1exchange.com/",
+ "companyId": "c1_exchange"
},
"c3_metrics": {
"name": "C3 Metrics",
"categoryId": 6,
- "url": "http://c3metrics.com/"
+ "url": "http://c3metrics.com/",
+ "companyId": "c3_metrics"
},
"c8_network": {
"name": "C8 Network",
"categoryId": 4,
- "url": "http://c8.net.ua/"
+ "url": "http://c8.net.ua/",
+ "companyId": "c8_network"
},
"cackle.me": {
"name": "Cackle",
"categoryId": 3,
- "url": "https://cackle.me/"
+ "url": "https://cackle.me/",
+ "companyId": null
},
"cadreon": {
"name": "Cadreon",
"categoryId": 4,
- "url": "http://www.cadreon.com/"
+ "url": "http://www.cadreon.com/",
+ "companyId": "cadreon"
},
"call_page": {
"name": "Call Page",
"categoryId": 2,
- "url": "https://www.callpage.io/"
+ "url": "https://www.callpage.io/",
+ "companyId": "call_page"
},
"callbackhunter": {
"name": "CallbackHunter",
"categoryId": 2,
- "url": "http://callbackhunter.com/main"
+ "url": "http://callbackhunter.com/main",
+ "companyId": "callbackhunter"
},
"callbox": {
"name": "CallBox",
"categoryId": 2,
- "url": "http://www.centuryinteractive.com"
+ "url": "http://www.centuryinteractive.com",
+ "companyId": "callbox"
},
"callibri": {
"name": "Callibri",
"categoryId": 4,
- "url": "https://callibri.ru/"
+ "url": "https://callibri.ru/",
+ "companyId": "callibri"
},
"callrail": {
"name": "CallRail",
"categoryId": 2,
- "url": "http://www.callrail.com/"
+ "url": "http://www.callrail.com/",
+ "companyId": "callrail"
},
"calltracking": {
"name": "Calltracking",
"categoryId": 2,
- "url": "https://calltracking.ru"
+ "url": "https://calltracking.ru",
+ "companyId": "calltracking"
},
"caltat.com": {
"name": "Caltat",
"categoryId": 2,
- "url": "https://caltat.com/"
+ "url": "https://caltat.com/",
+ "companyId": null
},
"cam-content.com": {
"name": "Cam-Content.com",
"categoryId": 3,
- "url": "https://www.cam-content.com/"
+ "url": "https://www.cam-content.com/",
+ "companyId": null
},
"camakaroda.com": {
"name": "camakaroda.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"campus_explorer": {
"name": "Campus Explorer",
"categoryId": 6,
- "url": "http://www.campusexplorer.com/"
+ "url": "http://www.campusexplorer.com/",
+ "companyId": "campus_explorer"
},
"canddi": {
"name": "CANDDI",
"categoryId": 6,
- "url": "https://www.canddi.com/"
+ "url": "https://www.canddi.com/",
+ "companyId": "canddi"
},
"canvas": {
"name": "Canvas",
"categoryId": 2,
- "url": "https://www.canvas.net/"
+ "url": "https://www.canvas.net/",
+ "companyId": null
},
"capitaldata": {
"name": "CapitalData",
"categoryId": 6,
- "url": "https://www.capitaldata.fr/"
+ "url": "https://www.capitaldata.fr/",
+ "companyId": "highco"
},
"captora": {
"name": "Captora",
"categoryId": 4,
- "url": "http://www.captora.com/"
+ "url": "http://www.captora.com/",
+ "companyId": "captora"
},
"capture_media": {
"name": "Capture Media",
"categoryId": 4,
- "url": "http://capturemedia.ch/"
+ "url": "http://capturemedia.ch/",
+ "companyId": "capture_media"
},
"capturly": {
"name": "Capturly",
"categoryId": 6,
- "url": "http://capturly.com/"
+ "url": "http://capturly.com/",
+ "companyId": "capturly"
},
"carambola": {
"name": "Carambola",
"categoryId": 4,
- "url": "http://carambo.la/"
+ "url": "http://carambo.la/",
+ "companyId": "carambola"
},
"carbonads": {
"name": "Carbon Ads",
"categoryId": 4,
- "url": "https://www.carbonads.net/"
+ "url": "https://www.carbonads.net/",
+ "companyId": "buysellads.com"
},
"cardinal": {
"name": "Cardinal",
"categoryId": 6,
- "url": "https://www.cardinalcommerce.com/"
+ "url": "https://www.cardinalcommerce.com/",
+ "companyId": "visa"
},
"cardlytics": {
"name": "Cardlytics",
"categoryId": 6,
- "url": "http://www.cardlytics.com/"
+ "url": "http://www.cardlytics.com/",
+ "companyId": null
},
"carrot_quest": {
"name": "Carrot Quest",
"categoryId": 6,
- "url": "http://www.carrotquest.io/"
+ "url": "http://www.carrotquest.io/",
+ "companyId": "carrot_quest"
},
"cartstack": {
"name": "CartStack",
"categoryId": 2,
- "url": "http://cartstack.com/"
+ "url": "http://cartstack.com/",
+ "companyId": "cartstack"
},
"caspion": {
"name": "Caspion",
"categoryId": 6,
- "url": "http://caspion.com/"
+ "url": "http://caspion.com/",
+ "companyId": "caspion"
},
"castle": {
"name": "Castle",
"categoryId": 2,
- "url": "https://castle.io"
+ "url": "https://castle.io",
+ "companyId": "castle"
},
"catchpoint": {
"name": "Catchpoint",
"categoryId": 6,
- "url": "http://www.catchpoint.com/"
+ "url": "http://www.catchpoint.com/",
+ "companyId": "catchpoint_systems"
},
"cbox": {
"name": "Cbox",
"categoryId": 2,
- "url": "http://cbox.ws"
+ "url": "http://cbox.ws",
+ "companyId": "cbox"
},
"cbs_interactive": {
"name": "CBS Interactive",
"categoryId": 0,
- "url": "http://www.cbsinteractive.com/"
+ "url": "http://www.cbsinteractive.com/",
+ "companyId": "cbs_interactive"
},
"ccm_benchmark": {
"name": "CCM Benchmark",
"categoryId": 4,
- "url": "http://www.ccmbenchmark.com/"
+ "url": "http://www.ccmbenchmark.com/",
+ "companyId": null
},
"cdk_digital_marketing": {
"name": "CDK Digital Marketing",
"categoryId": 4,
- "url": "http://www.cobaltgroup.com"
+ "url": "http://www.cobaltgroup.com",
+ "companyId": "cdk_digital_marketing"
},
"cdn-net.com": {
"name": "cdn-net.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"cdn13.com": {
"name": "cdn13.com",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"cdn77": {
"name": "CDN77",
"categoryId": 9,
- "url": "https://www.cdn77.com/"
+ "url": "https://www.cdn77.com/",
+ "companyId": null
},
"cdnetworks.net": {
"name": "cdnetworks.net",
"categoryId": 9,
- "url": "https://www.cdnetworks.com/"
+ "url": "https://www.cdnetworks.com/",
+ "companyId": null
},
"cdnnetwok_xyz": {
"name": "cdnnetwok.xyz",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"cdnondemand.org": {
"name": "cdnondemand.org",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"cdnsure.com": {
"name": "cdnsure.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"cdnvideo.com": {
"name": "CDNvideo",
"categoryId": 9,
- "url": "https://www.cdnvideo.com/"
+ "url": "https://www.cdnvideo.com/",
+ "companyId": "cdnvideo"
},
"cdnwidget.com": {
"name": "cdnwidget.com",
"categoryId": 9,
- "url": null
+ "url": null,
+ "companyId": null
},
"cedexis_radar": {
"name": "Cedexis Radar",
"categoryId": 6,
- "url": "http://www.cedexis.com/products_radar.html"
+ "url": "http://www.cedexis.com/products_radar.html",
+ "companyId": "cedexis"
},
"celebrus": {
"name": "Celebrus",
"categoryId": 6,
- "url": "https://www.celebrus.com/"
+ "url": "https://www.celebrus.com/",
+ "companyId": "celebrus"
},
"celtra": {
"name": "Celtra",
"categoryId": 0,
- "url": "http://www.celtra.com/"
+ "url": "http://www.celtra.com/",
+ "companyId": "celtra"
},
"cendyn": {
"name": "Cendyn",
"categoryId": 4,
- "url": "http://www.cendyn.com/"
+ "url": "http://www.cendyn.com/",
+ "companyId": "cendyn"
},
"centraltag": {
"name": "CentralTag",
"categoryId": 4,
- "url": "http://www.centraltag.com/"
+ "url": "http://www.centraltag.com/",
+ "companyId": "centraltag"
},
"centro": {
"name": "Centro",
"categoryId": 4,
- "url": "http://centro.net/"
+ "url": "http://centro.net/",
+ "companyId": "centro"
},
"cerberus_speed-trap": {
"name": "Cerberus Speed-Trap",
"categoryId": 6,
- "url": "http://cerberusip.com/"
+ "url": "http://cerberusip.com/",
+ "companyId": "cerberus"
},
"certainsource": {
"name": "CertainSource",
"categoryId": 4,
- "url": "http://www.ewaydirect.com"
+ "url": "http://www.ewaydirect.com",
+ "companyId": "certainsource"
},
"certifica_metric": {
"name": "Certifica Metric",
"categoryId": 4,
- "url": "http://www.comscore.com/Products_Services/Product_Index/Certifica_Metric"
+ "url": "http://www.comscore.com/Products_Services/Product_Index/Certifica_Metric",
+ "companyId": "comscore"
},
"certona": {
"name": "Certona",
"categoryId": 4,
- "url": "http://www.certona.com/products/recommendation.php"
+ "url": "http://www.certona.com/products/recommendation.php",
+ "companyId": "certona"
},
"chameleon": {
"name": "Chameleon",
"categoryId": 4,
- "url": "http://chameleon.ad/"
+ "url": "http://chameleon.ad/",
+ "companyId": "chamaleon"
},
"chango": {
"name": "Chango",
"categoryId": 4,
- "url": "http://www.chango.com/"
+ "url": "http://www.chango.com/",
+ "companyId": "rubicon_project"
},
"channel_intelligence": {
"name": "Channel Intelligence",
"categoryId": 4,
- "url": "http://www.channelintelligence.com/"
+ "url": "http://www.channelintelligence.com/",
+ "companyId": "google"
},
"channel_pilot_solutions": {
"name": "ChannelPilot Solutions",
"categoryId": 6,
- "url": "https://www.channelpilot.de/"
+ "url": "https://www.channelpilot.de/",
+ "companyId": null
},
"channeladvisor": {
"name": "ChannelAdvisor",
"categoryId": 4,
- "url": "http://www.channeladvisor.com/"
+ "url": "http://www.channeladvisor.com/",
+ "companyId": "channeladvisor"
},
"channelfinder": {
"name": "ChannelFinder",
"categoryId": 4,
- "url": "http://www.kpicentral.com/"
+ "url": "http://www.kpicentral.com/",
+ "companyId": "kaleidoscope_promotions"
},
"chaordic": {
"name": "Chaordic",
"categoryId": 4,
- "url": "https://www.chaordic.com.br/"
+ "url": "https://www.chaordic.com.br/",
+ "companyId": "chaordic"
},
"chartbeat": {
"name": "ChartBeat",
"categoryId": 6,
- "url": "http://chartbeat.com/"
+ "url": "http://chartbeat.com/",
+ "companyId": "chartbeat"
},
"chaser": {
"name": "Chaser",
"categoryId": 2,
- "url": "http://chaser.ru/"
+ "url": "http://chaser.ru/",
+ "companyId": "chaser"
},
"chat_beacon": {
"name": "Chat Beacon",
"categoryId": 2,
- "url": "https://www.chatbeacon.io/"
+ "url": "https://www.chatbeacon.io/",
+ "companyId": "chat_beacon"
},
"chatango": {
"name": "Chatango",
"categoryId": 2,
- "url": "http://www.chatango.com/"
+ "url": "http://www.chatango.com/",
+ "companyId": "chatango"
},
"chatra": {
"name": "Chatra",
"categoryId": 2,
- "url": "https://chatra.io"
+ "url": "https://chatra.io",
+ "companyId": "chatra"
},
"chaturbate.com": {
"name": "chaturbate.com",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"chatwing": {
"name": "ChatWing",
"categoryId": 2,
- "url": "http://chatwing.com/"
+ "url": "http://chatwing.com/",
+ "companyId": "chatwing"
},
"checkmystats": {
"name": "CheckMyStats",
"categoryId": 4,
- "url": "http://checkmystats.com.au"
+ "url": "http://checkmystats.com.au",
+ "companyId": "checkmystats"
},
"chefkoch_de": {
"name": "chefkoch.de",
"categoryId": 8,
- "url": "http://chefkoch.de/"
+ "url": "http://chefkoch.de/",
+ "companyId": null
},
"chin_media": {
"name": "Chin Media",
"categoryId": 4,
- "url": "http://www.chinmedia.vn/#"
+ "url": "http://www.chinmedia.vn/#",
+ "companyId": "chin_media"
},
"chinesean": {
"name": "ChineseAN",
"categoryId": 4,
- "url": "http://www.chinesean.com/"
+ "url": "http://www.chinesean.com/",
+ "companyId": "chinesean"
},
"chitika": {
"name": "Chitika",
"categoryId": 4,
- "url": "http://chitika.com/"
+ "url": "http://chitika.com/",
+ "companyId": "chitika"
},
"choicestream": {
"name": "ChoiceStream",
"categoryId": 4,
- "url": "http://www.choicestream.com/"
+ "url": "http://www.choicestream.com/",
+ "companyId": "choicestream"
},
"chute": {
"name": "Chute",
"categoryId": 5,
- "url": "https://www.getchute.com/"
+ "url": "https://www.getchute.com/",
+ "companyId": "esw_capital"
},
"circit": {
"name": "circIT",
"categoryId": 6,
- "url": "http://www.circit.de/"
+ "url": "http://www.circit.de/",
+ "companyId": null
},
"circulate": {
"name": "Circulate",
"categoryId": 6,
- "url": "http://circulate.com/"
+ "url": "http://circulate.com/",
+ "companyId": "circulate"
},
"city_spark": {
"name": "City Spark",
"categoryId": 4,
- "url": "http://www.cityspark.com/"
+ "url": "http://www.cityspark.com/",
+ "companyId": "city_spark"
},
"cityads": {
"name": "CityAds",
"categoryId": 4,
- "url": "http://cityads.ru/"
+ "url": "http://cityads.ru/",
+ "companyId": "cityads"
},
"ciuvo.com": {
"name": "ciuvo.com",
"categoryId": 12,
- "url": "https://www.ciuvo.com/"
+ "url": "https://www.ciuvo.com/",
+ "companyId": null
},
"civey_widgets": {
"name": "Civey Widgets",
"categoryId": 2,
- "url": "https://civey.com/"
+ "url": "https://civey.com/",
+ "companyId": "civey"
},
"civicscience.com": {
"name": "CivicScience",
"categoryId": 6,
- "url": "https://civicscience.com/"
+ "url": "https://civicscience.com/",
+ "companyId": "civicscience"
},
"ciwebgroup": {
"name": "CIWebGroup",
"categoryId": 4,
- "url": "http://www.ciwebgroup.com/"
+ "url": "http://www.ciwebgroup.com/",
+ "companyId": "ciwebgroup"
},
"clcknads.pro": {
"name": "clcknads.pro",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"clear_pier": {
"name": "ClearPier",
"categoryId": 4,
- "url": "http://clearpier.com/"
+ "url": "http://clearpier.com/",
+ "companyId": "clear_pier"
},
"clearbit.com": {
"name": "Clearbit",
"categoryId": 6,
- "url": "https://clearbit.com/"
+ "url": "https://clearbit.com/",
+ "companyId": "clearbit"
},
"clearsale": {
"name": "clearsale",
"categoryId": 4,
- "url": "https://www.clear.sale/"
+ "url": "https://www.clear.sale/",
+ "companyId": null
},
"clearstream.tv": {
"name": "Clearstream.TV",
"categoryId": 4,
- "url": "http://clearstream.tv/"
+ "url": "http://clearstream.tv/",
+ "companyId": "clearstream.tv"
},
"clerk.io": {
"name": "Clerk.io",
"categoryId": 4,
- "url": "https://clerk.io/"
+ "url": "https://clerk.io/",
+ "companyId": "clerk.io"
},
"clever_push": {
"name": "Clever Push",
"categoryId": 6,
- "url": "https://clevertap.com/"
+ "url": "https://clevertap.com/",
+ "companyId": "clever_push"
},
"clever_tap": {
"name": "CleverTap",
"categoryId": 6,
- "url": "https://clevertap.com/"
+ "url": "https://clevertap.com/",
+ "companyId": "clever_tap"
},
"cleversite": {
"name": "Cleversite",
"categoryId": 2,
- "url": "http://cleversite.ru/"
+ "url": "http://cleversite.ru/",
+ "companyId": "cleversite"
},
"click360": {
"name": "Click360",
"categoryId": 6,
- "url": "https://www.click360.io/"
+ "url": "https://www.click360.io/",
+ "companyId": "click360"
},
"click_and_chat": {
"name": "Click and Chat",
"categoryId": 2,
- "url": "http://www.clickandchat.com/"
+ "url": "http://www.clickandchat.com/",
+ "companyId": "clickandchat"
},
"click_back": {
"name": "Click Back",
"categoryId": 4,
- "url": "http://www.clickback.com/"
+ "url": "http://www.clickback.com/",
+ "companyId": "clickback"
},
"clickaider": {
"name": "ClickAider",
"categoryId": 4,
- "url": "http://clickaider.com/"
+ "url": "http://clickaider.com/",
+ "companyId": "clickaider"
},
"clickbank": {
"name": "ClickBank",
"categoryId": 4,
- "url": "http://www.clickbank.com/"
+ "url": "http://www.clickbank.com/",
+ "companyId": "clickbank"
},
"clickbank_proads": {
"name": "ClickBank ProAds",
"categoryId": 4,
- "url": "http://www.cbproads.com/"
+ "url": "http://www.cbproads.com/",
+ "companyId": "clickbank_proads"
},
"clickbooth": {
"name": "Clickbooth",
"categoryId": 4,
- "url": "http://www.clickbooth.com/"
+ "url": "http://www.clickbooth.com/",
+ "companyId": "clickbooth"
},
"clickcease": {
"name": "ClickCease",
"categoryId": 2,
- "url": "https://www.clickcease.com/"
+ "url": "https://www.clickcease.com/",
+ "companyId": "click_cease"
},
"clickcertain": {
"name": "ClickCertain",
"categoryId": 4,
- "url": "http://www.clickcertain.com"
+ "url": "http://www.clickcertain.com",
+ "companyId": "clickcertain"
},
"clickdesk": {
"name": "ClickDesk",
"categoryId": 2,
- "url": "https://www.clickdesk.com/"
+ "url": "https://www.clickdesk.com/",
+ "companyId": "clickdesk"
},
"clickdimensions": {
"name": "ClickDimensions",
"categoryId": 4,
- "url": "http://www.clickdimensions.com/"
+ "url": "http://www.clickdimensions.com/",
+ "companyId": "clickdimensions"
},
"clickequations": {
"name": "ClickEquations",
"categoryId": 4,
- "url": "http://www.clickequations.com/"
+ "url": "http://www.clickequations.com/",
+ "companyId": "acquisio"
},
"clickexperts": {
"name": "ClickExperts",
"categoryId": 4,
- "url": "http://clickexperts.com/corp/index.php?lang=en"
+ "url": "http://clickexperts.com/corp/index.php?lang=en",
+ "companyId": "clickexperts"
},
"clickforce": {
"name": "ClickForce",
"categoryId": 4,
- "url": "http://www.clickforce.com.tw/"
+ "url": "http://www.clickforce.com.tw/",
+ "companyId": "clickforce"
},
"clickinc": {
"name": "ClickInc",
"categoryId": 4,
- "url": "http://www.clickinc.com"
+ "url": "http://www.clickinc.com",
+ "companyId": "clickinc"
},
"clickintext": {
"name": "ClickInText",
"categoryId": 4,
- "url": "http://www.clickintext.com/"
+ "url": "http://www.clickintext.com/",
+ "companyId": "clickintext"
},
"clickky": {
"name": "Clickky",
"categoryId": 4,
- "url": "http://www.clickky.biz/"
+ "url": "http://www.clickky.biz/",
+ "companyId": "clickky"
},
"clickmeter": {
"name": "ClickMeter",
"categoryId": 4,
- "url": "http://www.clickmeter.com"
+ "url": "http://www.clickmeter.com",
+ "companyId": "clickmeter"
},
"clickonometrics": {
"name": "Clickonometrics",
"categoryId": 4,
- "url": "http://clickonometrics.pl/"
+ "url": "http://clickonometrics.pl/",
+ "companyId": "clickonometrics"
},
"clickpoint": {
"name": "Clickpoint",
"categoryId": 4,
- "url": "http://clickpoint.com/"
+ "url": "http://clickpoint.com/",
+ "companyId": "clickpoint"
},
"clickprotector": {
"name": "ClickProtector",
"categoryId": 6,
- "url": "http://www.clickprotector.com/"
+ "url": "http://www.clickprotector.com/",
+ "companyId": "clickprotector"
},
"clickreport": {
"name": "ClickReport",
"categoryId": 6,
- "url": "http://clickreport.com/"
+ "url": "http://clickreport.com/",
+ "companyId": "clickreport"
},
"clicks_thru_networks": {
"name": "Clicks Thru Networks",
"categoryId": 4,
- "url": "http://www.clicksthrunetwork.com/"
+ "url": "http://www.clicksthrunetwork.com/",
+ "companyId": "clicksthrunetwork"
},
"clicksor": {
"name": "Clicksor",
"categoryId": 4,
- "url": "http://clicksor.com/"
+ "url": "http://clicksor.com/",
+ "companyId": "clicksor"
},
"clicktale": {
"name": "ClickTale",
"categoryId": 6,
- "url": "http://www.clicktale.com/"
+ "url": "http://www.clicktale.com/",
+ "companyId": "clicktale"
},
"clicktripz": {
"name": "ClickTripz",
"categoryId": 4,
- "url": "https://www.clicktripz.com"
+ "url": "https://www.clicktripz.com",
+ "companyId": "clicktripz"
},
"clickwinks": {
"name": "Clickwinks",
"categoryId": 4,
- "url": "http://www.clickwinks.com/"
+ "url": "http://www.clickwinks.com/",
+ "companyId": "clickwinks"
},
"clicky": {
"name": "Clicky",
"categoryId": 6,
- "url": "http://getclicky.com/"
+ "url": "http://getclicky.com/",
+ "companyId": "clicky"
},
"clickyab": {
"name": "Clickyab",
"categoryId": 4,
- "url": "https://www.clickyab.com/"
+ "url": "https://www.clickyab.com/",
+ "companyId": "clickyab"
},
"clicmanager": {
"name": "ClicManager",
"categoryId": 4,
- "url": "http://www.clicmanager.fr/"
+ "url": "http://www.clicmanager.fr/",
+ "companyId": "clicmanager"
},
"clip_syndicate": {
"name": "Clip Syndicate",
"categoryId": 4,
- "url": "http://www.clipsyndicate.com/"
+ "url": "http://www.clipsyndicate.com/",
+ "companyId": "clip_syndicate"
},
"clixgalore": {
"name": "clixGalore",
"categoryId": 4,
- "url": "http://www.clixgalore.com/"
+ "url": "http://www.clixgalore.com/",
+ "companyId": "clixgalore"
},
"clixmetrix": {
"name": "ClixMetrix",
"categoryId": 4,
- "url": "http://www.clixmetrix.com/"
+ "url": "http://www.clixmetrix.com/",
+ "companyId": "clixmedia"
},
"clixsense": {
"name": "ClixSense",
"categoryId": 4,
- "url": "http://www.clixsense.com/"
+ "url": "http://www.clixsense.com/",
+ "companyId": "clixsense"
},
"cloud-media.fr": {
"name": "CloudMedia",
"categoryId": 4,
- "url": "https://cloudmedia.fr/"
+ "url": "https://cloudmedia.fr/",
+ "companyId": null
},
"cloudflare": {
"name": "CloudFlare",
"categoryId": 9,
- "url": "https://www.cloudflare.com/"
+ "url": "https://www.cloudflare.com/",
+ "companyId": "cloudflare"
},
"cloudimage.io": {
"name": "Cloudimage.io",
"categoryId": 9,
- "url": "https://www.cloudimage.io/en/home"
+ "url": "https://www.cloudimage.io/en/home",
+ "companyId": "scaleflex_sas"
},
"cloudinary": {
"name": "Cloudinary",
"categoryId": 9,
- "url": "https://cloudinary.com/"
+ "url": "https://cloudinary.com/",
+ "companyId": null
},
"clove_network": {
"name": "Clove Network",
"categoryId": 4,
- "url": "http://www.clovenetwork.com/"
+ "url": "http://www.clovenetwork.com/",
+ "companyId": "clove_network"
},
"clustrmaps": {
"name": "ClustrMaps",
"categoryId": 4,
- "url": "http://www.clustrmaps.com/"
+ "url": "http://www.clustrmaps.com/",
+ "companyId": "clustrmaps"
},
"cnbc": {
"name": "CNBC",
"categoryId": 8,
- "url": "https://www.cnbc.com/"
+ "url": "https://www.cnbc.com/",
+ "companyId": "nbcuniversal"
},
"cnetcontent.com": {
"name": "Cnetcontent",
"categoryId": 8,
- "url": "http://cnetcontent.com/"
+ "url": "http://cnetcontent.com/",
+ "companyId": "cbs_interactive"
},
"cnstats": {
"name": "CNStats",
"categoryId": 6,
- "url": "http://cnstats.ru/"
+ "url": "http://cnstats.ru/",
+ "companyId": "cnstats"
},
"cnzz.com": {
"name": "Umeng",
"categoryId": 6,
- "url": "http://www.umeng.com/"
+ "url": "http://www.umeng.com/",
+ "companyId": "umeng"
},
"coadvertise": {
"name": "COADVERTISE",
"categoryId": 4,
- "url": "http://www.coadvertise.com/"
+ "url": "http://www.coadvertise.com/",
+ "companyId": "coadvertise"
},
"cobrowser": {
"name": "CoBrowser",
"categoryId": 2,
- "url": "https://www.cobrowser.net/"
+ "url": "https://www.cobrowser.net/",
+ "companyId": "cobrowser.net"
},
"codeonclick.com": {
"name": "codeonclick.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"cogocast": {
"name": "CogoCast",
"categoryId": 4,
- "url": "http://www.cogocast.com"
+ "url": "http://www.cogocast.com",
+ "companyId": "cogocast"
},
"coin_have": {
"name": "Coin Have",
"categoryId": 4,
- "url": "https://coin-have.com/"
+ "url": "https://coin-have.com/",
+ "companyId": "coin_have"
},
"coin_traffic": {
"name": "Coin Traffic",
"categoryId": 2,
- "url": "https://cointraffic.io/"
+ "url": "https://cointraffic.io/",
+ "companyId": "coin_traffic"
},
"coinhive": {
"name": "Coinhive",
"categoryId": 8,
- "url": "https://coinhive.com/"
+ "url": "https://coinhive.com/",
+ "companyId": "coinhive"
},
"coinurl": {
"name": "CoinURL",
"categoryId": 4,
- "url": "https://coinurl.com/"
+ "url": "https://coinurl.com/",
+ "companyId": "coinurl"
},
"coll1onf.com": {
"name": "coll1onf.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"coll2onf.com": {
"name": "coll2onf.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"collarity": {
"name": "Collarity",
"categoryId": 4,
- "url": "http://www.collarity.com/"
+ "url": "http://www.collarity.com/",
+ "companyId": "collarity"
},
"columbia_online": {
"name": "Columbia Online",
"categoryId": 4,
- "url": "https://www.colombiaonline.com/"
+ "url": "https://www.colombiaonline.com/",
+ "companyId": "columbia_online"
},
"combotag": {
"name": "ComboTag",
"categoryId": 4,
- "url": "https://www.combotag.com/"
+ "url": "https://www.combotag.com/",
+ "companyId": null
},
"comcast_technology_solutions": {
"name": "Comcast Technology Solutions",
"categoryId": 0,
- "url": "https://www.comcasttechnologysolutions.com/"
+ "url": "https://www.comcasttechnologysolutions.com/",
+ "companyId": "comcast_technology_solutions"
},
"comm100": {
"name": "Comm100",
"categoryId": 2,
- "url": "http://www.comm100.com/"
+ "url": "http://www.comm100.com/",
+ "companyId": "comm100"
},
"commerce_sciences": {
"name": "Commerce Sciences",
"categoryId": 4,
- "url": "http://commercesciences.com/"
+ "url": "http://commercesciences.com/",
+ "companyId": "commerce_sciences"
},
"commercehub": {
"name": "CommerceHub",
"categoryId": 4,
- "url": "http://www.mercent.com/"
+ "url": "http://www.mercent.com/",
+ "companyId": "commercehub"
},
"commercialvalue.org": {
"name": "commercialvalue.org",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"commission_junction": {
"name": "CJ Affiliate",
"categoryId": 4,
- "url": "http://www.cj.com/"
+ "url": "http://www.cj.com/",
+ "companyId": "conversant"
},
"communicator_corp": {
"name": "Communicator Corp",
"categoryId": 4,
- "url": "http://www.communicatorcorp.com/"
+ "url": "http://www.communicatorcorp.com/",
+ "companyId": "communicator_corp"
},
"communigator": {
"name": "CommuniGator",
"categoryId": 6,
- "url": "http://www.wowanalytics.co.uk/"
+ "url": "http://www.wowanalytics.co.uk/",
+ "companyId": "communigator"
},
"competexl": {
"name": "CompeteXL",
"categoryId": 6,
- "url": "http://www.compete.com/help/s12"
+ "url": "http://www.compete.com/help/s12",
+ "companyId": "wpp"
},
"complex_media_network": {
"name": "Complex Media",
"categoryId": 4,
- "url": "https://www.complex.com/"
+ "url": "https://www.complex.com/",
+ "companyId": "verizon"
},
"comprigo": {
"name": "comprigo",
"categoryId": 12,
- "url": "https://www.comprigo.com/"
+ "url": "https://www.comprigo.com/",
+ "companyId": null
},
"comscore": {
"name": "ComScore, Inc.",
"categoryId": 6,
- "url": "https://www.comscore.com/"
+ "url": "https://www.comscore.com/",
+ "companyId": "comscore"
},
"conative.de": {
"name": "CoNative",
"categoryId": 4,
- "url": "http://www.conative.de/"
+ "url": "http://www.conative.de/",
+ "companyId": null
},
"condenastdigital.com": {
"name": "Condé Nast Digital",
"categoryId": 8,
- "url": "http://www.condenast.com/"
+ "url": "http://www.condenast.com/",
+ "companyId": "conde_nast"
},
"conduit": {
"name": "Conduit",
"categoryId": 4,
- "url": "http://www.conduit.com/"
+ "url": "http://www.conduit.com/",
+ "companyId": "conduit"
},
"confirmit": {
"name": "Confirmit",
"categoryId": 4,
- "url": "http://confirmit.com/"
+ "url": "http://confirmit.com/",
+ "companyId": "confirmit"
},
"congstar.de": {
"name": "congstar.de",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"connatix.com": {
"name": "Connatix",
"categoryId": 4,
- "url": "https://connatix.com/"
+ "url": "https://connatix.com/",
+ "companyId": "connatix"
},
"connectad": {
"name": "ConnectAd",
"categoryId": 4,
- "url": "https://connectad.io/"
+ "url": "https://connectad.io/",
+ "companyId": "connectad"
},
"connecto": {
"name": "Connecto",
"categoryId": 6,
- "url": "http://www.connecto.io/"
+ "url": "http://www.connecto.io/",
+ "companyId": "connecto"
},
"connexity": {
"name": "Connexity",
"categoryId": 4,
- "url": "http://www.connexity.com"
+ "url": "http://www.connexity.com",
+ "companyId": "shopzilla"
},
"connextra": {
"name": "Connextra",
"categoryId": 4,
- "url": "http://connextra.com/"
+ "url": "http://connextra.com/",
+ "companyId": "connextra"
},
"constant_contact": {
"name": "Constant Contact",
"categoryId": 4,
- "url": "http://www.constantcontact.com/index.jsp"
+ "url": "http://www.constantcontact.com/index.jsp",
+ "companyId": "constant_contact"
},
"consumable": {
"name": "Consumable",
"categoryId": 4,
- "url": "http://consumable.com/index.html"
+ "url": "http://consumable.com/index.html",
+ "companyId": "giftconnect"
},
"contact_at_once": {
"name": "Contact At Once!",
"categoryId": 2,
- "url": "http://www.contactatonce.com/"
+ "url": "http://www.contactatonce.com/",
+ "companyId": "contact_at_once!"
},
"contact_impact": {
"name": "Contact Impact",
"categoryId": 4,
- "url": "https://www.contactimpact.de/"
+ "url": "https://www.contactimpact.de/",
+ "companyId": "axel_springer"
},
"contactme": {
"name": "ContactMe",
"categoryId": 4,
- "url": "http://www.contactme.com"
+ "url": "http://www.contactme.com",
+ "companyId": "contactme"
},
"contaxe": {
"name": "Contaxe",
"categoryId": 5,
- "url": "http://www.contaxe.com/"
+ "url": "http://www.contaxe.com/",
+ "companyId": "contaxe"
},
"content.ad": {
"name": "Content.ad",
"categoryId": 4,
- "url": "https://www.content.ad/"
+ "url": "https://www.content.ad/",
+ "companyId": "content.ad"
},
"content_insights": {
"name": "Content Insights",
"categoryId": 6,
- "url": "https://contentinsights.com/"
+ "url": "https://contentinsights.com/",
+ "companyId": "content_insights"
},
"contentexchange.me": {
"name": "Content Exchange",
"categoryId": 6,
- "url": "https://www.contentexchange.me/"
+ "url": "https://www.contentexchange.me/",
+ "companyId": "i.r.v."
},
"contentful_gmbh": {
"name": "Contentful GmbH",
"categoryId": 9,
- "url": "https://www.contentful.com/"
+ "url": "https://www.contentful.com/",
+ "companyId": "contentful_gmbh"
},
"contentpass": {
"name": "ContentPass",
"categoryId": 6,
- "url": "https://www.contentpass.de/"
+ "url": "https://www.contentpass.de/",
+ "companyId": "contentpass"
},
"contentsquare.net": {
"name": "ContentSquare",
"categoryId": 4,
- "url": "https://www.contentsquare.com/"
+ "url": "https://www.contentsquare.com/",
+ "companyId": "content_square"
},
"contentwrx": {
"name": "Contentwrx",
"categoryId": 6,
- "url": "http://contentwrx.com/"
+ "url": "http://contentwrx.com/",
+ "companyId": "contentwrx"
},
"context": {
"name": "C|ON|TEXT",
"categoryId": 4,
- "url": "http://c-on-text.com"
+ "url": "http://c-on-text.com",
+ "companyId": "c_on_text"
},
"context.ad": {
"name": "Context.ad",
"categoryId": 4,
- "url": "http://contextad.pl/"
+ "url": "http://contextad.pl/",
+ "companyId": "context.ad"
},
"continum_net": {
"name": "continum.net",
"categoryId": 10,
- "url": "http://continum.net/"
+ "url": "http://continum.net/",
+ "companyId": null
},
"contribusource": {
"name": "Contribusource",
"categoryId": 4,
- "url": "https://www.contribusource.com/"
+ "url": "https://www.contribusource.com/",
+ "companyId": "contribusource"
},
"convergetrack": {
"name": "ConvergeTrack",
"categoryId": 6,
- "url": "http://www.convergedirect.com/technology/convergetrack.shtml"
+ "url": "http://www.convergedirect.com/technology/convergetrack.shtml",
+ "companyId": "convergedirect"
},
"conversant": {
"name": "Conversant",
"categoryId": 4,
- "url": "https://www.conversantmedia.eu/"
+ "url": "https://www.conversantmedia.eu/",
+ "companyId": "conversant"
},
"conversio": {
"name": "CM Commerce",
"categoryId": 6,
- "url": "https://cm-commerce.com/"
+ "url": "https://cm-commerce.com/",
+ "companyId": "conversio"
},
"conversion_logic": {
"name": "Conversion Logic",
"categoryId": 6,
- "url": "http://www.conversionlogic.com/"
+ "url": "http://www.conversionlogic.com/",
+ "companyId": "conversion_logic"
},
"conversionruler": {
"name": "ConversionRuler",
"categoryId": 4,
- "url": "http://www.conversionruler.com/"
+ "url": "http://www.conversionruler.com/",
+ "companyId": "market_ruler"
},
"conversions_box": {
"name": "Conversions Box",
"categoryId": 7,
- "url": "http://www.conversionsbox.com/"
+ "url": "http://www.conversionsbox.com/",
+ "companyId": "conversions_box"
},
"conversions_on_demand": {
"name": "Conversions On Demand",
"categoryId": 5,
- "url": "https://www.conversionsondemand.com/"
+ "url": "https://www.conversionsondemand.com/",
+ "companyId": "conversions_on_demand"
},
"conversive": {
"name": "Conversive",
"categoryId": 4,
- "url": "http://www.conversive.nl/"
+ "url": "http://www.conversive.nl/",
+ "companyId": "conversive"
},
"convert": {
"name": "Convert",
"categoryId": 6,
- "url": "https://www.convert.com/"
+ "url": "https://www.convert.com/",
+ "companyId": "convert"
},
"convertfox": {
"name": "ConvertFox",
"categoryId": 2,
- "url": "https://convertfox.com/"
+ "url": "https://convertfox.com/",
+ "companyId": "convertfox"
},
"convertro": {
"name": "Convertro",
"categoryId": 4,
- "url": "http://www.convertro.com/"
+ "url": "http://www.convertro.com/",
+ "companyId": "verizon"
},
"conviva": {
"name": "Conviva",
"categoryId": 6,
- "url": "http://www.conviva.com/"
+ "url": "http://www.conviva.com/",
+ "companyId": "conviva"
},
"cookie_consent": {
"name": "Cookie Consent",
"categoryId": 5,
- "url": "https://silktide.com/"
+ "url": "https://silktide.com/",
+ "companyId": "silktide"
},
"cookie_script": {
"name": "Cookie Script",
"categoryId": 5,
- "url": "https://cookie-script.com/"
+ "url": "https://cookie-script.com/",
+ "companyId": "cookie_script"
},
"cookiebot": {
"name": "Cookiebot",
"categoryId": 5,
- "url": "https://www.cookiebot.com/en/"
+ "url": "https://www.cookiebot.com/en/",
+ "companyId": "cybot"
},
"cookieq": {
"name": "CookieQ",
"categoryId": 5,
- "url": "http://cookieq.com/CookieQ"
+ "url": "http://cookieq.com/CookieQ",
+ "companyId": "baycloud"
},
"cooliris": {
"name": "Cooliris",
"categoryId": 2,
- "url": "http://www.cooliris.com"
+ "url": "http://www.cooliris.com",
+ "companyId": "cooliris"
},
"copacet": {
"name": "Copacet",
"categoryId": 4,
- "url": "http://copacet.com/"
+ "url": "http://copacet.com/",
+ "companyId": "copacet"
},
"coreaudience": {
"name": "CoreAudience",
"categoryId": 4,
- "url": "http://www.redaril.com/"
+ "url": "http://www.redaril.com/",
+ "companyId": "hearst"
},
"coremotives": {
"name": "CoreMotives",
"categoryId": 4,
- "url": "http://coremotives.com/"
+ "url": "http://coremotives.com/",
+ "companyId": "coremotives"
},
"coull": {
"name": "Coull",
"categoryId": 4,
- "url": "http://coull.com/"
+ "url": "http://coull.com/",
+ "companyId": "coull"
},
"cpm_rocket": {
"name": "CPM Rocket",
"categoryId": 4,
- "url": "http://www.cpmrocket.com/"
+ "url": "http://www.cpmrocket.com/",
+ "companyId": "cpm_rocket"
},
"cpmprofit": {
"name": "CPMProfit",
"categoryId": 4,
- "url": "http://www.cpmprofit.com/"
+ "url": "http://www.cpmprofit.com/",
+ "companyId": "cpmprofit"
},
"cpmstar": {
"name": "CPMStar",
"categoryId": 4,
- "url": "http://www.cpmstar.com"
+ "url": "http://www.cpmstar.com",
+ "companyId": "cpmstar"
},
"cpx.to": {
"name": "Captify",
"categoryId": 4,
- "url": "https://www.captify.co.uk/"
+ "url": "https://www.captify.co.uk/",
+ "companyId": "captify"
},
"cq_counter": {
"name": "CQ Counter",
"categoryId": 6,
- "url": "http://www.cqcounter.com/"
+ "url": "http://www.cqcounter.com/",
+ "companyId": "cq_counter"
},
"cqq5id8n.com": {
"name": "cqq5id8n.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"cquotient.com": {
"name": "CQuotient",
"categoryId": 6,
- "url": "https://www.demandware.com/#cquotient"
+ "url": "https://www.demandware.com/#cquotient",
+ "companyId": "salesforce"
},
"craftkeys": {
"name": "CraftKeys",
"categoryId": 4,
- "url": "http://craftkeys.com/"
+ "url": "http://craftkeys.com/",
+ "companyId": "craftkeys"
},
"crakmedia_network": {
"name": "Crakmedia Network",
"categoryId": 4,
- "url": "http://crakmedia.com/"
+ "url": "http://crakmedia.com/",
+ "companyId": "crakmedia_network"
},
"crankyads": {
"name": "CrankyAds",
"categoryId": 4,
- "url": "http://www.crankyads.com"
+ "url": "http://www.crankyads.com",
+ "companyId": "crankyads"
},
"crazy_egg": {
"name": "Crazy Egg",
"categoryId": 6,
- "url": "http://crazyegg.com/"
+ "url": "http://crazyegg.com/",
+ "companyId": "crazy_egg"
},
"creafi": {
"name": "Creafi",
"categoryId": 4,
- "url": "http://www.creafi.com/en/home/"
+ "url": "http://www.creafi.com/en/home/",
+ "companyId": "crazy4media"
},
"createjs": {
"name": "CreateJS",
"categoryId": 9,
- "url": "https://createjs.com/"
+ "url": "https://createjs.com/",
+ "companyId": null
},
"creative_commons": {
"name": "Creative Commons",
"categoryId": 8,
- "url": "https://creativecommons.org/"
+ "url": "https://creativecommons.org/",
+ "companyId": "creative_commons_corp"
},
"crimsonhexagon_com": {
"name": "Brandwatch",
"categoryId": 6,
- "url": "https://www.brandwatch.com/"
+ "url": "https://www.brandwatch.com/",
+ "companyId": "brandwatch"
},
"crimtan": {
"name": "Crimtan",
"categoryId": 4,
- "url": "http://www.crimtan.com/"
+ "url": "http://www.crimtan.com/",
+ "companyId": "crimtan"
},
"crisp": {
"name": "Crisp",
"categoryId": 2,
- "url": "https://crisp.chat/"
+ "url": "https://crisp.chat/",
+ "companyId": "crisp"
},
"criteo": {
"name": "Criteo",
"categoryId": 4,
- "url": "http://www.criteo.com/"
+ "url": "http://www.criteo.com/",
+ "companyId": "criteo"
},
"crm4d": {
"name": "CRM4D",
"categoryId": 6,
- "url": "https://crm4d.com/"
+ "url": "https://crm4d.com/",
+ "companyId": "crm4d"
},
"crossengage": {
"name": "CrossEngage",
"categoryId": 6,
- "url": "https://www.crossengage.io/"
+ "url": "https://www.crossengage.io/",
+ "companyId": "crossengage"
},
"crosspixel": {
"name": "Cross Pixel",
"categoryId": 4,
- "url": "http://crosspixel.net/"
+ "url": "http://crosspixel.net/",
+ "companyId": "cross_pixel"
},
"crosssell.info": {
"name": "econda Cross Sell",
"categoryId": 4,
- "url": "https://www.econda.de/en/solutions/personalization/cross-sell/"
+ "url": "https://www.econda.de/en/solutions/personalization/cross-sell/",
+ "companyId": "econda"
},
"crossss": {
"name": "Crossss",
"categoryId": 4,
- "url": "http://crossss.ru/"
+ "url": "http://crossss.ru/",
+ "companyId": "crossss"
},
"crowd_ignite": {
"name": "Crowd Ignite",
"categoryId": 4,
- "url": "http://get.crowdignite.com/"
+ "url": "http://get.crowdignite.com/",
+ "companyId": "gorilla_nation_media"
},
"crowd_science": {
"name": "Crowd Science",
"categoryId": 4,
- "url": "http://www.crowdscience.com/"
+ "url": "http://www.crowdscience.com/",
+ "companyId": "crowd_science"
},
"crowdprocess": {
"name": "CrowdProcess",
"categoryId": 2,
- "url": "https://crowdprocess.com"
+ "url": "https://crowdprocess.com",
+ "companyId": "crowdprocess"
},
"crowdynews": {
"name": "Crowdynews",
"categoryId": 7,
- "url": "http://www.crowdynews.com/"
+ "url": "http://www.crowdynews.com/",
+ "companyId": "crowdynews"
},
"crownpeak": {
"name": "Crownpeak",
"categoryId": 5,
- "url": "https://www.crownpeak.com/"
+ "url": "https://www.crownpeak.com/",
+ "companyId": "crownpeak"
},
"cryptoloot_miner": {
"name": "CryptoLoot Miner",
"categoryId": 4,
- "url": "https://crypto-loot.com/"
+ "url": "https://crypto-loot.com/",
+ "companyId": "cryptoloot"
},
"ctnetwork": {
"name": "CTnetwork",
"categoryId": 4,
- "url": "http://ctnetwork.hu/"
+ "url": "http://ctnetwork.hu/",
+ "companyId": "ctnetwork"
},
"ctrlshift": {
"name": "CtrlShift",
"categoryId": 4,
- "url": "http://www.adzcentral.com/"
+ "url": "http://www.adzcentral.com/",
+ "companyId": "ctrlshift"
},
"cubed": {
"name": "Cubed",
"categoryId": 6,
- "url": "http://withcubed.com/"
+ "url": "http://withcubed.com/",
+ "companyId": "cubed_attribution"
},
"cuelinks": {
"name": "CueLinks",
"categoryId": 4,
- "url": "http://www.cuelinks.com/"
+ "url": "http://www.cuelinks.com/",
+ "companyId": "cuelinks"
},
"cup_interactive": {
"name": "Cup Interactive",
"categoryId": 4,
- "url": "http://www.cupinteractive.com/"
+ "url": "http://www.cupinteractive.com/",
+ "companyId": "cup_interactive"
},
"curse.com": {
"name": "Curse",
"categoryId": 8,
- "url": "https://www.curse.com/"
+ "url": "https://www.curse.com/",
+ "companyId": "amazon_associates"
},
"cursecdn.com": {
"name": "Curse CDN",
"categoryId": 9,
- "url": "https://www.curse.com/"
+ "url": "https://www.curse.com/",
+ "companyId": "amazon_associates"
},
"customer.io": {
"name": "Customer.io",
"categoryId": 2,
- "url": "http://www.customer.io/"
+ "url": "http://www.customer.io/",
+ "companyId": "customer.io"
},
"customerly": {
"name": "Customerly",
"categoryId": 2,
- "url": "https://www.customerly.io/"
+ "url": "https://www.customerly.io/",
+ "companyId": "customerly"
},
"cxense": {
"name": "cXense",
"categoryId": 4,
- "url": "http://www.cxense.com/"
+ "url": "http://www.cxense.com/",
+ "companyId": "cxense"
},
"cxo.name": {
"name": "Chip Analytics",
"categoryId": 6,
- "url": "http://www.chip.de/"
+ "url": "http://www.chip.de/",
+ "companyId": null
},
"cyber_wing": {
"name": "Cyber Wing",
"categoryId": 4,
- "url": "http://www.cyberwing.co.jp/"
+ "url": "http://www.cyberwing.co.jp/",
+ "companyId": "cyberwing"
},
"cybersource": {
"name": "CyberSource",
"categoryId": 6,
- "url": "https://www.cybersource.com/en-gb.html"
+ "url": "https://www.cybersource.com/en-gb.html",
+ "companyId": "visa"
},
"cygnus": {
"name": "Cygnus",
"categoryId": 4,
- "url": "http://www.cygnus.com/"
+ "url": "http://www.cygnus.com/",
+ "companyId": "cygnus"
},
"da-ads.com": {
"name": "da-ads.com",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"dailymail.co.uk": {
"name": "Daily Mail",
"categoryId": 8,
- "url": "http://www.dailymail.co.uk/home/index.html"
+ "url": "http://www.dailymail.co.uk/home/index.html",
+ "companyId": "dmg_media"
},
"dailymotion": {
"name": "Dailymotion",
"categoryId": 8,
- "url": "https://vivendi.com/"
+ "url": "https://vivendi.com/",
+ "companyId": "vivendi"
},
"dailymotion_advertising": {
"name": "Dailymotion Advertising",
"categoryId": 4,
- "url": "http://advertising.dailymotion.com/"
+ "url": "http://advertising.dailymotion.com/",
+ "companyId": "vivendi"
},
"daisycon": {
"name": "Daisycon",
"categoryId": 4,
- "url": "http://www.daisycon.com"
+ "url": "http://www.daisycon.com",
+ "companyId": "daisycon"
},
"dantrack.net": {
"name": "DANtrack",
"categoryId": 4,
- "url": "http://media.dantrack.net/privacy/"
+ "url": "http://media.dantrack.net/privacy/",
+ "companyId": "dentsu_aegis_network"
},
"darwin_marketing": {
"name": "Darwin Marketing",
"categoryId": 4,
- "url": "http://www.darwinmarketing.com/"
+ "url": "http://www.darwinmarketing.com/",
+ "companyId": "darwin_marketing"
},
"dashboard_ad": {
"name": "Dashboard Ad",
"categoryId": 4,
- "url": "http://www.dashboardad.com/"
+ "url": "http://www.dashboardad.com/",
+ "companyId": "premium_access"
},
"datacaciques.com": {
"name": "DataCaciques",
"categoryId": 6,
- "url": "http://www.datacaciques.com/"
+ "url": "http://www.datacaciques.com/",
+ "companyId": null
},
"datacoral": {
"name": "Datacoral",
"categoryId": 4,
- "url": "https://datacoral.com/"
+ "url": "https://datacoral.com/",
+ "companyId": "datacoral"
},
"datacrushers": {
"name": "Datacrushers",
"categoryId": 6,
- "url": "https://www.datacrushers.com/"
+ "url": "https://www.datacrushers.com/",
+ "companyId": "datacrushers"
},
"datadome": {
"name": "DataDome",
"categoryId": 6,
- "url": "https://datadome.co/"
+ "url": "https://datadome.co/",
+ "companyId": "datadome"
},
"datalicious_datacollector": {
"name": "Datalicious DataCollector",
"categoryId": 6,
- "url": "http://www.datalicious.com/"
+ "url": "http://www.datalicious.com/",
+ "companyId": "datalicious"
},
"datalicious_supertag": {
"name": "Datalicious SuperTag",
"categoryId": 5,
- "url": "http://www.datalicious.com/"
+ "url": "http://www.datalicious.com/",
+ "companyId": "datalicious"
},
"datalogix": {
"name": "Datalogix",
"categoryId": 4,
- "url": "https://www.oracle.com/corporate/acquisitions/datalogix/"
+ "url": "https://www.oracle.com/corporate/acquisitions/datalogix/",
+ "companyId": "oracle"
},
"datamind.ru": {
"name": "DataMind",
"categoryId": 4,
- "url": "http://datamind.ru/"
+ "url": "http://datamind.ru/",
+ "companyId": "datamind"
},
"datatables": {
"name": "DataTables",
"categoryId": 2,
- "url": "https://datatables.net/"
+ "url": "https://datatables.net/",
+ "companyId": null
},
"datawrkz": {
"name": "Datawrkz",
"categoryId": 4,
- "url": "http://datawrkz.com/"
+ "url": "http://datawrkz.com/",
+ "companyId": "datawrkz"
},
"dataxpand": {
"name": "Dataxpand",
"categoryId": 4,
- "url": "http://dataxpand.com/"
+ "url": "http://dataxpand.com/",
+ "companyId": "dataxpand"
},
"dataxu": {
"name": "DataXu",
"categoryId": 4,
- "url": "http://www.dataxu.com/"
+ "url": "http://www.dataxu.com/",
+ "companyId": "dataxu"
},
"datds.net": {
"name": "datds.net",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"datonics": {
"name": "Datonics",
"categoryId": 4,
- "url": "http://datonics.com/"
+ "url": "http://datonics.com/",
+ "companyId": "almondnet"
},
"datran": {
"name": "Pulsepoint",
"categoryId": 4,
- "url": "https://www.pulsepoint.com/"
+ "url": "https://www.pulsepoint.com/",
+ "companyId": "pulsepoint_ad_exchange"
},
"davebestdeals.com": {
"name": "davebestdeals.com",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"dawandastatic.com": {
"name": "Dawanda CDN",
"categoryId": 8,
- "url": "https://dawanda.com/"
+ "url": "https://dawanda.com/",
+ "companyId": null
},
"dc_stormiq": {
"name": "DC StormIQ",
"categoryId": 4,
- "url": "http://www.dc-storm.com/"
+ "url": "http://www.dc-storm.com/",
+ "companyId": "dc_storm"
},
"dcbap.com": {
"name": "dcbap.com",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"dcmn.com": {
"name": "DCMN",
"categoryId": 4,
- "url": "https://www.dcmn.com/"
+ "url": "https://www.dcmn.com/",
+ "companyId": null
},
"de_persgroep": {
"name": "De Persgroep",
"categoryId": 4,
- "url": "https://www.persgroep.nl"
+ "url": "https://www.persgroep.nl",
+ "companyId": "de_persgroep"
},
"deadline_funnel": {
"name": "Deadline Funnel",
"categoryId": 6,
- "url": "https://deadlinefunnel.com/"
+ "url": "https://deadlinefunnel.com/",
+ "companyId": "deadline_funnel"
},
"dealer.com": {
"name": "Dealer.com",
"categoryId": 6,
- "url": "http://www.dealer.com/"
+ "url": "http://www.dealer.com/",
+ "companyId": "dealer.com"
},
"decibel_insight": {
"name": "Decibel Insight",
"categoryId": 6,
- "url": "https://www.decibelinsight.com/"
+ "url": "https://www.decibelinsight.com/",
+ "companyId": "decibel_insight"
},
"dedicated_media": {
"name": "Dedicated Media",
"categoryId": 4,
- "url": "http://www.dedicatedmedia.com/"
+ "url": "http://www.dedicatedmedia.com/",
+ "companyId": "dedicated_media"
},
"deep.bi": {
"name": "Deep.BI",
"categoryId": 6,
- "url": "http://www.deep.bi/#"
+ "url": "http://www.deep.bi/#",
+ "companyId": "deep.bi"
},
"deepintent.com": {
"name": "DeepIntent",
"categoryId": 4,
- "url": "https://www.deepintent.com/"
+ "url": "https://www.deepintent.com/",
+ "companyId": "deep_intent"
},
"defpush.com": {
"name": "defpush.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"deichmann.com": {
"name": "deichmann.com",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"delacon": {
"name": "Delacon",
"categoryId": 6,
- "url": "http://www.delacon.com.au/"
+ "url": "http://www.delacon.com.au/",
+ "companyId": "delacon"
},
"delivr": {
"name": "Delivr",
"categoryId": 6,
- "url": "http://www.percentmobile.com/"
+ "url": "http://www.percentmobile.com/",
+ "companyId": "delivr"
},
"delta_projects": {
"name": "Delta Projects",
"categoryId": 4,
- "url": "http://www.adaction.se/"
+ "url": "http://www.adaction.se/",
+ "companyId": "delta_projects"
},
"deluxe": {
"name": "Deluxe",
"categoryId": 6,
- "url": "https://ww.deluxe.com/"
+ "url": "https://ww.deluxe.com/",
+ "companyId": "deluxe"
},
"delve_networks": {
"name": "Delve Networks",
"categoryId": 7,
- "url": "http://www.delvenetworks.com/"
+ "url": "http://www.delvenetworks.com/",
+ "companyId": "limelight_networks"
},
"demandbase": {
"name": "Demandbase",
"categoryId": 4,
- "url": "http://www.demandbase.com/"
+ "url": "http://www.demandbase.com/",
+ "companyId": "demandbase"
},
"demandmedia": {
"name": "DemandMedia",
"categoryId": 4,
- "url": "http://www.demandmedia.com"
+ "url": "http://www.demandmedia.com",
+ "companyId": "leaf_group"
},
"deqwas": {
"name": "Deqwas",
"categoryId": 6,
- "url": "http://www.deqwas.com/"
+ "url": "http://www.deqwas.com/",
+ "companyId": "deqwas"
},
"devatics": {
"name": "Devatics",
"categoryId": 2,
- "url": "http://www.devatics.co.uk/"
+ "url": "http://www.devatics.co.uk/",
+ "companyId": "devatics"
},
"developer_media": {
"name": "Developer Media",
"categoryId": 4,
- "url": "http://www.developermedia.com/"
+ "url": "http://www.developermedia.com/",
+ "companyId": "developer_media"
},
"deviantart.net": {
"name": "deviantart.net",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"dex_platform": {
"name": "DEX Platform",
"categoryId": 4,
- "url": "http://blueadvertise.com/"
+ "url": "http://blueadvertise.com/",
+ "companyId": "dex_platform"
},
"dgm": {
"name": "dgm",
"categoryId": 4,
- "url": "http://www.dgm-au.com/"
+ "url": "http://www.dgm-au.com/",
+ "companyId": "apd"
},
"dialogtech": {
"name": "Dialogtech",
"categoryId": 6,
- "url": "https://www.dialogtech.com/"
+ "url": "https://www.dialogtech.com/",
+ "companyId": "dialogtech"
},
"dianomi": {
"name": "Dianomi",
"categoryId": 4,
- "url": "http://www.dianomi.com/cms/"
+ "url": "http://www.dianomi.com/cms/",
+ "companyId": "dianomi"
},
"didit_blizzard": {
"name": "Didit Blizzard",
"categoryId": 4,
- "url": "http://www.didit.com/blizzard"
+ "url": "http://www.didit.com/blizzard",
+ "companyId": "didit"
},
"didit_maestro": {
"name": "Didit Maestro",
"categoryId": 4,
- "url": "http://www.didit.com/maestro"
+ "url": "http://www.didit.com/maestro",
+ "companyId": "didit"
},
"didomi": {
"name": "Didomi",
"categoryId": 5,
- "url": "https://www.didomi.io/en/"
+ "url": "https://www.didomi.io/en/",
+ "companyId": "didomi"
},
"digg_widget": {
"name": "Digg Widget",
"categoryId": 2,
- "url": "http://digg.com/apple/Digg_Widget"
+ "url": "http://digg.com/apple/Digg_Widget",
+ "companyId": "buysellads.com"
},
"digicert_trust_seal": {
"name": "Digicert Trust Seal",
"categoryId": 5,
- "url": "http://www.digicert.com/"
+ "url": "http://www.digicert.com/",
+ "companyId": "digicert"
},
"digidip": {
"name": "Digidip",
"categoryId": 4,
- "url": "http://www.digidip.net/"
+ "url": "http://www.digidip.net/",
+ "companyId": "digidip"
},
"digiglitz": {
"name": "Digiglitz",
"categoryId": 6,
- "url": "http://www.digiglitz.com/"
+ "url": "http://www.digiglitz.com/",
+ "companyId": "digiglitz"
},
"digilant": {
"name": "Digilant",
"categoryId": 4,
- "url": "https://www.digilant.com/"
+ "url": "https://www.digilant.com/",
+ "companyId": "digilant"
},
"digioh": {
"name": "Digioh",
"categoryId": 4,
- "url": "https://digioh.com/"
+ "url": "https://digioh.com/",
+ "companyId": null
},
"digital.gov": {
"name": "Digital.gov",
"categoryId": 6,
- "url": "https://digital.gov/"
+ "url": "https://digital.gov/",
+ "companyId": "us_government"
},
"digital_control_room": {
"name": "Digital Control Room",
"categoryId": 5,
- "url": "http://www.cookiereports.com/"
+ "url": "http://www.cookiereports.com/",
+ "companyId": "digital_control_room"
},
"digital_nomads": {
"name": "Digital Nomads",
"categoryId": 4,
- "url": "http://dnomads.net/"
+ "url": "http://dnomads.net/",
+ "companyId": null
},
"digital_remedy": {
"name": "Digital Remedy",
"categoryId": 4,
- "url": "https://www.digitalremedy.com/"
+ "url": "https://www.digitalremedy.com/",
+ "companyId": "digital_remedy"
},
"digital_river": {
"name": "Digital River",
"categoryId": 4,
- "url": "http://corporate.digitalriver.com"
+ "url": "http://corporate.digitalriver.com",
+ "companyId": "digital_river"
},
"digital_window": {
"name": "Digital Window",
"categoryId": 4,
- "url": "http://www.digitalwindow.com/"
+ "url": "http://www.digitalwindow.com/",
+ "companyId": "axel_springer"
},
"digiteka": {
"name": "Digiteka",
"categoryId": 4,
- "url": "http://digiteka.com/"
+ "url": "http://digiteka.com/",
+ "companyId": "digiteka"
},
"digitrust": {
"name": "DigiTrust",
"categoryId": 4,
- "url": "http://www.digitru.st/"
+ "url": "http://www.digitru.st/",
+ "companyId": "iab"
},
"dihitt_badge": {
"name": "diHITT Badge",
"categoryId": 7,
- "url": "http://www.dihitt.com.br/"
+ "url": "http://www.dihitt.com.br/",
+ "companyId": "dihitt"
},
"dimml": {
"name": "DimML",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"direct_keyword_link": {
"name": "Direct Keyword Link",
"categoryId": 4,
- "url": "http://www.keywordsconnect.com/"
+ "url": "http://www.keywordsconnect.com/",
+ "companyId": "direct_keyword_link"
},
"directadvert": {
"name": "Direct/ADVERT",
"categoryId": 4,
- "url": "http://www.directadvert.ru/"
+ "url": "http://www.directadvert.ru/",
+ "companyId": "directadvert"
},
"directrev": {
"name": "DirectREV",
"categoryId": 4,
- "url": "http://www.directrev.com/"
+ "url": "http://www.directrev.com/",
+ "companyId": "directrev"
},
"discord": {
"name": "Discord",
"categoryId": 2,
- "url": "https://discordapp.com/"
+ "url": "https://discordapp.com/",
+ "companyId": null
},
"display_block": {
"name": "display block",
"categoryId": 4,
- "url": "https://www.displayblock.com/"
+ "url": "https://www.displayblock.com/",
+ "companyId": "display_block"
},
"disqus": {
"name": "Disqus",
"categoryId": 1,
- "url": "https://disqus.com/"
+ "url": "https://disqus.com/",
+ "companyId": "zeta"
},
"disqus_ads": {
"name": "Disqus Ads",
"categoryId": 4,
- "url": "https://disqusads.com/"
+ "url": "https://disqusads.com/",
+ "companyId": "zeta"
},
"distil_tag": {
"name": "Distil Networks",
"categoryId": 5,
- "url": "https://www.distilnetworks.com/"
+ "url": "https://www.distilnetworks.com/",
+ "companyId": "distil_networks"
},
"districtm.io": {
"name": "district m",
"categoryId": 4,
- "url": "https://districtm.net/"
+ "url": "https://districtm.net/",
+ "companyId": "district_m"
},
"distroscale": {
"name": "Distroscale",
"categoryId": 6,
- "url": "http://www.distroscale.com/"
+ "url": "http://www.distroscale.com/",
+ "companyId": "distroscale"
},
"div.show": {
"name": "div.show",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"diva": {
"name": "DiVa",
"categoryId": 6,
- "url": "http://www.vertriebsassistent.de/"
+ "url": "http://www.vertriebsassistent.de/",
+ "companyId": "diva"
},
"divvit": {
"name": "Divvit",
"categoryId": 6,
- "url": "https://www.divvit.com/"
+ "url": "https://www.divvit.com/",
+ "companyId": "divvit"
},
"dm2": {
"name": "DM2",
"categoryId": 4,
- "url": "http://digitalmediamanagement.com/"
+ "url": "http://digitalmediamanagement.com/",
+ "companyId": "digital_media_management"
},
"dmg_media": {
"name": "DMG Media",
"categoryId": 8,
- "url": "https://www.dmgmedia.co.uk/"
+ "url": "https://www.dmgmedia.co.uk/",
+ "companyId": "dmgt"
},
"dmm": {
"name": "DMM",
"categoryId": 3,
- "url": "http://www.dmm.co.jp"
+ "url": "http://www.dmm.co.jp",
+ "companyId": "dmm.r18"
},
"dmwd": {
"name": "DMWD",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"dockvine": {
"name": "dockvine",
"categoryId": 2,
- "url": "https://www.dockvine.com"
+ "url": "https://www.dockvine.com",
+ "companyId": "dockvine"
},
"docler": {
"name": "Docler",
"categoryId": 0,
- "url": "https://www.doclerholding.com/en/about/companies/33/"
+ "url": "https://www.doclerholding.com/en/about/companies/33/",
+ "companyId": "docler_ip"
},
"dogannet": {
"name": "Dogannet",
"categoryId": 4,
- "url": "http://s.dogannet.tv/"
+ "url": "http://s.dogannet.tv/",
+ "companyId": "dogannet"
},
"domodomain": {
"name": "DomoDomain",
"categoryId": 6,
- "url": "http://www.domodomain.com/"
+ "url": "http://www.domodomain.com/",
+ "companyId": "intelligencefocus"
},
"donationtools": {
"name": "iRobinHood",
"categoryId": 12,
- "url": "http://www.irobinhood.org"
+ "url": "http://www.irobinhood.org",
+ "companyId": null
},
"doofinder.com": {
"name": "doofinder",
"categoryId": 2,
- "url": "https://www.doofinder.com/"
+ "url": "https://www.doofinder.com/",
+ "companyId": null
},
"doorbell.io": {
"name": "Doorbell.io",
"categoryId": 5,
- "url": "https://doorbell.io/"
+ "url": "https://doorbell.io/",
+ "companyId": "doorbell.io"
},
"dotandmedia": {
"name": "DotAndMedia",
"categoryId": 4,
- "url": "http://www.dotandmedia.com"
+ "url": "http://www.dotandmedia.com",
+ "companyId": "dotandmedia"
},
"dotmailer": {
"name": "dotMailer",
"categoryId": 2,
- "url": "http://www.dotdigitalgroup.com/"
+ "url": "http://www.dotdigitalgroup.com/",
+ "companyId": "dotdigital_group"
},
"dotmetrics.net": {
"name": "Dotmetrics",
"categoryId": 6,
- "url": "https://dotmetrics.net/"
+ "url": "https://dotmetrics.net/",
+ "companyId": null
},
"dotomi": {
"name": "Dotomi",
"categoryId": 4,
- "url": "http://www.dotomi.com/"
+ "url": "http://www.dotomi.com/",
+ "companyId": "conversant"
},
"double.net": {
"name": "Double.net",
"categoryId": 4,
- "url": "http://double.net/en/"
+ "url": "http://double.net/en/",
+ "companyId": "double.net"
},
"doubleclick": {
"name": "DoubleClick",
"categoryId": 4,
- "url": "http://www.doubleclick.com"
+ "url": "http://www.doubleclick.com",
+ "companyId": "google"
},
"doubleclick_ad_buyer": {
"name": "DoubleClick Ad Exchange-Buyer",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"doubleclick_bid_manager": {
"name": "DoubleClick Bid Manager",
"categoryId": 4,
- "url": "http://www.invitemedia.com"
+ "url": "http://www.invitemedia.com",
+ "companyId": "google"
},
"doubleclick_floodlight": {
"name": "DoubleClick Floodlight",
"categoryId": 4,
- "url": "http://www.google.com/support/dfa/partner/bin/topic.py?topic=23943"
+ "url": "http://www.google.com/support/dfa/partner/bin/topic.py?topic=23943",
+ "companyId": "google"
},
"doubleclick_spotlight": {
"name": "DoubleClick Spotlight",
"categoryId": 4,
- "url": "http://www.doubleclick.com/products/richmedia"
+ "url": "http://www.doubleclick.com/products/richmedia",
+ "companyId": "google"
},
"doubleclick_video_stats": {
"name": "Doubleclick Video Stats",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"doublepimp": {
"name": "DoublePimp",
"categoryId": 3,
- "url": "http://www.doublepimp.com/"
+ "url": "http://www.doublepimp.com/",
+ "companyId": "doublepimp"
},
"doubleverify": {
"name": "DoubleVerify",
"categoryId": 4,
- "url": "http://www.doubleverify.com/"
+ "url": "http://www.doubleverify.com/",
+ "companyId": "doubleverify"
},
"dratio": {
"name": "Dratio",
"categoryId": 6,
- "url": "http://www.dratio.com/"
+ "url": "http://www.dratio.com/",
+ "companyId": "dratio"
},
"drawbridge": {
"name": "Drawbridge",
"categoryId": 4,
- "url": "http://www.drawbrid.ge/"
+ "url": "http://www.drawbrid.ge/",
+ "companyId": "drawbridge"
},
"dreamlab.pl": {
"name": "DreamLab.pl",
"categoryId": 4,
- "url": "https://www.dreamlab.pl/"
+ "url": "https://www.dreamlab.pl/",
+ "companyId": "onet.pl"
},
"drift": {
"name": "Drift",
"categoryId": 2,
- "url": "https://www.drift.com/"
+ "url": "https://www.drift.com/",
+ "companyId": "drift"
},
"drip": {
"name": "Drip",
"categoryId": 2,
- "url": "https://www.getdrip.com"
+ "url": "https://www.getdrip.com",
+ "companyId": "drip"
},
"dropbox.com": {
"name": "Dropbox",
"categoryId": 2,
- "url": "https://www.dropbox.com/"
+ "url": "https://www.dropbox.com/",
+ "companyId": null
},
"dsnr_media_group": {
"name": "DSNR Media Group",
"categoryId": 4,
- "url": "http://www.dsnrmg.com/"
+ "url": "http://www.dsnrmg.com/",
+ "companyId": "dsnr_media_group"
},
"dsp_rambler": {
"name": "Rambler DSP",
"categoryId": 4,
- "url": "http://dsp.rambler.ru/"
+ "url": "http://dsp.rambler.ru/",
+ "companyId": "rambler"
},
"dstillery": {
"name": "Dstillery",
"categoryId": 4,
- "url": "https://dstillery.com/"
+ "url": "https://dstillery.com/",
+ "companyId": "dstillery"
},
"dtscout.com": {
"name": "DTScout",
"categoryId": 4,
- "url": "http://www.dtscout.com/"
+ "url": "http://www.dtscout.com/",
+ "companyId": "dtscout"
},
"dudamobile": {
"name": "DudaMobile",
"categoryId": 4,
- "url": "https://www.dudamobile.com/"
+ "url": "https://www.dudamobile.com/",
+ "companyId": "dudamobile"
},
"dun_and_bradstreet": {
"name": "Dun and Bradstreet",
"categoryId": 6,
- "url": "http://www.dnb.com/#"
+ "url": "http://www.dnb.com/#",
+ "companyId": "dun_&_bradstreet"
},
"dwstat.cn": {
"name": "dwstat.cn",
"categoryId": 6,
- "url": "http://www.dwstat.cn/"
+ "url": "http://www.dwstat.cn/",
+ "companyId": "dwstat"
},
"dynad": {
"name": "DynAd",
"categoryId": 4,
- "url": "http://dynad.net/"
+ "url": "http://dynad.net/",
+ "companyId": "dynad"
},
"dynadmic": {
"name": "DynAdmic",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"dynamic_1001_gmbh": {
"name": "Dynamic 1001 GmbH",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"dynamic_logic": {
"name": "Dynamic Logic",
"categoryId": 4,
- "url": "http://www.dynamiclogic.com/"
+ "url": "http://www.dynamiclogic.com/",
+ "companyId": "millward_brown"
},
"dynamic_yield": {
"name": "Dynamic Yield",
"categoryId": 5,
- "url": "https://www.dynamicyield.com/"
+ "url": "https://www.dynamicyield.com/",
+ "companyId": "dynamic_yield"
},
"dynamic_yield_analytics": {
"name": "Dynamic Yield Analytics",
"categoryId": 6,
- "url": "http://www.dynamicyield.com/"
+ "url": "http://www.dynamicyield.com/",
+ "companyId": "dynamic_yield"
},
"dynata": {
"name": "Dynata",
"categoryId": 4,
- "url": "http://hottraffic.nl/en"
+ "url": "http://hottraffic.nl/en",
+ "companyId": "dynata"
},
"dynatrace.com": {
"name": "Dynatrace",
"categoryId": 6,
- "url": "https://www.dynatrace.com/"
+ "url": "https://www.dynatrace.com/",
+ "companyId": "thoma_bravo"
},
"dyncdn.me": {
"name": "dyncdn.me",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"e-planning": {
"name": "e-planning",
"categoryId": 4,
- "url": "http://www.e-planning.net/"
+ "url": "http://www.e-planning.net/",
+ "companyId": "e-planning"
},
"eadv": {
"name": "eADV",
"categoryId": 4,
- "url": "http://eadv.it/"
+ "url": "http://eadv.it/",
+ "companyId": "eadv"
},
"eanalyzer.de": {
"name": "eanalyzer.de",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"early_birds": {
"name": "Early Birds",
"categoryId": 4,
- "url": "http://www.early-birds.fr/"
+ "url": "http://www.early-birds.fr/",
+ "companyId": "early_birds"
},
"earnify": {
"name": "Earnify",
"categoryId": 4,
- "url": "https://www.earnify.com/"
+ "url": "https://www.earnify.com/",
+ "companyId": "earnify"
},
"earnify_tracker": {
"name": "Earnify Tracker",
"categoryId": 6,
- "url": "https://www.earnify.com/"
+ "url": "https://www.earnify.com/",
+ "companyId": "earnify"
},
"easyads": {
"name": "EasyAds",
"categoryId": 4,
- "url": "https://easyads.bg/"
+ "url": "https://easyads.bg/",
+ "companyId": "easyads"
},
"easylist_club": {
"name": "easylist.club",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"ebay": {
"name": "eBay Stats",
"categoryId": 4,
- "url": "https://partnernetwork.ebay.com/"
+ "url": "https://partnernetwork.ebay.com/",
+ "companyId": "ebay_partner_network"
},
"ebay_korea": {
"name": "eBay Korea",
"categoryId": 4,
- "url": "http://www.ebay.com/"
+ "url": "http://www.ebay.com/",
+ "companyId": "ebay"
},
"ebay_partner_network": {
"name": "eBay Partner Network",
"categoryId": 4,
- "url": "https://www.ebaypartnernetwork.com/files/hub/en-US/index.html"
+ "url": "https://www.ebaypartnernetwork.com/files/hub/en-US/index.html",
+ "companyId": "ebay_partner_network"
},
"ebuzzing": {
"name": "eBuzzing",
"categoryId": 4,
- "url": "http://www.ebuzzing.com/"
+ "url": "http://www.ebuzzing.com/",
+ "companyId": "ebuzzing"
},
"echo": {
"name": "Echo",
"categoryId": 4,
- "url": "http://js-kit.com/"
+ "url": "http://js-kit.com/",
+ "companyId": "echo"
},
"eclick": {
"name": "eClick",
"categoryId": 4,
- "url": "http://eclick.vn"
+ "url": "http://eclick.vn",
+ "companyId": "eclick"
},
"econda": {
"name": "Econda",
"categoryId": 6,
- "url": "http://www.econda.de/"
+ "url": "http://www.econda.de/",
+ "companyId": "econda"
},
"ecotag": {
"name": "ecotag",
"categoryId": 4,
- "url": "http://www.eco-tag.jp/"
+ "url": "http://www.eco-tag.jp/",
+ "companyId": "ecotag"
},
"edigitalresearch": {
"name": "eDigitalResearch",
"categoryId": 4,
- "url": "http://www.edigitalresearch.com/"
+ "url": "http://www.edigitalresearch.com/",
+ "companyId": "edigitalresearch"
},
"effective_measure": {
"name": "Effective Measure",
"categoryId": 4,
- "url": "http://www.effectivemeasure.com/"
+ "url": "http://www.effectivemeasure.com/",
+ "companyId": "effective_measure"
},
"effiliation": {
"name": "Effiliation",
"categoryId": 4,
- "url": "http://www.effiliation.com/"
+ "url": "http://www.effiliation.com/",
+ "companyId": "effiliation"
},
"egain": {
"name": "eGain",
"categoryId": 2,
- "url": "http://www.egain.com/"
+ "url": "http://www.egain.com/",
+ "companyId": "egain"
},
"egain_analytics": {
"name": "eGain Analytics",
"categoryId": 6,
- "url": "http://www.egain.com/"
+ "url": "http://www.egain.com/",
+ "companyId": "egain"
},
"ehi-siegel_de": {
"name": "ehi-siegel.de",
"categoryId": 2,
- "url": "http://ehi-siegel.de/"
+ "url": "http://ehi-siegel.de/",
+ "companyId": null
},
"ekmpinpoint": {
"name": "ekmPinPoint",
"categoryId": 6,
- "url": "http://ekmpinpoint.com/"
+ "url": "http://ekmpinpoint.com/",
+ "companyId": "ekmpinpoint"
},
"ekomi": {
"name": "eKomi",
"categoryId": 1,
- "url": "http://www.ekomi.co.uk"
+ "url": "http://www.ekomi.co.uk",
+ "companyId": "ekomi"
},
"elastic_ad": {
"name": "Elastic Ad",
"categoryId": 4,
- "url": "http://www.elasticad.com"
+ "url": "http://www.elasticad.com",
+ "companyId": "elastic_ad"
},
"elastic_beanstalk": {
"name": "Elastic Beanstalk",
"categoryId": 6,
- "url": "http://www.amazon.com/"
+ "url": "http://www.amazon.com/",
+ "companyId": "amazon_associates"
},
"elicit": {
"name": "elicit",
"categoryId": 4,
- "url": "http://www.elicitsearch.com/"
+ "url": "http://www.elicitsearch.com/",
+ "companyId": "elicit"
},
"eloqua": {
"name": "Eloqua",
"categoryId": 4,
- "url": "http://www.eloqua.com/"
+ "url": "http://www.eloqua.com/",
+ "companyId": "oracle"
},
"eluxer_net": {
"name": "eluxer.net",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"email_aptitude": {
"name": "Email Aptitude",
"categoryId": 4,
- "url": "http://www.emailaptitude.com/"
+ "url": "http://www.emailaptitude.com/",
+ "companyId": "email_aptitude"
},
"email_attitude": {
"name": "Email Attitude",
"categoryId": 4,
- "url": "http://us.email-attitude.com/Default.aspx"
+ "url": "http://us.email-attitude.com/Default.aspx",
+ "companyId": "1000mercis"
},
"emarketeer": {
"name": "emarketeer",
"categoryId": 4,
- "url": "http://www.emarketeer.com/"
+ "url": "http://www.emarketeer.com/",
+ "companyId": "emarketeer"
},
"embed.ly": {
"name": "Embedly",
"categoryId": 6,
- "url": "http://embed.ly/"
+ "url": "http://embed.ly/",
+ "companyId": "medium"
},
"emediate": {
"name": "Emediate",
"categoryId": 4,
- "url": "http://www.emediate.biz/"
+ "url": "http://www.emediate.biz/",
+ "companyId": "cxense"
},
"emetriq": {
"name": "emetriq",
"categoryId": 4,
- "url": "http://www.emetriq.com"
+ "url": "http://www.emetriq.com",
+ "companyId": "emetriq"
},
"emma": {
"name": "Emma",
"categoryId": 4,
- "url": "http://myemma.com/"
+ "url": "http://myemma.com/",
+ "companyId": "emma"
},
"emnet": {
"name": "eMnet",
"categoryId": 4,
- "url": "http://www.emnet.co.kr"
+ "url": "http://www.emnet.co.kr",
+ "companyId": "emnet"
},
"empathy": {
"name": "Empathy",
"categoryId": 4,
- "url": "http://www.colbenson.com"
+ "url": "http://www.colbenson.com",
+ "companyId": "empathy"
},
"emsmobile.de": {
"name": "EMS Mobile",
"categoryId": 8,
- "url": "http://www.emsmobile.com/"
+ "url": "http://www.emsmobile.com/",
+ "companyId": null
},
"encore_metrics": {
"name": "Encore Metrics",
"categoryId": 4,
- "url": "http://sitecompass.com"
+ "url": "http://sitecompass.com",
+ "companyId": "flashtalking"
},
"enecto_analytics": {
"name": "Enecto Analytics",
"categoryId": 6,
- "url": "http://www.enecto.com/en/"
+ "url": "http://www.enecto.com/en/",
+ "companyId": "enecto"
},
"engage_sciences": {
"name": "Engage Sciences",
"categoryId": 6,
- "url": "http://www.engagesciences.com/"
+ "url": "http://www.engagesciences.com/",
+ "companyId": "engagesciences"
},
"engageya_widget": {
"name": "Engageya Widget",
"categoryId": 4,
- "url": "http://www.engageya.com/home/"
+ "url": "http://www.engageya.com/home/",
+ "companyId": "engageya"
},
"engagio": {
"name": "Engagio",
"categoryId": 6,
- "url": "https://www.engagio.com/"
+ "url": "https://www.engagio.com/",
+ "companyId": "engagio"
},
"engineseeker": {
"name": "EngineSeeker",
"categoryId": 4,
- "url": "http://www.engineseeker.com/"
+ "url": "http://www.engineseeker.com/",
+ "companyId": "engineseeker"
},
"enquisite": {
"name": "Enquisite",
"categoryId": 4,
- "url": "http://www.enquisite.com/"
+ "url": "http://www.enquisite.com/",
+ "companyId": "inboundwriter"
},
"enreach": {
"name": "Enreach",
"categoryId": 4,
- "url": "https://enreach.me/"
+ "url": "https://enreach.me/",
+ "companyId": "enreach"
},
"ensemble": {
"name": "Ensemble",
"categoryId": 4,
- "url": "http://www.tumri.com"
+ "url": "http://www.tumri.com",
+ "companyId": "ensemble"
},
"ensighten": {
"name": "Ensighten",
"categoryId": 5,
- "url": "http://www.ensighten.com"
+ "url": "http://www.ensighten.com",
+ "companyId": "ensighten"
},
"envolve": {
"name": "Envolve",
"categoryId": 2,
- "url": "https://www.envolve.com/"
+ "url": "https://www.envolve.com/",
+ "companyId": "envolve"
},
"envybox": {
"name": "Envybox",
"categoryId": 2,
- "url": "https://envybox.io/"
+ "url": "https://envybox.io/",
+ "companyId": "envybox"
},
"eperflex": {
"name": "Eperflex",
"categoryId": 4,
- "url": "https://eperflex.com/"
+ "url": "https://eperflex.com/",
+ "companyId": "ividence"
},
"epic_game_ads": {
"name": "Epic Game Ads",
"categoryId": 4,
- "url": "http://www.epicgameads.com/"
+ "url": "http://www.epicgameads.com/",
+ "companyId": "epic_game_ads"
},
"epic_marketplace": {
"name": "Epic Marketplace",
"categoryId": 4,
- "url": "http://www.trafficmarketplace.com/"
+ "url": "http://www.trafficmarketplace.com/",
+ "companyId": "epic_advertising"
},
"epom": {
"name": "Epom",
"categoryId": 4,
- "url": "http://epom.com/"
+ "url": "http://epom.com/",
+ "companyId": "epom"
},
"epoq": {
"name": "epoq",
"categoryId": 2,
- "url": "http://www.epoq.de/"
+ "url": "http://www.epoq.de/",
+ "companyId": "epoq"
},
"eprice": {
"name": "ePrice",
"categoryId": 4,
- "url": "http://banzaiadv.it/"
+ "url": "http://banzaiadv.it/",
+ "companyId": "eprice"
},
"eproof": {
"name": "eProof",
"categoryId": 6,
- "url": "http://www.eproof.com/"
+ "url": "http://www.eproof.com/",
+ "companyId": "eproof"
},
"eqs_group": {
"name": "EQS Group",
"categoryId": 6,
- "url": "https://www.eqs.com/"
+ "url": "https://www.eqs.com/",
+ "companyId": "eqs_group"
},
"eqworks": {
"name": "EQWorks",
"categoryId": 4,
- "url": "http://eqads.com"
+ "url": "http://eqads.com",
+ "companyId": "eq_works"
},
"eroadvertising": {
"name": "EroAdvertising",
"categoryId": 3,
- "url": "http://www.ero-advertising.com/"
+ "url": "http://www.ero-advertising.com/",
+ "companyId": "ero_advertising"
},
"errorception": {
"name": "Errorception",
"categoryId": 6,
- "url": "http://errorception.com/"
+ "url": "http://errorception.com/",
+ "companyId": "errorception"
},
"eshopcomp.com": {
"name": "eshopcomp.com",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"espn_cdn": {
"name": "ESPN CDN",
"categoryId": 9,
- "url": "http://www.espn.com/"
+ "url": "http://www.espn.com/",
+ "companyId": "disney"
},
"esprit.de": {
"name": "esprit.de",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"estat": {
"name": "eStat",
"categoryId": 6,
- "url": "http://www.mediametrie-estat.com/"
+ "url": "http://www.mediametrie-estat.com/",
+ "companyId": "mediametrie"
},
"etag": {
"name": "etag",
"categoryId": 4,
- "url": "http://etagdigital.com.br/"
+ "url": "http://etagdigital.com.br/",
+ "companyId": "etag"
},
"etahub.com": {
"name": "etahub.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"etarget": {
"name": "Etarget",
"categoryId": 4,
- "url": "http://etargetnet.com/"
+ "url": "http://etargetnet.com/",
+ "companyId": "etarget"
},
"ethnio": {
"name": "Ethnio",
"categoryId": 4,
- "url": "http://ethn.io/"
+ "url": "http://ethn.io/",
+ "companyId": "ethnio"
},
"etology": {
"name": "Etology",
"categoryId": 4,
- "url": "http://www.etology.com"
+ "url": "http://www.etology.com",
+ "companyId": "etology"
},
"etp": {
"name": "ETP",
"categoryId": 6,
- "url": "https://www.etpgroup.com"
+ "url": "https://www.etpgroup.com",
+ "companyId": "etp"
},
"etracker": {
"name": "etracker",
"categoryId": 6,
- "url": "http://www.etracker.com/en/"
+ "url": "http://www.etracker.com/en/",
+ "companyId": "etracker_gmbh"
},
"etrigue": {
"name": "eTrigue",
"categoryId": 4,
- "url": "http://www.etrigue.com/"
+ "url": "http://www.etrigue.com/",
+ "companyId": "etrigue"
},
"etsystatic": {
"name": "Etsy CDN",
"categoryId": 9,
- "url": "https://www.etsy.com/"
+ "url": "https://www.etsy.com/",
+ "companyId": "etsy"
},
"eulerian": {
"name": "Eulerian",
"categoryId": 6,
- "url": "https://www.eulerian.com/"
+ "url": "https://www.eulerian.com/",
+ "companyId": "eulerian"
},
"euroads": {
"name": "Euroads",
"categoryId": 4,
- "url": "http://euroads.com/en/"
+ "url": "http://euroads.com/en/",
+ "companyId": "euroads"
},
"europecash": {
"name": "Europecash",
"categoryId": 4,
- "url": "https://www.europacash.com/"
+ "url": "https://www.europacash.com/",
+ "companyId": "europacash"
},
"euroweb_counter": {
"name": "Euroweb Counter",
"categoryId": 4,
- "url": "http://www.euroweb.de/"
+ "url": "http://www.euroweb.de/",
+ "companyId": "euroweb"
},
"evergage.com": {
"name": "Evergage",
"categoryId": 2,
- "url": "https://www.evergage.com"
+ "url": "https://www.evergage.com",
+ "companyId": "evergage"
},
"everstring": {
"name": "Everstring",
"categoryId": 6,
- "url": "http://www.everstring.com/"
+ "url": "http://www.everstring.com/",
+ "companyId": "everstring"
},
"everyday_health": {
"name": "Everyday Health",
"categoryId": 7,
- "url": "http://www.everydayhealth.com/"
+ "url": "http://www.everydayhealth.com/",
+ "companyId": "everyday_health"
},
"evidon": {
"name": "Evidon",
"categoryId": 5,
- "url": "https://www.evidon.com/"
+ "url": "https://www.evidon.com/",
+ "companyId": "crownpeak"
},
"evisit_analyst": {
"name": "eVisit Analyst",
"categoryId": 4,
- "url": "http://www.evisitanalyst.com"
+ "url": "http://www.evisitanalyst.com",
+ "companyId": "evisit_analyst"
},
"exact_drive": {
"name": "Exact Drive",
"categoryId": 4,
- "url": "http://www.exactdrive.com/"
+ "url": "http://www.exactdrive.com/",
+ "companyId": "exact_drive"
},
"exactag": {
"name": "Exactag",
"categoryId": 6,
- "url": "http://www.exactag.com"
+ "url": "http://www.exactag.com",
+ "companyId": "exactag"
},
"exelate": {
"name": "eXelate",
"categoryId": 4,
- "url": "http://www.exelate.com/"
+ "url": "http://www.exelate.com/",
+ "companyId": "nielsen"
},
"exitjunction": {
"name": "ExitJunction",
"categoryId": 4,
- "url": "https://secure.exitjunction.com"
+ "url": "https://secure.exitjunction.com",
+ "companyId": "exitjunction"
},
"exoclick": {
"name": "ExoClick",
"categoryId": 3,
- "url": "http://exoclick.com/"
+ "url": "http://exoclick.com/",
+ "companyId": "exoclick"
},
"exoticads.com": {
"name": "exoticads",
"categoryId": 3,
- "url": "https://exoticads.com/welcome/"
+ "url": "https://exoticads.com/welcome/",
+ "companyId": null
},
"expedia": {
"name": "Expedia",
"categoryId": 8,
- "url": "https://www.trvl-px.com/"
+ "url": "https://www.trvl-px.com/",
+ "companyId": "iac_apps"
},
"experian": {
"name": "Experian",
"categoryId": 8,
- "url": "https://www.experian.com/"
+ "url": "https://www.experian.com/",
+ "companyId": "experian_inc"
},
"experian_marketing_services": {
"name": "Experian Marketing Services",
"categoryId": 4,
- "url": "http://www.experian.com/"
+ "url": "http://www.experian.com/",
+ "companyId": "experian_inc"
},
"expo-max": {
"name": "expo-MAX",
"categoryId": 4,
- "url": "http://expo-max.com/"
+ "url": "http://expo-max.com/",
+ "companyId": "expo-max"
},
"expose_box": {
"name": "Expose Box",
"categoryId": 4,
- "url": "http://www.exposebox.com/"
+ "url": "http://www.exposebox.com/",
+ "companyId": "expose_box"
},
"expose_box_widgets": {
"name": "Expose Box Widgets",
"categoryId": 2,
- "url": "http://www.exposebox.com/"
+ "url": "http://www.exposebox.com/",
+ "companyId": "expose_box"
},
"express.co.uk": {
"name": "express.co.uk",
"categoryId": 8,
- "url": "https://www.express.co.uk/"
+ "url": "https://www.express.co.uk/",
+ "companyId": null
},
"expressvpn": {
"name": "ExpressVPN",
"categoryId": 2,
- "url": "https://www.expressvpn.com/"
+ "url": "https://www.expressvpn.com/",
+ "companyId": "expressvpn"
},
"extreme_tracker": {
"name": "eXTReMe Tracker",
"categoryId": 6,
- "url": "http://www.extremetracking.com/"
+ "url": "http://www.extremetracking.com/",
+ "companyId": "extreme_digital"
},
"eye_newton": {
"name": "Eye Newton",
"categoryId": 2,
- "url": "http://eyenewton.ru/"
+ "url": "http://eyenewton.ru/",
+ "companyId": "eyenewton"
},
"eyeota": {
"name": "Eyeota",
"categoryId": 4,
- "url": "http://www.eyeota.com/"
+ "url": "http://www.eyeota.com/",
+ "companyId": "eyeota"
},
"eyereturnmarketing": {
"name": "Eyereturn Marketing",
"categoryId": 4,
- "url": "https://eyereturnmarketing.com/"
+ "url": "https://eyereturnmarketing.com/",
+ "companyId": "torstar_corp"
},
"eyeview": {
"name": "Eyeview",
"categoryId": 4,
- "url": "http://www.eyeviewdigital.com/"
+ "url": "http://www.eyeviewdigital.com/",
+ "companyId": "eyeview"
},
"ezakus": {
"name": "Ezakus",
"categoryId": 4,
- "url": "http://www.ezakus.com/"
+ "url": "http://www.ezakus.com/",
+ "companyId": "np6"
},
"f11-ads.com": {
"name": "Factor Eleven",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"facebook": {
"name": "Facebook",
"categoryId": 4,
- "url": "https://www.facebook.com"
+ "url": "https://www.facebook.com",
+ "companyId": "facebook"
},
"facebook_beacon": {
"name": "Facebook Beacon",
"categoryId": 7,
- "url": "http://www.facebook.com/beacon/faq.php"
+ "url": "http://www.facebook.com/beacon/faq.php",
+ "companyId": "facebook"
},
"facebook_cdn": {
"name": "Facebook CDN",
"categoryId": 9,
- "url": "https://www.facebook.com"
+ "url": "https://www.facebook.com",
+ "companyId": "facebook"
},
"facebook_connect": {
"name": "Facebook Connect",
"categoryId": 6,
- "url": "https://developers.facebook.com/connect.php"
+ "url": "https://developers.facebook.com/connect.php",
+ "companyId": "facebook"
},
"facebook_conversion_tracking": {
"name": "Facebook Conversion Tracking",
"categoryId": 4,
- "url": "http://www.facebook.com/"
+ "url": "http://www.facebook.com/",
+ "companyId": "facebook"
},
"facebook_custom_audience": {
"name": "Facebook Custom Audience",
"categoryId": 4,
- "url": "https://www.facebook.com"
+ "url": "https://www.facebook.com",
+ "companyId": "facebook"
},
"facebook_graph": {
"name": "Facebook Social Graph",
"categoryId": 7,
- "url": "https://developers.facebook.com/docs/reference/api/"
+ "url": "https://developers.facebook.com/docs/reference/api/",
+ "companyId": "facebook"
},
"facebook_impressions": {
"name": "Facebook Impressions",
"categoryId": 4,
- "url": "https://www.facebook.com/"
+ "url": "https://www.facebook.com/",
+ "companyId": "facebook"
},
"facebook_social_plugins": {
"name": "Facebook Social Plugins",
"categoryId": 7,
- "url": "https://developers.facebook.com/plugins"
+ "url": "https://developers.facebook.com/plugins",
+ "companyId": "facebook"
},
"facetz.dca": {
"name": "Facetz.DCA",
"categoryId": 4,
- "url": "http://facetz.net"
+ "url": "http://facetz.net",
+ "companyId": "dca"
},
"facilitate_digital": {
"name": "Facilitate Digital",
"categoryId": 4,
- "url": "http://www.facilitatedigital.com/"
+ "url": "http://www.facilitatedigital.com/",
+ "companyId": "adslot"
},
"faktor.io": {
"name": "faktor.io",
"categoryId": 6,
- "url": "https://faktor.io/"
+ "url": "https://faktor.io/",
+ "companyId": "faktor.io"
},
"fancy_widget": {
"name": "Fancy Widget",
"categoryId": 7,
- "url": "http://www.thefancy.com/"
+ "url": "http://www.thefancy.com/",
+ "companyId": "fancy"
},
"fanplayr": {
"name": "Fanplayr",
"categoryId": 4,
- "url": "http://www.fanplayr.com/"
+ "url": "http://www.fanplayr.com/",
+ "companyId": "fanplayr"
},
"fap.to": {
"name": "Imagefap",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"fastly_insights": {
"name": "Fastly Insights",
"categoryId": 6,
- "url": "https://insights.fastlylabs.com/"
+ "url": "https://insights.fastlylabs.com/",
+ "companyId": "fastly"
},
"fastlylb.net": {
"name": "Fastly",
"categoryId": 9,
- "url": "https://www.fastly.com/"
+ "url": "https://www.fastly.com/",
+ "companyId": "fastly"
},
"fastpic.ru": {
"name": "FastPic",
"categoryId": 10,
- "url": "http://fastpic.ru/"
+ "url": "http://fastpic.ru/",
+ "companyId": "fastpic"
},
"federated_media": {
"name": "Federated Media",
"categoryId": 4,
- "url": "http://www.federatedmedia.net/"
+ "url": "http://www.federatedmedia.net/",
+ "companyId": "hyfn"
},
"feedbackify": {
"name": "Feedbackify",
"categoryId": 2,
- "url": "http://www.feedbackify.com/"
+ "url": "http://www.feedbackify.com/",
+ "companyId": "feedbackify"
},
"feedburner.com": {
"name": "FeedBurner",
"categoryId": 4,
- "url": "https://feedburner.com"
+ "url": "https://feedburner.com",
+ "companyId": "google"
},
"feedify": {
"name": "Feedify",
"categoryId": 7,
- "url": "http://feedify.de/"
+ "url": "http://feedify.de/",
+ "companyId": "feedify"
},
"feedjit": {
"name": "Feedjit",
"categoryId": 4,
- "url": "http://feedjit.com/"
+ "url": "http://feedjit.com/",
+ "companyId": "feedjit"
},
"feedperfect": {
"name": "FeedPerfect",
"categoryId": 4,
- "url": "http://www.feedperfect.com/"
+ "url": "http://www.feedperfect.com/",
+ "companyId": "feedperfect"
},
"feedsportal": {
"name": "Feedsportal",
"categoryId": 4,
- "url": "http://www.mediafed.com/"
+ "url": "http://www.mediafed.com/",
+ "companyId": "mediafed"
},
"feefo": {
"name": "Feefo",
"categoryId": 2,
- "url": "http://www.feefo.com/web/en/us/"
+ "url": "http://www.feefo.com/web/en/us/",
+ "companyId": "feefo"
},
"fidelity_media": {
"name": "Fidelity Media",
"categoryId": 4,
- "url": "http://fidelity-media.com/"
+ "url": "http://fidelity-media.com/",
+ "companyId": "fidelity_media"
},
"fiksu": {
"name": "Fiksu",
"categoryId": 4,
- "url": "https://fiksu.com/"
+ "url": "https://fiksu.com/",
+ "companyId": "noosphere"
},
"filament.io": {
"name": "Filament.io",
"categoryId": 4,
- "url": "http://sharethis.com/"
+ "url": "http://sharethis.com/",
+ "companyId": "sharethis"
},
"fileserve": {
"name": "FileServe",
"categoryId": 10,
- "url": "http://fileserve.com/"
+ "url": "http://fileserve.com/",
+ "companyId": "fileserve"
},
"financeads": {
"name": "FinanceADs",
"categoryId": 4,
- "url": "https://www.financeads.net/"
+ "url": "https://www.financeads.net/",
+ "companyId": "financeads_gmbh_&_co._kg"
},
"financial_content": {
"name": "Financial Content",
"categoryId": 4,
- "url": "http://www.financialcontent.com"
+ "url": "http://www.financialcontent.com",
+ "companyId": "financial_content"
},
"findizer.fr": {
"name": "Findizer",
"categoryId": 8,
- "url": "http://www.findizer.fr/"
+ "url": "http://www.findizer.fr/",
+ "companyId": null
},
"findologic.com": {
"name": "Findologic",
"categoryId": 2,
- "url": "https://www.findologic.com/"
+ "url": "https://www.findologic.com/",
+ "companyId": "findologic"
},
"firebaseio.com": {
"name": "Firebase",
"categoryId": 8,
- "url": "https://firebase.google.com/"
+ "url": "https://firebase.google.com/",
+ "companyId": "google"
},
"first_impression": {
"name": "First Impression",
"categoryId": 4,
- "url": "http://www.firstimpression.io"
+ "url": "http://www.firstimpression.io",
+ "companyId": "first_impression"
},
"fit_analytics": {
"name": "Fit Analytics",
"categoryId": 6,
- "url": "http://www.fitanalytics.com/"
+ "url": "http://www.fitanalytics.com/",
+ "companyId": "fit_analytics"
},
"fivetran": {
"name": "Fivetran",
"categoryId": 6,
- "url": "https://fivetran.com/"
+ "url": "https://fivetran.com/",
+ "companyId": "fivetran"
},
"flag_ads": {
"name": "Flag Ads",
"categoryId": 4,
- "url": "http://www.flagads.net/"
+ "url": "http://www.flagads.net/",
+ "companyId": "flag_ads"
},
"flag_counter": {
"name": "Flag Counter",
"categoryId": 4,
- "url": "http://flagcounter.com/"
+ "url": "http://flagcounter.com/",
+ "companyId": "flag_counter"
},
"flashtalking": {
"name": "Flashtalking",
"categoryId": 4,
- "url": "http://www.flashtalking.com/"
+ "url": "http://www.flashtalking.com/",
+ "companyId": "flashtalking"
},
"flattr_button": {
"name": "Flattr Button",
"categoryId": 7,
- "url": "http://flattr.com/"
+ "url": "http://flattr.com/",
+ "companyId": "flattr"
},
"flexoffers": {
"name": "FlexOffers",
"categoryId": 4,
- "url": "http://www.flexoffers.com/"
+ "url": "http://www.flexoffers.com/",
+ "companyId": "flexoffers.com"
},
"flickr_badge": {
"name": "Flickr Badge",
"categoryId": 7,
- "url": "http://www.flickr.com/"
+ "url": "http://www.flickr.com/",
+ "companyId": "smugmug"
},
"flipboard": {
"name": "Flipboard",
"categoryId": 6,
- "url": "http://www.flipboard.com/"
+ "url": "http://www.flipboard.com/",
+ "companyId": "flipboard"
},
"flite": {
"name": "Flite",
"categoryId": 4,
- "url": "http://www.flite.com/"
+ "url": "http://www.flite.com/",
+ "companyId": "flite"
},
"flixcdn.com": {
"name": "flixcdn.com",
"categoryId": 9,
- "url": null
+ "url": null,
+ "companyId": null
},
"flixmedia": {
"name": "Flixmedia",
"categoryId": 8,
- "url": "https://flixmedia.eu"
+ "url": "https://flixmedia.eu",
+ "companyId": "flixmedia"
},
"flocktory.com": {
"name": "Flocktory",
"categoryId": 6,
- "url": "https://www.flocktory.com/"
+ "url": "https://www.flocktory.com/",
+ "companyId": "flocktory"
},
"flowplayer": {
"name": "Flowplayer",
"categoryId": 4,
- "url": "https://flowplayer.org/"
+ "url": "https://flowplayer.org/",
+ "companyId": "flowplayer"
},
"fluct": {
"name": "Fluct",
"categoryId": 4,
- "url": "https://corp.fluct.jp/"
+ "url": "https://corp.fluct.jp/",
+ "companyId": "fluct"
},
"fluent": {
"name": "Fluent",
"categoryId": 4,
- "url": "http://www.fluentco.com/"
+ "url": "http://www.fluentco.com/",
+ "companyId": "fluent"
},
"fluid": {
"name": "Fluid",
"categoryId": 4,
- "url": "http://www.8thbridge.com/"
+ "url": "http://www.8thbridge.com/",
+ "companyId": "fluid"
},
"fluidads": {
"name": "FluidAds",
"categoryId": 4,
- "url": "http://www.fluidads.co/"
+ "url": "http://www.fluidads.co/",
+ "companyId": "fluidads"
},
"fluidsurveys": {
"name": "FluidSurveys",
"categoryId": 2,
- "url": "http://fluidsurveys.com/"
+ "url": "http://fluidsurveys.com/",
+ "companyId": "fluidware"
},
"flurry": {
"name": "Flurry",
"categoryId": 6,
- "url": "http://www.flurry.com/"
+ "url": "http://www.flurry.com/",
+ "companyId": "verizon"
},
"flxone": {
"name": "FLXONE",
"categoryId": 4,
- "url": "http://www.flxone.com/"
+ "url": "http://www.flxone.com/",
+ "companyId": "flxone"
},
"flyertown": {
"name": "Flyertown",
"categoryId": 6,
- "url": "http://www.flyertown.ca/"
+ "url": "http://www.flyertown.ca/",
+ "companyId": "flyertown"
},
"fmadserving": {
"name": "FMAdserving",
"categoryId": 4,
- "url": "http://www.fmadserving.dk/"
+ "url": "http://www.fmadserving.dk/",
+ "companyId": "fm_adserving"
},
"fonbet": {
"name": "Fonbet",
"categoryId": 6,
- "url": "https://www.fonbet.ru"
+ "url": "https://www.fonbet.ru",
+ "companyId": "fonbet"
},
"fonecta": {
"name": "Fonecta",
"categoryId": 2,
- "url": "http://www.fonecta.com/"
+ "url": "http://www.fonecta.com/",
+ "companyId": "fonecta"
},
"fontawesome_com": {
"name": "fontawesome.com",
"categoryId": 9,
- "url": "http://fontawesome.com/"
+ "url": "http://fontawesome.com/",
+ "companyId": null
},
"foodie_blogroll": {
"name": "Foodie Blogroll",
"categoryId": 7,
- "url": "http://www.foodieblogroll.com"
+ "url": "http://www.foodieblogroll.com",
+ "companyId": "foodie_blogroll"
},
"footprint": {
"name": "Footprint",
"categoryId": 4,
- "url": "http://www.footprintlive.com/"
+ "url": "http://www.footprintlive.com/",
+ "companyId": "opentracker"
},
"footprintdns.com": {
"name": "Footprint DNS",
"categoryId": 11,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"forcetrac": {
"name": "ForceTrac",
"categoryId": 2,
- "url": "http://www.forcetrac.com/"
+ "url": "http://www.forcetrac.com/",
+ "companyId": "force_marketing"
},
"forensiq": {
"name": "Forensiq",
"categoryId": 4,
- "url": "http://www.cpadetective.com/"
+ "url": "http://www.cpadetective.com/",
+ "companyId": "impact"
},
"foresee": {
"name": "ForeSee",
"categoryId": 5,
- "url": "https://www.foresee.com/"
+ "url": "https://www.foresee.com/",
+ "companyId": "foresee_results"
},
"formisimo": {
"name": "Formisimo",
"categoryId": 4,
- "url": "https://www.formisimo.com/"
+ "url": "https://www.formisimo.com/",
+ "companyId": "formisimo"
},
"forter": {
"name": "Forter",
"categoryId": 4,
- "url": "https://www.forter.com/"
+ "url": "https://www.forter.com/",
+ "companyId": "forter"
},
"fortlachanhecksof.info": {
"name": "fortlachanhecksof.info",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"foursquare_widget": {
"name": "Foursquare Widget",
"categoryId": 4,
- "url": "https://foursquare.com/"
+ "url": "https://foursquare.com/",
+ "companyId": "foursquare"
},
"fout.jp": {
"name": "FreakOut",
"categoryId": 4,
- "url": "https://www.fout.co.jp/"
+ "url": "https://www.fout.co.jp/",
+ "companyId": "freakout"
},
"fox_audience_network": {
"name": "Fox Audience Network",
"categoryId": 4,
- "url": "https://publishers.foxaudiencenetwork.com/"
+ "url": "https://publishers.foxaudiencenetwork.com/",
+ "companyId": "fox_audience_network"
},
"foxnews_static": {
"name": "Fox News CDN",
"categoryId": 9,
- "url": "http://www.foxnews.com/"
+ "url": "http://www.foxnews.com/",
+ "companyId": "fox_news"
},
"foxpush": {
"name": "FoxPush",
"categoryId": 4,
- "url": "https://www.foxpush.com/"
+ "url": "https://www.foxpush.com/",
+ "companyId": "foxpush"
},
"foxydeal_com": {
"name": "foxydeal.com",
"categoryId": 12,
- "url": "https://www.foxydeal.de"
+ "url": "https://www.foxydeal.de",
+ "companyId": null
},
"fraudlogix": {
"name": "FraudLogix",
"categoryId": 4,
- "url": "https://www.fraudlogix.com/"
+ "url": "https://www.fraudlogix.com/",
+ "companyId": null
},
"free_counter": {
"name": "Free Counter",
"categoryId": 6,
- "url": "http://www.statcounterfree.com/"
+ "url": "http://www.statcounterfree.com/",
+ "companyId": "free_counter"
},
"free_online_users": {
"name": "Free Online Users",
"categoryId": 6,
- "url": "http://www.freeonlineusers.com"
+ "url": "http://www.freeonlineusers.com",
+ "companyId": "free_online_users"
},
"free_pagerank": {
"name": "Free PageRank",
"categoryId": 6,
- "url": "http://www.free-pagerank.com/"
+ "url": "http://www.free-pagerank.com/",
+ "companyId": "free_pagerank"
},
"freedom_mortgage": {
"name": "Freedom Mortgage",
"categoryId": 6,
- "url": "https://www.freedommortgage.com/"
+ "url": "https://www.freedommortgage.com/",
+ "companyId": "freedom_mortgage"
},
"freegeoip_net": {
"name": "freegeoip.net",
"categoryId": 6,
- "url": "http://freegeoip.net/"
+ "url": "http://freegeoip.net/",
+ "companyId": null
},
"freenet_de": {
"name": "freenet.de",
"categoryId": 4,
- "url": "http://freenet.de/"
+ "url": "http://freenet.de/",
+ "companyId": "debitel"
},
"freewheel": {
"name": "FreeWheel",
"categoryId": 4,
- "url": "http://www.freewheel.tv/"
+ "url": "http://www.freewheel.tv/",
+ "companyId": "comcast"
},
"fresh8": {
"name": "Fresh8",
"categoryId": 6,
- "url": "http://fresh8gaming.com/"
+ "url": "http://fresh8gaming.com/",
+ "companyId": "fresh_8_gaming"
},
"freshdesk": {
"name": "Freshdesk",
"categoryId": 2,
- "url": "http://www.freshdesk.com"
+ "url": "http://www.freshdesk.com",
+ "companyId": "freshdesk"
},
"freshplum": {
"name": "Freshplum",
"categoryId": 4,
- "url": "https://freshplum.com/"
+ "url": "https://freshplum.com/",
+ "companyId": "freshplum"
},
"friendbuy": {
"name": "FriendBuy",
"categoryId": 6,
- "url": "https://www.friendbuy.com"
+ "url": "https://www.friendbuy.com",
+ "companyId": "friendbuy"
},
"friendfeed": {
"name": "FriendFeed",
"categoryId": 7,
- "url": "http://friendfeed.com/"
+ "url": "http://friendfeed.com/",
+ "companyId": "facebook"
},
"friendfinder_network": {
"name": "FriendFinder Network",
"categoryId": 3,
- "url": "http://www.ffn.com/"
+ "url": "http://www.ffn.com/",
+ "companyId": "friendfinder_networks"
},
"frosmo_optimizer": {
"name": "Frosmo Optimizer",
"categoryId": 4,
- "url": "http://frosmo.com/"
+ "url": "http://frosmo.com/",
+ "companyId": "frosmo"
},
"fruitflan": {
"name": "FruitFlan",
"categoryId": 4,
- "url": "http://flan-tech.com/"
+ "url": "http://flan-tech.com/",
+ "companyId": "keytiles"
},
"fstrk.net": {
"name": "24metrics Fraudshield",
"categoryId": 6,
- "url": "https://24metrics.com/"
+ "url": "https://24metrics.com/",
+ "companyId": "24metrics"
},
"fuelx": {
"name": "FuelX",
"categoryId": 4,
- "url": "http://fuelx.com/"
+ "url": "http://fuelx.com/",
+ "companyId": "fuelx"
},
"fullstory": {
"name": "FullStory",
"categoryId": 6,
- "url": "http://fullstory.com"
+ "url": "http://fullstory.com",
+ "companyId": "fullstory"
},
"funnelytics": {
"name": "Funnelytics",
"categoryId": 6,
- "url": "https://funnelytics.io/"
+ "url": "https://funnelytics.io/",
+ "companyId": "funnelytics"
},
"fyber": {
"name": "Fyber",
"categoryId": 4,
- "url": "https://www.fyber.com/"
+ "url": "https://www.fyber.com/",
+ "companyId": "fyber"
},
"ga_audiences": {
"name": "GA Audiences",
"categoryId": 6,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"game_advertising_online": {
"name": "Game Advertising Online",
"categoryId": 4,
- "url": "http://www.game-advertising-online.com/"
+ "url": "http://www.game-advertising-online.com/",
+ "companyId": "game_advertising_online"
},
"gamedistribution.com": {
"name": "Gamedistribution.com",
"categoryId": 8,
- "url": "http://gamedistribution.com/"
+ "url": "http://gamedistribution.com/",
+ "companyId": null
},
"gamerdna": {
"name": "gamerDNA",
"categoryId": 7,
- "url": "http://www.gamerdnamedia.com/"
+ "url": "http://www.gamerdnamedia.com/",
+ "companyId": "gamerdna_media"
},
"gannett": {
"name": "Gannett Media",
"categoryId": 0,
- "url": "https://www.gannett.com/"
+ "url": "https://www.gannett.com/",
+ "companyId": "gannett_digital_media_network"
},
"gaug.es": {
"name": "Gaug.es",
"categoryId": 6,
- "url": "http://get.gaug.es/"
+ "url": "http://get.gaug.es/",
+ "companyId": "euroweb"
},
"gazprom-media_digital": {
"name": "Gazprom-Media Digital",
"categoryId": 0,
- "url": "http://www.gpm-digital.com/"
+ "url": "http://www.gpm-digital.com/",
+ "companyId": "gazprom-media_digital"
},
"gb-world": {
"name": "GB-World",
"categoryId": 7,
- "url": "http://www.gb-world.net/"
+ "url": "http://www.gb-world.net/",
+ "companyId": "gb-world"
},
"gdeslon": {
"name": "GdeSlon",
"categoryId": 4,
- "url": "http://www.gdeslon.ru/"
+ "url": "http://www.gdeslon.ru/",
+ "companyId": "gdeslon"
},
"gdm_digital": {
"name": "GDM Digital",
"categoryId": 4,
- "url": "http://www.gdmdigital.com/"
+ "url": "http://www.gdmdigital.com/",
+ "companyId": "ve_interactive"
},
"geeen": {
"name": "Geeen",
"categoryId": 6,
- "url": "https://www.geeen.co.jp/"
+ "url": "https://www.geeen.co.jp/",
+ "companyId": "geeen"
},
"gemius": {
"name": "Gemius",
"categoryId": 4,
- "url": "http://www.gemius.com"
+ "url": "http://www.gemius.com",
+ "companyId": "gemius_sa"
},
"generaltracking_de": {
"name": "generaltracking.de",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"genesis": {
"name": "Genesis",
"categoryId": 4,
- "url": "http://genesismedia.com/"
+ "url": "http://genesismedia.com/",
+ "companyId": "genesis_media"
},
"geniee": {
"name": "GENIEE",
"categoryId": 4,
- "url": "http://geniee.co.jp/"
+ "url": "http://geniee.co.jp/",
+ "companyId": null
},
"genius": {
"name": "Genius",
"categoryId": 6,
- "url": "http://www.genius.com/"
+ "url": "http://www.genius.com/",
+ "companyId": "genius"
},
"genoo": {
"name": "Genoo",
"categoryId": 4,
- "url": "http://www.genoo.com/"
+ "url": "http://www.genoo.com/",
+ "companyId": "genoo"
},
"geoads": {
"name": "GeoAds",
"categoryId": 4,
- "url": "http://www.geoads.com"
+ "url": "http://www.geoads.com",
+ "companyId": "geoads"
},
"geolify": {
"name": "Geolify",
"categoryId": 4,
- "url": "http://geolify.com/"
+ "url": "http://geolify.com/",
+ "companyId": "geolify"
},
"geoplugin": {
"name": "geoPlugin",
"categoryId": 6,
- "url": "http://www.geoplugin.com/"
+ "url": "http://www.geoplugin.com/",
+ "companyId": "geoplugin"
},
"geotrust": {
"name": "GeoTrust",
"categoryId": 5,
- "url": "http://www.geotrust.com/"
+ "url": "http://www.geotrust.com/",
+ "companyId": "symantec"
},
"geovisite": {
"name": "Geovisite",
"categoryId": 6,
- "url": "http://www.geovisite.com/"
+ "url": "http://www.geovisite.com/",
+ "companyId": "geovisite"
},
"gestionpub": {
"name": "GestionPub",
"categoryId": 4,
- "url": "http://www.gestionpub.com/"
+ "url": "http://www.gestionpub.com/",
+ "companyId": "gestionpub"
},
"get_response": {
"name": "Get Response",
"categoryId": 2,
- "url": "https://www.getresponse.com/?marketing_gv=v2"
+ "url": "https://www.getresponse.com/?marketing_gv=v2",
+ "companyId": "getresponse"
},
"get_site_control": {
"name": "Get Site Control",
"categoryId": 4,
- "url": "https://getsitecontrol.com/"
+ "url": "https://getsitecontrol.com/",
+ "companyId": "getsitecontrol"
},
"getconversion": {
"name": "GetConversion",
"categoryId": 2,
- "url": "http://www.getconversion.net/"
+ "url": "http://www.getconversion.net/",
+ "companyId": "getconversion"
},
"getglue": {
"name": "GetGlue",
"categoryId": 0,
- "url": "http://getglue.com"
+ "url": "http://getglue.com",
+ "companyId": "telfie"
},
"getintent": {
"name": "GetIntent",
"categoryId": 4,
- "url": "http://www.getintent.com/"
+ "url": "http://www.getintent.com/",
+ "companyId": "getintent"
},
"getkudos": {
"name": "GetKudos",
"categoryId": 1,
- "url": "https://www.getkudos.me/"
+ "url": "https://www.getkudos.me/",
+ "companyId": "zendesk"
},
"getmyad": {
"name": "GetMyAd",
"categoryId": 4,
- "url": "http://yottos.com"
+ "url": "http://yottos.com",
+ "companyId": "yottos"
},
"getsatisfaction": {
"name": "GetSatisfaction",
"categoryId": 1,
- "url": "http://getsatisfaction.com/"
+ "url": "http://getsatisfaction.com/",
+ "companyId": "get_satisfaction"
},
"gettyimages": {
"name": "Getty Images",
"categoryId": 8,
- "url": "https://www.gettyimages.com/"
+ "url": "https://www.gettyimages.com/",
+ "companyId": null
},
"gfk": {
"name": "GfK",
"categoryId": 4,
- "url": "http://nurago.com/"
+ "url": "http://nurago.com/",
+ "companyId": "gfk_nurago"
},
"gfycat.com": {
"name": "gfycat",
"categoryId": 7,
- "url": "https://gfycat.com/"
+ "url": "https://gfycat.com/",
+ "companyId": null
},
"giant_realm": {
"name": "Giant Realm",
"categoryId": 4,
- "url": "http://corp.giantrealm.com/"
+ "url": "http://corp.giantrealm.com/",
+ "companyId": "giant_realm"
},
"giantmedia": {
"name": "GiantMedia",
"categoryId": 4,
- "url": "http://giantmedia.com/"
+ "url": "http://giantmedia.com/",
+ "companyId": "adknowledge"
},
"giga": {
"name": "Giga",
"categoryId": 4,
- "url": "https://gigaonclick.com"
+ "url": "https://gigaonclick.com",
+ "companyId": "giga"
},
"gigya": {
"name": "Gigya",
"categoryId": 6,
- "url": "https://www.sap.com/index.html"
+ "url": "https://www.sap.com/index.html",
+ "companyId": "sap"
},
"gigya_beacon": {
"name": "Gigya Beacon",
"categoryId": 2,
- "url": "http://www.gigya.com"
+ "url": "http://www.gigya.com",
+ "companyId": "sap"
},
"gigya_socialize": {
"name": "Gigya Socialize",
"categoryId": 2,
- "url": "http://www.gigya.com"
+ "url": "http://www.gigya.com",
+ "companyId": "sap"
},
"gigya_toolbar": {
"name": "Gigya Toolbar",
"categoryId": 2,
- "url": "http://www.gigya.com/"
+ "url": "http://www.gigya.com/",
+ "companyId": "sap"
},
"giosg": {
"name": "Giosg",
"categoryId": 6,
- "url": "https://www.giosg.com/"
+ "url": "https://www.giosg.com/",
+ "companyId": "giosg"
},
"giphy.com": {
"name": "Giphy",
"categoryId": 7,
- "url": "https://giphy.com/"
+ "url": "https://giphy.com/",
+ "companyId": null
},
"giraff.io": {
"name": "Giraff.io",
"categoryId": 4,
- "url": "https://www.giraff.io/"
+ "url": "https://www.giraff.io/",
+ "companyId": null
},
"github": {
"name": "GitHub",
"categoryId": 2,
- "url": "https://github.com/"
+ "url": "https://github.com/",
+ "companyId": "github"
},
"github_apps": {
"name": "GitHub Apps",
"categoryId": 2,
- "url": "https://github.com/"
+ "url": "https://github.com/",
+ "companyId": "github"
},
"github_pages": {
"name": "Github Pages",
"categoryId": 10,
- "url": "https://pages.github.com/"
+ "url": "https://pages.github.com/",
+ "companyId": "github"
},
"gittigidiyor_affiliate_program": {
"name": "GittiGidiyor Affiliate Program",
"categoryId": 4,
- "url": "http://www.ebay.com/"
+ "url": "http://www.ebay.com/",
+ "companyId": "ebay"
},
"gittip": {
"name": "Gittip",
"categoryId": 2,
- "url": "https://www.gittip.com/"
+ "url": "https://www.gittip.com/",
+ "companyId": "gittip"
},
"glad_cube": {
"name": "Glad Cube",
"categoryId": 6,
- "url": "http://www.glad-cube.com/"
+ "url": "http://www.glad-cube.com/",
+ "companyId": "glad_cube_inc."
},
"glganltcs.space": {
"name": "glganltcs.space",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"global_web_index": {
"name": "GlobalWebIndex",
"categoryId": 6,
- "url": "https://www.globalwebindex.com/"
+ "url": "https://www.globalwebindex.com/",
+ "companyId": "global_web_index"
},
"globalnotifier.com": {
"name": "globalnotifier.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"globalsign": {
"name": "GlobalSign",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"globaltakeoff": {
"name": "GlobalTakeoff",
"categoryId": 4,
- "url": "http://www.globaltakeoff.net/"
+ "url": "http://www.globaltakeoff.net/",
+ "companyId": "globaltakeoff"
},
"glomex.com": {
"name": "Glomex",
"categoryId": 0,
- "url": "https://www.glomex.com/"
+ "url": "https://www.glomex.com/",
+ "companyId": "glomex"
},
"glotgrx.com": {
"name": "glotgrx.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"gm_delivery": {
"name": "GM Delivery",
"categoryId": 4,
- "url": "http://a.gmdelivery.com/"
+ "url": "http://a.gmdelivery.com/",
+ "companyId": "gm_delivery"
},
"gmo": {
"name": "GMO",
"categoryId": 4,
- "url": "https://www.gmo.media/"
+ "url": "https://www.gmo.media/",
+ "companyId": "gmo_media"
},
"gmx_net": {
"name": "gmx.net",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"go.com": {
"name": "go.com",
"categoryId": 8,
- "url": "go.com"
+ "url": "go.com",
+ "companyId": "disney"
},
"godaddy_affiliate_program": {
"name": "GoDaddy Affiliate Program",
"categoryId": 4,
- "url": "http://www.godaddy.com/"
+ "url": "http://www.godaddy.com/",
+ "companyId": "godaddy"
},
"godaddy_site_analytics": {
"name": "GoDaddy Site Analytics",
"categoryId": 6,
- "url": "https://www.godaddy.com/gdshop/hosting/stats_"
+ "url": "https://www.godaddy.com/gdshop/hosting/stats_",
+ "companyId": "godaddy"
},
"godaddy_site_seal": {
"name": "GoDaddy Site Seal",
"categoryId": 5,
- "url": "http://www.godaddy.com/"
+ "url": "http://www.godaddy.com/",
+ "companyId": "godaddy"
},
"godatafeed": {
"name": "GoDataFeed",
"categoryId": 6,
- "url": "http://www.godatafeed.com"
+ "url": "http://www.godatafeed.com",
+ "companyId": "godatafeed"
},
"goingup": {
"name": "GoingUp",
"categoryId": 6,
- "url": "http://www.goingup.com/"
+ "url": "http://www.goingup.com/",
+ "companyId": "goingup"
},
"gomez": {
"name": "Gomez",
"categoryId": 6,
- "url": "http://www.gomez.com/"
+ "url": "http://www.gomez.com/",
+ "companyId": "dynatrace"
},
"goodadvert": {
"name": "GoodADVERT",
"categoryId": 4,
- "url": "http://goodadvert.ru/"
+ "url": "http://goodadvert.ru/",
+ "companyId": "goodadvert"
},
"google": {
"name": "Google",
"categoryId": 4,
- "url": "https://www.google.com/"
+ "url": "https://www.google.com/",
+ "companyId": "google"
},
"google_ads_measurement": {
"name": "Google Ads Measurement",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_adsense": {
"name": "Google Adsense",
"categoryId": 4,
- "url": "https://www.google.com/adsense/"
+ "url": "https://www.google.com/adsense/",
+ "companyId": "google"
},
"google_adservices": {
"name": "Google AdServices",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_adwords_conversion": {
"name": "Google AdWords Conversion",
"categoryId": 4,
- "url": "https://adwords.google.com/"
+ "url": "https://adwords.google.com/",
+ "companyId": "google"
},
"google_adwords_user_lists": {
"name": "Google Adwords User Lists",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_analytics": {
"name": "Google Analytics",
"categoryId": 6,
- "url": "http://www.google.com/analytics/"
+ "url": "http://www.google.com/analytics/",
+ "companyId": "google"
},
"google_appspot": {
"name": "Google Appspot",
"categoryId": 10,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
+ },
+ "google_beacons": {
+ "name": "Google Beacons",
+ "categoryId": 6,
+ "url": "https://google.xyz",
+ "companyId": "google"
},
"google_custom_search": {
"name": "Google Custom Search Ads",
"categoryId": 4,
- "url": "https://developers.google.com/custom-search-ads/"
+ "url": "https://developers.google.com/custom-search-ads/",
+ "companyId": "google"
},
"google_custom_search_engine": {
"name": "Google Programmable Search Engine",
"categoryId": 5,
- "url": "https://programmablesearchengine.google.com/about/"
+ "url": "https://programmablesearchengine.google.com/about/",
+ "companyId": "google"
},
"google_email": {
"name": "Google Email",
"categoryId": 13,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_fonts": {
"name": "Google Fonts",
"categoryId": 9,
- "url": "https://fonts.google.com/"
+ "url": "https://fonts.google.com/",
+ "companyId": "google"
},
"google_ima": {
"name": "Google IMA",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_photos": {
"name": "Google Photos",
"categoryId": 9,
- "url": "https://photos.google.com/"
+ "url": "https://photos.google.com/",
+ "companyId": "google"
},
"google_pingback": {
"name": "Google Pingback",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_plus": {
"name": "Google+ Platform",
"categoryId": 7,
- "url": "http://www.google.com/+1/button/"
+ "url": "http://www.google.com/+1/button/",
+ "companyId": "google"
},
"google_publisher_tags": {
"name": "Google Publisher Tags",
"categoryId": 6,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_remarketing": {
"name": "Google Dynamic Remarketing",
"categoryId": 4,
- "url": "http://adwords.google.com/"
+ "url": "http://adwords.google.com/",
+ "companyId": "google"
},
"google_safeframe": {
"name": "Google Safeframe",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_servers": {
"name": "Google Servers",
"categoryId": 8,
- "url": "https://support.google.com/faqs/answer/174717?hl=en"
+ "url": "https://support.google.com/faqs/answer/174717?hl=en",
+ "companyId": "google"
},
"google_shopping_reviews": {
"name": "Google Shopping Reviews",
"categoryId": 2,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_syndication": {
"name": "Google Syndication",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_tag_manager": {
"name": "Google Tag Manager",
"categoryId": 5,
- "url": "https://marketingplatform.google.com/about/tag-manager/"
+ "url": "https://marketingplatform.google.com/about/tag-manager/",
+ "companyId": "google"
},
"google_translate": {
"name": "Google Translate",
"categoryId": 2,
- "url": "https://translate.google.com/manager"
+ "url": "https://translate.google.com/manager",
+ "companyId": "google"
},
"google_travel_adds": {
"name": "Google Travel Adds",
"categoryId": 4,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_trusted_stores": {
"name": "Google Trusted Stores",
"categoryId": 6,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_users": {
"name": "Google User Content",
"categoryId": 9,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"google_website_optimizer": {
"name": "Google Website Optimizer",
"categoryId": 6,
- "url": "https://www.google.com/analytics/siteopt/prev"
+ "url": "https://www.google.com/analytics/siteopt/prev",
+ "companyId": "google"
},
"google_widgets": {
"name": "Google Widgets",
"categoryId": 2,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"googleapis.com": {
"name": "Google APIs",
"categoryId": 9,
- "url": "https://www.googleapis.com/"
+ "url": "https://www.googleapis.com/",
+ "companyId": "google"
},
"goooal": {
"name": "Goooal",
"categoryId": 6,
- "url": "http://mailchimp.com/"
+ "url": "http://mailchimp.com/",
+ "companyId": "mailchimp"
},
"gorilla_nation": {
"name": "Gorilla Nation",
"categoryId": 4,
- "url": "http://www.gorillanationmedia.com"
+ "url": "http://www.gorillanationmedia.com",
+ "companyId": "gorilla_nation_media"
},
"gosquared": {
"name": "GoSquared",
"categoryId": 6,
- "url": "http://www.gosquared.com/livestats/"
+ "url": "http://www.gosquared.com/livestats/",
+ "companyId": "gosquared"
},
"gostats": {
"name": "GoStats",
"categoryId": 6,
- "url": "http://gostats.com/"
+ "url": "http://gostats.com/",
+ "companyId": "gostats"
},
"govmetric": {
"name": "GovMetric",
"categoryId": 6,
- "url": "http://www.govmetric.com/"
+ "url": "http://www.govmetric.com/",
+ "companyId": "govmetric"
},
"grabo_affiliate": {
"name": "Grabo Affiliate",
"categoryId": 4,
- "url": "http://grabo.bg/"
+ "url": "http://grabo.bg/",
+ "companyId": "grabo_media"
},
"grandslammedia": {
"name": "GrandSlamMedia",
"categoryId": 4,
- "url": "http://www.grandslammedia.com/"
+ "url": "http://www.grandslammedia.com/",
+ "companyId": "grand_slam_media"
},
"granify": {
"name": "Granify",
"categoryId": 6,
- "url": "http://granify.com/"
+ "url": "http://granify.com/",
+ "companyId": "granify"
},
"grapeshot": {
"name": "Grapeshot",
"categoryId": 4,
- "url": "https://www.grapeshot.com/"
+ "url": "https://www.grapeshot.com/",
+ "companyId": "oracle"
},
"graph_comment": {
"name": "Graph Comment",
"categoryId": 5,
- "url": "https://graphcomment.com/en/"
+ "url": "https://graphcomment.com/en/",
+ "companyId": "graph_comment"
},
"gravatar": {
"name": "Gravatar",
"categoryId": 7,
- "url": "http://en.gravatar.com/"
+ "url": "http://en.gravatar.com/",
+ "companyId": "automattic"
},
"gravitec": {
"name": "Gravitec",
"categoryId": 6,
- "url": "https://gravitec.net/"
+ "url": "https://gravitec.net/",
+ "companyId": "gravitec"
},
"gravity_insights": {
"name": "Gravity Insights",
"categoryId": 6,
- "url": "http://www.gravity.com/"
+ "url": "http://www.gravity.com/",
+ "companyId": "verizon"
},
"greatviews.de": {
"name": "GreatViews",
"categoryId": 4,
- "url": "http://greatviews.de/"
+ "url": "http://greatviews.de/",
+ "companyId": "parship"
},
"green_and_red": {
"name": "Green and Red",
"categoryId": 4,
- "url": "http://www.green-red.com/"
+ "url": "http://www.green-red.com/",
+ "companyId": "green_&_red_technologies"
},
"green_certified_site": {
"name": "Green Certified Site",
"categoryId": 2,
- "url": "http://www.advenity.com/"
+ "url": "http://www.advenity.com/",
+ "companyId": "advenity"
},
"green_story": {
"name": "Green Story",
"categoryId": 6,
- "url": "https://greenstory.ca/"
+ "url": "https://greenstory.ca/",
+ "companyId": "green_story"
},
"greentube.com": {
"name": "Greentube Internet Entertainment Solutions",
"categoryId": 7,
- "url": "https://www.greentube.com/"
+ "url": "https://www.greentube.com/",
+ "companyId": null
},
"greystripe": {
"name": "Greystripe",
"categoryId": 4,
- "url": "http://www.greystripe.com/"
+ "url": "http://www.greystripe.com/",
+ "companyId": "conversant"
},
"groove": {
"name": "Groove",
"categoryId": 2,
- "url": "http://www.groovehq.com/"
+ "url": "http://www.groovehq.com/",
+ "companyId": "groove_networks"
},
"groovinads": {
"name": "GroovinAds",
"categoryId": 4,
- "url": "http://www.groovinads.com/en"
+ "url": "http://www.groovinads.com/en",
+ "companyId": "groovinads"
},
"groundtruth": {
"name": "GroundTruth",
"categoryId": 4,
- "url": "http://www.groundtruth.com/"
+ "url": "http://www.groundtruth.com/",
+ "companyId": "groundtruth"
},
"groupm_server": {
"name": "GroupM Server",
"categoryId": 4,
- "url": "http://www.groupm.com/"
+ "url": "http://www.groupm.com/",
+ "companyId": "wpp"
},
"gsi_media": {
"name": "GSI Media",
"categoryId": 4,
- "url": "http://gsimedia.net"
+ "url": "http://gsimedia.net",
+ "companyId": "gsi_media_network"
},
"gstatic": {
"name": "Google Static",
"categoryId": 9,
- "url": "http://www.google.com"
+ "url": "http://www.google.com",
+ "companyId": "google"
},
"gtop": {
"name": "GTop",
"categoryId": 6,
- "url": "http://www.gtopstats.com"
+ "url": "http://www.gtopstats.com",
+ "companyId": "gtopstats"
},
"gugaboo": {
"name": "Gugaboo",
"categoryId": 4,
- "url": "https://www.gubagoo.com/"
+ "url": "https://www.gubagoo.com/",
+ "companyId": "gubagoo"
},
"guj.de": {
"name": "Gruner + Jahr",
"categoryId": 4,
- "url": "https://www.guj.de/"
+ "url": "https://www.guj.de/",
+ "companyId": "gruner_jahr_ag"
},
"gujems": {
"name": "G+J e|MS",
"categoryId": 4,
- "url": "http://www.gujmedia.de/"
+ "url": "http://www.gujmedia.de/",
+ "companyId": "gruner_jahr_ag"
},
"gumgum": {
"name": "gumgum",
"categoryId": 4,
- "url": "http://gumgum.com/"
+ "url": "http://gumgum.com/",
+ "companyId": "gumgum"
},
"gumroad": {
"name": "Gumroad",
"categoryId": 7,
- "url": "https://gumroad.com/"
+ "url": "https://gumroad.com/",
+ "companyId": "gumroad"
},
"gunggo": {
"name": "Gunggo",
"categoryId": 4,
- "url": "http://www.gunggo.com/"
+ "url": "http://www.gunggo.com/",
+ "companyId": "gunggo"
},
"h12_ads": {
"name": "H12 Ads",
"categoryId": 4,
- "url": "http://www.h12-media.com/"
+ "url": "http://www.h12-media.com/",
+ "companyId": "h12_media_ads"
},
"hacker_news_button": {
"name": "Hacker News Button",
"categoryId": 7,
- "url": "http://news.ycombinator.com/"
+ "url": "http://news.ycombinator.com/",
+ "companyId": "hacker_news"
},
"haendlerbund.de": {
"name": "Händlerbund",
"categoryId": 2,
- "url": "https://www.haendlerbund.de/en"
+ "url": "https://www.haendlerbund.de/en",
+ "companyId": null
},
"halogen_network": {
"name": "Halogen Network",
"categoryId": 7,
- "url": "http://www.halogennetwork.com/"
+ "url": "http://www.halogennetwork.com/",
+ "companyId": "social_chorus"
},
"happy_fox_chat": {
"name": "Happy Fox Chat",
"categoryId": 2,
- "url": "https://happyfoxchat.com/"
+ "url": "https://happyfoxchat.com/",
+ "companyId": "happy_fox_chat"
},
"harren_media": {
"name": "Harren Media",
"categoryId": 4,
- "url": "http://www.harrenmedia.com/index.html"
+ "url": "http://www.harrenmedia.com/index.html",
+ "companyId": "harren_media"
},
"hatchbuck": {
"name": "Hatchbuck",
"categoryId": 6,
- "url": "http://www.hatchbuck.com/"
+ "url": "http://www.hatchbuck.com/",
+ "companyId": "hatchbuck"
},
"head_hunter": {
"name": "Head Hunter",
"categoryId": 6,
- "url": "https://hh.ru/"
+ "url": "https://hh.ru/",
+ "companyId": "head_hunter"
},
"healte.de": {
"name": "healte.de",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"heap": {
"name": "Heap",
"categoryId": 6,
- "url": "https://heapanalytics.com/"
+ "url": "https://heapanalytics.com/",
+ "companyId": "heap"
},
"heatmap": {
"name": "Heatmap",
"categoryId": 6,
- "url": "https://heatmap.me/"
+ "url": "https://heatmap.me/",
+ "companyId": "heatmap"
},
"heimspiel": {
"name": "HEIM:SPIEL Medien GmbH",
"categoryId": 8,
- "url": "http://www.heimspiel.de"
+ "url": "http://www.heimspiel.de",
+ "companyId": null
},
"hello_bar": {
"name": "Hello Bar",
"categoryId": 7,
- "url": "https://www.hellobar.com/"
+ "url": "https://www.hellobar.com/",
+ "companyId": "crazy_egg"
},
"hellosociety": {
"name": "HelloSociety",
"categoryId": 6,
- "url": "http://hellosociety.com"
+ "url": "http://hellosociety.com",
+ "companyId": "hellosociety"
},
"here": {
"name": "HERE",
"categoryId": 8,
- "url": "https://www.here.com/"
+ "url": "https://www.here.com/",
+ "companyId": null
},
"heroku": {
"name": "Heroku",
"categoryId": 10,
- "url": null
+ "url": null,
+ "companyId": null
},
"heureka-widget": {
"name": "Heureka-Widget",
"categoryId": 4,
- "url": "https://www.heurekashopping.cz/"
+ "url": "https://www.heurekashopping.cz/",
+ "companyId": "heureka"
},
"heybubble": {
"name": "HeyBubble",
"categoryId": 2,
- "url": "https://www.heybubble.com/"
+ "url": "https://www.heybubble.com/",
+ "companyId": "heybubble"
},
"heyos": {
"name": "Heyos",
"categoryId": 4,
- "url": "http://www.heyos.com/"
+ "url": "http://www.heyos.com/",
+ "companyId": "heyos"
},
"hi-media_performance": {
"name": "Hi-Media Performance",
"categoryId": 4,
- "url": "http://www.hi-mediaperformance.co.uk/"
+ "url": "http://www.hi-mediaperformance.co.uk/",
+ "companyId": "hi-media_performance"
},
"hiconversion": {
"name": "HiConversion",
"categoryId": 4,
- "url": "http://www.hiconversion.com"
+ "url": "http://www.hiconversion.com",
+ "companyId": "hiconversion"
},
"highwebmedia.com": {
"name": "highwebmedia.com",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"highwinds": {
"name": "Highwinds",
"categoryId": 6,
- "url": "https://www.highwinds.com/"
+ "url": "https://www.highwinds.com/",
+ "companyId": "highwinds"
},
"hiiir": {
"name": "Hiiir",
"categoryId": 4,
- "url": "http://adpower.hiiir.com/"
+ "url": "http://adpower.hiiir.com/",
+ "companyId": "hiiir"
},
"hiro": {
"name": "HIRO",
"categoryId": 4,
- "url": "http://www.hiro-media.com/"
+ "url": "http://www.hiro-media.com/",
+ "companyId": "hiro_media"
},
"histats": {
"name": "Histats",
"categoryId": 4,
- "url": "http://www.histats.com/"
+ "url": "http://www.histats.com/",
+ "companyId": "histats"
},
"hit-parade": {
"name": "Hit-Parade",
"categoryId": 4,
- "url": "http://www.hit-parade.com/"
+ "url": "http://www.hit-parade.com/",
+ "companyId": "hit-parade"
},
"hit.ua": {
"name": "HIT.UA",
"categoryId": 4,
- "url": "http://hit.ua/"
+ "url": "http://hit.ua/",
+ "companyId": "hit.ua"
},
"hitslink": {
"name": "HitsLink",
"categoryId": 4,
- "url": "http://www.hitslink.com/"
+ "url": "http://www.hitslink.com/",
+ "companyId": "net_applications"
},
"hitsniffer": {
"name": "HitSniffer",
"categoryId": 4,
- "url": "http://hitsniffer.com/"
+ "url": "http://hitsniffer.com/",
+ "companyId": "hit_sniffer"
},
"hittail": {
"name": "HitTail",
"categoryId": 4,
- "url": "http://www.hittail.com/"
+ "url": "http://www.hittail.com/",
+ "companyId": "hittail"
},
"hivedx.com": {
"name": "hiveDX",
"categoryId": 4,
- "url": "https://www.hivedx.com/"
+ "url": "https://www.hivedx.com/",
+ "companyId": null
},
"hiveworks": {
"name": "Hive Networks",
"categoryId": 4,
- "url": "https://hiveworkscomics.com/"
+ "url": "https://hiveworkscomics.com/",
+ "companyId": "hive_works"
},
"hoholikik.club": {
"name": "hoholikik.club",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"hola_player": {
"name": "Hola Player",
"categoryId": 0,
- "url": "https://holacdn.com/"
+ "url": "https://holacdn.com/",
+ "companyId": "hola_cdn"
},
"homeaway": {
"name": "HomeAway",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"honeybadger": {
"name": "Honeybadger",
"categoryId": 6,
- "url": "https://www.honeybadger.io/"
+ "url": "https://www.honeybadger.io/",
+ "companyId": "honeybadger"
},
"hooklogic": {
"name": "HookLogic",
"categoryId": 4,
- "url": "http://hooklogic.com/"
+ "url": "http://hooklogic.com/",
+ "companyId": "criteo"
},
"hop-cube": {
"name": "Hop-Cube",
"categoryId": 4,
- "url": "http://www.hop-cube.com/"
+ "url": "http://www.hop-cube.com/",
+ "companyId": "hop-cube"
},
"hotdogsandads.com": {
"name": "hotdogsandads.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"hotjar": {
"name": "Hotjar",
"categoryId": 6,
- "url": "http://www.hotjar.com/"
+ "url": "http://www.hotjar.com/",
+ "companyId": "hotjar"
},
"hotkeys": {
"name": "HotKeys",
"categoryId": 4,
- "url": "http://www.demandmedia.com/"
+ "url": "http://www.demandmedia.com/",
+ "companyId": "leaf_group"
},
"hotlog.ru": {
"name": "HotLog",
"categoryId": 4,
- "url": "https://hotlog.ru/"
+ "url": "https://hotlog.ru/",
+ "companyId": "hotlog"
},
"hotwords": {
"name": "HOTWords",
"categoryId": 4,
- "url": "http://hotwords.com/"
+ "url": "http://hotwords.com/",
+ "companyId": "hotwords"
},
"howtank.com": {
"name": "howtank",
"categoryId": 7,
- "url": "https://www.howtank.com/"
+ "url": "https://www.howtank.com/",
+ "companyId": null
},
"hqentertainmentnetwork.com": {
"name": "HQ Entertainment Network",
"categoryId": 4,
- "url": "https://hqentertainmentnetwork.com/"
+ "url": "https://hqentertainmentnetwork.com/",
+ "companyId": null
},
"hsoub": {
"name": "Hsoub",
"categoryId": 4,
- "url": "http://www.hsoub.com/"
+ "url": "http://www.hsoub.com/",
+ "companyId": "hsoub"
},
"hstrck.com": {
"name": "HEIM:SPIEL Medien GmbH",
"categoryId": 8,
- "url": "https://www.heimspiel.de/"
+ "url": "https://www.heimspiel.de/",
+ "companyId": null
},
"httpool": {
"name": "HTTPool",
"categoryId": 4,
- "url": "http://www.httpool.com/"
+ "url": "http://www.httpool.com/",
+ "companyId": "httpool"
},
"hubrus": {
"name": "HUBRUS",
"categoryId": 4,
- "url": "http://www.hubrus.com/"
+ "url": "http://www.hubrus.com/",
+ "companyId": "hubrus"
},
"hubspot": {
"name": "HubSpot",
"categoryId": 6,
- "url": "http://www.hubspot.com/"
+ "url": "http://www.hubspot.com/",
+ "companyId": "hubspot"
},
"hubspot_forms": {
"name": "HubSpot Forms",
"categoryId": 2,
- "url": "http://www.hubspot.com"
+ "url": "http://www.hubspot.com",
+ "companyId": "hubspot"
},
"hubvisor.io": {
"name": "Hubvisor",
"categoryId": 4,
- "url": "https://hubvisor.io/"
+ "url": "https://hubvisor.io/",
+ "companyId": null
},
"hucksterbot": {
"name": "HucksterBot",
"categoryId": 4,
- "url": "http://hucksterbot.ru/"
+ "url": "http://hucksterbot.ru/",
+ "companyId": "hucksterbot"
},
"hupso": {
"name": "Hupso",
"categoryId": 7,
- "url": "http://www.hupso.com/"
+ "url": "http://www.hupso.com/",
+ "companyId": "hupso"
},
"hurra_tracker": {
"name": "Hurra Tracker",
"categoryId": 4,
- "url": "http://www.hurra.com/en/"
+ "url": "http://www.hurra.com/en/",
+ "companyId": "hurra_communications"
},
"hybrid.ai": {
"name": "Hybrid.ai",
"categoryId": 4,
- "url": "https://hybrid.ai/"
+ "url": "https://hybrid.ai/",
+ "companyId": "hybrid_adtech"
},
"hype_exchange": {
"name": "Hype Exchange",
"categoryId": 4,
- "url": "http://www.hypeexchange.com/"
+ "url": "http://www.hypeexchange.com/",
+ "companyId": "hype_exchange"
},
"hypercomments": {
"name": "HyperComments",
"categoryId": 1,
- "url": "http://www.hypercomments.com/"
+ "url": "http://www.hypercomments.com/",
+ "companyId": "hypercomments"
},
"hyves_widgets": {
"name": "Hyves Widgets",
"categoryId": 4,
- "url": "http://www.hyves.nl/"
+ "url": "http://www.hyves.nl/",
+ "companyId": "hyves"
},
"hyvyd": {
"name": "Hyvyd GmbH",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"i-behavior": {
"name": "i-Behavior",
"categoryId": 4,
- "url": "http://www.i-behavior.com/"
+ "url": "http://www.i-behavior.com/",
+ "companyId": "kbm_group"
},
"i-mobile": {
"name": "i-mobile",
"categoryId": 4,
- "url": "https://www2.i-mobile.co.jp/en/index.aspx"
+ "url": "https://www2.i-mobile.co.jp/en/index.aspx",
+ "companyId": "i-mobile"
},
"i.ua": {
"name": "i.ua",
"categoryId": 4,
- "url": "http://www.i.ua/"
+ "url": "http://www.i.ua/",
+ "companyId": "i.ua"
},
"i10c.net": {
"name": "i10c.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"i2i.jp": {
"name": "i2i.jp",
"categoryId": 6,
- "url": "http://www.i2i.jp/"
+ "url": "http://www.i2i.jp/",
+ "companyId": "i2i.jp"
},
"iab_consent": {
"name": "IAB Consent",
"categoryId": 5,
- "url": "https://iabtechlab.com/standards/gdpr-transparency-and-consent-framework/"
+ "url": "https://iabtechlab.com/standards/gdpr-transparency-and-consent-framework/",
+ "companyId": "iab"
},
"iadvize": {
"name": "iAdvize",
"categoryId": 2,
- "url": "http://www.iadvize.com/"
+ "url": "http://www.iadvize.com/",
+ "companyId": "iadvize"
},
"ibm_customer_experience": {
"name": "IBM Digital Analytics",
"categoryId": 6,
- "url": "http://www.coremetrics.com/"
+ "url": "http://www.coremetrics.com/",
+ "companyId": "ibm"
},
"icerocket_tracker": {
"name": "IceRocket Tracker",
"categoryId": 7,
- "url": "http://tracker.icerocket.com/"
+ "url": "http://tracker.icerocket.com/",
+ "companyId": "meltwater_icerocket"
},
"icf_technology": {
"name": "ICF Technology",
"categoryId": 2,
- "url": "http://www.icftechnology.com/"
+ "url": "http://www.icftechnology.com/",
+ "companyId": null
},
"iclick": {
"name": "iClick",
"categoryId": 4,
- "url": "http://optimix.asia/"
+ "url": "http://optimix.asia/",
+ "companyId": "iclick_interactive"
},
"icrossing": {
"name": "iCrossing",
"categoryId": 4,
- "url": "http://www.icrossing.com/"
+ "url": "http://www.icrossing.com/",
+ "companyId": "hearst"
},
"icstats": {
"name": "ICStats",
"categoryId": 6,
- "url": "http://www.icstats.nl/"
+ "url": "http://www.icstats.nl/",
+ "companyId": "icstats"
},
"icuazeczpeoohx.com": {
"name": "icuazeczpeoohx.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"id-news.net": {
"name": "Ippen Digital",
"categoryId": 4,
- "url": "https://www.ippen-digital.de/"
+ "url": "https://www.ippen-digital.de/",
+ "companyId": null
},
"id_services": {
"name": "ID Services",
"categoryId": 6,
- "url": "https://id.services/"
+ "url": "https://id.services/",
+ "companyId": "id_services"
},
"ideal_media": {
"name": "Ideal Media",
"categoryId": 4,
- "url": "http://idealmedia.com/"
+ "url": "http://idealmedia.com/",
+ "companyId": "ideal_media"
},
"idealo_com": {
"name": "idealo.com",
"categoryId": 4,
- "url": "http://idealo.com/"
+ "url": "http://idealo.com/",
+ "companyId": null
},
"ideoclick": {
"name": "IdeoClick",
"categoryId": 4,
- "url": "http://ideoclick.com"
+ "url": "http://ideoclick.com",
+ "companyId": "ideoclick"
},
"idio": {
"name": "Idio",
"categoryId": 4,
- "url": "https://www.idio.ai/"
+ "url": "https://www.idio.ai/",
+ "companyId": "idio"
},
"ie8eamus.com": {
"name": "ie8eamus.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"ientry": {
"name": "iEntry",
"categoryId": 4,
- "url": "http://www.ientry.com/"
+ "url": "http://www.ientry.com/",
+ "companyId": "ientry"
},
"iflychat": {
"name": "iFlyChat",
"categoryId": 2,
- "url": "https://iflychat.com/"
+ "url": "https://iflychat.com/",
+ "companyId": "iflychat"
},
"ignitionone": {
"name": "IgnitionOne",
"categoryId": 6,
- "url": "https://www.ignitionone.com/"
+ "url": "https://www.ignitionone.com/",
+ "companyId": "zeta"
},
"igodigital": {
"name": "iGoDigital",
"categoryId": 2,
- "url": "http://igodigital.com/"
+ "url": "http://igodigital.com/",
+ "companyId": "salesforce"
},
"ihs_markit": {
"name": "IHS Markit",
"categoryId": 6,
- "url": "https://ihsmarkit.com/index.html"
+ "url": "https://ihsmarkit.com/index.html",
+ "companyId": "ihs"
},
"ihs_markit_online_shopper_insigh": {
"name": "IHS Markit Online Shopper Insigh",
"categoryId": 6,
- "url": "http://www.visicogn.com/vcu.htm"
+ "url": "http://www.visicogn.com/vcu.htm",
+ "companyId": "ihs"
},
"ihvmcqojoj.com": {
"name": "ihvmcqojoj.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"iias.eu": {
"name": "Insight Image",
"categoryId": 3,
- "url": "http://insightimage.com/"
+ "url": "http://insightimage.com/",
+ "companyId": null
},
"ijento": {
"name": "iJento",
"categoryId": 6,
- "url": "http://www.ijento.com/"
+ "url": "http://www.ijento.com/",
+ "companyId": "ijento"
},
"imad": {
"name": "imad",
"categoryId": 4,
- "url": "http://www.imad.co.kr/"
+ "url": "http://www.imad.co.kr/",
+ "companyId": "i'mad_republic"
},
"image_advantage": {
"name": "Image Advantage",
"categoryId": 4,
- "url": "http://www.worthathousandwords.com/"
+ "url": "http://www.worthathousandwords.com/",
+ "companyId": "image_advantage"
},
"image_space_media": {
"name": "Image Space Media",
"categoryId": 4,
- "url": "http://www.imagespacemedia.com/"
+ "url": "http://www.imagespacemedia.com/",
+ "companyId": "image_space_media"
},
"imgix.net": {
"name": "ImgIX",
"categoryId": 9,
- "url": "https://www.imgix.com/"
+ "url": "https://www.imgix.com/",
+ "companyId": null
},
"imgur": {
"name": "Imgur",
"categoryId": 8,
- "url": "https://imgur.com/"
+ "url": "https://imgur.com/",
+ "companyId": "imgur"
},
"imho_vi": {
"name": "imho vi",
"categoryId": 4,
- "url": "http://www.imho.ru"
+ "url": "http://www.imho.ru",
+ "companyId": "imho"
},
"immanalytics": {
"name": "Immanalytics",
"categoryId": 2,
- "url": "https://www.roku.com/"
+ "url": "https://www.roku.com/",
+ "companyId": "roku"
},
"immobilienscout24_de": {
"name": "immobilienscout24.de",
"categoryId": 8,
- "url": "http://www.scout24.com/"
+ "url": "http://www.scout24.com/",
+ "companyId": "scout24"
},
"imonomy": {
"name": "imonomy",
"categoryId": 6,
- "url": "http://imonomy.com/"
+ "url": "http://imonomy.com/",
+ "companyId": "imonomy"
},
"impact_radius": {
"name": "Impact Radius",
"categoryId": 5,
- "url": "http://www.impactradius.com/"
+ "url": "http://www.impactradius.com/",
+ "companyId": "impact_radius"
},
"impresiones_web": {
"name": "Impresiones Web",
"categoryId": 4,
- "url": "http://www.iw-advertising.com/"
+ "url": "http://www.iw-advertising.com/",
+ "companyId": "impresiones_web"
},
"improve_digital": {
"name": "Improve Digital",
"categoryId": 4,
- "url": "http://www.improvedigital.com/"
+ "url": "http://www.improvedigital.com/",
+ "companyId": "improve_digital"
},
"improvely": {
"name": "Improvely",
"categoryId": 6,
- "url": "https://www.improvely.com/"
+ "url": "https://www.improvely.com/",
+ "companyId": "awio_web_services"
},
"inbenta": {
"name": "Inbenta",
"categoryId": 6,
- "url": "https://www.inbenta.com/en/"
+ "url": "https://www.inbenta.com/en/",
+ "companyId": "inbenta"
},
"inboxsdk.com": {
"name": "Inbox SDK",
"categoryId": 8,
- "url": "https://www.inboxsdk.com/"
+ "url": "https://www.inboxsdk.com/",
+ "companyId": null
},
"indeed": {
"name": "Indeed",
"categoryId": 4,
- "url": "http://www.indeed.com/"
+ "url": "http://www.indeed.com/",
+ "companyId": "indeed"
},
"index_exchange": {
"name": "Index Exchange",
"categoryId": 4,
- "url": "http://www.casalemedia.com/"
+ "url": "http://www.casalemedia.com/",
+ "companyId": "index_exchange"
},
"indieclick": {
"name": "IndieClick",
"categoryId": 4,
- "url": "http://www.indieclick.com/"
+ "url": "http://www.indieclick.com/",
+ "companyId": "leaf_group"
},
"industry_brains": {
"name": "Industry Brains",
"categoryId": 4,
- "url": "http://www.industrybrains.com/"
+ "url": "http://www.industrybrains.com/",
+ "companyId": "industrybrains"
},
"infectious_media": {
"name": "Impression Desk",
"categoryId": 4,
- "url": "https://impressiondesk.com/"
+ "url": "https://impressiondesk.com/",
+ "companyId": "infectious_media"
},
"infinite_analytics": {
"name": "Infinite Analytics",
"categoryId": 6,
- "url": "http://infiniteanalytics.com/products/"
+ "url": "http://infiniteanalytics.com/products/",
+ "companyId": "infinite_analytics"
},
"infinity_tracking": {
"name": "Infinity Tracking",
"categoryId": 6,
- "url": "http://www.infinity-tracking.com"
+ "url": "http://www.infinity-tracking.com",
+ "companyId": "infinity_tracking"
},
"influads": {
"name": "InfluAds",
"categoryId": 4,
- "url": "http://www.influads.com/"
+ "url": "http://www.influads.com/",
+ "companyId": "influads"
},
"infolinks": {
"name": "InfoLinks",
"categoryId": 4,
- "url": "http://www.infolinks.com/"
+ "url": "http://www.infolinks.com/",
+ "companyId": "infolinks"
},
"infonline": {
"name": "INFOnline",
"categoryId": 6,
- "url": "http://www.infonline.de/"
+ "url": "http://www.infonline.de/",
+ "companyId": "infonline"
},
"informer_technologies": {
"name": "Informer Technologies",
"categoryId": 6,
- "url": "http://www.informer.com/"
+ "url": "http://www.informer.com/",
+ "companyId": "informer_technologies"
},
"infusionsoft": {
"name": "Infusionsoft by Keap",
"categoryId": 4,
- "url": "https://keap.com/"
+ "url": "https://keap.com/",
+ "companyId": "infusionsoft"
},
"innity": {
"name": "Innity",
"categoryId": 4,
- "url": "http://www.innity.com/"
+ "url": "http://www.innity.com/",
+ "companyId": "innity"
},
"innogames.de": {
"name": "InnoGames",
"categoryId": 8,
- "url": "https://www.innogames.com/"
+ "url": "https://www.innogames.com/",
+ "companyId": null
},
"innovid": {
"name": "Innovid",
"categoryId": 4,
- "url": "https://www.innovid.com/"
+ "url": "https://www.innovid.com/",
+ "companyId": "innovid"
},
"inside": {
"name": "inside",
"categoryId": 7,
- "url": "http://www.inside.tm/"
+ "url": "http://www.inside.tm/",
+ "companyId": "powerfront"
},
"insider": {
"name": "Insider",
"categoryId": 6,
- "url": "http://useinsider.com/"
+ "url": "http://useinsider.com/",
+ "companyId": "insider"
},
"insightexpress": {
"name": "InsightExpress",
"categoryId": 6,
- "url": "https://www.millwardbrowndigital.com/"
+ "url": "https://www.millwardbrowndigital.com/",
+ "companyId": "millward_brown"
},
"inskin_media": {
"name": "InSkin Media",
"categoryId": 4,
- "url": "http://www.inskinmedia.com/"
+ "url": "http://www.inskinmedia.com/",
+ "companyId": "inskin_media"
},
"inspectlet": {
"name": "Inspectlet",
"categoryId": 6,
- "url": "https://www.inspectlet.com/"
+ "url": "https://www.inspectlet.com/",
+ "companyId": "inspectlet"
},
"inspsearchapi.com": {
"name": "Infospace Search",
"categoryId": 4,
- "url": "http://infospace.com/"
+ "url": "http://infospace.com/",
+ "companyId": "system1"
},
"instagram_com": {
"name": "Instagram",
"categoryId": 8,
- "url": "https://www.facebook.com/"
+ "url": "https://www.facebook.com/",
+ "companyId": "facebook"
},
"instant_check_mate": {
"name": "Instant Check Mate",
"categoryId": 2,
- "url": "https://www.instantcheckmate.com/"
+ "url": "https://www.instantcheckmate.com/",
+ "companyId": "instant_check_mate"
},
"instart_logic": {
"name": "Instart Logic",
"categoryId": 4,
- "url": "https://www.instartlogic.com/"
+ "url": "https://www.instartlogic.com/",
+ "companyId": "instart_logic_inc"
},
"insticator": {
"name": "Insticator",
"categoryId": 4,
- "url": "https://www.insticator.com/landingpage"
+ "url": "https://www.insticator.com/landingpage",
+ "companyId": "insticator"
},
"instinctive": {
"name": "Instinctive",
"categoryId": 4,
- "url": "https://instinctive.io/"
+ "url": "https://instinctive.io/",
+ "companyId": "instinctive"
},
"intango": {
"name": "Intango",
"categoryId": 4,
- "url": "https://intango.com/"
+ "url": "https://intango.com/",
+ "companyId": "intango"
},
"integral_ad_science": {
"name": "Integral Ad Science",
"categoryId": 4,
- "url": "https://integralads.com/"
+ "url": "https://integralads.com/",
+ "companyId": "integral_ad_science"
},
"integral_marketing": {
"name": "Integral Marketing",
"categoryId": 4,
- "url": "http://integral-marketing.com/"
+ "url": "http://integral-marketing.com/",
+ "companyId": "integral_marketing"
},
"intelliad": {
"name": "intelliAd",
"categoryId": 6,
- "url": "http://www.intelliad.de/"
+ "url": "http://www.intelliad.de/",
+ "companyId": "intelliad"
},
"intelligencefocus": {
"name": "IntelligenceFocus",
"categoryId": 6,
- "url": "http://www.intelligencefocus.com"
+ "url": "http://www.intelligencefocus.com",
+ "companyId": "intelligencefocus"
},
"intelligent_reach": {
"name": "Intelligent Reach",
"categoryId": 4,
- "url": "http://www.intelligentreach.com/"
+ "url": "http://www.intelligentreach.com/",
+ "companyId": "intelligent_reach"
},
"intense_debate": {
"name": "Intense Debate",
"categoryId": 2,
- "url": "http://intensedebate.com/"
+ "url": "http://intensedebate.com/",
+ "companyId": "automattic"
},
"intent_iq": {
"name": "Intent IQ",
"categoryId": 4,
- "url": "http://datonics.com/"
+ "url": "http://datonics.com/",
+ "companyId": "almondnet"
},
"intent_media": {
"name": "Intent",
"categoryId": 4,
- "url": "https://intent.com/"
+ "url": "https://intent.com/",
+ "companyId": "intent_media"
},
"intercom": {
"name": "Intercom",
"categoryId": 2,
- "url": "http://intercom.io/"
+ "url": "http://intercom.io/",
+ "companyId": "intercom"
},
"interedy.info": {
"name": "interedy.info",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"intergi": {
"name": "Intergi",
"categoryId": 4,
- "url": "http://www.intergi.com/"
+ "url": "http://www.intergi.com/",
+ "companyId": "intergi_entertainment"
},
"intermarkets.net": {
"name": "Intermarkets",
"categoryId": 4,
- "url": "http://intermarkets.net/"
+ "url": "http://intermarkets.net/",
+ "companyId": "intermarkets"
},
"intermundo_media": {
"name": "InterMundo Media",
"categoryId": 4,
- "url": "http://intermundomedia.com/"
+ "url": "http://intermundomedia.com/",
+ "companyId": "intermundo_media"
},
"internet_billboard": {
"name": "Internet BillBoard",
"categoryId": 4,
- "url": "http://www.ibillboard.com/en/"
+ "url": "http://www.ibillboard.com/en/",
+ "companyId": "internet_billboard"
},
"internetaudioads": {
"name": "InternetAudioAds",
"categoryId": 0,
- "url": "http://www.internetaudioads.com/"
+ "url": "http://www.internetaudioads.com/",
+ "companyId": "internetaudioads"
},
"internetbrands": {
"name": "InternetBrands",
"categoryId": 7,
- "url": "http://www.internetbrands.com/"
+ "url": "http://www.internetbrands.com/",
+ "companyId": "internet_brands"
},
"interpolls": {
"name": "Interpolls",
"categoryId": 4,
- "url": "http://www.interpolls.com/"
+ "url": "http://www.interpolls.com/",
+ "companyId": "interpolls"
},
"interyield": {
"name": "Interyield",
"categoryId": 4,
- "url": "http://www.advertise.com/publisher-solutions/"
+ "url": "http://www.advertise.com/publisher-solutions/",
+ "companyId": "advertise.com"
},
"intilery": {
"name": "Intilery",
"categoryId": 6,
- "url": "http://www.intilery.com"
+ "url": "http://www.intilery.com",
+ "companyId": "intilery"
},
"intimate_merger": {
"name": "Intimate Merger",
"categoryId": 6,
- "url": "https://corp.intimatemerger.com/"
+ "url": "https://corp.intimatemerger.com/",
+ "companyId": "intimate_merger"
},
"investingchannel": {
"name": "Investing Channel",
"categoryId": 8,
- "url": "http://www.investingchannel.com/"
+ "url": "http://www.investingchannel.com/",
+ "companyId": "investingchannel"
},
"inviziads": {
"name": "InviziAds",
"categoryId": 4,
- "url": "http://www.inviziads.com"
+ "url": "http://www.inviziads.com",
+ "companyId": "inviziads"
},
"invoca": {
"name": "Invoca",
"categoryId": 4,
- "url": "http://www.invoca.com/"
+ "url": "http://www.invoca.com/",
+ "companyId": "invoca"
},
"invodo": {
"name": "Invodo",
"categoryId": 6,
- "url": "http://www.invodo.com/"
+ "url": "http://www.invodo.com/",
+ "companyId": "invodo"
},
"ionicframework.com": {
"name": "Ionic",
"categoryId": 8,
- "url": "https://ionicframework.com/"
+ "url": "https://ionicframework.com/",
+ "companyId": null
},
"iotec": {
"name": "iotec",
"categoryId": 4,
- "url": "https://www.iotecglobal.com/"
+ "url": "https://www.iotecglobal.com/",
+ "companyId": "iotec"
},
"iovation": {
"name": "iovation",
"categoryId": 5,
- "url": "http://www.iovation.com/"
+ "url": "http://www.iovation.com/",
+ "companyId": "iovation"
},
"ip-label": {
"name": "ip-label",
"categoryId": 6,
- "url": "http://www.ip-label.co.uk/"
+ "url": "http://www.ip-label.co.uk/",
+ "companyId": "ip-label"
},
"ip_targeting": {
"name": "IP Targeting",
"categoryId": 6,
- "url": "https://www.iptargeting.com/"
+ "url": "https://www.iptargeting.com/",
+ "companyId": "el_toro"
},
"ip_tracker": {
"name": "IP Tracker",
"categoryId": 6,
- "url": "http://www.ip-tracker.org/"
+ "url": "http://www.ip-tracker.org/",
+ "companyId": "ip_tracker"
},
"iperceptions": {
"name": "iPerceptions",
"categoryId": 2,
- "url": "http://www.iperceptions.com/"
+ "url": "http://www.iperceptions.com/",
+ "companyId": "iperceptions"
},
"ipfingerprint": {
"name": "IPFingerprint",
"categoryId": 6,
- "url": "http://www.ipfingerprint.com/"
+ "url": "http://www.ipfingerprint.com/",
+ "companyId": "ipfingerprint"
},
"ipg_mediabrands": {
"name": "IPG Mediabrands",
"categoryId": 4,
- "url": "https://www.ipgmediabrands.com/"
+ "url": "https://www.ipgmediabrands.com/",
+ "companyId": "ipg_mediabrands"
},
"ipify": {
"name": "ipify",
"categoryId": 8,
- "url": "https://www.ipify.org/"
+ "url": "https://www.ipify.org/",
+ "companyId": null
},
"ipinfo": {
"name": "Ipinfo",
"categoryId": 2,
- "url": "https://ipinfo.io/"
+ "url": "https://ipinfo.io/",
+ "companyId": "ipinfo.io"
},
"iplogger": {
"name": "IPLogger",
"categoryId": 6,
- "url": "http://iplogger.ru/"
+ "url": "http://iplogger.ru/",
+ "companyId": "iplogger"
},
"iprom": {
"name": "iprom",
"categoryId": 4,
- "url": "http://www.iprom.si/"
+ "url": "http://www.iprom.si/",
+ "companyId": "iprom"
},
"ipromote": {
"name": "iPromote",
"categoryId": 4,
- "url": "http://www.ipromote.com/"
+ "url": "http://www.ipromote.com/",
+ "companyId": "ipromote"
},
"iprospect": {
"name": "iProspect",
"categoryId": 4,
- "url": "http://www.iprospect.com/"
+ "url": "http://www.iprospect.com/",
+ "companyId": "dentsu_aegis_network"
},
"ironsource": {
"name": "ironSource",
"categoryId": 4,
- "url": "https://www.ironsrc.com/"
+ "url": "https://www.ironsrc.com/",
+ "companyId": "ironsource"
},
"isocket": {
"name": "isocket",
"categoryId": 4,
- "url": "http://www.isocket.com/"
+ "url": "http://www.isocket.com/",
+ "companyId": "rubicon_project"
},
"ispot.tv": {
"name": "iSpot.tv",
"categoryId": 4,
- "url": "https://www.ispot.tv/"
+ "url": "https://www.ispot.tv/",
+ "companyId": null
},
"itineraire.info": {
"name": "itineraire.info",
"categoryId": 2,
- "url": "https://www.itineraire.info/"
+ "url": "https://www.itineraire.info/",
+ "companyId": null
},
"itunes_link_maker": {
"name": "iTunes Link Maker",
"categoryId": 4,
- "url": "https://www.apple.com/"
+ "url": "https://www.apple.com/",
+ "companyId": "apple"
},
"ity.im": {
"name": "ity.im",
"categoryId": 4,
- "url": "http://ity.im/"
+ "url": "http://ity.im/",
+ "companyId": "ity.im"
},
"iubenda.com": {
"name": "iubenda",
"categoryId": 5,
- "url": "https://www.iubenda.com/"
+ "url": "https://www.iubenda.com/",
+ "companyId": "iubenda"
},
"ivcbrasil.org.br": {
"name": "IVC Brasil",
"categoryId": 6,
- "url": "https://ivcbrasil.org.br/#/home"
+ "url": "https://ivcbrasil.org.br/#/home",
+ "companyId": null
},
"ividence": {
"name": "Ividence",
"categoryId": 4,
- "url": "https://www.ividence.com/home/"
+ "url": "https://www.ividence.com/home/",
+ "companyId": "sien"
},
"iwiw_widgets": {
"name": "iWiW Widgets",
"categoryId": 2,
- "url": "http://iwiw.hu"
+ "url": "http://iwiw.hu",
+ "companyId": "iwiw"
},
"ixi_digital": {
"name": "IXI Digital",
"categoryId": 4,
- "url": "http://www.equifax.com/home/en_us"
+ "url": "http://www.equifax.com/home/en_us",
+ "companyId": "equifax"
},
"ixquick.com": {
"name": "ixquick",
"categoryId": 8,
- "url": "https://www.ixquick.com/"
+ "url": "https://www.ixquick.com/",
+ "companyId": "startpage"
},
"izooto": {
"name": "iZooto",
"categoryId": 6,
- "url": "https://www.izooto.com/"
+ "url": "https://www.izooto.com/",
+ "companyId": "izooto"
},
"j-list_affiliate_program": {
"name": "J-List Affiliate Program",
"categoryId": 4,
- "url": "http://www.jlist.com/page/affiliates.html"
+ "url": "http://www.jlist.com/page/affiliates.html",
+ "companyId": "j-list"
},
"jaco": {
"name": "Jaco",
"categoryId": 6,
- "url": "https://www.walkme.com/"
+ "url": "https://www.walkme.com/",
+ "companyId": "walkme"
},
"janrain": {
"name": "Janrain",
"categoryId": 6,
- "url": "http://www.janrain.com/"
+ "url": "http://www.janrain.com/",
+ "companyId": "akamai"
},
"jeeng": {
"name": "Jeeng",
"categoryId": 4,
- "url": "https://jeeng.com/"
+ "url": "https://jeeng.com/",
+ "companyId": "jeeng"
},
"jeeng_widgets": {
"name": "Jeeng Widgets",
"categoryId": 4,
- "url": "https://jeeng.com/"
+ "url": "https://jeeng.com/",
+ "companyId": "jeeng"
},
"jet_interactive": {
"name": "Jet Interactive",
"categoryId": 6,
- "url": "http://www.jetinteractive.com.au/"
+ "url": "http://www.jetinteractive.com.au/",
+ "companyId": "jet_interactive"
},
"jetlore": {
"name": "Jetlore",
"categoryId": 6,
- "url": "http://www.jetlore.com/"
+ "url": "http://www.jetlore.com/",
+ "companyId": "jetlore"
},
"jetpack": {
"name": "Jetpack",
"categoryId": 6,
- "url": "https://jetpack.com/"
+ "url": "https://jetpack.com/",
+ "companyId": "automattic"
},
"jetpack_digital": {
"name": "Jetpack Digital",
"categoryId": 6,
- "url": "http://www.jetpack.com/"
+ "url": "http://www.jetpack.com/",
+ "companyId": "jetpack_digital"
},
"jimdo.com": {
"name": "jimdo.com",
"categoryId": 10,
- "url": null
+ "url": null,
+ "companyId": null
},
"jink": {
"name": "Jink",
"categoryId": 4,
- "url": "http://www.jink.de/"
+ "url": "http://www.jink.de/",
+ "companyId": "jink"
},
"jirafe": {
"name": "Jirafe",
"categoryId": 6,
- "url": "http://jirafe.com/"
+ "url": "http://jirafe.com/",
+ "companyId": "jirafe"
},
"jivochat": {
"name": "JivoSite",
"categoryId": 2,
- "url": "https://www.jivochat.com/"
+ "url": "https://www.jivochat.com/",
+ "companyId": "jivochat"
},
"jivox": {
"name": "Jivox",
"categoryId": 4,
- "url": "http://www.jivox.com/"
+ "url": "http://www.jivox.com/",
+ "companyId": "jivox"
},
"jobs_2_careers": {
"name": "Jobs 2 Careers",
"categoryId": 4,
- "url": "http://www.jobs2careers.com/"
+ "url": "http://www.jobs2careers.com/",
+ "companyId": "jobs_2_careers"
},
"joinhoney": {
"name": "Honey",
"categoryId": 8,
- "url": "https://www.joinhoney.com/"
+ "url": "https://www.joinhoney.com/",
+ "companyId": null
},
"jornaya": {
"name": "Jornaya",
"categoryId": 6,
- "url": "http://leadid.com/"
+ "url": "http://leadid.com/",
+ "companyId": "jornaya"
},
"jquery": {
"name": "jQuery",
"categoryId": 9,
- "url": "https://jquery.org/"
+ "url": "https://jquery.org/",
+ "companyId": "js_foundation"
},
"js_communications": {
"name": "JS Communications",
"categoryId": 4,
- "url": "http://www.jssearch.net/"
+ "url": "http://www.jssearch.net/",
+ "companyId": "js_communications"
},
"jsdelivr": {
"name": "jsDelivr",
"categoryId": 9,
- "url": "https://www.jsdelivr.com/"
+ "url": "https://www.jsdelivr.com/",
+ "companyId": null
},
"jse_coin": {
"name": "JSE Coin",
"categoryId": 4,
- "url": "https://jsecoin.com/"
+ "url": "https://jsecoin.com/",
+ "companyId": "jse_coin"
},
"jsuol.com.br": {
"name": "jsuol.com.br",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"juggcash": {
"name": "JuggCash",
"categoryId": 3,
- "url": "http://www.juggcash.com"
+ "url": "http://www.juggcash.com",
+ "companyId": "juggcash"
},
"juiceadv": {
"name": "JuiceADV",
"categoryId": 4,
- "url": "http://juiceadv.com/"
+ "url": "http://juiceadv.com/",
+ "companyId": "juiceadv"
},
"juicyads": {
"name": "JuicyAds",
"categoryId": 3,
- "url": "http://www.juicyads.com/"
+ "url": "http://www.juicyads.com/",
+ "companyId": "juicyads"
},
"jumplead": {
"name": "Jumplead",
"categoryId": 6,
- "url": "https://jumplead.com/"
+ "url": "https://jumplead.com/",
+ "companyId": "jumplead"
},
"jumpstart_tagging_solutions": {
"name": "Jumpstart Tagging Solutions",
"categoryId": 6,
- "url": "http://www.hearst.com/"
+ "url": "http://www.hearst.com/",
+ "companyId": "hearst"
},
"jumptap": {
"name": "Jumptap",
"categoryId": 4,
- "url": "http://www.jumptap.com/"
+ "url": "http://www.jumptap.com/",
+ "companyId": "verizon"
},
"jumptime": {
"name": "JumpTime",
"categoryId": 6,
- "url": "http://www.jumptime.com/"
+ "url": "http://www.jumptime.com/",
+ "companyId": "openx"
},
"just_answer": {
"name": "Just Answer",
"categoryId": 2,
- "url": "https://www.justanswer.com/"
+ "url": "https://www.justanswer.com/",
+ "companyId": "just_answer"
},
"just_premium": {
"name": "Just Premium",
"categoryId": 4,
- "url": "http://justpremium.com/"
+ "url": "http://justpremium.com/",
+ "companyId": "just_premium"
},
"just_relevant": {
"name": "Just Relevant",
"categoryId": 4,
- "url": "http://www.justrelevant.com/"
+ "url": "http://www.justrelevant.com/",
+ "companyId": "just_relevant"
},
"jvc.gg": {
"name": "Jeuxvideo CDN",
"categoryId": 9,
- "url": "http://www.jeuxvideo.com/"
+ "url": "http://www.jeuxvideo.com/",
+ "companyId": null
},
"jw_player": {
"name": "JW Player",
"categoryId": 0,
- "url": "https://www.jwplayer.com/"
+ "url": "https://www.jwplayer.com/",
+ "companyId": "jw_player"
},
"jw_player_ad_solutions": {
"name": "JW Player Ad Solutions",
"categoryId": 4,
- "url": "http://www.longtailvideo.com/adsolution/"
+ "url": "http://www.longtailvideo.com/adsolution/",
+ "companyId": "jw_player"
},
"kaeufersiegel.de": {
"name": "Käufersiegel",
"categoryId": 2,
- "url": "https://www.kaeufersiegel.de/"
+ "url": "https://www.kaeufersiegel.de/",
+ "companyId": null
},
"kairion.de": {
"name": "kairion",
"categoryId": 4,
- "url": "https://kairion.de/"
+ "url": "https://kairion.de/",
+ "companyId": "prosieben_sat1"
},
"kaloo.ga": {
"name": "Kalooga",
"categoryId": 4,
- "url": "https://www.kalooga.com/"
+ "url": "https://www.kalooga.com/",
+ "companyId": "kalooga"
},
"kalooga_widget": {
"name": "Kalooga Widget",
"categoryId": 4,
- "url": "http://kalooga.com/"
+ "url": "http://kalooga.com/",
+ "companyId": "kalooga"
},
"kaltura": {
"name": "Kaltura",
"categoryId": 0,
- "url": "http://corp.kaltura.com/"
+ "url": "http://corp.kaltura.com/",
+ "companyId": "kaltura"
},
"kameleoon": {
"name": "Kameleoon",
"categoryId": 6,
- "url": "http://www.kameleoon.com/"
+ "url": "http://www.kameleoon.com/",
+ "companyId": "kameleoon"
},
"kampyle": {
"name": "Medallia",
"categoryId": 2,
- "url": "http://www.kampyle.com/"
+ "url": "http://www.kampyle.com/",
+ "companyId": "medallia"
},
"kanoodle": {
"name": "Kanoodle",
"categoryId": 4,
- "url": "http://www.kanoodle.com/"
+ "url": "http://www.kanoodle.com/",
+ "companyId": "kanoodle"
},
"kantar_media": {
"name": "Kantar Media",
"categoryId": 4,
- "url": "https://www.kantarmedia.com/"
+ "url": "https://www.kantarmedia.com/",
+ "companyId": "wpp"
},
"kargo": {
"name": "Kargo",
"categoryId": 4,
- "url": "http://www.kargo.com/"
+ "url": "http://www.kargo.com/",
+ "companyId": "kargo"
},
"kaspersky-labs.com": {
"name": "Kaspersky Labs",
"categoryId": 12,
- "url": "https://www.kaspersky.com/"
+ "url": "https://www.kaspersky.com/",
+ "companyId": "AO Kaspersky Lab"
},
"kataweb.it": {
"name": "KataWeb",
"categoryId": 4,
- "url": "http://www.kataweb.it/"
+ "url": "http://www.kataweb.it/",
+ "companyId": null
},
"katchup": {
"name": "Katchup",
"categoryId": 4,
- "url": "http://www.katchup.fr/"
+ "url": "http://www.katchup.fr/",
+ "companyId": "katchup"
},
"kauli": {
"name": "Kauli",
"categoryId": 4,
- "url": "http://kau.li/"
+ "url": "http://kau.li/",
+ "companyId": "kauli"
},
"kavanga": {
"name": "Kavanga",
"categoryId": 4,
- "url": "http://kavanga.ru/"
+ "url": "http://kavanga.ru/",
+ "companyId": "kavanga"
},
"keen_io": {
"name": "Keen IO",
"categoryId": 6,
- "url": "https://keen.io"
+ "url": "https://keen.io",
+ "companyId": "keen_io"
},
"kelkoo": {
"name": "Kelkoo",
"categoryId": 4,
- "url": "http://www.kelkoo.com/"
+ "url": "http://www.kelkoo.com/",
+ "companyId": "kelkoo"
},
"kenshoo": {
"name": "Kenshoo",
"categoryId": 6,
- "url": "http://www.kenshoo.com/"
+ "url": "http://www.kenshoo.com/",
+ "companyId": "kenshoo"
},
"keymetric": {
"name": "KeyMetric",
"categoryId": 6,
- "url": "http://keymetric.net/"
+ "url": "http://keymetric.net/",
+ "companyId": "keymetric"
},
"keytiles": {
"name": "Keytiles",
"categoryId": 6,
- "url": "http://keytiles.com/"
+ "url": "http://keytiles.com/",
+ "companyId": "keytiles"
},
"keywee": {
"name": "Keywee",
"categoryId": 6,
- "url": "https://keywee.co/"
+ "url": "https://keywee.co/",
+ "companyId": "keywee"
},
"keywordmax": {
"name": "KeywordMax",
"categoryId": 4,
- "url": "http://www.keywordmax.com/"
+ "url": "http://www.keywordmax.com/",
+ "companyId": "digital_river"
},
"khoros": {
"name": "Khoros",
"categoryId": 7,
- "url": "http://www.massrelevance.com/"
+ "url": "http://www.massrelevance.com/",
+ "companyId": "khoros"
},
"khzbeucrltin.com": {
"name": "khzbeucrltin.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"kickfactory": {
"name": "Kickfactory",
"categoryId": 4,
- "url": "https://kickfactory.com/"
+ "url": "https://kickfactory.com/",
+ "companyId": "kickfactory"
},
"kickfire": {
"name": "Kickfire",
"categoryId": 4,
- "url": "http://www.visistat.com/"
+ "url": "http://www.visistat.com/",
+ "companyId": "kickfire"
},
"king.com": {
"name": "King.com",
"categoryId": 4,
- "url": "http://www.king.com/"
+ "url": "http://www.king.com/",
+ "companyId": "king.com"
},
"king_com": {
"name": "King.com",
"categoryId": 8,
- "url": "https://king.com/"
+ "url": "https://king.com/",
+ "companyId": "activision_blizzard"
},
"kinja.com": {
"name": "Kinja",
"categoryId": 6,
- "url": "https://kinja.com/"
+ "url": "https://kinja.com/",
+ "companyId": "gizmodo"
},
"kiosked": {
"name": "Kiosked",
"categoryId": 4,
- "url": "http://www.kiosked.com/"
+ "url": "http://www.kiosked.com/",
+ "companyId": "kiosked"
},
"kissmetrics.com": {
"name": "Kissmetrics",
"categoryId": 6,
- "url": "https://www.kissmetrics.com/"
+ "url": "https://www.kissmetrics.com/",
+ "companyId": "kissmetrics"
},
"kitara_media": {
"name": "Kitara Media",
"categoryId": 4,
- "url": "http://www.kitaramedia.com/"
+ "url": "http://www.kitaramedia.com/",
+ "companyId": "kitara_media"
},
"kixer": {
"name": "Kixer",
"categoryId": 4,
- "url": "http://www.kixer.com"
+ "url": "http://www.kixer.com",
+ "companyId": "kixer"
},
"klarna.com": {
"name": "Klarna",
"categoryId": 2,
- "url": "https://www.klarna.com/"
+ "url": "https://www.klarna.com/",
+ "companyId": null
},
"klaviyo": {
"name": "Klaviyo",
"categoryId": 6,
- "url": "https://www.klaviyo.com/"
+ "url": "https://www.klaviyo.com/",
+ "companyId": "klaviyo"
},
"klikki": {
"name": "Klikki",
"categoryId": 4,
- "url": "http://www.klikki.com/"
+ "url": "http://www.klikki.com/",
+ "companyId": "klikki"
},
"kliksaya": {
"name": "KlikSaya",
"categoryId": 4,
- "url": "http://www.kliksaya.com"
+ "url": "http://www.kliksaya.com",
+ "companyId": "kliksaya"
},
"kmeleo": {
"name": "Kméléo",
"categoryId": 4,
- "url": "http://www.6peo.com/"
+ "url": "http://www.6peo.com/",
+ "companyId": "6peo"
},
"knoopstat": {
"name": "Knoopstat",
"categoryId": 6,
- "url": "http://www.knoopstat.nl"
+ "url": "http://www.knoopstat.nl",
+ "companyId": "knoopstat"
},
"knotch": {
"name": "Knotch",
"categoryId": 2,
- "url": "http://knotch.it"
+ "url": "http://knotch.it",
+ "companyId": "knotch"
},
"komoona": {
"name": "Komoona",
"categoryId": 4,
- "url": "http://www.komoona.com/"
+ "url": "http://www.komoona.com/",
+ "companyId": "komoona"
},
"kontera_contentlink": {
"name": "Kontera ContentLink",
"categoryId": 4,
- "url": "http://www.kontera.com/"
+ "url": "http://www.kontera.com/",
+ "companyId": "singtel"
},
"kontextr": {
"name": "Kontextr",
"categoryId": 4,
- "url": "https://www.kontextr.com/"
+ "url": "https://www.kontextr.com/",
+ "companyId": "kontext"
},
"kontextua": {
"name": "Kontextua",
"categoryId": 4,
- "url": "http://www.kontextua.com/"
+ "url": "http://www.kontextua.com/",
+ "companyId": "kontextua"
},
"korrelate": {
"name": "Korrelate",
"categoryId": 4,
- "url": "http://korrelate.com/"
+ "url": "http://korrelate.com/",
+ "companyId": "korrelate"
},
"kortx": {
"name": "Kortx",
"categoryId": 6,
- "url": "http://www.kortx.io/"
+ "url": "http://www.kortx.io/",
+ "companyId": "kortx"
},
"kount": {
"name": "Kount",
"categoryId": 6,
- "url": "https://kount.com/"
+ "url": "https://kount.com/",
+ "companyId": null
},
"krux_digital": {
"name": "Salesforce DMP",
"categoryId": 4,
- "url": "https://www.salesforce.com/products/marketing-cloud/data-management/?mc=DMP"
+ "url": "https://www.salesforce.com/products/marketing-cloud/data-management/?mc=DMP",
+ "companyId": "salesforce"
},
"kupona": {
"name": "Kupona",
"categoryId": 4,
- "url": "http://www.kupona-media.de/en/retargeting-and-performance-media-width-kupona"
+ "url": "http://www.kupona-media.de/en/retargeting-and-performance-media-width-kupona",
+ "companyId": "kupona"
},
"kxcdn.com": {
"name": "Keycdn",
"categoryId": 9,
- "url": "https://www.keycdn.com/"
+ "url": "https://www.keycdn.com/",
+ "companyId": null
},
"kyto": {
"name": "Kyto",
"categoryId": 6,
- "url": "https://www.kyto.com/"
+ "url": "https://www.kyto.com/",
+ "companyId": "kyto"
},
"ladsp.com": {
"name": "Logicad",
"categoryId": 4,
- "url": "https://www.logicad.com/"
+ "url": "https://www.logicad.com/",
+ "companyId": "logicad"
},
"lanista_concepts": {
"name": "Lanista Concepts",
"categoryId": 4,
- "url": "http://lanistaconcepts.com/"
+ "url": "http://lanistaconcepts.com/",
+ "companyId": "lanista_concepts"
},
"latimes": {
"name": "Los Angeles Times",
"categoryId": 8,
- "url": "http://www.latimes.com/"
+ "url": "http://www.latimes.com/",
+ "companyId": "latimes"
},
"launch_darkly": {
"name": "Launch Darkly",
"categoryId": 5,
- "url": "https://launchdarkly.com/index.html"
+ "url": "https://launchdarkly.com/index.html",
+ "companyId": "launch_darkly"
},
"launchbit": {
"name": "LaunchBit",
"categoryId": 4,
- "url": "https://www.launchbit.com/"
+ "url": "https://www.launchbit.com/",
+ "companyId": "launchbit"
},
"layer-ad.org": {
"name": "Layer-ADS.net",
"categoryId": 4,
- "url": "http://layer-ads.net/"
+ "url": "http://layer-ads.net/",
+ "companyId": null
},
"lazada": {
"name": "Lazada",
"categoryId": 4,
- "url": "https://www.lazada.com/"
+ "url": "https://www.lazada.com/",
+ "companyId": "lazada"
},
"lcx_digital": {
"name": "LCX Digital",
"categoryId": 4,
- "url": "http://www.lcx.com/"
+ "url": "http://www.lcx.com/",
+ "companyId": "lcx_digital"
},
"le_monde.fr": {
"name": "Le Monde.fr",
"categoryId": 8,
- "url": "http://www.lemonde.fr/"
+ "url": "http://www.lemonde.fr/",
+ "companyId": "le_monde.fr"
},
"lead_liaison": {
"name": "Lead Liaison",
"categoryId": 6,
- "url": "https://www.leadliaison.com"
+ "url": "https://www.leadliaison.com",
+ "companyId": "lead_liaison"
},
"leadback": {
"name": "Leadback",
"categoryId": 6,
- "url": "http://leadback.ru/?utm_source=leadback_widget&utm_medium=eas-balt.ru&utm_campaign=self_ad"
+ "url": "http://leadback.ru/?utm_source=leadback_widget&utm_medium=eas-balt.ru&utm_campaign=self_ad",
+ "companyId": "leadback"
},
"leaddyno": {
"name": "LeadDyno",
"categoryId": 4,
- "url": "http://www.leaddyno.com"
+ "url": "http://www.leaddyno.com",
+ "companyId": "leaddyno"
},
"leadforensics": {
"name": "LeadForensics",
"categoryId": 4,
- "url": "http://www.leadforensics.com/"
+ "url": "http://www.leadforensics.com/",
+ "companyId": "lead_forensics"
},
"leadgenic": {
"name": "LeadGENIC",
"categoryId": 4,
- "url": "https://leadgenic.com/"
+ "url": "https://leadgenic.com/",
+ "companyId": "leadgenic"
},
"leadhit": {
"name": "LeadHit",
"categoryId": 2,
- "url": "http://leadhit.ru/"
+ "url": "http://leadhit.ru/",
+ "companyId": "leadhit"
},
"leadin": {
"name": "Leadin",
"categoryId": 6,
- "url": "https://www.hubspot.com/"
+ "url": "https://www.hubspot.com/",
+ "companyId": "hubspot"
},
"leading_reports": {
"name": "Leading Reports",
"categoryId": 4,
- "url": "https://www.leadingreports.de/"
+ "url": "https://www.leadingreports.de/",
+ "companyId": "leading_reports"
},
"leadinspector": {
"name": "LeadInspector",
"categoryId": 6,
- "url": "https://www.leadinspector.de/"
+ "url": "https://www.leadinspector.de/",
+ "companyId": "leadinspector"
},
"leadlander": {
"name": "LeadLander",
"categoryId": 6,
- "url": "http://www.leadlander.com/"
+ "url": "http://www.leadlander.com/",
+ "companyId": "leadlander"
},
"leadlife": {
"name": "LeadLife",
"categoryId": 2,
- "url": "http://leadlife.com/"
+ "url": "http://leadlife.com/",
+ "companyId": "leadlife"
},
"leadpages": {
"name": "Leadpages",
"categoryId": 6,
- "url": "https://www.leadpages.net/"
+ "url": "https://www.leadpages.net/",
+ "companyId": "leadpages"
},
"leadplace": {
"name": "LeadPlace",
"categoryId": 6,
- "url": "https://temelio.com"
+ "url": "https://temelio.com",
+ "companyId": "leadplace"
},
"leads_by_web.com": {
"name": "Leads by Web.com",
"categoryId": 4,
- "url": "http://www.leadsbyweb.com"
+ "url": "http://www.leadsbyweb.com",
+ "companyId": "web.com_group"
},
"leadscoreapp": {
"name": "LeadScoreApp",
"categoryId": 2,
- "url": "http://leadscoreapp.com"
+ "url": "http://leadscoreapp.com",
+ "companyId": "leadscoreapp"
},
"leadsius": {
"name": "Leadsius",
"categoryId": 4,
- "url": "http://www.leadsius.com/"
+ "url": "http://www.leadsius.com/",
+ "companyId": "leadsius"
},
"leady": {
"name": "Leady",
"categoryId": 4,
- "url": "http://www.leady.cz/"
+ "url": "http://www.leady.cz/",
+ "companyId": "leady"
},
"leiki": {
"name": "Leiki",
"categoryId": 4,
- "url": "http://www.leiki.com"
+ "url": "http://www.leiki.com",
+ "companyId": "leiki"
},
"lengow": {
"name": "Lengow",
"categoryId": 4,
- "url": "http://www.lengow.com/"
+ "url": "http://www.lengow.com/",
+ "companyId": "lengow"
},
"lenmit.com": {
"name": "lenmit.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"lentainform.com": {
"name": "lentainform.com",
"categoryId": 8,
- "url": "https://www.lentainform.com/"
+ "url": "https://www.lentainform.com/",
+ "companyId": null
},
"lenua.de": {
"name": "Lenua System",
"categoryId": 4,
- "url": "http://lenua.de/"
+ "url": "http://lenua.de/",
+ "companyId": "synatix"
},
"let_reach": {
"name": "Let Reach",
"categoryId": 2,
- "url": "https://letreach.com/"
+ "url": "https://letreach.com/",
+ "companyId": "let_reach"
},
"letv": {
"name": "LeTV",
"categoryId": 6,
- "url": "http://www.le.com/"
+ "url": "http://www.le.com/",
+ "companyId": "letv"
},
"level3_communications": {
"name": "Level 3 Communications, Inc.",
"categoryId": 8,
- "url": "http://www.level3.com/en/"
+ "url": "http://www.level3.com/en/",
+ "companyId": "level3_communications"
},
"licensebuttons.net": {
"name": "licensebuttons.net",
"categoryId": 9,
- "url": "https://licensebuttons.net/"
+ "url": "https://licensebuttons.net/",
+ "companyId": null
},
"lifestreet_media": {
"name": "LifeStreet Media",
"categoryId": 4,
- "url": "http://lifestreetmedia.com/"
+ "url": "http://lifestreetmedia.com/",
+ "companyId": "lifestreet_media"
},
"ligatus": {
"name": "Ligatus",
"categoryId": 4,
- "url": "http://www.ligatus.com/"
+ "url": "http://www.ligatus.com/",
+ "companyId": "outbrain"
},
"limk": {
"name": "Limk",
"categoryId": 4,
- "url": "https://limk.com/"
+ "url": "https://limk.com/",
+ "companyId": "limk"
},
"line_apps": {
"name": "Line",
"categoryId": 6,
- "url": "https://line.me/en-US/"
+ "url": "https://line.me/en-US/",
+ "companyId": "line"
},
"linezing": {
"name": "LineZing",
"categoryId": 4,
- "url": "http://www.linezing.com/"
+ "url": "http://www.linezing.com/",
+ "companyId": "linezing"
},
"linkbucks": {
"name": "Linkbucks",
"categoryId": 4,
- "url": "http://www.linkbucks.com/"
+ "url": "http://www.linkbucks.com/",
+ "companyId": "linkbucks"
},
"linkconnector": {
"name": "LinkConnector",
"categoryId": 4,
- "url": "http://www.linkconnector.com"
+ "url": "http://www.linkconnector.com",
+ "companyId": "linkconnector"
},
"linkedin": {
"name": "LinkedIn",
"categoryId": 8,
- "url": "https://www.linkedin.com/"
+ "url": "https://www.linkedin.com/",
+ "companyId": "microsoft"
},
"linkedin_ads": {
"name": "LinkedIn Ads",
"categoryId": 4,
- "url": "http://www.linkedin.com/"
+ "url": "http://www.linkedin.com/",
+ "companyId": "microsoft"
},
"linkedin_analytics": {
"name": "LinkedIn Analytics",
"categoryId": 6,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"linkedin_marketing_solutions": {
"name": "LinkedIn Marketing Solutions",
"categoryId": 4,
- "url": "https://business.linkedin.com/marketing-solutions"
+ "url": "https://business.linkedin.com/marketing-solutions",
+ "companyId": "microsoft"
},
"linkedin_widgets": {
"name": "LinkedIn Widgets",
"categoryId": 7,
- "url": "https://www.linkedin.com"
+ "url": "https://www.linkedin.com",
+ "companyId": "microsoft"
},
"linker": {
"name": "Linker",
"categoryId": 4,
- "url": "https://linker.hr/"
+ "url": "https://linker.hr/",
+ "companyId": "linker"
},
"linkprice": {
"name": "LinkPrice",
"categoryId": 4,
- "url": "http://www.linkprice.com/"
+ "url": "http://www.linkprice.com/",
+ "companyId": "linkprice"
},
"linkpulse": {
"name": "Linkpulse",
"categoryId": 6,
- "url": "http://www.linkpulse.com/"
+ "url": "http://www.linkpulse.com/",
+ "companyId": "linkpulse"
},
"linksalpha": {
"name": "LinksAlpha",
"categoryId": 7,
- "url": "http://www.linksalpha.com"
+ "url": "http://www.linksalpha.com",
+ "companyId": "linksalpha"
},
"linksmart": {
"name": "LinkSmart",
"categoryId": 4,
- "url": "http://www.linksmart.com/"
+ "url": "http://www.linksmart.com/",
+ "companyId": "sovrn"
},
"linkstorm": {
"name": "Linkstorm",
"categoryId": 2,
- "url": "http://www.linkstorms.com/"
+ "url": "http://www.linkstorms.com/",
+ "companyId": "linkstorm"
},
"linksynergy.com": {
"name": "Rakuten LinkShare",
"categoryId": 4,
- "url": "https://rakutenmarketing.com/affiliate"
+ "url": "https://rakutenmarketing.com/affiliate",
+ "companyId": "rakuten"
},
"linkup": {
"name": "LinkUp",
"categoryId": 6,
- "url": "http://www.linkup.com/"
+ "url": "http://www.linkup.com/",
+ "companyId": "linkup"
},
"linkwise": {
"name": "Linkwise",
"categoryId": 4,
- "url": "http://linkwi.se/global-en/"
+ "url": "http://linkwi.se/global-en/",
+ "companyId": "linkwise"
},
"linkwithin": {
"name": "LinkWithin",
"categoryId": 7,
- "url": "http://www.linkwithin.com/"
+ "url": "http://www.linkwithin.com/",
+ "companyId": "linkwithin"
},
"liquidm_technology_gmbh": {
"name": "LiquidM Technology GmbH",
"categoryId": 4,
- "url": "https://liquidm.com/"
+ "url": "https://liquidm.com/",
+ "companyId": "liquidm"
},
"liqwid": {
"name": "Liqwid",
"categoryId": 4,
- "url": "https://liqwid.com/"
+ "url": "https://liqwid.com/",
+ "companyId": "liqwid"
},
"list.ru": {
"name": "Rating@Mail.Ru",
"categoryId": 7,
- "url": "http://list.ru/"
+ "url": "http://list.ru/",
+ "companyId": "megafon"
},
"listrak": {
"name": "Listrak",
"categoryId": 2,
- "url": "http://www.listrak.com/"
+ "url": "http://www.listrak.com/",
+ "companyId": "listrak"
},
"live2support": {
"name": "Live2Support",
"categoryId": 2,
- "url": "http://www.live2support.com/"
+ "url": "http://www.live2support.com/",
+ "companyId": "live2support"
},
"live800": {
"name": "Live800",
"categoryId": 2,
- "url": "http://live800.com"
+ "url": "http://live800.com",
+ "companyId": "live800"
},
"live_agent": {
"name": "Live Agent",
"categoryId": 2,
- "url": "https://www.ladesk.com/"
+ "url": "https://www.ladesk.com/",
+ "companyId": "liveagent"
},
"live_help_now": {
"name": "Live Help Now",
"categoryId": 2,
- "url": "http://www.livehelpnow.net/"
+ "url": "http://www.livehelpnow.net/",
+ "companyId": "live_help_now"
},
"live_intent": {
"name": "Live Intent",
"categoryId": 6,
- "url": "http://www.liveintent.com/"
+ "url": "http://www.liveintent.com/",
+ "companyId": "liveintent"
},
"live_journal": {
"name": "Live Journal",
"categoryId": 6,
- "url": "http://www.livejournal.com/"
+ "url": "http://www.livejournal.com/",
+ "companyId": "livejournal"
},
"liveadexchanger.com": {
"name": "liveadexchanger.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"livechat": {
"name": "LiveChat",
"categoryId": 2,
- "url": "http://www.livechatinc.com"
+ "url": "http://www.livechatinc.com",
+ "companyId": "livechat"
},
"livechatnow": {
"name": "LiveChatNow",
"categoryId": 2,
- "url": "http://www.livechatnow.com/"
+ "url": "http://www.livechatnow.com/",
+ "companyId": "livechatnow!"
},
"liveclicker": {
"name": "Liveclicker",
"categoryId": 2,
- "url": "http://www.liveclicker.com"
+ "url": "http://www.liveclicker.com",
+ "companyId": "liveclicker"
},
"livecounter": {
"name": "Livecounter",
"categoryId": 6,
- "url": "http://www.livecounter.dk/"
+ "url": "http://www.livecounter.dk/",
+ "companyId": "livecounter"
},
"livefyre": {
"name": "Livefyre",
"categoryId": 1,
- "url": "http://www.livefyre.com/"
+ "url": "http://www.livefyre.com/",
+ "companyId": "adobe"
},
"liveinternet": {
"name": "LiveInternet",
"categoryId": 1,
- "url": "http://www.liveinternet.ru/"
+ "url": "http://www.liveinternet.ru/",
+ "companyId": "liveinternet"
},
"liveperson": {
"name": "LivePerson",
"categoryId": 2,
- "url": "http://www.liveperson.com/"
+ "url": "http://www.liveperson.com/",
+ "companyId": "liveperson"
},
"liveramp": {
"name": "LiveRamp",
"categoryId": 4,
- "url": "https://liveramp.com/"
+ "url": "https://liveramp.com/",
+ "companyId": "acxiom"
},
"livere": {
"name": "LiveRe",
"categoryId": 7,
- "url": "http://www.livere.com/"
+ "url": "http://www.livere.com/",
+ "companyId": "livere"
},
"livesportmedia.eu": {
"name": "Livesport Media",
"categoryId": 8,
- "url": "http://www.livesportmedia.eu/"
+ "url": "http://www.livesportmedia.eu/",
+ "companyId": null
},
"livestream": {
"name": "Livestream",
"categoryId": 0,
- "url": "http://vimeo.com/"
+ "url": "http://vimeo.com/",
+ "companyId": "vimeo"
},
"livetex.ru": {
"name": "LiveTex",
"categoryId": 2,
- "url": "https://livetex.ru/"
+ "url": "https://livetex.ru/",
+ "companyId": "livetex"
},
"lkqd": {
"name": "LKQD",
"categoryId": 4,
- "url": "http://www.lkqd.com/"
+ "url": "http://www.lkqd.com/",
+ "companyId": "nexstar"
},
"loadbee.com": {
"name": "Loadbee",
"categoryId": 4,
- "url": "https://company.loadbee.com/de/loadbee-home"
+ "url": "https://company.loadbee.com/de/loadbee-home",
+ "companyId": null
},
"loadercdn.com": {
"name": "loadercdn.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"loadsource.org": {
"name": "loadsource.org",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"localytics": {
"name": "Localytics",
"categoryId": 6,
- "url": "http://www.localytics.com/"
+ "url": "http://www.localytics.com/",
+ "companyId": "localytics"
},
"lockerdome": {
"name": "LockerDome",
"categoryId": 7,
- "url": "https://lockerdome.com"
+ "url": "https://lockerdome.com",
+ "companyId": "lockerdome"
},
"lockerz_share": {
"name": "AddToAny",
"categoryId": 7,
- "url": "http://www.addtoany.com/"
+ "url": "http://www.addtoany.com/",
+ "companyId": "addtoany"
},
"logan_media": {
"name": "Logan Media",
"categoryId": 6,
- "url": "http://loganmedia.mobi/"
+ "url": "http://loganmedia.mobi/",
+ "companyId": "logan_media"
},
"logdna": {
"name": "LogDNA",
"categoryId": 4,
- "url": "http://www.answerbook.com/"
+ "url": "http://www.answerbook.com/",
+ "companyId": "logdna"
},
"loggly": {
"name": "Loggly",
"categoryId": 6,
- "url": "http://loggly.com/"
+ "url": "http://loggly.com/",
+ "companyId": "loggly"
},
"logly": {
"name": "logly",
"categoryId": 6,
- "url": "http://logly.co.jp/"
+ "url": "http://logly.co.jp/",
+ "companyId": "logly"
},
"logsss.com": {
"name": "logsss.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"lomadee": {
"name": "Lomadee",
"categoryId": 4,
- "url": "http://lomadee.com"
+ "url": "http://lomadee.com",
+ "companyId": "lomadee"
},
"longtail_video_analytics": {
"name": "JW Player Analytics",
"categoryId": 4,
- "url": "http://www.longtailvideo.com/"
+ "url": "http://www.longtailvideo.com/",
+ "companyId": "jw_player"
},
"loomia": {
"name": "Loomia",
"categoryId": 4,
- "url": "http://www.loomia.com/"
+ "url": "http://www.loomia.com/",
+ "companyId": "loomia"
},
"loop11": {
"name": "Loop11",
"categoryId": 6,
- "url": "https://360i.com/"
+ "url": "https://360i.com/",
+ "companyId": "360i"
},
"loopfuse_oneview": {
"name": "LoopFuse OneView",
"categoryId": 4,
- "url": "http://www.loopfuse.com/"
+ "url": "http://www.loopfuse.com/",
+ "companyId": "loopfuse"
},
"lotame": {
"name": "Lotame",
"categoryId": 4,
- "url": "http://www.lotame.com/"
+ "url": "http://www.lotame.com/",
+ "companyId": "lotame"
},
"lottex_inc": {
"name": "vidcpm.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"lucid": {
"name": "Lucid",
"categoryId": 4,
- "url": "https://luc.id/"
+ "url": "https://luc.id/",
+ "companyId": "luc.id"
},
"lucid_media": {
"name": "Lucid Media",
"categoryId": 4,
- "url": "http://www.lucidmedia.com/"
+ "url": "http://www.lucidmedia.com/",
+ "companyId": "singtel"
},
"lucini": {
"name": "Lucini",
"categoryId": 4,
- "url": "http://www.lucinilucini.com/"
+ "url": "http://www.lucinilucini.com/",
+ "companyId": "lucini_&_lucini_communications"
},
"lucky_orange": {
"name": "Lucky Orange",
"categoryId": 6,
- "url": "http://www.luckyorange.com/"
+ "url": "http://www.luckyorange.com/",
+ "companyId": "lucky_orange"
},
"luckypushh.com": {
"name": "luckypushh.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"lxr100": {
"name": "LXR100",
"categoryId": 4,
- "url": "http://www.netelixir.com/lxr100_PPC_management_tool.html"
+ "url": "http://www.netelixir.com/lxr100_PPC_management_tool.html",
+ "companyId": "netelixir"
},
"lynchpin_analytics": {
"name": "Lynchpin Analytics",
"categoryId": 4,
- "url": "http://www.lynchpin.com/"
+ "url": "http://www.lynchpin.com/",
+ "companyId": "lynchpin_analytics"
},
"lytics": {
"name": "Lytics",
"categoryId": 6,
- "url": "https://www.lytics.com/"
+ "url": "https://www.lytics.com/",
+ "companyId": "lytics"
},
"lyuoaxruaqdo.com": {
"name": "lyuoaxruaqdo.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"m-pathy": {
"name": "m-pathy",
"categoryId": 4,
- "url": "http://www.m-pathy.com/"
+ "url": "http://www.m-pathy.com/",
+ "companyId": "m-pathy"
},
"m._p._newmedia": {
"name": "M. P. NEWMEDIA",
"categoryId": 4,
- "url": "http://www.mp-newmedia.com/"
+ "url": "http://www.mp-newmedia.com/",
+ "companyId": "sticky"
},
"m4n": {
"name": "M4N",
"categoryId": 4,
- "url": "http://www.zanox.com/us/"
+ "url": "http://www.zanox.com/us/",
+ "companyId": "axel_springer"
},
"mad_ads_media": {
"name": "Mad Ads Media",
"categoryId": 4,
- "url": "http://www.madadsmedia.com/"
+ "url": "http://www.madadsmedia.com/",
+ "companyId": "mad_ads_media"
},
"madeleine.de": {
"name": "madeleine.de",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"madison_logic": {
"name": "Madison Logic",
"categoryId": 4,
- "url": "http://www.madisonlogic.com/"
+ "url": "http://www.madisonlogic.com/",
+ "companyId": "madison_logic"
},
"madnet": {
"name": "MADNET",
"categoryId": 4,
- "url": "http://madnet.ru/en"
+ "url": "http://madnet.ru/en",
+ "companyId": "madnet"
},
"mads": {
"name": "MADS",
"categoryId": 4,
- "url": "http://www.mads.com/"
+ "url": "http://www.mads.com/",
+ "companyId": "mads"
},
"magna_advertise": {
"name": "Magna Advertise",
"categoryId": 4,
- "url": "http://magna.ru/"
+ "url": "http://magna.ru/",
+ "companyId": "magna_advertise"
},
"magnetic": {
"name": "Magnetic",
"categoryId": 4,
- "url": "http://www.magnetic.is"
+ "url": "http://www.magnetic.is",
+ "companyId": "magnetic"
},
"magnetise_group": {
"name": "Magnetise Group",
"categoryId": 4,
- "url": "http://magnetisegroup.com/"
+ "url": "http://magnetisegroup.com/",
+ "companyId": "magnetise_group"
},
"magnify360": {
"name": "Magnify360",
"categoryId": 6,
- "url": "http://www.magnify360.com/"
+ "url": "http://www.magnify360.com/",
+ "companyId": "magnify360"
},
"magnuum.com": {
"name": "magnuum.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"mail.ru_banner": {
"name": "Mail.Ru Banner Network",
"categoryId": 4,
- "url": "http://mail.ru/"
+ "url": "http://mail.ru/",
+ "companyId": "megafon"
},
"mail.ru_counter": {
"name": "Mail.Ru Counter",
"categoryId": 2,
- "url": "https://corp.megafon.com/"
+ "url": "https://corp.megafon.com/",
+ "companyId": "megafon"
},
"mail.ru_group": {
"name": "Mail.Ru Group",
"categoryId": 7,
- "url": "http://mail.ru/"
+ "url": "http://mail.ru/",
+ "companyId": "megafon"
},
"mailchimp_tracking": {
"name": "MailChimp Tracking",
"categoryId": 4,
- "url": "http://mailchimp.com/"
+ "url": "http://mailchimp.com/",
+ "companyId": "mailchimp"
},
"mailerlite.com": {
"name": "Mailerlite",
"categoryId": 10,
- "url": "https://www.mailerlite.com/"
+ "url": "https://www.mailerlite.com/",
+ "companyId": "mailerlite"
},
"mailtrack.io": {
"name": "MailTrack.io",
"categoryId": 4,
- "url": "https://mailtrack.io"
+ "url": "https://mailtrack.io",
+ "companyId": "mailtrack"
},
"mainadv": {
"name": "mainADV",
"categoryId": 4,
- "url": "http://www.mainadv.com/"
+ "url": "http://www.mainadv.com/",
+ "companyId": "mainadv"
},
"makazi": {
"name": "Makazi",
"categoryId": 4,
- "url": "http://www.makazi.com/en/"
+ "url": "http://www.makazi.com/en/",
+ "companyId": "makazi_group"
},
"makeappdev.xyz": {
"name": "makeappdev.xyz",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"makesource.cool": {
"name": "makesource.cool",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"mango": {
"name": "Mango",
"categoryId": 4,
- "url": "https://www.mango-office.ru/"
+ "url": "https://www.mango-office.ru/",
+ "companyId": "mango_office"
},
"manycontacts": {
"name": "ManyContacts",
"categoryId": 4,
- "url": "https://www.manycontacts.com/"
+ "url": "https://www.manycontacts.com/",
+ "companyId": "manycontacts"
},
"mapandroute.de": {
"name": "Map and Route",
"categoryId": 2,
- "url": "http://www.mapandroute.de/"
+ "url": "http://www.mapandroute.de/",
+ "companyId": null
},
"mapbox": {
"name": "Mapbox",
"categoryId": 2,
- "url": "https://www.mapbox.com/"
+ "url": "https://www.mapbox.com/",
+ "companyId": null
},
"maploco": {
"name": "MapLoco",
"categoryId": 4,
- "url": "http://www.maploco.com/"
+ "url": "http://www.maploco.com/",
+ "companyId": "maploco"
},
"marchex": {
"name": "Marchex",
"categoryId": 4,
- "url": "http://www.industrybrains.com/"
+ "url": "http://www.industrybrains.com/",
+ "companyId": "marchex"
},
"marimedia": {
"name": "Marimedia",
"categoryId": 4,
- "url": "http://www.marimedia.net/"
+ "url": "http://www.marimedia.net/",
+ "companyId": "tremor_video"
},
"marin_search_marketer": {
"name": "Marin Search Marketer",
"categoryId": 4,
- "url": "http://www.marinsoftware.com/"
+ "url": "http://www.marinsoftware.com/",
+ "companyId": "marin_software"
},
"mark_+_mini": {
"name": "Mark & Mini",
"categoryId": 4,
- "url": "http://www.markandmini.com/index.cfm"
+ "url": "http://www.markandmini.com/index.cfm",
+ "companyId": "edm_group"
},
"market_thunder": {
"name": "Market Thunder",
"categoryId": 4,
- "url": "https://www.makethunder.com/"
+ "url": "https://www.makethunder.com/",
+ "companyId": "market_thunder"
},
"marketgid": {
"name": "MarketGid",
"categoryId": 4,
- "url": "http://www.mgid.com/"
+ "url": "http://www.mgid.com/",
+ "companyId": "marketgid_usa"
},
"marketing_automation": {
"name": "Marketing Automation",
"categoryId": 4,
- "url": "https://en.frodx.com"
+ "url": "https://en.frodx.com",
+ "companyId": "frodx"
},
"marketo": {
"name": "Marketo",
"categoryId": 4,
- "url": "http://www.marketo.com/"
+ "url": "http://www.marketo.com/",
+ "companyId": "marketo"
},
"markmonitor": {
"name": "MarkMonitor",
"categoryId": 4,
- "url": "https://www.markmonitor.com/"
+ "url": "https://www.markmonitor.com/",
+ "companyId": "markmonitor"
},
"marktest": {
"name": "Marktest",
"categoryId": 4,
- "url": "http://www.marktest.com/"
+ "url": "http://www.marktest.com/",
+ "companyId": "marktest_group"
},
"marshadow.io": {
"name": "marshadow.io",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"martini_media": {
"name": "Martini Media",
"categoryId": 4,
- "url": "http://martinimediainc.com/"
+ "url": "http://martinimediainc.com/",
+ "companyId": "martini_media"
},
"maru-edu": {
"name": "Maru-EDU",
"categoryId": 2,
- "url": "https://www.maruedr.com"
+ "url": "https://www.maruedr.com",
+ "companyId": "maruedr"
},
"marvellous_machine": {
"name": "Marvellous Machine",
"categoryId": 6,
- "url": "https://www.marvellousmachine.net/"
+ "url": "https://www.marvellousmachine.net/",
+ "companyId": "marvellous_machine"
},
"master_banner_network": {
"name": "Master Banner Network",
"categoryId": 4,
- "url": "http://www.mbn.com.ua/"
+ "url": "http://www.mbn.com.ua/",
+ "companyId": "master_banner_network"
},
"mastertarget": {
"name": "MasterTarget",
"categoryId": 4,
- "url": "http://mastertarget.ru/"
+ "url": "http://mastertarget.ru/",
+ "companyId": "mastertarget"
},
"matelso": {
"name": "Matelso",
"categoryId": 6,
- "url": "https://www.matelso.de"
+ "url": "https://www.matelso.de",
+ "companyId": "matelso"
},
"mather_analytics": {
"name": "Mather Analytics",
"categoryId": 6,
- "url": "https://www.mathereconomics.com/"
+ "url": "https://www.mathereconomics.com/",
+ "companyId": "mather_economics"
},
"mathjax.org": {
"name": "MathJax",
"categoryId": 9,
- "url": "https://www.mathjax.org/"
+ "url": "https://www.mathjax.org/",
+ "companyId": null
},
"matiro": {
"name": "Matiro",
"categoryId": 6,
- "url": "http://matiro.com/"
+ "url": "http://matiro.com/",
+ "companyId": "matiro"
},
"matomo": {
"name": "Matomo",
"categoryId": 6,
- "url": "https://matomo.org/s"
+ "url": "https://matomo.org/s",
+ "companyId": "matomo"
},
"matomy_market": {
"name": "Matomy Market",
"categoryId": 4,
- "url": "http://www.matomymarket.com/"
+ "url": "http://www.matomymarket.com/",
+ "companyId": "matomy_media"
},
"maxbounty": {
"name": "MaxBounty",
"categoryId": 5,
- "url": "http://www.maxbounty.com/"
+ "url": "http://www.maxbounty.com/",
+ "companyId": "maxbounty"
},
"maxcdn": {
"name": "MaxCDN",
"categoryId": 9,
- "url": "https://www.maxcdn.com/"
+ "url": "https://www.maxcdn.com/",
+ "companyId": null
},
"maxlab": {
"name": "Maxlab",
"categoryId": 4,
- "url": "http://maxlab.ru"
+ "url": "http://maxlab.ru",
+ "companyId": "maxlab"
},
"maxmind": {
"name": "MaxMind",
"categoryId": 4,
- "url": "http://www.maxmind.com/"
+ "url": "http://www.maxmind.com/",
+ "companyId": "maxmind"
},
"maxonclick_com": {
"name": "maxonclick.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"maxpoint_interactive": {
"name": "MaxPoint Interactive",
"categoryId": 4,
- "url": "http://www.maxpointinteractive.com/"
+ "url": "http://www.maxpointinteractive.com/",
+ "companyId": "maxpoint_interactive"
},
"maxymiser": {
"name": "Oracle Maxymiser",
"categoryId": 4,
- "url": "https://www.oracle.com/marketingcloud/products/testing-and-optimization/index.html"
+ "url": "https://www.oracle.com/marketingcloud/products/testing-and-optimization/index.html",
+ "companyId": "oracle"
},
"mbr_targeting": {
"name": "mbr targeting",
"categoryId": 4,
- "url": "https://mbr-targeting.com/"
+ "url": "https://mbr-targeting.com/",
+ "companyId": "stroer"
},
"mbuy": {
"name": "MBuy",
"categoryId": 4,
- "url": "http://www.adbuyer.com/"
+ "url": "http://www.adbuyer.com/",
+ "companyId": "mbuy"
},
"mcabi": {
"name": "mCabi",
"categoryId": 4,
- "url": "https://mcabi.mcloudglobal.com/#"
+ "url": "https://mcabi.mcloudglobal.com/#",
+ "companyId": "mcabi"
},
"mcafee_secure": {
"name": "McAfee Secure",
"categoryId": 5,
- "url": "http://www.mcafeesecure.com/us/"
+ "url": "http://www.mcafeesecure.com/us/",
+ "companyId": "mcafee"
},
"mconet": {
"name": "MCOnet",
"categoryId": 4,
- "url": "http://mconet.biz/"
+ "url": "http://mconet.biz/",
+ "companyId": "mconet"
},
"mdotlabs": {
"name": "MdotLabs",
"categoryId": 4,
- "url": "http://www.mdotlabs.com/"
+ "url": "http://www.mdotlabs.com/",
+ "companyId": "comscore"
},
"media-clic": {
"name": "Media-clic",
"categoryId": 4,
- "url": "http://www.media-clic.com/"
+ "url": "http://www.media-clic.com/",
+ "companyId": "media-click"
},
"media-imdb.com": {
"name": "IMDB CDN",
"categoryId": 9,
- "url": "https://www.imdb.com/"
+ "url": "https://www.imdb.com/",
+ "companyId": "amazon_associates"
},
"media.net": {
"name": "Media.net",
"categoryId": 4,
- "url": "http://www.media.net/"
+ "url": "http://www.media.net/",
+ "companyId": "media.net"
},
"media_impact": {
"name": "Media Impact",
"categoryId": 4,
- "url": "https://mediaimpact.de/index.html"
+ "url": "https://mediaimpact.de/index.html",
+ "companyId": "media_impact"
},
"media_innovation_group": {
"name": "Xaxis",
"categoryId": 4,
- "url": "https://www.xaxis.com/"
+ "url": "https://www.xaxis.com/",
+ "companyId": "media_innovation_group"
},
"media_today": {
"name": "Media Today",
"categoryId": 4,
- "url": "http://mediatoday.ru/"
+ "url": "http://mediatoday.ru/",
+ "companyId": "media_today"
},
"mediaad": {
"name": "MediaAd",
"categoryId": 4,
- "url": "https://mediaad.org"
+ "url": "https://mediaad.org",
+ "companyId": "mediaad"
},
"mediaglu": {
"name": "Mediaglu",
"categoryId": 4,
- "url": "http://mlnadvertising.com/"
+ "url": "http://mlnadvertising.com/",
+ "companyId": "appnexus"
},
"mediahub": {
"name": "MediaHub",
"categoryId": 4,
- "url": "http://www.mediahub.com/"
+ "url": "http://www.mediahub.com/",
+ "companyId": "mediahub"
},
"medialand": {
"name": "Medialand",
"categoryId": 4,
- "url": "http://medialand.ru"
+ "url": "http://medialand.ru",
+ "companyId": "medialand"
},
"medialead": {
"name": "Medialead",
"categoryId": 4,
- "url": "https://www.medialead.de/"
+ "url": "https://www.medialead.de/",
+ "companyId": "the_reach_group"
},
"mediamath": {
"name": "MediaMath",
"categoryId": 4,
- "url": "http://www.mediamath.com/"
+ "url": "http://www.mediamath.com/",
+ "companyId": "mediamath"
},
"mediametrics": {
"name": "Mediametrics",
"categoryId": 7,
- "url": "http://mediametrics.ru"
+ "url": "http://mediametrics.ru",
+ "companyId": "mediametrics"
},
"median": {
"name": "Median",
"categoryId": 4,
- "url": "http://median.hu"
+ "url": "http://median.hu",
+ "companyId": "median"
},
"mediapass": {
"name": "MediaPass",
"categoryId": 4,
- "url": "http://www.mediapass.com/"
+ "url": "http://www.mediapass.com/",
+ "companyId": "mediapass"
},
"mediapost_communications": {
"name": "Mediapost Communications",
"categoryId": 6,
- "url": "https://vrm.mediapostcommunication.net/"
+ "url": "https://vrm.mediapostcommunication.net/",
+ "companyId": "mediapost_communications"
},
"mediarithmics.com": {
"name": "Mediarithmics",
"categoryId": 4,
- "url": "http://www.mediarithmics.com/en/"
+ "url": "http://www.mediarithmics.com/en/",
+ "companyId": "mediarithmics"
},
"mediascope": {
"name": "Mediascope",
"categoryId": 6,
- "url": "http://mediascope.net/"
+ "url": "http://mediascope.net/",
+ "companyId": "mediascope"
},
"mediashakers": {
"name": "MediaShakers",
"categoryId": 4,
- "url": "http://www.mediashakers.com/"
+ "url": "http://www.mediashakers.com/",
+ "companyId": "mediashakers"
},
"mediashift": {
"name": "MediaShift",
"categoryId": 4,
- "url": "http://www.mediashift.com/"
+ "url": "http://www.mediashift.com/",
+ "companyId": "mediashift"
},
"mediator.media": {
"name": "Mediator",
"categoryId": 6,
- "url": "https://mediator.media/"
+ "url": "https://mediator.media/",
+ "companyId": "mycom_bv"
},
"mediav": {
"name": "MediaV",
"categoryId": 4,
- "url": "https://www.mediav.com/"
+ "url": "https://www.mediav.com/",
+ "companyId": "mediav"
},
"mediawhiz": {
"name": "Mediawhiz",
"categoryId": 4,
- "url": "http://www.mediawhiz.com/"
+ "url": "http://www.mediawhiz.com/",
+ "companyId": "matomy_media"
},
"medigo": {
"name": "Medigo",
"categoryId": 4,
- "url": "https://www.mediego.com/en/"
+ "url": "https://www.mediego.com/en/",
+ "companyId": "mediego"
},
"medley": {
"name": "Medley",
"categoryId": 4,
- "url": "http://medley.com/"
+ "url": "http://medley.com/",
+ "companyId": "friendfinder_networks"
},
"medyanet": {
"name": "MedyaNet",
"categoryId": 4,
- "url": "http://www.medyanet.com.tr/"
+ "url": "http://www.medyanet.com.tr/",
+ "companyId": "medyanet"
},
"meebo_bar": {
"name": "Meebo Bar",
"categoryId": 7,
- "url": "http://bar.meebo.com/"
+ "url": "http://bar.meebo.com/",
+ "companyId": "google"
},
"meetrics": {
"name": "Meetrics",
"categoryId": 4,
- "url": "http://www.meetrics.de/"
+ "url": "http://www.meetrics.de/",
+ "companyId": "meetrics"
},
"megaindex": {
"name": "MegaIndex",
"categoryId": 4,
- "url": "http://www.megaindex.ru"
+ "url": "http://www.megaindex.ru",
+ "companyId": "megaindex"
},
"mein-bmi.com": {
"name": "mein-bmi.com",
"categoryId": 12,
- "url": "https://www.mein-bmi.com/"
+ "url": "https://www.mein-bmi.com/",
+ "companyId": null
},
"melissa": {
"name": "Melissa",
"categoryId": 6,
- "url": "https://www.melissa.com/"
+ "url": "https://www.melissa.com/",
+ "companyId": "melissa_global_intelligence"
},
"melt": {
"name": "Melt",
"categoryId": 4,
- "url": "http://meltdsp.com/"
+ "url": "http://meltdsp.com/",
+ "companyId": "melt"
},
"menlo": {
"name": "Menlo",
"categoryId": 4,
- "url": "http://www.menlotechnologies.cn/"
+ "url": "http://www.menlotechnologies.cn/",
+ "companyId": "menlotechnologies"
},
"mentad": {
"name": "MentAd",
"categoryId": 4,
- "url": "http://www.mentad.com/"
+ "url": "http://www.mentad.com/",
+ "companyId": "mentad"
},
"mercado": {
"name": "Mercado",
"categoryId": 4,
- "url": "https://www.mercadolivre.com.br/"
+ "url": "https://www.mercadolivre.com.br/",
+ "companyId": "mercado_livre"
},
"merchantadvantage": {
"name": "MerchantAdvantage",
"categoryId": 4,
- "url": "http://www.merchantadvantage.com/channelmanagement.cfm"
+ "url": "http://www.merchantadvantage.com/channelmanagement.cfm",
+ "companyId": "merchantadvantage"
},
"merchenta": {
"name": "Merchenta",
"categoryId": 4,
- "url": "http://www.merchenta.com/"
+ "url": "http://www.merchenta.com/",
+ "companyId": "merchenta"
},
"mercury_media": {
"name": "Mercury Media",
"categoryId": 4,
- "url": "http://trackingsoft.com/"
+ "url": "http://trackingsoft.com/",
+ "companyId": "mercury_media"
},
"merkle_research": {
"name": "Merkle Research",
"categoryId": 6,
- "url": "http://www.dentsuaegisnetwork.com/"
+ "url": "http://www.dentsuaegisnetwork.com/",
+ "companyId": "dentsu_aegis_network"
},
"merkle_rkg": {
"name": "Merkle RKG",
"categoryId": 6,
- "url": "https://www.merkleinc.com/what-we-do/digital-agency-services/rkg-now-fully-integrated-merkle"
+ "url": "https://www.merkleinc.com/what-we-do/digital-agency-services/rkg-now-fully-integrated-merkle",
+ "companyId": "dentsu_aegis_network"
},
"messenger.com": {
"name": "Facebook Messenger",
"categoryId": 7,
- "url": "https://messenger.com"
+ "url": "https://messenger.com",
+ "companyId": "facebook"
},
"meta_network": {
"name": "Meta Network",
"categoryId": 7,
- "url": "http://www.metanetwork.com/"
+ "url": "http://www.metanetwork.com/",
+ "companyId": "meta_network"
},
"metaffiliation.com": {
"name": "Netaffiliation",
"categoryId": 4,
- "url": "http://netaffiliation.com/"
+ "url": "http://netaffiliation.com/",
+ "companyId": "kwanko"
},
"metapeople": {
"name": "Metapeople",
"categoryId": 4,
- "url": "http://www.metapeople.com/us/"
+ "url": "http://www.metapeople.com/us/",
+ "companyId": "metapeople"
},
"metrigo": {
"name": "Metrigo",
"categoryId": 4,
- "url": "http://metrigo.com/"
+ "url": "http://metrigo.com/",
+ "companyId": "metrigo"
},
"metriweb": {
"name": "MetriWeb",
"categoryId": 4,
- "url": "http://www.metriware.be/"
+ "url": "http://www.metriware.be/",
+ "companyId": "metriware"
},
"miaozhen": {
"name": "Miaozhen",
"categoryId": 4,
- "url": "http://miaozhen.com/en/index.html"
+ "url": "http://miaozhen.com/en/index.html",
+ "companyId": "miaozhen"
},
"microad": {
"name": "MicroAd",
"categoryId": 4,
- "url": "https://www.microad.co.jp/"
+ "url": "https://www.microad.co.jp/",
+ "companyId": "microad"
},
"microsoft": {
"name": "Microsoft Services",
"categoryId": 8,
- "url": "http://www.microsoft.com/"
+ "url": "http://www.microsoft.com/",
+ "companyId": "microsoft"
},
"microsoft_adcenter_conversion": {
"name": "Microsoft adCenter Conversion",
"categoryId": 4,
- "url": "https://adcenter.microsoft.com/"
+ "url": "https://adcenter.microsoft.com/",
+ "companyId": "microsoft"
},
"microsoft_analytics": {
"name": "Microsoft Analytics",
"categoryId": 4,
- "url": "https://adcenter.microsoft.com"
+ "url": "https://adcenter.microsoft.com",
+ "companyId": "microsoft"
+ },
+ "microsoft_clarity": {
+ "name": "Microsoft Clarity",
+ "categoryId": 6,
+ "url": "https://clarity.microsoft.com/",
+ "companyId": "microsoft"
},
"mindset_media": {
"name": "Mindset Media",
"categoryId": 4,
- "url": "http://www.mindset-media.com/"
+ "url": "http://www.mindset-media.com/",
+ "companyId": "google"
},
"mindspark": {
"name": "Mindspark",
"categoryId": 6,
- "url": "http://www.mindspark.com/"
+ "url": "http://www.mindspark.com/",
+ "companyId": "iac_apps"
},
"mindviz_tracker": {
"name": "MindViz Tracker",
"categoryId": 4,
- "url": "http://mvtracker.com/"
+ "url": "http://mvtracker.com/",
+ "companyId": "mindviz"
},
"minewhat": {
"name": "MineWhat",
"categoryId": 4,
- "url": "http://www.minewhat.com"
+ "url": "http://www.minewhat.com",
+ "companyId": "minewhat"
},
"mints_app": {
"name": "Mints App",
"categoryId": 2,
- "url": "https://mintsapp.io/"
+ "url": "https://mintsapp.io/",
+ "companyId": "mints_app"
},
"minute.ly": {
"name": "minute.ly",
"categoryId": 0,
- "url": "http://minute.ly/"
+ "url": "http://minute.ly/",
+ "companyId": "minute.ly"
},
"minute.ly_video": {
"name": "minute.ly video",
"categoryId": 0,
- "url": "http://minute.ly/"
+ "url": "http://minute.ly/",
+ "companyId": "minute.ly"
},
"mirando": {
"name": "Mirando",
"categoryId": 4,
- "url": "http://mirando.de"
+ "url": "http://mirando.de",
+ "companyId": "mirando"
},
"mirtesen.ru": {
"name": "mirtesen.ru",
"categoryId": 7,
- "url": "https://mirtesen.ru/"
+ "url": "https://mirtesen.ru/",
+ "companyId": null
},
"mister_bell": {
"name": "Mister Bell",
"categoryId": 4,
- "url": "http://misterbell.fr/"
+ "url": "http://misterbell.fr/",
+ "companyId": "mister_bell"
},
"mixi": {
"name": "mixi",
"categoryId": 7,
- "url": "http://mixi.jp/"
+ "url": "http://mixi.jp/",
+ "companyId": "mixi"
},
"mixpanel": {
"name": "Mixpanel",
"categoryId": 6,
- "url": "http://mixpanel.com/"
+ "url": "http://mixpanel.com/",
+ "companyId": "mixpanel"
},
"mixpo": {
"name": "Mixpo",
"categoryId": 4,
- "url": "http://dynamicvideoad.mixpo.com/"
+ "url": "http://dynamicvideoad.mixpo.com/",
+ "companyId": "mixpo"
},
"mluvii": {
"name": "Mluvii",
"categoryId": 2,
- "url": "https://www.mluvii.com"
+ "url": "https://www.mluvii.com",
+ "companyId": "mluvii"
},
"mncdn.com": {
"name": "MediaNova CDN",
"categoryId": 9,
- "url": "https://www.medianova.com/"
+ "url": "https://www.medianova.com/",
+ "companyId": null
},
"moat": {
"name": "Moat",
"categoryId": 4,
- "url": "http://www.moat.com/"
+ "url": "http://www.moat.com/",
+ "companyId": "oracle"
},
"mobicow": {
"name": "Mobicow",
"categoryId": 4,
- "url": "http://www.mobicow.com/"
+ "url": "http://www.mobicow.com/",
+ "companyId": "mobicow"
},
"mobify": {
"name": "Mobify",
"categoryId": 4,
- "url": "http://www.mobify.com/"
+ "url": "http://www.mobify.com/",
+ "companyId": "mobify"
},
"mobtrks.com": {
"name": "mobtrks.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"mocean_mobile": {
"name": "mOcean Mobile",
"categoryId": 4,
- "url": "http://www.moceanmobile.com/"
+ "url": "http://www.moceanmobile.com/",
+ "companyId": "pubmatic"
},
"mochapp": {
"name": "MoChapp",
"categoryId": 2,
- "url": "http://www.mochapp.com/"
+ "url": "http://www.mochapp.com/",
+ "companyId": "mochapp"
},
"modern_impact": {
"name": "Modern Impact",
"categoryId": 4,
- "url": "http://www.modernimpact.com/"
+ "url": "http://www.modernimpact.com/",
+ "companyId": "modern_impact"
},
"modernus": {
"name": "Modernus",
"categoryId": 6,
- "url": "http://www.modernus.is"
+ "url": "http://www.modernus.is",
+ "companyId": "modernus"
},
"modulepush.com": {
"name": "modulepush.com",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"mogo_interactive": {
"name": "Mogo Interactive",
"categoryId": 4,
- "url": "http://www.mogomarketing.com/"
+ "url": "http://www.mogomarketing.com/",
+ "companyId": "mogo_interactive"
},
"mokono_analytics": {
"name": "Mokono Analytics",
"categoryId": 4,
- "url": "http://www.populis.com"
+ "url": "http://www.populis.com",
+ "companyId": "populis"
},
"monero_miner": {
"name": "Monero Miner",
"categoryId": 8,
- "url": "http://devappgrant.space/"
+ "url": "http://devappgrant.space/",
+ "companyId": null
},
"monetate": {
"name": "Monetate",
"categoryId": 6,
- "url": "http://monetate.com"
+ "url": "http://monetate.com",
+ "companyId": "monetate"
},
"monetize_me": {
"name": "Monetize Me",
"categoryId": 4,
- "url": "http://www.monetize-me.com/"
+ "url": "http://www.monetize-me.com/",
+ "companyId": "monetize_me"
},
"moneytizer": {
"name": "Moneytizer",
"categoryId": 4,
- "url": "https://www.themoneytizer.com/"
+ "url": "https://www.themoneytizer.com/",
+ "companyId": "the_moneytizer"
},
"mongoose_metrics": {
"name": "Mongoose Metrics",
"categoryId": 4,
- "url": "http://www.mongoosemetrics.com/"
+ "url": "http://www.mongoosemetrics.com/",
+ "companyId": "mongoose_metrics"
},
"monitis": {
"name": "Monitis",
"categoryId": 6,
- "url": "http://www.monitis.com/"
+ "url": "http://www.monitis.com/",
+ "companyId": "monitis"
},
"monitus": {
"name": "Monitus",
"categoryId": 6,
- "url": "http://www.monitus.net/"
+ "url": "http://www.monitus.net/",
+ "companyId": "monitus"
},
"monotype_gmbh": {
"name": "Monotype GmbH",
"categoryId": 9,
- "url": "http://www.monotype.com/"
+ "url": "http://www.monotype.com/",
+ "companyId": "monotype"
},
"monotype_imaging": {
"name": "Fonts.com Store",
"categoryId": 2,
- "url": "https://www.fonts.com/"
+ "url": "https://www.fonts.com/",
+ "companyId": "monotype"
},
"monsido": {
"name": "Monsido",
"categoryId": 6,
- "url": "https://monsido.com/"
+ "url": "https://monsido.com/",
+ "companyId": "monsido"
},
"monster_advertising": {
"name": "Monster Advertising",
"categoryId": 4,
- "url": "http://www.monster.com/"
+ "url": "http://www.monster.com/",
+ "companyId": "monster_worldwide"
},
"mooxar": {
"name": "Mooxar",
"categoryId": 4,
- "url": "http://mooxar.com/"
+ "url": "http://mooxar.com/",
+ "companyId": "mooxar"
},
"mopinion.com": {
"name": "Mopinion",
- "categoryId": 6,
- "url": "https://mopinion.com/"
+ "categoryId": 2,
+ "url": "https://mopinion.com/",
+ "companyId": "mopinion"
},
"mopub": {
"name": "MoPub",
"categoryId": 4,
- "url": "https://www.mopub.com/"
+ "url": "https://www.mopub.com/",
+ "companyId": "twitter"
},
"more_communication": {
"name": "More Communication",
"categoryId": 4,
- "url": "http://www.more-com.co.jp/"
+ "url": "http://www.more-com.co.jp/",
+ "companyId": "more_communication"
},
"moreads": {
"name": "moreAds",
"categoryId": 4,
- "url": "https://www.moras.jp"
+ "url": "https://www.moras.jp",
+ "companyId": "moreads"
},
"motigo_webstats": {
"name": "Motigo Webstats",
"categoryId": 7,
- "url": "http://webstats.motigo.com/"
+ "url": "http://webstats.motigo.com/",
+ "companyId": "motigo"
},
"motionpoint": {
"name": "MotionPoint",
"categoryId": 6,
- "url": "http://www.motionpoint.com/"
+ "url": "http://www.motionpoint.com/",
+ "companyId": "motionpoint_corporation"
},
"mouseflow": {
"name": "Mouseflow",
"categoryId": 6,
- "url": "http://mouseflow.com/"
+ "url": "http://mouseflow.com/",
+ "companyId": "mouseflow"
},
"mousestats": {
"name": "MouseStats",
"categoryId": 4,
- "url": "http://www.mousestats.com/"
+ "url": "http://www.mousestats.com/",
+ "companyId": "mousestats"
},
"mousetrace": {
"name": "MouseTrace",
"categoryId": 6,
- "url": "http://www.mousetrace.com/"
+ "url": "http://www.mousetrace.com/",
+ "companyId": "mousetrace"
},
"mov.ad": {
"name": "Mov.ad ",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"movable_ink": {
"name": "Movable Ink",
"categoryId": 2,
- "url": "https://movableink.com/"
+ "url": "https://movableink.com/",
+ "companyId": "movable_ink"
},
"movable_media": {
"name": "Movable Media",
"categoryId": 4,
- "url": "http://www.movablemedia.com/"
+ "url": "http://www.movablemedia.com/",
+ "companyId": "movable_media"
},
"moz": {
"name": "Moz",
"categoryId": 8,
- "url": "https://moz.com/"
+ "url": "https://moz.com/",
+ "companyId": null
},
"mozoo": {
"name": "MoZoo",
"categoryId": 4,
- "url": "http://mozoo.com/"
+ "url": "http://mozoo.com/",
+ "companyId": "mozoo"
},
"mrp": {
"name": "MRP",
"categoryId": 4,
- "url": "https://www.mrpfd.com/"
+ "url": "https://www.mrpfd.com/",
+ "companyId": "mrp"
},
"mrpdata": {
"name": "MRP",
"categoryId": 6,
- "url": "http://mrpdata.com/Account/Login?ReturnUrl=%2F"
+ "url": "http://mrpdata.com/Account/Login?ReturnUrl=%2F",
+ "companyId": "fifth_story"
},
"mrskincash": {
"name": "MrSkinCash",
"categoryId": 3,
- "url": "http://mrskincash.com/"
+ "url": "http://mrskincash.com/",
+ "companyId": "mrskincash.com"
},
"msn": {
"name": "Microsoft Network",
"categoryId": 8,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"muscula": {
"name": "Muscula",
"categoryId": 4,
- "url": "https://www.universe-surf.de/"
+ "url": "https://www.universe-surf.de/",
+ "companyId": "universe_surf"
},
"mux_inc": {
"name": "Mux",
"categoryId": 0,
- "url": "https://mux.com/"
+ "url": "https://mux.com/",
+ "companyId": "mux_inc"
},
"mybloglog": {
"name": "MyBlogLog",
"categoryId": 7,
- "url": "http://www.mybloglog.com/"
+ "url": "http://www.mybloglog.com/",
+ "companyId": "verizon"
},
"mybuys": {
"name": "MyBuys",
"categoryId": 4,
- "url": "http://www.mybuys.com/"
+ "url": "http://www.mybuys.com/",
+ "companyId": "magnetic"
},
"mycdn.me": {
"name": "Mail.Ru CDN",
"categoryId": 9,
- "url": "https://corp.megafon.com/"
+ "url": "https://corp.megafon.com/",
+ "companyId": "megafon"
},
"mycliplister.com": {
"name": "Cliplister",
"categoryId": 2,
- "url": "https://www.cliplister.com/"
+ "url": "https://www.cliplister.com/",
+ "companyId": null
},
"mycounter.ua": {
"name": "MyCounter.ua",
"categoryId": 6,
- "url": "http://mycounter.ua"
+ "url": "http://mycounter.ua",
+ "companyId": "mycounter.ua"
},
"myfonts": {
"name": "MyFonts",
"categoryId": 6,
- "url": "http://www.myfonts.com/"
+ "url": "http://www.myfonts.com/",
+ "companyId": "myfonts"
},
"myfonts_counter": {
"name": "MyFonts",
"categoryId": 6,
- "url": "http://www.myfonts.com/"
+ "url": "http://www.myfonts.com/",
+ "companyId": "myfonts"
},
"mypagerank": {
"name": "MyPagerank",
"categoryId": 6,
- "url": "http://www.mypagerank.net/"
+ "url": "http://www.mypagerank.net/",
+ "companyId": "mypagerank"
},
"mystat": {
"name": "MyStat",
"categoryId": 7,
- "url": "http://mystat.hu/"
+ "url": "http://mystat.hu/",
+ "companyId": "myst_statistics"
},
"mythings": {
"name": "myThings",
"categoryId": 4,
- "url": "http://www.mythings.com/"
+ "url": "http://www.mythings.com/",
+ "companyId": "mythings"
},
"mytop_counter": {
"name": "Mytop Counter",
"categoryId": 7,
- "url": "http://mytop-in.net/"
+ "url": "http://mytop-in.net/",
+ "companyId": "mytop-in"
},
"nakanohito.jp": {
"name": "Nakanohito",
"categoryId": 4,
- "url": "http://nakanohito.jp/"
+ "url": "http://nakanohito.jp/",
+ "companyId": "userinsight"
},
"namogoo": {
"name": "Namoogoo",
"categoryId": 4,
- "url": "https://www.namogoo.com/"
+ "url": "https://www.namogoo.com/",
+ "companyId": null
},
"nanigans": {
"name": "Nanigans",
"categoryId": 4,
- "url": "http://www.nanigans.com/"
+ "url": "http://www.nanigans.com/",
+ "companyId": "nanigans"
},
"nano_interactive": {
"name": "Nano Interactive",
"categoryId": 4,
- "url": "http://www.nanointeractive.com/home/de"
+ "url": "http://www.nanointeractive.com/home/de",
+ "companyId": "nano_interactive"
},
"nanorep": {
"name": "nanoRep",
"categoryId": 2,
- "url": "http://www.nanorep.com/"
+ "url": "http://www.nanorep.com/",
+ "companyId": "logmein"
},
"narando": {
"name": "Narando",
"categoryId": 0,
- "url": "https://narando.com/"
+ "url": "https://narando.com/",
+ "companyId": "narando"
},
"narrativ": {
"name": "Narrativ",
"categoryId": 4,
- "url": "https://narrativ.com/"
+ "url": "https://narrativ.com/",
+ "companyId": "narrativ"
},
"narrative_io": {
"name": "Narrative",
"categoryId": 6,
- "url": "http://www.narrative.io/"
+ "url": "http://www.narrative.io/",
+ "companyId": "narrative.io"
},
"natimatica": {
"name": "Natimatica",
"categoryId": 4,
- "url": "http://natimatica.com/"
+ "url": "http://natimatica.com/",
+ "companyId": "natimatica"
},
"nativeads.com": {
"name": "native ads",
"categoryId": 4,
- "url": "https://nativeads.com/"
+ "url": "https://nativeads.com/",
+ "companyId": null
},
"nativeroll": {
"name": "Nativeroll",
"categoryId": 0,
- "url": "http://nativeroll.tv/"
+ "url": "http://nativeroll.tv/",
+ "companyId": "native_roll"
},
"nativo": {
"name": "Nativo",
"categoryId": 4,
- "url": "http://www.nativo.net/"
+ "url": "http://www.nativo.net/",
+ "companyId": "nativo"
},
"navegg_dmp": {
"name": "Navegg",
"categoryId": 6,
- "url": "https://www.navegg.com/en/"
+ "url": "https://www.navegg.com/en/",
+ "companyId": "navegg"
},
"naver.com": {
"name": "Naver",
"categoryId": 4,
- "url": "https://www.naver.com/"
+ "url": "https://www.naver.com/",
+ "companyId": "naver"
},
"naver_search": {
"name": "Naver Search",
"categoryId": 2,
- "url": "http://www.naver.com/"
+ "url": "http://www.naver.com/",
+ "companyId": "naver"
},
"nbc_news": {
"name": "NBC News",
"categoryId": 8,
- "url": "https://www.nbcnews.com/"
+ "url": "https://www.nbcnews.com/",
+ "companyId": null
},
"ncol": {
"name": "NCOL",
"categoryId": 4,
- "url": "http://www.ncol.com/"
+ "url": "http://www.ncol.com/",
+ "companyId": "ncol"
},
"needle": {
"name": "Needle",
"categoryId": 2,
- "url": "http://www.needle.com"
+ "url": "http://www.needle.com",
+ "companyId": "needle"
},
"nekudo.com": {
"name": "Nekudo",
"categoryId": 2,
- "url": "https://nekudo.com/"
+ "url": "https://nekudo.com/",
+ "companyId": "nekudo"
},
"neodata": {
"name": "Neodata",
"categoryId": 4,
- "url": "http://neodatagroup.com/"
+ "url": "http://neodatagroup.com/",
+ "companyId": "neodata"
},
"neory": {
"name": "NEORY ",
"categoryId": 4,
- "url": "https://www.neory.com/"
+ "url": "https://www.neory.com/",
+ "companyId": "neory"
},
"nerfherdersolo_com": {
"name": "nerfherdersolo.com",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"net-metrix": {
"name": "NET-Metrix",
"categoryId": 6,
- "url": "http://www.net-metrix.ch/"
+ "url": "http://www.net-metrix.ch/",
+ "companyId": "net-metrix"
},
"net-results": {
"name": "Net-Results",
"categoryId": 4,
- "url": "http://www.net-results.com/"
+ "url": "http://www.net-results.com/",
+ "companyId": "net-results"
},
"net_avenir": {
"name": "Net Avenir",
"categoryId": 4,
- "url": "http://www.netavenir.com/"
+ "url": "http://www.netavenir.com/",
+ "companyId": "net_avenir"
},
"net_communities": {
"name": "Net Communities",
"categoryId": 4,
- "url": "http://www.netcommunities.com/"
+ "url": "http://www.netcommunities.com/",
+ "companyId": "net_communities"
},
"net_visibility": {
"name": "NET Visibility",
"categoryId": 4,
- "url": "http://www.netvisibility.co.uk"
+ "url": "http://www.netvisibility.co.uk",
+ "companyId": "net_visibility"
},
"netbiscuits": {
"name": "Netbiscuits",
"categoryId": 6,
- "url": "http://www.netbiscuits.net/"
+ "url": "http://www.netbiscuits.net/",
+ "companyId": "netbiscuits"
},
"netbooster_group": {
"name": "NetBooster Group",
"categoryId": 4,
- "url": "http://www.netbooster.com/"
+ "url": "http://www.netbooster.com/",
+ "companyId": "netbooster_group"
},
"netflix": {
"name": "Netflix",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"netletix": {
"name": "Netletix",
"categoryId": 4,
- "url": "http://www.netletix.com//"
+ "url": "http://www.netletix.com//",
+ "companyId": "ip_de"
},
"netminers": {
"name": "Netminers",
"categoryId": 6,
- "url": "http://netminers.dk/"
+ "url": "http://netminers.dk/",
+ "companyId": "netminers"
},
"netmining": {
"name": "Netmining",
"categoryId": 4,
- "url": "http://www.netmining.com/"
+ "url": "http://www.netmining.com/",
+ "companyId": "zeta"
},
"netmonitor": {
"name": "NetMonitor",
"categoryId": 6,
- "url": "http://www.netmanager.net/en/"
+ "url": "http://www.netmanager.net/en/",
+ "companyId": "netmonitor"
},
"netratings_sitecensus": {
"name": "NetRatings SiteCensus",
"categoryId": 4,
- "url": "http://www.nielsen-online.com/intlpage.html"
+ "url": "http://www.nielsen-online.com/intlpage.html",
+ "companyId": "nielsen"
},
"netrk.net": {
"name": "nfxTrack",
"categoryId": 6,
- "url": "https://netrk.net/"
+ "url": "https://netrk.net/",
+ "companyId": "netzeffekt"
},
"netseer": {
"name": "NetSeer",
"categoryId": 4,
- "url": "http://www.netseer.com/"
+ "url": "http://www.netseer.com/",
+ "companyId": "netseer"
},
"netshelter": {
"name": "NetShelter",
"categoryId": 4,
- "url": "http://www.netshelter.net/"
+ "url": "http://www.netshelter.net/",
+ "companyId": "netshelter"
},
"netsprint_audience": {
"name": "Netsprint Audience",
"categoryId": 6,
- "url": "http://audience.netsprint.eu/"
+ "url": "http://audience.netsprint.eu/",
+ "companyId": "netsprint"
},
"networkedblogs": {
"name": "NetworkedBlogs",
"categoryId": 7,
- "url": "http://w.networkedblogs.com/"
+ "url": "http://w.networkedblogs.com/",
+ "companyId": "networkedblogs"
},
"neustar_adadvisor": {
"name": "Neustar AdAdvisor",
"categoryId": 4,
- "url": "http://www.targusinfo.com/"
+ "url": "http://www.targusinfo.com/",
+ "companyId": "neustar"
},
"new_relic": {
"name": "New Relic",
"categoryId": 6,
- "url": "http://newrelic.com/"
+ "url": "http://newrelic.com/",
+ "companyId": "new_relic"
},
"newscgp.com": {
"name": "News Connect",
"categoryId": 4,
- "url": "https://newscorp.com/"
+ "url": "https://newscorp.com/",
+ "companyId": "news_corp"
},
"newsmax": {
"name": "Newsmax",
"categoryId": 4,
- "url": "http://www.newsmax.com/"
+ "url": "http://www.newsmax.com/",
+ "companyId": "newsmax"
},
"newstogram": {
"name": "Newstogram",
"categoryId": 4,
- "url": "http://www.newstogram.com/"
+ "url": "http://www.newstogram.com/",
+ "companyId": "dailyme"
},
"newsupdatedir.info": {
"name": "newsupdatedir.info",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"newsupdatewe.info": {
"name": "newsupdatewe.info",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"newtention": {
"name": "Newtention",
"categoryId": 4,
- "url": "http://www.newtention.de/"
+ "url": "http://www.newtention.de/",
+ "companyId": "next_audience"
},
"nexage": {
"name": "Nexage",
"categoryId": 4,
- "url": "http://www.nexage.com/"
+ "url": "http://www.nexage.com/",
+ "companyId": "verizon"
},
"nexeps.com": {
"name": "neXeps",
"categoryId": 4,
- "url": "http://nexeps.com/"
+ "url": "http://nexeps.com/",
+ "companyId": null
},
"next_performance": {
"name": "Next Performance",
"categoryId": 4,
- "url": "http://www.nextperformance.com/"
+ "url": "http://www.nextperformance.com/",
+ "companyId": "nextperf"
},
"next_user": {
"name": "Next User",
"categoryId": 4,
- "url": "https://www.nextuser.com/"
+ "url": "https://www.nextuser.com/",
+ "companyId": "next_user"
},
"nextag_roi_optimizer": {
"name": "Nextag ROI Optimizer",
"categoryId": 4,
- "url": "http://www.nextag.com/"
+ "url": "http://www.nextag.com/",
+ "companyId": "nextag"
},
"nextclick": {
"name": "Nextclick",
"categoryId": 4,
- "url": "http://nextclick.pl/"
+ "url": "http://nextclick.pl/",
+ "companyId": "leadbullet"
},
"nextstat": {
"name": "NextSTAT",
"categoryId": 6,
- "url": "http://www.nextstat.com/"
+ "url": "http://www.nextstat.com/",
+ "companyId": "nextstat"
},
"neytiv": {
"name": "Neytiv",
"categoryId": 6,
- "url": "http://neytiv.com/"
+ "url": "http://neytiv.com/",
+ "companyId": "neytiv"
},
"ngage_inc.": {
"name": "NGage INC.",
"categoryId": 6,
- "url": "https://www.nginx.com/"
+ "url": "https://www.nginx.com/",
+ "companyId": "nginx"
},
"nice264.com": {
"name": "Nice264",
"categoryId": 0,
- "url": "http://nice264.com/"
+ "url": "http://nice264.com/",
+ "companyId": null
},
"nimblecommerce": {
"name": "NimbleCommerce",
"categoryId": 4,
- "url": "http://www.nimblecommerce.com/"
+ "url": "http://www.nimblecommerce.com/",
+ "companyId": "nimblecommerce"
},
"ninja_access_analysis": {
"name": "Ninja Access Analysis",
"categoryId": 6,
- "url": "http://www.ninja.co.jp/analysis/"
+ "url": "http://www.ninja.co.jp/analysis/",
+ "companyId": "samurai_factory"
},
"nirror": {
"name": "Nirror",
"categoryId": 6,
- "url": "https://www.nirror.com/"
+ "url": "https://www.nirror.com/",
+ "companyId": "nirror"
},
"nitropay": {
"name": "NitroPay",
"categoryId": 4,
- "url": "https://nitropay.com/"
+ "url": "https://nitropay.com/",
+ "companyId": "gg_software"
},
"nk.pl_widgets": {
"name": "NK.pl Widgets",
"categoryId": 4,
- "url": "http://nk.pl"
+ "url": "http://nk.pl",
+ "companyId": "nk.pl"
},
"noaa.gov": {
"name": "National Oceanic and Atmospheric Administration",
"categoryId": 8,
- "url": "https://noaa.gov/"
+ "url": "https://noaa.gov/",
+ "companyId": null
},
"noddus": {
"name": "Noddus",
"categoryId": 4,
- "url": "https://www.enterprise.noddus.com/"
+ "url": "https://www.enterprise.noddus.com/",
+ "companyId": "noddus"
},
"nolix": {
"name": "Nolix",
"categoryId": 4,
- "url": "http://nolix.ru/"
+ "url": "http://nolix.ru/",
+ "companyId": "nolix"
},
"nonstop_consulting": {
"name": "Resolution Media",
"categoryId": 4,
- "url": "https://resolutionmedia.com/"
+ "url": "https://resolutionmedia.com/",
+ "companyId": "resolution_media"
},
"noop.style": {
"name": "noop.style",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"nosto.com": {
"name": "nosto",
"categoryId": 6,
- "url": "http://www.nosto.com/"
+ "url": "http://www.nosto.com/",
+ "companyId": null
},
"notify": {
"name": "Notify",
"categoryId": 4,
- "url": "http://notify.ag/en/"
+ "url": "http://notify.ag/en/",
+ "companyId": null
},
"notifyfox": {
"name": "Notifyfox",
"categoryId": 6,
- "url": "https://notifyfox.com/"
+ "url": "https://notifyfox.com/",
+ "companyId": "notifyfox"
},
"now_interact": {
"name": "Now Interact",
"categoryId": 6,
- "url": "http://nowinteract.com/"
+ "url": "http://nowinteract.com/",
+ "companyId": "now_interact"
},
"npario": {
"name": "nPario",
"categoryId": 6,
- "url": "http://npario.com/"
+ "url": "http://npario.com/",
+ "companyId": "npario"
},
"nplexmedia": {
"name": "nPlexMedia",
"categoryId": 4,
- "url": "http://www.nplexmedia.com/"
+ "url": "http://www.nplexmedia.com/",
+ "companyId": "nplexmedia"
},
"nrelate": {
"name": "nRelate",
"categoryId": 2,
- "url": "http://nrelate.com/"
+ "url": "http://nrelate.com/",
+ "companyId": "iac_apps"
},
"ns8": {
"name": "NS8",
"categoryId": 4,
- "url": "https://www.ns8.com/"
+ "url": "https://www.ns8.com/",
+ "companyId": null
},
"nt.vc": {
"name": "Next Tuesday GmbH",
"categoryId": 8,
- "url": "http://www.nexttuesday.de/"
+ "url": "http://www.nexttuesday.de/",
+ "companyId": null
},
"ntent": {
"name": "NTENT",
"categoryId": 4,
- "url": "http://www.verticalsearchworks.com"
+ "url": "http://www.verticalsearchworks.com",
+ "companyId": "ntent"
},
"nttcom_online_marketing_solutions": {
"name": "NTTCom Online Marketing Solutions",
"categoryId": 6,
- "url": "http://www.digitalforest.co.jp/"
+ "url": "http://www.digitalforest.co.jp/",
+ "companyId": "nttcom_online_marketing_solutions"
},
"nuffnang": {
"name": "Nuffnang",
"categoryId": 4,
- "url": "http://nuffnang.com/"
+ "url": "http://nuffnang.com/",
+ "companyId": "nuffnang"
},
"nugg.ad": {
"name": "Nugg.Ad",
"categoryId": 4,
- "url": "http://www.nugg.ad/"
+ "url": "http://www.nugg.ad/",
+ "companyId": "nugg.ad"
},
"nui_media": {
"name": "NUI Media",
"categoryId": 4,
- "url": "http://adjuggler.com/"
+ "url": "http://adjuggler.com/",
+ "companyId": "nui_media"
},
"numbers.md": {
"name": "Numbers.md",
"categoryId": 6,
- "url": "https://numbers.md/"
+ "url": "https://numbers.md/",
+ "companyId": "numbers.md"
},
"numerator": {
"name": "Numerator",
"categoryId": 5,
- "url": "http://www.channeliq.com/"
+ "url": "http://www.channeliq.com/",
+ "companyId": "numerator"
},
"ny_times_tagx": {
"name": "NY Times TagX",
"categoryId": 6,
- "url": "https://www.nytimes.com/"
+ "url": "https://www.nytimes.com/",
+ "companyId": "the_new_york_times"
},
"nyacampwk.com": {
"name": "nyacampwk.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"nyetm2mkch.com": {
"name": "nyetm2mkch.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"nyt.com": {
"name": "The New York Times",
"categoryId": 8,
- "url": "https://www.nytimes.com/"
+ "url": "https://www.nytimes.com/",
+ "companyId": "the_new_york_times"
},
"o12zs3u2n.com": {
"name": "o12zs3u2n.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"o2.pl": {
"name": "o2.pl",
"categoryId": 8,
- "url": "https://www.o2.pl/"
+ "url": "https://www.o2.pl/",
+ "companyId": "o2.pl"
},
"o2online.de": {
"name": "o2online.de",
"categoryId": 8,
- "url": "https://www.o2online.de/"
+ "url": "https://www.o2online.de/",
+ "companyId": null
},
"oath_inc": {
"name": "Oath",
"categoryId": 8,
- "url": "https://www.oath.com/"
+ "url": "https://www.oath.com/",
+ "companyId": "verizon"
},
"observer": {
"name": "Observer",
"categoryId": 4,
- "url": "http://www.observerapp.com"
+ "url": "http://www.observerapp.com",
+ "companyId": "observer"
},
"ocioso": {
"name": "Ocioso",
"categoryId": 7,
- "url": "http://ocioso.com.br/"
+ "url": "http://ocioso.com.br/",
+ "companyId": "ocioso"
},
"oclasrv.com": {
"name": "oclasrv.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"octapi.net": {
"name": "octapi.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"octavius": {
"name": "Octavius",
"categoryId": 4,
- "url": "http://octavius.rocks/"
+ "url": "http://octavius.rocks/",
+ "companyId": "octavius"
},
"office.com": {
"name": "office.com",
"categoryId": 8,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"office.net": {
"name": "office.net",
"categoryId": 8,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"office365.com": {
"name": "office365.com",
"categoryId": 8,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"oghub.io": {
"name": "OG Hub",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"oh_my_stats": {
"name": "Oh My Stats",
"categoryId": 6,
- "url": "https://ohmystats.com/"
+ "url": "https://ohmystats.com/",
+ "companyId": "oh_my_stats"
},
"ohana_advertising_network": {
"name": "Ohana Advertising Network",
"categoryId": 4,
- "url": "http://adohana.com/"
+ "url": "http://adohana.com/",
+ "companyId": "ohana_advertising_network"
},
"olapic": {
"name": "Olapic",
"categoryId": 4,
- "url": "https://www.olapic.com/"
+ "url": "https://www.olapic.com/",
+ "companyId": "olapic"
},
"olark": {
"name": "Olark",
"categoryId": 2,
- "url": "http://www.olark.com/"
+ "url": "http://www.olark.com/",
+ "companyId": "olark"
},
"olx-st.com": {
"name": "OLX",
"categoryId": 8,
- "url": "http://www.olx.com/"
+ "url": "http://www.olx.com/",
+ "companyId": null
},
"omarsys.com": {
"name": "Omarsys",
"categoryId": 4,
- "url": "http://omarsys.com/"
+ "url": "http://omarsys.com/",
+ "companyId": "xcaliber"
},
"ometria": {
"name": "Ometria",
"categoryId": 4,
- "url": "http://www.ometria.com/"
+ "url": "http://www.ometria.com/",
+ "companyId": "ometria"
},
"omg": {
"name": "OMG",
"categoryId": 7,
- "url": "http://uk.omgpm.com/"
+ "url": "http://uk.omgpm.com/",
+ "companyId": "optimise_media"
},
"omniconvert.com": {
"name": "Omniconvert",
"categoryId": 4,
- "url": "https://www.omniconvert.com/"
+ "url": "https://www.omniconvert.com/",
+ "companyId": "omniconvert"
},
"omniscienta": {
"name": "Omniscienta",
"categoryId": 4,
- "url": "http://www.omniscienta.com/"
+ "url": "http://www.omniscienta.com/",
+ "companyId": null
},
"oms": {
"name": "OMS",
"categoryId": 4,
- "url": "http://oms.eu/"
+ "url": "http://oms.eu/",
+ "companyId": null
},
"onaudience": {
"name": "OnAudience",
"categoryId": 4,
- "url": "http://www.onaudience.com/"
+ "url": "http://www.onaudience.com/",
+ "companyId": "cloud_technologies"
},
"oneall": {
"name": "Oneall",
"categoryId": 7,
- "url": "http://www.oneall.com/"
+ "url": "http://www.oneall.com/",
+ "companyId": "oneall"
},
"onefeed": {
"name": "Onefeed",
"categoryId": 6,
- "url": "http://www.onefeed.co.uk"
+ "url": "http://www.onefeed.co.uk",
+ "companyId": "onefeed"
},
"onesignal": {
"name": "OneSignal",
"categoryId": 5,
- "url": "https://onesignal.com/"
+ "url": "https://onesignal.com/",
+ "companyId": "onesignal"
},
"onestat": {
"name": "OneStat",
"categoryId": 6,
- "url": "http://www.onestat.com/"
+ "url": "http://www.onestat.com/",
+ "companyId": "onestat_international_b.v."
},
"onet.pl": {
"name": "onet",
"categoryId": 8,
- "url": "https://www.onet.pl/"
+ "url": "https://www.onet.pl/",
+ "companyId": null
},
"onetag": {
"name": "OneTag",
"categoryId": 4,
- "url": "https://www.onetag.com/"
+ "url": "https://www.onetag.com/",
+ "companyId": "onetag"
},
"onetrust": {
"name": "OneTrust",
"categoryId": 5,
- "url": "https://www.onetrust.com/"
+ "url": "https://www.onetrust.com/",
+ "companyId": "onetrust"
},
"onfocus.io": {
"name": "OnFocus",
"categoryId": 4,
- "url": "http://onfocus.io/"
+ "url": "http://onfocus.io/",
+ "companyId": "onfocus"
},
"onlinewebstat": {
"name": "Onlinewebstat",
"categoryId": 6,
- "url": "http://www.onlinewebstats.com/index.php?lang=en"
+ "url": "http://www.onlinewebstats.com/index.php?lang=en",
+ "companyId": "onlinewebstat"
},
"onswipe": {
"name": "Onswipe",
"categoryId": 4,
- "url": "http://www.onswipe.com/"
+ "url": "http://www.onswipe.com/",
+ "companyId": "onswipe"
},
"onthe.io": {
"name": "OnThe.io",
"categoryId": 6,
- "url": "https://t.onthe.io/media"
+ "url": "https://t.onthe.io/media",
+ "companyId": "onthe.io"
},
"ontraport_autopilot": {
"name": "Ontraport Autopilot",
"categoryId": 4,
- "url": "http://www.moon-ray.com/"
+ "url": "http://www.moon-ray.com/",
+ "companyId": "ontraport"
},
"ooyala.com": {
"name": "Ooyala Player",
"categoryId": 0,
- "url": "https://www.ooyala.com/"
+ "url": "https://www.ooyala.com/",
+ "companyId": "telstra"
},
"ooyala_analytics": {
"name": "Ooyala Analytics",
"categoryId": 6,
- "url": "https://www.telstraglobal.com/"
+ "url": "https://www.telstraglobal.com/",
+ "companyId": "telstra"
},
"open_adexchange": {
"name": "Open AdExchange",
"categoryId": 4,
- "url": "http://openadex.dk/"
+ "url": "http://openadex.dk/",
+ "companyId": "open_adexchange"
},
"open_adstream": {
"name": "Open Adstream",
"categoryId": 4,
- "url": "https://www.appnexus.com/en"
+ "url": "https://www.appnexus.com/en",
+ "companyId": "appnexus"
},
"open_share_count": {
"name": "Open Share Count",
"categoryId": 4,
- "url": "http://opensharecount.com/"
+ "url": "http://opensharecount.com/",
+ "companyId": "open_share_count"
},
"openload": {
"name": "Openload",
"categoryId": 9,
- "url": "https://openload.co/"
+ "url": "https://openload.co/",
+ "companyId": null
},
"openstat": {
"name": "OpenStat",
"categoryId": 6,
- "url": "https://www.openstat.ru/"
+ "url": "https://www.openstat.ru/",
+ "companyId": "openstat"
},
"opentracker": {
"name": "Opentracker",
"categoryId": 6,
- "url": "http://www.opentracker.net/"
+ "url": "http://www.opentracker.net/",
+ "companyId": "opentracker"
},
"openwebanalytics": {
"name": "Open Web Analytics",
"categoryId": 6,
- "url": "http://www.openwebanalytics.com/"
+ "url": "http://www.openwebanalytics.com/",
+ "companyId": "open_web_analytics"
},
"openx": {
"name": "OpenX",
"categoryId": 4,
- "url": "https://www.openx.com"
+ "url": "https://www.openx.com",
+ "companyId": "openx"
},
"operative_media": {
"name": "Operative Media",
"categoryId": 4,
- "url": "http://www.operative.com/"
+ "url": "http://www.operative.com/",
+ "companyId": "operative_media"
},
"opinary": {
"name": "Opinary",
"categoryId": 2,
- "url": "http://opinary.com/"
+ "url": "http://opinary.com/",
+ "companyId": "opinary"
},
"opinionbar": {
"name": "OpinionBar",
"categoryId": 2,
- "url": "http://www.metrixlab.com"
+ "url": "http://www.metrixlab.com",
+ "companyId": "metrixlab"
},
"oplytic": {
"name": "Oplytic",
"categoryId": 6,
- "url": "http://www.oplytic.com"
+ "url": "http://www.oplytic.com",
+ "companyId": "oplytic"
},
"opta.net": {
"name": "Opta",
"categoryId": 6,
- "url": "http://www.optasports.de/"
+ "url": "http://www.optasports.de/",
+ "companyId": "opta_sports"
},
"optaim": {
"name": "OptAim",
"categoryId": 4,
- "url": "http://optaim.com/"
+ "url": "http://optaim.com/",
+ "companyId": "optaim"
},
"optanaon": {
"name": "Optanaon by OneTrust",
"categoryId": 5,
- "url": "https://www.cookielaw.org/"
+ "url": "https://www.cookielaw.org/",
+ "companyId": "onetrust"
},
"optify": {
"name": "Optify",
"categoryId": 4,
- "url": "http://www.optify.net"
+ "url": "http://www.optify.net",
+ "companyId": "optify"
},
"optimatic": {
"name": "Optimatic",
"categoryId": 0,
- "url": "http://www.optimatic.com/"
+ "url": "http://www.optimatic.com/",
+ "companyId": "optimatic"
},
"optimax_media_delivery": {
"name": "Optimax Media Delivery",
"categoryId": 4,
- "url": "http://optmd.com/"
+ "url": "http://optmd.com/",
+ "companyId": "optimax_media_delivery"
},
"optimicdn.com": {
"name": "OptimiCDN",
"categoryId": 9,
- "url": "https://en.optimicdn.com/"
+ "url": "https://en.optimicdn.com/",
+ "companyId": null
},
"optimizely": {
"name": "Optimizely",
"categoryId": 6,
- "url": "https://www.optimizely.com/"
+ "url": "https://www.optimizely.com/",
+ "companyId": "optimizely"
},
"optimizely_error_log": {
"name": "Optimizely Error Log",
"categoryId": 6,
- "url": "https://www.optimizely.com/"
+ "url": "https://www.optimizely.com/",
+ "companyId": "optimizely"
},
"optimizely_geo_targeting": {
"name": "Optimizely Geographical Targeting",
"categoryId": 6,
- "url": "https://www.optimizely.com/"
+ "url": "https://www.optimizely.com/",
+ "companyId": "optimizely"
},
"optimizely_logging": {
"name": "Optimizely Logging",
"categoryId": 6,
- "url": "https://www.optimizely.com/"
+ "url": "https://www.optimizely.com/",
+ "companyId": "optimizely"
},
"optimonk": {
"name": "Optimonk",
"categoryId": 6,
- "url": "https://www.optimonk.com/"
+ "url": "https://www.optimonk.com/",
+ "companyId": "optimonk"
},
"optinmonster": {
"name": "OptInMonster",
"categoryId": 2,
- "url": "https://optinmonster.com/"
+ "url": "https://optinmonster.com/",
+ "companyId": "optinmonster"
},
"optinproject.com": {
"name": "OptinProject",
"categoryId": 4,
- "url": "https://www.optincollect.com/en"
+ "url": "https://www.optincollect.com/en",
+ "companyId": "optincollect"
},
"optomaton": {
"name": "Optomaton",
"categoryId": 4,
- "url": "http://www.optomaton.com/"
+ "url": "http://www.optomaton.com/",
+ "companyId": "ve"
},
"ora.tv": {
"name": "Ora.TV",
"categoryId": 4,
- "url": "http://www.ora.tv/"
+ "url": "http://www.ora.tv/",
+ "companyId": "ora.tv"
},
"oracle_live_help": {
"name": "Oracle Live Help",
"categoryId": 2,
- "url": "http://www.oracle.com/us/products/applications/atg/live-help-on-demand/index.html"
+ "url": "http://www.oracle.com/us/products/applications/atg/live-help-on-demand/index.html",
+ "companyId": "oracle"
},
"oracle_rightnow": {
"name": "Oracle RightNow",
"categoryId": 8,
- "url": "http://www.oracle.com/"
+ "url": "http://www.oracle.com/",
+ "companyId": "oracle"
},
"orange": {
"name": "Orange",
"categoryId": 4,
- "url": "http://www.orange.co.uk/"
+ "url": "http://www.orange.co.uk/",
+ "companyId": "orange_mobile"
},
"orange142": {
"name": "Orange142",
"categoryId": 4,
- "url": "http://www.orange142.com/"
+ "url": "http://www.orange142.com/",
+ "companyId": "orange142"
},
"orange_france": {
"name": "Orange France",
"categoryId": 8,
- "url": "https://www.orange.fr/"
+ "url": "https://www.orange.fr/",
+ "companyId": "orange_france"
},
"orangesoda": {
"name": "OrangeSoda",
"categoryId": 4,
- "url": "http://www.orangesoda.com/"
+ "url": "http://www.orangesoda.com/",
+ "companyId": "orangesoda"
},
"orc_international": {
"name": "ORC International",
"categoryId": 4,
- "url": "https://orcinternational.com/"
+ "url": "https://orcinternational.com/",
+ "companyId": "engine_group"
},
"order_groove": {
"name": "Order Groove",
"categoryId": 4,
- "url": "http://ordergroove.com/"
+ "url": "http://ordergroove.com/",
+ "companyId": "order_groove"
},
"orel_site": {
"name": "Orel Site",
"categoryId": 2,
- "url": "https://www.orelsite.ru/"
+ "url": "https://www.orelsite.ru/",
+ "companyId": "orel_site"
},
"otclick": {
"name": "otClick",
"categoryId": 4,
- "url": "http://otclick-adv.ru/"
+ "url": "http://otclick-adv.ru/",
+ "companyId": "otclick"
},
"othersearch.info": {
"name": "FlowSurf",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"otm-r.com": {
"name": "OTM",
"categoryId": 4,
- "url": "http://otm-r.com/"
+ "url": "http://otm-r.com/",
+ "companyId": null
},
"otto.de": {
"name": "Otto Group",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"outbrain": {
"name": "Outbrain",
"categoryId": 4,
- "url": "https://www.outbrain.com/"
+ "url": "https://www.outbrain.com/",
+ "companyId": "outbrain"
},
"outbrain_amplify": {
"name": "Outbrain Amplify",
"categoryId": 4,
- "url": "http://www.outbrain.com/"
+ "url": "http://www.outbrain.com/",
+ "companyId": "outbrain"
},
"outbrain_analytics": {
"name": "Outbrain Analytics",
"categoryId": 6,
- "url": "http://www.outbrain.com/"
+ "url": "http://www.outbrain.com/",
+ "companyId": "outbrain"
},
"outbrain_logger": {
"name": "Outbrain Logger",
"categoryId": 4,
- "url": "http://www.outbrain.com/"
+ "url": "http://www.outbrain.com/",
+ "companyId": "outbrain"
},
"outbrain_pixel": {
"name": "Outbrain Pixel",
"categoryId": 4,
- "url": "http://www.outbrain.com/"
+ "url": "http://www.outbrain.com/",
+ "companyId": "outbrain"
},
"outbrain_utilities": {
"name": "Outbrain Utilities",
"categoryId": 6,
- "url": "http://www.outbrain.com/"
+ "url": "http://www.outbrain.com/",
+ "companyId": "outbrain"
},
"outbrain_widgets": {
"name": "Outbrain Widgets",
"categoryId": 4,
- "url": "http://www.outbrain.com/"
+ "url": "http://www.outbrain.com/",
+ "companyId": "outbrain"
},
"overheat.it": {
"name": "overheat",
"categoryId": 6,
- "url": "https://overheat.io/"
+ "url": "https://overheat.io/",
+ "companyId": null
},
"owa": {
"name": "OWA",
"categoryId": 6,
- "url": "http://oewa.at/"
+ "url": "http://oewa.at/",
+ "companyId": "the_austrian_web_analysis"
},
"owneriq": {
"name": "OwnerIQ",
"categoryId": 4,
- "url": "http://www.owneriq.com/"
+ "url": "http://www.owneriq.com/",
+ "companyId": "owneriq"
},
"ownpage": {
"name": "Ownpage",
"categoryId": 2,
- "url": "http://www.ownpage.fr/index.en.html"
+ "url": "http://www.ownpage.fr/index.en.html",
+ "companyId": null
},
"owox.com": {
"name": "OWOX",
"categoryId": 6,
- "url": "https://www.owox.com/"
+ "url": "https://www.owox.com/",
+ "companyId": "owox_inc"
},
"oxamedia": {
"name": "OxaMedia",
"categoryId": 2,
- "url": "http://www.oxamedia.com/"
+ "url": "http://www.oxamedia.com/",
+ "companyId": "oxamedia"
},
"oxomi.com": {
"name": "Oxomi",
"categoryId": 4,
- "url": "https://oxomi.com/"
+ "url": "https://oxomi.com/",
+ "companyId": null
},
"pageanalytics.space": {
"name": "pageanalytics.space",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"pagefair": {
"name": "PageFair",
"categoryId": 2,
- "url": "https://pagefair.com/"
+ "url": "https://pagefair.com/",
+ "companyId": "blockthrough"
},
"pagescience": {
"name": "PageScience",
"categoryId": 4,
- "url": "http://www.precisionhealthmedia.com/index.html"
+ "url": "http://www.precisionhealthmedia.com/index.html",
+ "companyId": "pagescience"
},
"paid-to-promote": {
"name": "Paid-To-Promote",
"categoryId": 4,
- "url": "http://www.paid-to-promote.net/"
+ "url": "http://www.paid-to-promote.net/",
+ "companyId": "paid-to-promote"
},
"paperg": {
"name": "PaperG",
"categoryId": 4,
- "url": "http://www.paperg.com/"
+ "url": "http://www.paperg.com/",
+ "companyId": "paperg"
},
"pardot": {
"name": "Pardot",
"categoryId": 6,
- "url": "http://www.pardot.com/"
+ "url": "http://www.pardot.com/",
+ "companyId": "pardot"
},
"parsely": {
"name": "Parse.ly",
"categoryId": 6,
- "url": "https://www.parse.ly/"
+ "url": "https://www.parse.ly/",
+ "companyId": "parse.ly"
},
"partner-ads": {
"name": "Partner-Ads",
"categoryId": 4,
- "url": "http://www.partner-ads.com/"
+ "url": "http://www.partner-ads.com/",
+ "companyId": "partner-ads"
},
"passionfruit": {
"name": "Passionfruit",
"categoryId": 4,
- "url": "http://passionfruitads.com/"
+ "url": "http://passionfruitads.com/",
+ "companyId": "passionfruit"
},
"pathful": {
"name": "Pathful",
"categoryId": 6,
- "url": "http://www.pathful.com/"
+ "url": "http://www.pathful.com/",
+ "companyId": "pathful"
},
"pay-hit": {
"name": "Pay-Hit",
"categoryId": 4,
- "url": "http://pay-hit.com/"
+ "url": "http://pay-hit.com/",
+ "companyId": "pay-hit"
},
"payclick": {
"name": "PayClick",
"categoryId": 4,
- "url": "http://payclick.it/"
+ "url": "http://payclick.it/",
+ "companyId": "payclick"
},
"paykickstart": {
"name": "PayKickstart",
"categoryId": 6,
- "url": "https://paykickstart.com/"
+ "url": "https://paykickstart.com/",
+ "companyId": "paykickstart"
},
"paypal": {
"name": "PayPal",
"categoryId": 2,
- "url": "https://www.paypal.com"
+ "url": "https://www.paypal.com",
+ "companyId": "ebay"
},
"pcvark.com": {
"name": "pcvark.com",
"categoryId": 11,
- "url": "https://pcvark.com/"
+ "url": "https://pcvark.com/",
+ "companyId": null
},
"peer39": {
"name": "Peer39",
"categoryId": 4,
- "url": "http://www.peer39.com/"
+ "url": "http://www.peer39.com/",
+ "companyId": "peer39"
},
"peer5.com": {
"name": "Peer5",
"categoryId": 9,
- "url": "https://www.peer5.com/"
+ "url": "https://www.peer5.com/",
+ "companyId": "peer5"
},
"peerius": {
"name": "Peerius",
"categoryId": 2,
- "url": "http://www.peerius.com/"
+ "url": "http://www.peerius.com/",
+ "companyId": "peerius"
},
"pendo.io": {
"name": "pendo",
"categoryId": 6,
- "url": "https://www.pendo.io/"
+ "url": "https://www.pendo.io/",
+ "companyId": null
},
"pepper.com": {
"name": "Pepper",
"categoryId": 4,
- "url": "https://www.pepper.com/"
+ "url": "https://www.pepper.com/",
+ "companyId": "6minutes"
},
"pepperjam": {
"name": "Pepperjam",
"categoryId": 4,
- "url": "http://www.pepperjam.com"
+ "url": "http://www.pepperjam.com",
+ "companyId": "pepperjam"
},
"pepsia": {
"name": "Pepsia",
"categoryId": 6,
- "url": "http://pepsia.com/en/"
+ "url": "http://pepsia.com/en/",
+ "companyId": "pepsia"
},
"perfdrive.com": {
"name": "perfdrive.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"perfect_audience": {
"name": "Perfect Audience",
"categoryId": 4,
- "url": "https://www.perfectaudience.com/"
+ "url": "https://www.perfectaudience.com/",
+ "companyId": "perfect_audience"
},
"perfect_market": {
"name": "Perfect Market",
"categoryId": 4,
- "url": "http://perfectmarket.com/"
+ "url": "http://perfectmarket.com/",
+ "companyId": "perfect_market"
},
"perform_group": {
"name": "Perform Group",
"categoryId": 5,
- "url": "http://www.performgroup.co.uk/"
+ "url": "http://www.performgroup.co.uk/",
+ "companyId": "perform_group"
},
"performable": {
"name": "Performable",
"categoryId": 6,
- "url": "http://www.performable.com/"
+ "url": "http://www.performable.com/",
+ "companyId": "hubspot"
},
"performancing_metrics": {
"name": "Performancing Metrics",
"categoryId": 6,
- "url": "http://pmetrics.performancing.com"
+ "url": "http://pmetrics.performancing.com",
+ "companyId": "performancing"
},
"performax": {
"name": "Performax",
"categoryId": 4,
- "url": "https://www.performax.cz/"
+ "url": "https://www.performax.cz/",
+ "companyId": "performax"
},
"perimeterx.net": {
"name": "Perimeterx",
"categoryId": 6,
- "url": "https://www.perimeterx.com/"
+ "url": "https://www.perimeterx.com/",
+ "companyId": null
},
"permutive": {
"name": "Permutive",
"categoryId": 4,
- "url": "http://permutive.com/"
+ "url": "http://permutive.com/",
+ "companyId": "permutive"
},
"persgroep": {
"name": "De Persgroep",
"categoryId": 4,
- "url": "https://www.persgroep.be/"
+ "url": "https://www.persgroep.be/",
+ "companyId": "de_persgroep"
},
"persianstat": {
"name": "PersianStat",
"categoryId": 6,
- "url": "http://www.persianstat.com"
+ "url": "http://www.persianstat.com",
+ "companyId": "persianstat"
},
"persio": {
"name": "Persio",
"categoryId": 4,
- "url": "http://www.pers.io/"
+ "url": "http://www.pers.io/",
+ "companyId": "pers.io"
},
"personyze": {
"name": "Personyze",
"categoryId": 2,
- "url": "http://personyze.com/"
+ "url": "http://personyze.com/",
+ "companyId": "personyze"
},
"petametrics": {
"name": "LiftIgniter",
"categoryId": 2,
- "url": "https://www.liftigniter.com/"
+ "url": "https://www.liftigniter.com/",
+ "companyId": "liftigniter"
},
"pheedo": {
"name": "Pheedo",
"categoryId": 4,
- "url": "http://pheedo.com/"
+ "url": "http://pheedo.com/",
+ "companyId": "pheedo"
},
"phonalytics": {
"name": "Phonalytics",
"categoryId": 2,
- "url": "http://www.phonalytics.com/"
+ "url": "http://www.phonalytics.com/",
+ "companyId": "phonalytics"
},
"phunware": {
"name": "Phunware",
"categoryId": 4,
- "url": "https://www.phunware.com"
+ "url": "https://www.phunware.com",
+ "companyId": "phunware"
},
"piguiqproxy.com": {
"name": "piguiqproxy.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"pilot": {
"name": "Pilot",
"categoryId": 6,
- "url": "http://www.pilot.de/en/home.html"
+ "url": "http://www.pilot.de/en/home.html",
+ "companyId": "pilot_gmbh"
},
"pingdom": {
"name": "Pingdom",
"categoryId": 6,
- "url": "https://www.pingdom.com/"
+ "url": "https://www.pingdom.com/",
+ "companyId": "pingdom"
},
"pinterest": {
"name": "Pinterest",
"categoryId": 7,
- "url": "http://pinterest.com/"
+ "url": "http://pinterest.com/",
+ "companyId": "pinterest"
},
"pinterest_conversion_tracker": {
"name": "Pinterest Conversion Tracker",
"categoryId": 6,
- "url": "http://pinterest.com/"
+ "url": "http://pinterest.com/",
+ "companyId": "pinterest"
},
"pipz": {
"name": "Pipz",
"categoryId": 4,
- "url": "https://pipz.com/br/"
+ "url": "https://pipz.com/br/",
+ "companyId": "pipz_automation"
},
"piwik": {
- "name": "Piwik Pro",
+ "name": "Tombstone (Matomo/Piwik before the split)",
"categoryId": 6,
- "url": "http://piwik.org/"
+ "url": "http://piwik.org/",
+ "companyId": "matomo"
+ },
+ "piwik_pro_analytics_suite": {
+ "name": "Piwik PRO Analytics Suite",
+ "categoryId": 6,
+ "url": "https://piwik.pro/",
+ "companyId": "piwik_pro"
},
"pixalate": {
"name": "Pixalate",
"categoryId": 4,
- "url": "http://www.pixalate.com/"
+ "url": "http://www.pixalate.com/",
+ "companyId": "pixalate"
},
"pixel_union": {
"name": "Pixel Union",
"categoryId": 4,
- "url": "https://www.pixelunion.net/"
+ "url": "https://www.pixelunion.net/",
+ "companyId": "pixel_union"
},
"pixfuture": {
"name": "PixFuture",
"categoryId": 4,
- "url": "http://www.pixfuture.com"
+ "url": "http://www.pixfuture.com",
+ "companyId": "pixfuture"
},
"piximedia": {
"name": "Piximedia",
"categoryId": 4,
- "url": "http://www.piximedia.com/piximedia?en"
+ "url": "http://www.piximedia.com/piximedia?en",
+ "companyId": "piximedia"
},
"pizzaandads_com": {
"name": "pizzaandads.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"placester": {
"name": "Placester",
"categoryId": 4,
- "url": "https://placester.com/"
+ "url": "https://placester.com/",
+ "companyId": "placester"
},
"pladform.ru": {
"name": "Pladform",
"categoryId": 4,
- "url": "https://distribution.pladform.ru/"
+ "url": "https://distribution.pladform.ru/",
+ "companyId": "pladform"
},
"plan.net_experience_cloud": {
"name": "Plan.net Experience Cloud",
"categoryId": 6,
- "url": "https://www.serviceplan.com/"
+ "url": "https://www.serviceplan.com/",
+ "companyId": "serviceplan"
},
"platform360": {
"name": "Platform360",
"categoryId": 4,
- "url": "http://www.platform360.co/#home"
+ "url": "http://www.platform360.co/#home",
+ "companyId": null
},
"platformone": {
"name": "Platform One",
"categoryId": 4,
- "url": "https://www.platform-one.co.jp/"
+ "url": "https://www.platform-one.co.jp/",
+ "companyId": "daconsortium"
},
"play_by_mamba": {
"name": "Play by Mamba",
"categoryId": 4,
- "url": "http://play.mamba.ru/"
+ "url": "http://play.mamba.ru/",
+ "companyId": "mamba"
},
"playbuzz.com": {
"name": "Playbuzz",
"categoryId": 2,
- "url": "https://www.playbuzz.com/"
+ "url": "https://www.playbuzz.com/",
+ "companyId": "playbuzz"
},
"plenty_of_fish": {
"name": "Plenty Of Fish",
"categoryId": 6,
- "url": "http://www.pof.com/"
+ "url": "http://www.pof.com/",
+ "companyId": "plentyoffish"
},
"plex_metrics": {
"name": "Plex Metrics",
"categoryId": 6,
- "url": "https://www.plex.tv/"
+ "url": "https://www.plex.tv/",
+ "companyId": "plex"
},
"plista": {
"name": "Plista",
"categoryId": 4,
- "url": "http://www.plista.com"
+ "url": "http://www.plista.com",
+ "companyId": "plista"
},
"plugrush": {
"name": "PlugRush",
"categoryId": 4,
- "url": "http://www.plugrush.com/"
+ "url": "http://www.plugrush.com/",
+ "companyId": "plugrush"
},
"pluso.ru": {
"name": "Pluso",
"categoryId": 7,
- "url": "https://share.pluso.ru/"
+ "url": "https://share.pluso.ru/",
+ "companyId": "pluso"
},
"plutusads": {
"name": "Plutusads",
"categoryId": 4,
- "url": "http://plutusads.com"
+ "url": "http://plutusads.com",
+ "companyId": "plutusads"
},
"pmddby.com": {
"name": "pmddby.com",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"pnamic.com": {
"name": "pnamic.com",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"po.st": {
"name": "Po.st",
"categoryId": 7,
- "url": "https://www.po.st/"
+ "url": "https://www.po.st/",
+ "companyId": "rythmone"
},
"pocket": {
"name": "Pocket",
"categoryId": 6,
- "url": "http://getpocket.com/"
+ "url": "http://getpocket.com/",
+ "companyId": "pocket"
},
"pocketcents": {
"name": "PocketCents",
"categoryId": 4,
- "url": "http://pocketcents.com/"
+ "url": "http://pocketcents.com/",
+ "companyId": "pocketcents"
},
"pointific": {
"name": "Pointific",
"categoryId": 6,
- "url": "http://www.pontiflex.com/"
+ "url": "http://www.pontiflex.com/",
+ "companyId": "pontiflex"
},
"pointroll": {
"name": "PointRoll",
"categoryId": 4,
- "url": "http://www.pointroll.com/"
+ "url": "http://www.pointroll.com/",
+ "companyId": "gannett_digital_media_network"
},
"poirreleast.club": {
"name": "poirreleast.club",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"polar.me": {
"name": "Polar",
"categoryId": 4,
- "url": "https://polar.me/"
+ "url": "https://polar.me/",
+ "companyId": "polar_inc"
},
"polldaddy": {
"name": "Polldaddy",
"categoryId": 2,
- "url": "http://polldaddy.com/"
+ "url": "http://polldaddy.com/",
+ "companyId": "automattic"
},
"polyad": {
"name": "PolyAd",
"categoryId": 4,
- "url": "http://polyad.net"
+ "url": "http://polyad.net",
+ "companyId": "polyad"
},
"polyfill.io": {
"name": "Polyfill",
"categoryId": 8,
- "url": "https://polyfill.io/"
+ "url": "https://polyfill.io/",
+ "companyId": "polyfill.io"
},
"popads": {
"name": "PopAds",
"categoryId": 4,
- "url": "https://www.popads.net/"
+ "url": "https://www.popads.net/",
+ "companyId": "popads"
},
"popcash": {
"name": "Popcash",
"categoryId": 4,
- "url": "http://popcash.net/"
+ "url": "http://popcash.net/",
+ "companyId": "popcash_network"
},
"popcorn_metrics": {
"name": "Popcorn Metrics",
"categoryId": 6,
- "url": "https://www.popcornmetrics.com/"
+ "url": "https://www.popcornmetrics.com/",
+ "companyId": "popcorn_metrics"
},
"popin.cc": {
"name": "popIn",
"categoryId": 7,
- "url": "https://www.popin.cc/"
+ "url": "https://www.popin.cc/",
+ "companyId": "popin"
},
"popmyads": {
"name": "PopMyAds",
"categoryId": 4,
- "url": "http://popmyads.com/"
+ "url": "http://popmyads.com/",
+ "companyId": "popmyads"
},
"poponclick": {
"name": "PopOnClick",
"categoryId": 4,
- "url": "http://poponclick.com"
+ "url": "http://poponclick.com",
+ "companyId": "poponclick"
},
"populis": {
"name": "Populis",
"categoryId": 4,
- "url": "http://www.populis.com"
+ "url": "http://www.populis.com",
+ "companyId": "populis"
},
"pornhub": {
"name": "PornHub",
"categoryId": 3,
- "url": "https://www.pornhub.com/"
+ "url": "https://www.pornhub.com/",
+ "companyId": "pornhub"
},
"pornwave": {
"name": "Pornwave",
"categoryId": 3,
- "url": "http://pornwave.com"
+ "url": "http://pornwave.com",
+ "companyId": "pornwave.com"
},
"porta_brazil": {
"name": "Porta Brazil",
"categoryId": 4,
- "url": "http://brasil.gov.br/"
+ "url": "http://brasil.gov.br/",
+ "companyId": "portal_brazil"
},
"post_affiliate_pro": {
"name": "Post Affiliate Pro",
"categoryId": 4,
- "url": "http://www.qualityunit.com/"
+ "url": "http://www.qualityunit.com/",
+ "companyId": "qualityunit"
},
"powerlinks": {
"name": "PowerLinks",
"categoryId": 4,
- "url": "http://www.powerlinks.com/"
+ "url": "http://www.powerlinks.com/",
+ "companyId": "powerlinks"
},
"powerreviews": {
"name": "PowerReviews",
"categoryId": 2,
- "url": "http://www.powerreviews.com/"
+ "url": "http://www.powerreviews.com/",
+ "companyId": "powerreviews"
},
"powr.io": {
"name": "POWr",
"categoryId": 6,
- "url": "https://www.powr.io/"
+ "url": "https://www.powr.io/",
+ "companyId": "powr"
},
"pozvonim": {
"name": "Pozvonim",
"categoryId": 4,
- "url": "https://pozvonim.com/"
+ "url": "https://pozvonim.com/",
+ "companyId": "pozvonim"
},
"prebid": {
"name": "Prebid",
"categoryId": 4,
- "url": "http://prebid.org/"
+ "url": "http://prebid.org/",
+ "companyId": null
},
"precisionclick": {
"name": "PrecisionClick",
"categoryId": 4,
- "url": "http://www.precisionclick.com/"
+ "url": "http://www.precisionclick.com/",
+ "companyId": "precisionclick"
},
"predicta": {
"name": "Predicta",
"categoryId": 4,
- "url": "http://predicta.com.br/"
+ "url": "http://predicta.com.br/",
+ "companyId": "predicta"
},
"premonix": {
"name": "Premonix",
"categoryId": 4,
- "url": "http://www.premonix.com/"
+ "url": "http://www.premonix.com/",
+ "companyId": "premonix"
},
"press": {
"name": "Press+",
"categoryId": 4,
- "url": "http://www.mypressplus.com/"
+ "url": "http://www.mypressplus.com/",
+ "companyId": "press+"
},
"pressly": {
"name": "Pressly",
"categoryId": 4,
- "url": "https://www.pressly.com/"
+ "url": "https://www.pressly.com/",
+ "companyId": "pressly"
},
"pricegrabber": {
"name": "PriceGrabber",
"categoryId": 4,
- "url": "http://www.pricegrabber.com"
+ "url": "http://www.pricegrabber.com",
+ "companyId": "pricegrabber"
},
"pricespider": {
"name": "Pricespider",
"categoryId": 4,
- "url": "http://www.pricespider.com/"
+ "url": "http://www.pricespider.com/",
+ "companyId": "price_spider"
},
"prismamediadigital.com": {
"name": "Prisma Media Digital",
"categoryId": 4,
- "url": "http://www.pmdrecrute.com/"
+ "url": "http://www.pmdrecrute.com/",
+ "companyId": "prisma_media_digital"
},
"privy.com": {
"name": "Privy",
"categoryId": 2,
- "url": "https://privy.com/"
+ "url": "https://privy.com/",
+ "companyId": "privy"
},
"proclivity": {
"name": "Proclivity",
"categoryId": 4,
- "url": "http://www.proclivitysystems.com/"
+ "url": "http://www.proclivitysystems.com/",
+ "companyId": "proclivity_media"
},
"prodperfect": {
"name": "ProdPerfect",
"categoryId": 6,
- "url": "https://prodperfect.com/"
+ "url": "https://prodperfect.com/",
+ "companyId": "prodperfect"
},
"productsup": {
"name": "ProductsUp",
"categoryId": 4,
- "url": "https://productsup.io/"
+ "url": "https://productsup.io/",
+ "companyId": "productsup"
},
"profiliad": {
"name": "Profiliad",
"categoryId": 6,
- "url": "http://profiliad.com/"
+ "url": "http://profiliad.com/",
+ "companyId": "profiliad"
},
"profitshare": {
"name": "Profitshare",
"categoryId": 6,
- "url": "https://profitshare.ro/"
+ "url": "https://profitshare.ro/",
+ "companyId": "profitshare"
},
"proformics": {
"name": "Proformics",
"categoryId": 6,
- "url": "http://proformics.com/"
+ "url": "http://proformics.com/",
+ "companyId": "proformics_digital"
},
"programattik": {
"name": "Programattik",
"categoryId": 4,
- "url": "http://www.programattik.com/"
+ "url": "http://www.programattik.com/",
+ "companyId": "ttnet"
},
"project_wonderful": {
"name": "Project Wonderful",
"categoryId": 4,
- "url": "http://www.projectwonderful.com/"
+ "url": "http://www.projectwonderful.com/",
+ "companyId": "project_wonderful"
},
"propel_marketing": {
"name": "Propel Marketing",
"categoryId": 4,
- "url": "http://propelmarketing.com/"
+ "url": "http://propelmarketing.com/",
+ "companyId": "propel_marketing"
},
"propeller_ads": {
"name": "Propeller Ads",
"categoryId": 4,
- "url": "http://www.propellerads.com/"
+ "url": "http://www.propellerads.com/",
+ "companyId": "propeller_ads"
},
"propermedia": {
"name": "Proper Media",
"categoryId": 4,
- "url": "https://proper.io/"
+ "url": "https://proper.io/",
+ "companyId": "propermedia"
},
"props": {
"name": "Props",
"categoryId": 4,
- "url": "http://props.id/"
+ "url": "http://props.id/",
+ "companyId": "props"
},
"propvideo_net": {
"name": "propvideo.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"prospecteye": {
"name": "ProspectEye",
"categoryId": 4,
- "url": "https://www.prospecteye.com/"
+ "url": "https://www.prospecteye.com/",
+ "companyId": "prospecteye"
},
"prosperent": {
"name": "Prosperent",
"categoryId": 4,
- "url": "http://prosperent.com"
+ "url": "http://prosperent.com",
+ "companyId": "prosperent"
},
"prostor": {
"name": "Prostor",
"categoryId": 4,
- "url": "http://prostor-lite.ru/"
+ "url": "http://prostor-lite.ru/",
+ "companyId": "prostor"
},
"provide_support": {
"name": "Provide Support",
"categoryId": 2,
- "url": "http://www.providesupport.com/"
+ "url": "http://www.providesupport.com/",
+ "companyId": "provide_support"
},
"proximic": {
"name": "Proximic",
"categoryId": 4,
- "url": "http://www.proximic.com/"
+ "url": "http://www.proximic.com/",
+ "companyId": "proximic"
},
"proxistore.com": {
"name": "Proxistore",
"categoryId": 4,
- "url": "https://www.proxistore.com/"
+ "url": "https://www.proxistore.com/",
+ "companyId": "proxistore"
},
"pscp.tv": {
"name": "Periscope",
"categoryId": 7,
- "url": "https://www.pscp.tv/"
+ "url": "https://www.pscp.tv/",
+ "companyId": "periscope"
},
"pstatic.net": {
"name": "Naver CDN",
"categoryId": 9,
- "url": "https://www.naver.com/"
+ "url": "https://www.naver.com/",
+ "companyId": "naver"
},
"psyma": {
"name": "Psyma",
"categoryId": 4,
- "url": "http://www.psyma.com/"
+ "url": "http://www.psyma.com/",
+ "companyId": "psyma"
},
"pt_engine": {
"name": "Pt engine",
"categoryId": 6,
- "url": "http://www.ptengine.jp/"
+ "url": "http://www.ptengine.jp/",
+ "companyId": "pt_engine"
},
"pub-fit": {
"name": "Pub-Fit",
"categoryId": 4,
- "url": "http://www.pub-fit.com/"
+ "url": "http://www.pub-fit.com/",
+ "companyId": "pub-fit"
},
"pub.network": {
"name": "pub.network",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"pubble": {
"name": "Pubble",
"categoryId": 2,
- "url": "http://www.pubble.co/"
+ "url": "http://www.pubble.co/",
+ "companyId": "pubble"
},
"pubdirecte": {
"name": "Pubdirecte",
"categoryId": 4,
- "url": "http://www.pubdirecte.com/"
+ "url": "http://www.pubdirecte.com/",
+ "companyId": "pubdirecte"
},
"pubgears": {
"name": "PubGears",
"categoryId": 4,
- "url": "http://pubgears.com/"
+ "url": "http://pubgears.com/",
+ "companyId": "pubgears"
},
"public_ideas": {
"name": "Public Ideas",
"categoryId": 4,
- "url": "http://www.publicidees.co.uk/"
+ "url": "http://www.publicidees.co.uk/",
+ "companyId": "public-idees"
},
"publicidad.net": {
"name": "Publicidad.net",
"categoryId": 4,
- "url": "http://www.en.publicidad.net/"
+ "url": "http://www.en.publicidad.net/",
+ "companyId": "publicidad.net"
},
"publir": {
"name": "Publir",
"categoryId": 4,
- "url": "http://www.publir.com"
+ "url": "http://www.publir.com",
+ "companyId": "publir"
},
"pubmatic": {
"name": "PubMatic",
"categoryId": 4,
- "url": "http://www.pubmatic.com/"
+ "url": "http://www.pubmatic.com/",
+ "companyId": "pubmatic"
},
"pubnub.com": {
"name": "PubNub",
"categoryId": 8,
- "url": "https://www.pubnub.com/"
+ "url": "https://www.pubnub.com/",
+ "companyId": null
},
"puboclic": {
"name": "Puboclic",
"categoryId": 4,
- "url": "http://www.puboclic.com/"
+ "url": "http://www.puboclic.com/",
+ "companyId": "puboclic"
},
"pulpix.com": {
"name": "Pulpix",
"categoryId": 4,
- "url": "https://www.pulpix.com/"
+ "url": "https://www.pulpix.com/",
+ "companyId": "adyoulike"
},
"pulpo_media": {
"name": "Pulpo Media",
"categoryId": 4,
- "url": "http://www.pulpomedia.com/home.html"
+ "url": "http://www.pulpomedia.com/home.html",
+ "companyId": "pulpo_media"
},
"pulse360": {
"name": "Pulse360",
"categoryId": 4,
- "url": "http://www.pulse360.com"
+ "url": "http://www.pulse360.com",
+ "companyId": "pulse360"
},
"pulse_insights": {
"name": "Pulse Insights",
"categoryId": 6,
- "url": "http://pulseinsights.com/"
+ "url": "http://pulseinsights.com/",
+ "companyId": "pulse_insights"
},
"pulsepoint": {
"name": "PulsePoint",
"categoryId": 4,
- "url": "http://www.contextweb.com/"
+ "url": "http://www.contextweb.com/",
+ "companyId": "pulsepoint_ad_exchange"
},
"punchtab": {
"name": "PunchTab",
"categoryId": 4,
- "url": "http://www.punchtab.com/"
+ "url": "http://www.punchtab.com/",
+ "companyId": "punchtab"
},
"purch": {
"name": "Purch",
"categoryId": 4,
- "url": "http://www.purch.com/"
+ "url": "http://www.purch.com/",
+ "companyId": "purch"
},
"pure_chat": {
"name": "Pure Chat",
"categoryId": 2,
- "url": "https://www.purechat.com"
+ "url": "https://www.purechat.com",
+ "companyId": "pure_chat"
},
"pureprofile": {
"name": "Pureprofile",
"categoryId": 6,
- "url": "https://www.pureprofile.com/us/"
+ "url": "https://www.pureprofile.com/us/",
+ "companyId": "pureprofile"
},
"purlive": {
"name": "PurLive",
"categoryId": 4,
- "url": "http://www.purlive.com/"
+ "url": "http://www.purlive.com/",
+ "companyId": "purlive"
},
"puserving.com": {
"name": "puserving.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"push.world": {
"name": "Push.world",
"categoryId": 2,
- "url": "https://push.world/en"
+ "url": "https://push.world/en",
+ "companyId": "push.world"
},
"push_engage": {
"name": "Push Engage",
"categoryId": 2,
- "url": "https://www.pushengage.com/"
+ "url": "https://www.pushengage.com/",
+ "companyId": "push_engage"
},
"pushame.com": {
"name": "pushame.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"pushbullet": {
"name": "Pushbullet",
"categoryId": 2,
- "url": "https://www.pushbullet.com/"
+ "url": "https://www.pushbullet.com/",
+ "companyId": "pushbullet"
},
"pushcrew": {
"name": "VWO Engage",
"categoryId": 2,
- "url": "https://vwo.com/engage/"
+ "url": "https://vwo.com/engage/",
+ "companyId": "wingify"
},
"pusher.com": {
"name": "Pusher",
"categoryId": 6,
- "url": "https://pusher.com/"
+ "url": "https://pusher.com/",
+ "companyId": null
},
"pushnative.com": {
"name": "pushnative.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"pushnews": {
"name": "Pushnews",
"categoryId": 4,
- "url": "https://www.pushnews.eu/"
+ "url": "https://www.pushnews.eu/",
+ "companyId": "pushnews"
},
"pushno.com": {
"name": "pushno.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"pushwhy.com": {
"name": "pushwhy.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"pushwoosh.com": {
"name": "Pushwoosh",
"categoryId": 2,
- "url": "https://www.pushwoosh.com/"
+ "url": "https://www.pushwoosh.com/",
+ "companyId": "pushwoosh"
},
"pvclouds.com": {
"name": "pvclouds.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"q1media": {
"name": "Q1Media",
"categoryId": 4,
- "url": "http://q1media.com/"
+ "url": "http://q1media.com/",
+ "companyId": "q1media"
},
"q_division": {
"name": "Q-Division",
"categoryId": 4,
- "url": "https://q-division.de/"
+ "url": "https://q-division.de/",
+ "companyId": null
},
"qbaka": {
"name": "Qbaka",
"categoryId": 6,
- "url": "https://qbaka.com/"
+ "url": "https://qbaka.com/",
+ "companyId": "qbaka"
},
"qcri_analytics": {
"name": "QCRI Analytics",
"categoryId": 6,
- "url": "http://qcri.org/"
+ "url": "http://qcri.org/",
+ "companyId": "qatar_computing_research_institute"
},
"qeado": {
"name": "Qeado",
"categoryId": 6,
- "url": "https://www.qeado.com/"
+ "url": "https://www.qeado.com/",
+ "companyId": "qeado"
},
"qihoo_360": {
"name": "Qihoo 360",
"categoryId": 6,
- "url": "https://www.360totalsecurity.com/en/"
+ "url": "https://www.360totalsecurity.com/en/",
+ "companyId": "qihoo_360_technology"
},
"qq.com": {
"name": "qq.com",
"categoryId": 8,
- "url": "http://www.qq.com/"
+ "url": "http://www.qq.com/",
+ "companyId": "qq.com"
},
"qrius": {
"name": "Qrius",
"categoryId": 7,
- "url": "http://www.qrius.me/"
+ "url": "http://www.qrius.me/",
+ "companyId": "mediafed"
},
"qualaroo": {
"name": "Qualaroo",
"categoryId": 6,
- "url": null
+ "url": null,
+ "companyId": null
},
"qualia": {
"name": "Qualia",
"categoryId": 4,
- "url": "http://www.bluecava.com/"
+ "url": "http://www.bluecava.com/",
+ "companyId": "qualia"
},
"qualtrics": {
"name": "Qualtrics",
"categoryId": 6,
- "url": "http://www.qualtrics.com/"
+ "url": "http://www.qualtrics.com/",
+ "companyId": "qualtrics"
},
"quantcast": {
"name": "Quantcast",
"categoryId": 4,
- "url": "http://www.quantcast.com/"
+ "url": "http://www.quantcast.com/",
+ "companyId": "quantcast"
},
"quantcount": {
"name": "Quantcount",
"categoryId": 6,
- "url": "http://www.quantcast.com"
+ "url": "http://www.quantcast.com",
+ "companyId": "quantcast"
},
"quantum_metric": {
"name": "Quantum Metric",
"categoryId": 6,
- "url": "https://www.quantummetric.com/"
+ "url": "https://www.quantummetric.com/",
+ "companyId": "quantum_metric"
},
"quartic.pl": {
"name": "Quartic",
"categoryId": 6,
- "url": "https://www.quarticon.com/"
+ "url": "https://www.quarticon.com/",
+ "companyId": "quarticon"
},
"qubit": {
"name": "Qubit Opentag",
"categoryId": 6,
- "url": "http://www.qubit.com/"
+ "url": "http://www.qubit.com/",
+ "companyId": "qubit"
},
"questback": {
"name": "Questback",
"categoryId": 2,
- "url": "http://www1.questback.com/"
+ "url": "http://www1.questback.com/",
+ "companyId": "questback"
},
"queue-it": {
"name": "Queue-it",
"categoryId": 6,
- "url": "https://queue-it.com/"
+ "url": "https://queue-it.com/",
+ "companyId": null
},
"quick-counter.net": {
"name": "Quick-counter.net",
"categoryId": 6,
- "url": "http://www.quick-counter.net/"
+ "url": "http://www.quick-counter.net/",
+ "companyId": "quick-counter.net"
},
"quigo_adsonar": {
"name": "Quigo AdSonar",
"categoryId": 4,
- "url": "http://www.quigo.com"
+ "url": "http://www.quigo.com",
+ "companyId": "verizon"
},
"quinstreet": {
"name": "QuinStreet",
"categoryId": 4,
- "url": "http://www.quinstreet.com/"
+ "url": "http://www.quinstreet.com/",
+ "companyId": "quinstreet"
},
"quintelligence": {
"name": "Quintelligence",
"categoryId": 6,
- "url": "http://www.quintelligence.com/"
+ "url": "http://www.quintelligence.com/",
+ "companyId": "quintelligence"
},
"quisma": {
"name": "Quisma",
"categoryId": 4,
- "url": "http://www.quisma.com/en/"
+ "url": "http://www.quisma.com/en/",
+ "companyId": "wpp"
},
"quora.com": {
"name": "Quora",
"categoryId": 7,
- "url": "https://quora.com/"
+ "url": "https://quora.com/",
+ "companyId": null
},
"r_advertising": {
"name": "R-Advertising",
"categoryId": 4,
- "url": "http://www.r-advertising.com/"
+ "url": "http://www.r-advertising.com/",
+ "companyId": "r-advertising"
},
"rackcdn.com": {
"name": "Rackspace",
"categoryId": 9,
- "url": "https://www.rackspace.com/"
+ "url": "https://www.rackspace.com/",
+ "companyId": null
},
"radarurl": {
"name": "RadarURL",
"categoryId": 6,
- "url": "http://radarurl.com/"
+ "url": "http://radarurl.com/",
+ "companyId": "radarurl"
},
"radial": {
"name": "Radial",
"categoryId": 4,
- "url": "http://www.clearsaleing.com/"
+ "url": "http://www.clearsaleing.com/",
+ "companyId": "radial"
},
"radiumone": {
"name": "RadiumOne",
"categoryId": 4,
- "url": "http://www.radiumone.com/index.html"
+ "url": "http://www.radiumone.com/index.html",
+ "companyId": "rythmone"
},
"raisenow": {
"name": "RaiseNow",
"categoryId": 6,
- "url": "https://www.raisenow.com/de"
+ "url": "https://www.raisenow.com/de",
+ "companyId": "raisenow"
},
"rakuten_display": {
"name": "Rakuten Display",
"categoryId": 4,
- "url": "https://rakutenmarketing.com/display"
+ "url": "https://rakutenmarketing.com/display",
+ "companyId": "rakuten"
},
"rakuten_globalmarket": {
"name": "Rakuten",
"categoryId": 4,
- "url": "https://www.rakuten.co.jp/"
+ "url": "https://www.rakuten.co.jp/",
+ "companyId": "rakuten"
},
"rakuten_widget": {
"name": "Rakuten Widget",
"categoryId": 4,
- "url": "http://global.rakuten.com/corp/"
+ "url": "http://global.rakuten.com/corp/",
+ "companyId": "rakuten"
},
"rambler": {
"name": "Rambler",
"categoryId": 6,
- "url": "https://www.rambler.ru/"
+ "url": "https://www.rambler.ru/",
+ "companyId": "rambler"
},
"rambler_count": {
"name": "Rambler Count",
"categoryId": 2,
- "url": "http://www.rambler.ru/"
+ "url": "http://www.rambler.ru/",
+ "companyId": "rambler"
},
"rambler_widget": {
"name": "Rambler Widget",
"categoryId": 2,
- "url": "http://www.rambler.ru/"
+ "url": "http://www.rambler.ru/",
+ "companyId": "rambler"
},
"rapidspike": {
"name": "RapidSpike",
"categoryId": 6,
- "url": "https://www.rapidspike.com"
+ "url": "https://www.rapidspike.com",
+ "companyId": "rapidspike"
},
"ravelin": {
"name": "Ravelin",
"categoryId": 6,
- "url": "https://www.ravelin.com/"
+ "url": "https://www.ravelin.com/",
+ "companyId": null
},
"rawgit": {
"name": "RawGit",
"categoryId": 9,
- "url": "http://rawgit.com/"
+ "url": "http://rawgit.com/",
+ "companyId": null
},
"raygun": {
"name": "Raygun",
"categoryId": 4,
- "url": "https://raygun.com/"
+ "url": "https://raygun.com/",
+ "companyId": "raygun"
},
"rbc_counter": {
"name": "RBC Counter",
"categoryId": 6,
- "url": "http://www.rbc.ru/"
+ "url": "http://www.rbc.ru/",
+ "companyId": "rbc_group"
},
"rcs.it": {
"name": "RCS",
"categoryId": 4,
- "url": "http://www.rcsmediagroup.it/"
+ "url": "http://www.rcsmediagroup.it/",
+ "companyId": "rcs"
},
"rd_station": {
"name": "RD Station",
"categoryId": 6,
- "url": "http://www.rdstation.com/en/"
+ "url": "http://www.rdstation.com/en/",
+ "companyId": "rd_station"
},
"reachforce": {
"name": "ReachForce",
"categoryId": 6,
- "url": "http://www.reachforce.com/"
+ "url": "http://www.reachforce.com/",
+ "companyId": "reachforce"
},
"reachjunction": {
"name": "ReachJunction",
"categoryId": 4,
- "url": "http://www.reachjunction.com/"
+ "url": "http://www.reachjunction.com/",
+ "companyId": "reachjunction"
},
"reachlocal": {
"name": "ReachLocal",
"categoryId": 4,
- "url": "http://www.reachlocal.com/"
+ "url": "http://www.reachlocal.com/",
+ "companyId": "reachlocal"
},
"reactful": {
"name": "Reactful",
"categoryId": 4,
- "url": "http://www.reactful.com/"
+ "url": "http://www.reactful.com/",
+ "companyId": "reactful"
},
"reactivpub": {
"name": "Reactivpub",
"categoryId": 6,
- "url": "http://www.reactivpub.com/"
+ "url": "http://www.reactivpub.com/",
+ "companyId": "r-advertising"
},
"reactx": {
"name": "ReactX",
"categoryId": 4,
- "url": "http://home.skinected.com"
+ "url": "http://home.skinected.com",
+ "companyId": "reactx"
},
"readerboard": {
"name": "ReaderBoard",
"categoryId": 7,
- "url": "http://www.readrboard.com"
+ "url": "http://www.readrboard.com",
+ "companyId": "centre_phi"
},
"readme": {
"name": "ReadMe",
"categoryId": 6,
- "url": "https://readme.com/"
+ "url": "https://readme.com/",
+ "companyId": "readme"
},
"readspeaker.com": {
"name": "ReadSpeaker",
"categoryId": 2,
- "url": "https://www.readspeaker.com/"
+ "url": "https://www.readspeaker.com/",
+ "companyId": null
},
"realclick": {
"name": "RealClick",
"categoryId": 4,
- "url": "http://www.realclick.co.kr/"
+ "url": "http://www.realclick.co.kr/",
+ "companyId": "realclick"
},
"realperson.de": {
"name": "Realperson Chat",
"categoryId": 2,
- "url": "http://www.optimise-it.de/"
+ "url": "http://www.optimise-it.de/",
+ "companyId": "optimise_it"
},
"realtime": {
"name": "Realtime",
"categoryId": 2,
- "url": "http://www.realtime.co/"
+ "url": "http://www.realtime.co/",
+ "companyId": "realtime"
},
"realytics": {
"name": "Realytics",
"categoryId": 6,
- "url": "https://www.realytics.io/"
+ "url": "https://www.realytics.io/",
+ "companyId": "realytics"
},
"rebel_mouse": {
"name": "Rebel Mouse",
"categoryId": 6,
- "url": "https://www.rebelmouse.com/"
+ "url": "https://www.rebelmouse.com/",
+ "companyId": "rebelmouse"
},
"recettes.net": {
"name": "Recettes.net",
"categoryId": 8,
- "url": "http://www.recettes.net/"
+ "url": "http://www.recettes.net/",
+ "companyId": "recettes.net"
},
"recopick": {
"name": "RecoPick",
"categoryId": 4,
- "url": "https://recopick.com/"
+ "url": "https://recopick.com/",
+ "companyId": "recopick"
},
"recreativ": {
"name": "Recreativ",
"categoryId": 4,
- "url": "http://recreativ.ru/"
+ "url": "http://recreativ.ru/",
+ "companyId": "recreativ"
},
"recruitics": {
"name": "Recruitics",
"categoryId": 6,
- "url": "http://recruitics.com/"
+ "url": "http://recruitics.com/",
+ "companyId": "recruitics"
},
"red_ventures": {
"name": "Red Ventures",
"categoryId": 6,
- "url": "https://www.redventures.com/"
+ "url": "https://www.redventures.com/",
+ "companyId": "red_ventures"
},
"redblue_de": {
"name": "redblue",
"categoryId": 6,
- "url": "https://www.redblue.de/"
+ "url": "https://www.redblue.de/",
+ "companyId": null
},
"redcdn.pl": {
"name": "redGalaxy CDN",
"categoryId": 9,
- "url": "http://www.atendesoftware.pl/"
+ "url": "http://www.atendesoftware.pl/",
+ "companyId": "atende_software"
},
"reddit": {
"name": "Reddit",
"categoryId": 7,
- "url": "http://reddit.com"
+ "url": "http://reddit.com",
+ "companyId": "reddit"
},
"redhelper": {
"name": "RedHelper",
"categoryId": 2,
- "url": "http://redhelper.com/"
+ "url": "http://redhelper.com/",
+ "companyId": "redhelper"
},
"redlotus": {
"name": "RedLotus",
"categoryId": 4,
- "url": "http://triggit.com/"
+ "url": "http://triggit.com/",
+ "companyId": "redlotus"
},
"redtram": {
"name": "RedTram",
"categoryId": 4,
- "url": "http://www.redtram.com/"
+ "url": "http://www.redtram.com/",
+ "companyId": "redtram"
},
"redtube.com": {
"name": "redtube.com",
"categoryId": 9,
- "url": null
+ "url": null,
+ "companyId": null
},
"redux_media": {
"name": "Redux Media",
"categoryId": 4,
- "url": "http://reduxmedia.com/"
+ "url": "http://reduxmedia.com/",
+ "companyId": "redux_media"
},
"reed_business_information": {
"name": "Reed Business Information",
"categoryId": 6,
- "url": "http://www.reedbusiness.com/"
+ "url": "http://www.reedbusiness.com/",
+ "companyId": "andera_partners"
},
"reembed.com": {
"name": "reEmbed",
"categoryId": 0,
- "url": "https://www.reembed.com/"
+ "url": "https://www.reembed.com/",
+ "companyId": "reembed"
},
"reevoo.com": {
"name": "Reevoo",
"categoryId": 4,
- "url": "https://www.reevoo.com/en/"
+ "url": "https://www.reevoo.com/en/",
+ "companyId": "reevoo"
},
"refericon": {
"name": "Refericon",
"categoryId": 4,
- "url": "https://refericon.pl/#"
+ "url": "https://refericon.pl/#",
+ "companyId": "refericon"
},
"referlocal": {
"name": "ReferLocal",
"categoryId": 4,
- "url": "http://referlocal.com/"
+ "url": "http://referlocal.com/",
+ "companyId": "referlocal"
},
"refersion": {
"name": "Refersion",
"categoryId": 4,
- "url": "https://www.refersion.com/"
+ "url": "https://www.refersion.com/",
+ "companyId": "refersion"
},
"refined_labs": {
"name": "Refined Labs",
"categoryId": 4,
- "url": "http://www.refinedlabs.com"
+ "url": "http://www.refinedlabs.com",
+ "companyId": "refined_labs"
},
"reflektion": {
"name": "Reflektion",
"categoryId": 4,
- "url": "http://"
+ "url": "http://",
+ "companyId": "reflektion"
},
"reformal": {
"name": "Reformal",
"categoryId": 2,
- "url": "http://reformal.ru/"
+ "url": "http://reformal.ru/",
+ "companyId": "reformal"
},
"reinvigorate": {
"name": "Reinvigorate",
"categoryId": 6,
- "url": "http://www.reinvigorate.net/"
+ "url": "http://www.reinvigorate.net/",
+ "companyId": "media_temple"
},
"rekko": {
"name": "Rekko",
"categoryId": 4,
- "url": "http://convert.us/"
+ "url": "http://convert.us/",
+ "companyId": "rekko"
},
"reklam_store": {
"name": "Reklam Store",
"categoryId": 4,
- "url": "http://www.reklamstore.com"
+ "url": "http://www.reklamstore.com",
+ "companyId": "reklam_store"
},
"reklamport": {
"name": "Reklamport",
"categoryId": 4,
- "url": "http://www.reklamport.com/"
+ "url": "http://www.reklamport.com/",
+ "companyId": "reklamport"
},
"reklamz": {
"name": "ReklamZ",
"categoryId": 4,
- "url": "http://www.reklamz.com/"
+ "url": "http://www.reklamz.com/",
+ "companyId": "reklamz"
},
"rekmob": {
"name": "Rekmob",
"categoryId": 4,
- "url": "https://www.rekmob.com/"
+ "url": "https://www.rekmob.com/",
+ "companyId": "rekmob"
},
"relap": {
"name": "Relap",
"categoryId": 4,
- "url": "https://relap.io/"
+ "url": "https://relap.io/",
+ "companyId": "relap"
},
"relay42": {
"name": "Relay42",
"categoryId": 5,
- "url": "http://synovite.com"
+ "url": "http://synovite.com",
+ "companyId": "relay42"
},
"relestar": {
"name": "Relestar",
"categoryId": 6,
- "url": "https://relestar.com/"
+ "url": "https://relestar.com/",
+ "companyId": "relestar"
},
"relevant4.com": {
"name": "relevant4 GmbH",
"categoryId": 8,
- "url": "https://www.relevant4.com/"
+ "url": "https://www.relevant4.com/",
+ "companyId": null
},
"remintrex": {
"name": "Remintrex",
"categoryId": 4,
- "url": "http://www.remintrex.com/"
+ "url": "http://www.remintrex.com/",
+ "companyId": null
},
"remove.video": {
"name": "remove.video",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"repost.us": {
"name": "Repost.us",
"categoryId": 4,
- "url": "http://www.freerangecontent.com/"
+ "url": "http://www.freerangecontent.com/",
+ "companyId": "repost"
},
"republer.com": {
"name": "Republer",
"categoryId": 4,
- "url": "http://republer.com/"
+ "url": "http://republer.com/",
+ "companyId": "republer"
},
"res-meter": {
"name": "Res-meter",
"categoryId": 6,
- "url": "http://respublica.al/res-meter"
+ "url": "http://respublica.al/res-meter",
+ "companyId": "respublica"
},
"research_now": {
"name": "Research Now",
"categoryId": 4,
- "url": "http://www.researchnow.com/"
+ "url": "http://www.researchnow.com/",
+ "companyId": "research_now"
},
"resonate_networks": {
"name": "Resonate Networks",
"categoryId": 4,
- "url": "http://www.resonatenetworks.com/"
+ "url": "http://www.resonatenetworks.com/",
+ "companyId": "resonate"
},
"respond": {
"name": "Respond",
"categoryId": 4,
- "url": "http://respondhq.com/"
+ "url": "http://respondhq.com/",
+ "companyId": "respond"
},
"responsetap": {
"name": "ResponseTap",
"categoryId": 4,
- "url": "http://www.adinsight.eu/"
+ "url": "http://www.adinsight.eu/",
+ "companyId": "responsetap"
},
"result_links": {
"name": "Result Links",
"categoryId": 4,
- "url": "http://www.resultlinks.com/"
+ "url": "http://www.resultlinks.com/",
+ "companyId": "result_links"
},
"resultspage.com": {
"name": "SLI Systems",
"categoryId": 6,
- "url": "https://www.sli-systems.com/"
+ "url": "https://www.sli-systems.com/",
+ "companyId": "sli_systems"
},
"retailrocket.net": {
"name": "Retail Rocket",
"categoryId": 4,
- "url": "https://retailrocket.net/"
+ "url": "https://retailrocket.net/",
+ "companyId": "retail_rocket"
},
"retarget_app": {
"name": "Retarget App",
"categoryId": 4,
- "url": "https://retargetapp.com/"
+ "url": "https://retargetapp.com/",
+ "companyId": "retargetapp"
},
"retargeter_beacon": {
"name": "ReTargeter Beacon",
"categoryId": 4,
- "url": "http://www.retargeter.com/"
+ "url": "http://www.retargeter.com/",
+ "companyId": "retargeter"
},
"retargeting.cl": {
"name": "Retargeting.cl",
"categoryId": 4,
- "url": "http://retargeting.cl/"
+ "url": "http://retargeting.cl/",
+ "companyId": "retargeting"
},
"retention_science": {
"name": "Retention Science",
"categoryId": 4,
- "url": "http://retentionscience.com/"
+ "url": "http://retentionscience.com/",
+ "companyId": "retention_science"
},
"reuters_media": {
"name": "Reuters media",
"categoryId": 9,
- "url": "https://reuters.com"
+ "url": "https://reuters.com",
+ "companyId": null
},
"revcontent": {
"name": "RevContent",
"categoryId": 4,
- "url": "https://www.revcontent.com/"
+ "url": "https://www.revcontent.com/",
+ "companyId": "revcontent"
},
"reve_marketing": {
"name": "Reve Marketing",
"categoryId": 4,
- "url": "http://tellafriend.socialtwist.com/"
+ "url": "http://tellafriend.socialtwist.com/",
+ "companyId": "reve_marketing"
},
"revenue": {
"name": "Revenue",
"categoryId": 4,
- "url": "https://revenue.com/"
+ "url": "https://revenue.com/",
+ "companyId": "revenue"
},
"revenuehits": {
"name": "RevenueHits",
"categoryId": 4,
- "url": "http://www.revenuehits.com/"
+ "url": "http://www.revenuehits.com/",
+ "companyId": "revenuehits"
},
"revenuemantra": {
"name": "RevenueMantra",
"categoryId": 4,
- "url": "http://www.revenuemantra.com/"
+ "url": "http://www.revenuemantra.com/",
+ "companyId": "revenuemantra"
},
"revive_adserver": {
"name": "Revive Adserver",
"categoryId": 4,
- "url": "https://www.revive-adserver.com/"
+ "url": "https://www.revive-adserver.com/",
+ "companyId": "revive_adserver"
},
"revolver_maps": {
"name": "Revolver Maps",
"categoryId": 6,
- "url": "http://www.revolvermaps.com/"
+ "url": "http://www.revolvermaps.com/",
+ "companyId": "revolver_maps"
},
"revresponse": {
"name": "RevResponse",
"categoryId": 4,
- "url": "http://www.netline.com/"
+ "url": "http://www.netline.com/",
+ "companyId": "netline"
},
"rewords": {
"name": "ReWords",
"categoryId": 4,
- "url": "http://www.rewords.pl/"
+ "url": "http://www.rewords.pl/",
+ "companyId": "rewords"
},
"rhythmone": {
"name": "RhythmOne",
"categoryId": 4,
- "url": "http://www.adconductor.com/"
+ "url": "http://www.adconductor.com/",
+ "companyId": "rhythmone"
},
"rhythmone_beacon": {
"name": "Rhythmone Beacon",
"categoryId": 4,
- "url": "https://www.rhythmone.com/"
+ "url": "https://www.rhythmone.com/",
+ "companyId": "rythmone"
},
"ria.ru": {
"name": "ria.ru",
"categoryId": 8,
- "url": "https://ria.ru/"
+ "url": "https://ria.ru/",
+ "companyId": null
},
"rich_media_banner_network": {
"name": "Rich Media Banner Network",
"categoryId": 4,
- "url": "http://rmbn.ru/"
+ "url": "http://rmbn.ru/",
+ "companyId": "rich_media_banner_network"
},
"richrelevance": {
"name": "RichRelevance",
"categoryId": 2,
- "url": "http://www.richrelevance.com/"
+ "url": "http://www.richrelevance.com/",
+ "companyId": "richrelevance"
},
"ringier.ch": {
"name": "Ringier",
"categoryId": 6,
- "url": "http://ringier.ch/en"
+ "url": "http://ringier.ch/en",
+ "companyId": "ringier"
},
"rio_seo": {
"name": "Rio SEO",
"categoryId": 7,
- "url": "http://www.meteorsolutions.com"
+ "url": "http://www.meteorsolutions.com",
+ "companyId": "rio_seo"
},
"riskfield.com": {
"name": "Riskified",
"categoryId": 2,
- "url": "https://www.riskified.com/"
+ "url": "https://www.riskified.com/",
+ "companyId": "riskfield"
},
"rncdn3.com": {
"name": "Reflected Networks",
"categoryId": 9,
- "url": "http://www.rncdn3.com/"
+ "url": "http://www.rncdn3.com/",
+ "companyId": null
},
"ro2.biz": {
"name": "Ro2.biz",
"categoryId": 4,
- "url": "http://ro2.biz/index.php?r=adikku"
+ "url": "http://ro2.biz/index.php?r=adikku",
+ "companyId": "ro2.biz"
},
"roblox": {
"name": "Roblox",
"categoryId": 8,
- "url": "https://www.roblox.com/"
+ "url": "https://www.roblox.com/",
+ "companyId": null
},
"rockerbox": {
"name": "Rockerbox",
"categoryId": 6,
- "url": "https://www.rockerbox.com/privacy"
+ "url": "https://www.rockerbox.com/privacy",
+ "companyId": "rockerbox"
},
"rocket.ia": {
"name": "Rocket.ia",
"categoryId": 4,
- "url": "https://rocket.la/"
+ "url": "https://rocket.la/",
+ "companyId": "rocket.la"
},
"roi_trax": {
"name": "ROI trax",
"categoryId": 4,
- "url": "http://www.oneupweb.com/"
+ "url": "http://www.oneupweb.com/",
+ "companyId": "oneupweb"
},
"roistat": {
"name": "Roistat",
"categoryId": 6,
- "url": "https://roistat.com"
+ "url": "https://roistat.com",
+ "companyId": "roistat"
},
"rollad": {
"name": "Rollad",
"categoryId": 4,
- "url": "http://rollad.ru"
+ "url": "http://rollad.ru",
+ "companyId": "rollad"
},
"rollbar": {
"name": "Rollbar",
"categoryId": 6,
- "url": "http://www.rollbar.com/"
+ "url": "http://www.rollbar.com/",
+ "companyId": "rollbar"
},
"roost": {
"name": "Roost",
"categoryId": 6,
- "url": "http://roost.me/"
+ "url": "http://roost.me/",
+ "companyId": "roost"
},
"rooster": {
"name": "Rooster",
"categoryId": 6,
- "url": "http://www.getrooster.com/"
+ "url": "http://www.getrooster.com/",
+ "companyId": "rooster"
},
"roq.ad": {
"name": "Roq.ad",
"categoryId": 4,
- "url": "https://www.roq.ad/"
+ "url": "https://www.roq.ad/",
+ "companyId": "roq.ad"
},
"rotaban": {
"name": "RotaBan",
"categoryId": 4,
- "url": "http://www.rotaban.ru/"
+ "url": "http://www.rotaban.ru/",
+ "companyId": "rotaban"
},
"routenplaner-karten.com": {
"name": "Routenplaner Karten",
"categoryId": 2,
- "url": "https://www.routenplaner-karten.com/"
+ "url": "https://www.routenplaner-karten.com/",
+ "companyId": null
},
"rovion": {
"name": "Rovion",
"categoryId": 4,
- "url": "http://www.rovion.com/"
+ "url": "http://www.rovion.com/",
+ "companyId": "rovion"
},
"rsspump": {
"name": "RSSPump",
"categoryId": 2,
- "url": "http://www.rsspump.com"
+ "url": "http://www.rsspump.com",
+ "companyId": "rsspump"
},
"rtb_house": {
"name": "RTB House",
"categoryId": 4,
- "url": "http://en.adpilot.com/"
+ "url": "http://en.adpilot.com/",
+ "companyId": "rtb_house"
},
"rtblab": {
"name": "RTBmarkt",
"categoryId": 4,
- "url": "http://www.rtbmarkt.de/en/home/"
+ "url": "http://www.rtbmarkt.de/en/home/",
+ "companyId": "rtbmarkt"
},
"rtbsuperhub.com": {
"name": "rtbsuperhub.com",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"rtl_group": {
"name": "RTL Group",
"categoryId": 8,
- "url": "http://www.rtlgroup.com/www/htm/home.aspx"
+ "url": "http://www.rtlgroup.com/www/htm/home.aspx",
+ "companyId": "rtl_group"
},
"rtmark.net": {
"name": "Advertising Technologies Ltd",
"categoryId": 4,
- "url": "http://rtmark.net/"
+ "url": "http://rtmark.net/",
+ "companyId": "big_wall_vision"
},
"rubicon": {
"name": "Rubicon",
"categoryId": 4,
- "url": "http://rubiconproject.com/"
+ "url": "http://rubiconproject.com/",
+ "companyId": "rubicon_project"
},
"ruhrgebiet": {
"name": "Ruhrgebiet",
"categoryId": 4,
- "url": "https://www.ruhrgebiet-onlineservices.de/"
+ "url": "https://www.ruhrgebiet-onlineservices.de/",
+ "companyId": "ruhrgebiet"
},
"rummycircle": {
"name": "RummyCircle",
"categoryId": 4,
- "url": "https://www.rummycircle.com/"
+ "url": "https://www.rummycircle.com/",
+ "companyId": "rummycircle"
},
"run": {
"name": "RUN",
"categoryId": 4,
- "url": "http://www.rundsp.com/"
+ "url": "http://www.rundsp.com/",
+ "companyId": "run"
},
"runative": {
"name": "Runative",
"categoryId": 4,
- "url": "https://runative.com/"
+ "url": "https://runative.com/",
+ "companyId": null
},
"rune": {
"name": "Rune",
"categoryId": 6,
- "url": "http://www.secretrune.com/"
+ "url": "http://www.secretrune.com/",
+ "companyId": "rune_inc."
},
"runmewivel.com": {
"name": "runmewivel.com",
"categoryId": 10,
- "url": null
+ "url": null,
+ "companyId": null
},
"rythmxchange": {
"name": "Rythmxchange",
"categoryId": 0,
- "url": "https://www.rhythmone.com/"
+ "url": "https://www.rhythmone.com/",
+ "companyId": "rythmone"
},
"s24_com": {
"name": "Shopping24 internet group",
"categoryId": 4,
- "url": "https://www.s24.com/"
+ "url": "https://www.s24.com/",
+ "companyId": null
},
"s3xified.com": {
"name": "s3xified.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"sabavision": {
"name": "SabaVision",
"categoryId": 4,
- "url": "http://www.sabavision.com/en/"
+ "url": "http://www.sabavision.com/en/",
+ "companyId": "sabavision"
},
"sagemetrics": {
"name": "SageMetrics",
"categoryId": 4,
- "url": "http://www.sagemetrics.com"
+ "url": "http://www.sagemetrics.com",
+ "companyId": "ipmg"
},
"sailthru_horizon": {
"name": "Sailthru Horizon",
"categoryId": 4,
- "url": "https://www.sailthru.com"
+ "url": "https://www.sailthru.com",
+ "companyId": "sailthru"
},
"salecycle": {
"name": "SaleCycle",
"categoryId": 4,
- "url": "http://www.salecycle.com/"
+ "url": "http://www.salecycle.com/",
+ "companyId": "salecycle"
},
"sales_feed": {
"name": "Sales Feed",
"categoryId": 4,
- "url": "https://www.salesfeed.com/"
+ "url": "https://www.salesfeed.com/",
+ "companyId": "sales_feed"
},
"sales_manago": {
"name": "SALESmanago",
"categoryId": 6,
- "url": "https://www.salesmanago.com/"
+ "url": "https://www.salesmanago.com/",
+ "companyId": "sales_manago"
},
"salesforce.com": {
"name": "Salesforce",
"categoryId": 4,
- "url": "https://www.salesforce.com/eu/"
+ "url": "https://www.salesforce.com/eu/",
+ "companyId": "salesforce"
},
"salesforce_live_agent": {
"name": "Salesforce Live Agent",
"categoryId": 2,
- "url": "http://www.salesforce.com/"
+ "url": "http://www.salesforce.com/",
+ "companyId": "salesforce"
},
"salesfusion": {
"name": "SalesFUSION",
"categoryId": 4,
- "url": "http://salesfusion.com/"
+ "url": "http://salesfusion.com/",
+ "companyId": "salesfusion"
},
"salespider_media": {
"name": "SaleSpider Media",
"categoryId": 4,
- "url": "http://salespidermedia.com/"
+ "url": "http://salespidermedia.com/",
+ "companyId": "salespider_media"
},
"salesviewer": {
"name": "SalesViewer",
"categoryId": 6,
- "url": "https://www.salesviewer.com/"
+ "url": "https://www.salesviewer.com/",
+ "companyId": "salesviewer"
},
"samba.tv": {
"name": "Samba TV",
"categoryId": 4,
- "url": "https://samba.tv/"
+ "url": "https://samba.tv/",
+ "companyId": "samba_tv"
},
"sanoma.fi": {
"name": "Sanoma",
"categoryId": 4,
- "url": "https://sanoma.com/"
+ "url": "https://sanoma.com/",
+ "companyId": "sanoma"
},
"sap_crm": {
"name": "SAP CRM",
"categoryId": 6,
- "url": "https://www.sap.com/products/crm.html"
+ "url": "https://www.sap.com/products/crm.html",
+ "companyId": "sap"
},
"sap_sales_cloud": {
"name": "SAP Sales Cloud",
"categoryId": 2,
- "url": "http://leadforce1.com/"
+ "url": "http://leadforce1.com/",
+ "companyId": "sap"
},
"sap_xm": {
"name": "SAP Exchange Media",
"categoryId": 4,
- "url": "http://sapexchange.media/"
+ "url": "http://sapexchange.media/",
+ "companyId": null
},
"sape.ru": {
"name": "Sape",
"categoryId": 6,
- "url": "https://www.sape.ru/en"
+ "url": "https://www.sape.ru/en",
+ "companyId": "sape"
},
"sapo_ads": {
"name": "SAPO Ads",
"categoryId": 4,
- "url": "http://www.sapo.pt/"
+ "url": "http://www.sapo.pt/",
+ "companyId": "sapo"
},
"sas": {
"name": "SAS",
"categoryId": 6,
- "url": "http://www.sas.com/"
+ "url": "http://www.sas.com/",
+ "companyId": "sas"
},
"say.ac": {
"name": "Say.ac",
"categoryId": 4,
- "url": "http://say.ac"
+ "url": "http://say.ac",
+ "companyId": "say.ac"
},
"say_media": {
"name": "Say Media",
"categoryId": 4,
- "url": "http://www.saymedia.com/"
+ "url": "http://www.saymedia.com/",
+ "companyId": "say_media"
},
"sayyac": {
"name": "Sayyac",
"categoryId": 6,
- "url": "http://www.sayyac.com/"
+ "url": "http://www.sayyac.com/",
+ "companyId": "sayyac"
},
"scarabresearch": {
"name": "Scarab Research",
"categoryId": 4,
- "url": "https://www.scarabresearch.com/"
+ "url": "https://www.scarabresearch.com/",
+ "companyId": "emarsys"
},
"schibsted": {
"name": "Schibsted Media Group",
"categoryId": 8,
- "url": "http://www.schibsted.com/"
+ "url": "http://www.schibsted.com/",
+ "companyId": "schibsted_asa"
},
"schneevonmorgen.com": {
"name": "Schnee von Morgen",
"categoryId": 0,
- "url": "http://www.schneevonmorgen.com/"
+ "url": "http://www.schneevonmorgen.com/",
+ "companyId": null
},
"scoota": {
"name": "Scoota",
"categoryId": 4,
- "url": "http://scoota.com/"
+ "url": "http://scoota.com/",
+ "companyId": "rockabox"
},
"scorecard_research_beacon": {
"name": "ScoreCard Research Beacon",
"categoryId": 6,
- "url": "https://www.scorecardresearch.com/"
+ "url": "https://www.scorecardresearch.com/",
+ "companyId": "comscore"
},
"scout_analytics": {
"name": "Scout Analytics",
"categoryId": 4,
- "url": "http://scoutanalytics.com/"
+ "url": "http://scoutanalytics.com/",
+ "companyId": "scout_analytics"
},
"scribblelive": {
"name": "ScribbleLive",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"scribol": {
"name": "Scribol",
"categoryId": 4,
- "url": "http://scribol.com/"
+ "url": "http://scribol.com/",
+ "companyId": "scribol"
},
"scripps_analytics": {
"name": "Scripps Analytics",
"categoryId": 6,
- "url": "http://www.scrippsnetworksinteractive.com/"
+ "url": "http://www.scrippsnetworksinteractive.com/",
+ "companyId": "scripps_networks"
},
"scroll": {
"name": "Scroll",
"categoryId": 5,
- "url": "https://scroll.com/"
+ "url": "https://scroll.com/",
+ "companyId": "scroll"
},
"scupio": {
"name": "Scupio",
"categoryId": 4,
- "url": "http://ad.scupio.com/"
+ "url": "http://ad.scupio.com/",
+ "companyId": "bridgewell"
},
"search123": {
"name": "Search123",
"categoryId": 4,
- "url": "http://www.search123.com/"
+ "url": "http://www.search123.com/",
+ "companyId": "search123"
},
"searchforce": {
"name": "SearchForce",
"categoryId": 4,
- "url": "http://www.searchforce.com/"
+ "url": "http://www.searchforce.com/",
+ "companyId": "searchforce"
},
"searchignite": {
"name": "SearchIgnite",
"categoryId": 4,
- "url": "https://searchignite.com/"
+ "url": "https://searchignite.com/",
+ "companyId": "zeta"
},
"searchrev": {
"name": "SearchRev",
"categoryId": 4,
- "url": "http://www.searchrev.com/"
+ "url": "http://www.searchrev.com/",
+ "companyId": "searchrev"
},
"second_media": {
"name": "Second Media",
"categoryId": 4,
- "url": "http://www.secondmedia.com/"
+ "url": "http://www.secondmedia.com/",
+ "companyId": "second_media"
},
"securedtouch": {
"name": "SecuredTouch",
"categoryId": 6,
- "url": "https://www.securedtouch.com/"
+ "url": "https://www.securedtouch.com/",
+ "companyId": null
},
"securedvisit": {
"name": "SecuredVisit",
"categoryId": 4,
- "url": "http://securedvisit.com/"
+ "url": "http://securedvisit.com/",
+ "companyId": "securedvisit"
},
"seeding_alliance": {
"name": "Seeding Alliance",
"categoryId": 4,
- "url": "http://seeding-alliance.de"
+ "url": "http://seeding-alliance.de",
+ "companyId": "stroer"
},
"seedtag.com": {
"name": "Seedtag",
"categoryId": 4,
- "url": "https://www.seedtag.com/en/"
+ "url": "https://www.seedtag.com/en/",
+ "companyId": "seedtag"
},
"seevolution": {
"name": "SeeVolution",
"categoryId": 6,
- "url": "http://www.seevolution.com"
+ "url": "http://www.seevolution.com",
+ "companyId": "seevolution"
},
"segment": {
"name": "Segment",
"categoryId": 6,
- "url": "https://segment.io/"
+ "url": "https://segment.io/",
+ "companyId": "segment"
},
"segmento": {
"name": "Segmento",
"categoryId": 4,
- "url": "https://segmento.ru/en"
+ "url": "https://segmento.ru/en",
+ "companyId": "segmento"
},
"segmint": {
"name": "Segmint",
"categoryId": 6,
- "url": "http://www.segmint.com/"
+ "url": "http://www.segmint.com/",
+ "companyId": "segmint"
},
"sekindo": {
"name": "Sekindo",
"categoryId": 4,
- "url": "http://www.sekindo.com/"
+ "url": "http://www.sekindo.com/",
+ "companyId": "sekindo"
},
"sellpoints": {
"name": "Sellpoints",
"categoryId": 4,
- "url": "https://www.sellpoints.com/"
+ "url": "https://www.sellpoints.com/",
+ "companyId": "sellpoints"
},
"semantiqo.com": {
"name": "Semantiqo",
"categoryId": 4,
- "url": "https://semantiqo.com/"
+ "url": "https://semantiqo.com/",
+ "companyId": null
},
"semasio": {
"name": "Semasio",
"categoryId": 4,
- "url": "http://semasio.com/"
+ "url": "http://semasio.com/",
+ "companyId": "semasio"
},
"semilo": {
"name": "Semilo",
"categoryId": 4,
- "url": "http://www.semilo.nl/"
+ "url": "http://www.semilo.nl/",
+ "companyId": "semilo"
},
"semknox.com": {
"name": "SEMKNOX GmbH",
"categoryId": 5,
- "url": "https://semknox.com/"
+ "url": "https://semknox.com/",
+ "companyId": null
},
"sendinblue": {
"name": "sendinblue",
"categoryId": 4,
- "url": "https://fr.sendinblue.com/"
+ "url": "https://fr.sendinblue.com/",
+ "companyId": "sendinblue"
},
"sendpulse.com": {
"name": "SendPulse",
"categoryId": 3,
- "url": "https://sendpulse.com/"
+ "url": "https://sendpulse.com/",
+ "companyId": null
},
"sendsay": {
"name": "Sendsay",
"categoryId": 2,
- "url": "https://sendsay.ru"
+ "url": "https://sendsay.ru",
+ "companyId": "sendsay"
},
"sense_digital": {
"name": "Sense Digital",
"categoryId": 6,
- "url": "http://sensedigital.in/"
+ "url": "http://sensedigital.in/",
+ "companyId": "sense_digital"
},
"sensors_data": {
"name": "Sensors Data",
"categoryId": 6,
- "url": "https://www.sensorsdata.cn/"
+ "url": "https://www.sensorsdata.cn/",
+ "companyId": "sensors_data"
},
"sentifi.com": {
"name": "Sentifi",
"categoryId": 6,
- "url": "https://sentifi.com/"
+ "url": "https://sentifi.com/",
+ "companyId": "sentifi"
},
"sentry": {
"name": "Sentry",
"categoryId": 6,
- "url": "https://sentry.io/"
+ "url": "https://sentry.io/",
+ "companyId": "sentry"
},
"sepyra": {
"name": "Sepyra",
"categoryId": 4,
- "url": "http://sepyra.com/"
+ "url": "http://sepyra.com/",
+ "companyId": "sepyra"
},
"sessioncam": {
"name": "SessionCam",
"categoryId": 6,
- "url": "http://www.sessioncam.com/"
+ "url": "http://www.sessioncam.com/",
+ "companyId": "sessioncam"
},
"sessionly": {
"name": "Sessionly",
"categoryId": 2,
- "url": "https://www.sessionly.io/"
+ "url": "https://www.sessionly.io/",
+ "companyId": "sessionly"
},
"sevenone_media": {
"name": "SevenOne Media",
"categoryId": 4,
- "url": null
+ "url": null,
+ "companyId": null
},
"sexadnetwork": {
"name": "SexAdNetwork",
"categoryId": 3,
- "url": "http://www.sexadnetwork.com/"
+ "url": "http://www.sexadnetwork.com/",
+ "companyId": "sexadnetwork"
},
"sexinyourcity": {
"name": "SexInYourCity",
"categoryId": 3,
- "url": "http://www.sexinyourcity.com/"
+ "url": "http://www.sexinyourcity.com/",
+ "companyId": "sexinyourcity"
},
"sextracker": {
"name": "SexTracker",
"categoryId": 3,
- "url": "http://webmasters.sextracker.com/"
+ "url": "http://webmasters.sextracker.com/",
+ "companyId": "sextracker"
},
"sexypartners.net": {
"name": "sexypartners.net",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"seznam": {
"name": "Seznam",
"categoryId": 6,
- "url": "https://onas.seznam.cz/cz/"
+ "url": "https://onas.seznam.cz/cz/",
+ "companyId": "seznam"
},
"shareaholic": {
"name": "Shareaholic",
"categoryId": 6,
- "url": "hhttps://www.shareaholic.com/"
+ "url": "hhttps://www.shareaholic.com/",
+ "companyId": "shareaholic"
},
"shareasale": {
"name": "ShareASale",
"categoryId": 4,
- "url": "http://www.shareasale.com/"
+ "url": "http://www.shareasale.com/",
+ "companyId": "shareasale"
},
"sharecompany": {
"name": "ShareCompany",
"categoryId": 2,
- "url": "http://sharecompany.nl"
+ "url": "http://sharecompany.nl",
+ "companyId": "sharecompany"
},
"sharepoint": {
"name": "Microsoft SharePoint",
"categoryId": 2,
- "url": "https://products.office.com/en-us/sharepoint/sharepoint-online-collaboration-software"
+ "url": "https://products.office.com/en-us/sharepoint/sharepoint-online-collaboration-software",
+ "companyId": "microsoft"
},
"sharethis": {
"name": "ShareThis",
"categoryId": 4,
- "url": "http://sharethis.com/"
+ "url": "http://sharethis.com/",
+ "companyId": "sharethis"
},
"sharethrough": {
"name": "ShareThrough",
"categoryId": 4,
- "url": "http://www.sharethrough.com/"
+ "url": "http://www.sharethrough.com/",
+ "companyId": "sharethrough"
},
"sharpspring": {
"name": "Sharpspring",
"categoryId": 6,
- "url": "https://sharpspring.com/"
+ "url": "https://sharpspring.com/",
+ "companyId": "sharpspring"
},
"sheego.de": {
"name": "sheego.de",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"sheerid": {
"name": "SheerID",
"categoryId": 4,
- "url": "http://www.sheerid.com/"
+ "url": "http://www.sheerid.com/",
+ "companyId": "sheerid"
},
"shinystat": {
"name": "ShinyStat",
"categoryId": 6,
- "url": "http://www.shinystat.com/"
+ "url": "http://www.shinystat.com/",
+ "companyId": "shinystat"
},
"shop_target": {
"name": "Shop Target",
"categoryId": 4,
- "url": "http://shoptarget.com.br/"
+ "url": "http://shoptarget.com.br/",
+ "companyId": "shopback"
},
"shopauskunft.de": {
"name": "ShopAuskunft.de",
"categoryId": 2,
- "url": "https://shopauskunft.de/"
+ "url": "https://shopauskunft.de/",
+ "companyId": null
},
"shopgate.com": {
"name": "Shopgate",
"categoryId": 2,
- "url": "https://www.shopgate.com/"
+ "url": "https://www.shopgate.com/",
+ "companyId": null
},
"shopify_stats": {
"name": "Shopify Stats",
"categoryId": 4,
- "url": "http://www.shopify.com/"
+ "url": "http://www.shopify.com/",
+ "companyId": "shopify"
},
"shopifycdn.com": {
"name": "Shopify CDN",
"categoryId": 9,
- "url": "https://www.shopify.com/"
+ "url": "https://www.shopify.com/",
+ "companyId": "shopify"
},
"shopifycloud.com": {
"name": "Shopify Cloud",
"categoryId": 2,
- "url": "https://www.shopify.com/"
+ "url": "https://www.shopify.com/",
+ "companyId": "shopify"
},
"shopper_approved": {
"name": "Shopper Approved",
"categoryId": 2,
- "url": "http://www.shopperapproved.com"
+ "url": "http://www.shopperapproved.com",
+ "companyId": "shopper_approved"
},
"shopping_com": {
"name": "Shopping.com",
"categoryId": 4,
- "url": "https://partnernetwork.ebay.com/"
+ "url": "https://partnernetwork.ebay.com/",
+ "companyId": "ebay_partner_network"
},
"shopping_flux": {
"name": "Shopping Flux",
"categoryId": 6,
- "url": "http://www.shopping-flux.com/"
+ "url": "http://www.shopping-flux.com/",
+ "companyId": "shopping_flux"
},
"shoprunner": {
"name": "ShopRunner",
"categoryId": 2,
- "url": "https://www.shoprunner.com"
+ "url": "https://www.shoprunner.com",
+ "companyId": "shoprunner"
},
"shopsocially": {
"name": "ShopSocially",
"categoryId": 2,
- "url": "http://shopsocially.com/"
+ "url": "http://shopsocially.com/",
+ "companyId": "shopsocially"
},
"shopzilla": {
"name": "Shopzilla",
"categoryId": 4,
- "url": "http://www.shopzilla.com/"
+ "url": "http://www.shopzilla.com/",
+ "companyId": "shopzilla"
},
"shortnews": {
"name": "ShortNews.de",
"categoryId": 8,
- "url": "http://www.shortnews.de/#"
+ "url": "http://www.shortnews.de/#",
+ "companyId": null
},
"shrink": {
"name": "Shrink",
"categoryId": 2,
- "url": "http://shink.in/"
+ "url": "http://shink.in/",
+ "companyId": "shrink.in"
},
"shutterstock": {
"name": "Shutterstock",
"categoryId": 8,
- "url": "https://www.shutterstock.com/"
+ "url": "https://www.shutterstock.com/",
+ "companyId": "shutterstock_inc"
},
"siblesectiveal.club": {
"name": "siblesectiveal.club",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"sidecar": {
"name": "Sidecar",
"categoryId": 6,
- "url": "http://hello.getsidecar.com/"
+ "url": "http://hello.getsidecar.com/",
+ "companyId": "sidecar"
},
"sift_science": {
"name": "Sift Science",
"categoryId": 6,
- "url": "https://siftscience.com/"
+ "url": "https://siftscience.com/",
+ "companyId": "sift_science"
},
"signal": {
"name": "Signal",
"categoryId": 5,
- "url": "https://www.signal.co/"
+ "url": "https://www.signal.co/",
+ "companyId": "signal_digital"
},
"signifyd": {
"name": "Signifyd",
"categoryId": 6,
- "url": "https://www.signifyd.com/"
+ "url": "https://www.signifyd.com/",
+ "companyId": "signifyd"
},
"silverpop": {
"name": "Silverpop",
"categoryId": 2,
- "url": "http://www.silverpop.com/"
+ "url": "http://www.silverpop.com/",
+ "companyId": "ibm"
},
"similardeals.net": {
"name": "SimilarDeals",
"categoryId": 8,
- "url": "http://www.similardeals.net/"
+ "url": "http://www.similardeals.net/",
+ "companyId": null
},
"simplereach": {
"name": "SimpleReach",
"categoryId": 6,
- "url": "https://www.nativo.com/simplereach"
+ "url": "https://www.nativo.com/simplereach",
+ "companyId": "nativo"
},
"simpli.fi": {
"name": "Simpli.fi",
"categoryId": 4,
- "url": "http://www.simpli.fi"
+ "url": "http://www.simpli.fi",
+ "companyId": "simpli.fi"
},
"sina": {
"name": "Sina",
"categoryId": 6,
- "url": "http://www.sina.com/"
+ "url": "http://www.sina.com/",
+ "companyId": "sina"
},
"sina_cdn": {
"name": "Sina CDN",
"categoryId": 9,
- "url": "https://www.sina.com.cn/"
+ "url": "https://www.sina.com.cn/",
+ "companyId": "sina"
},
"singlefeed": {
"name": "SingleFeed",
"categoryId": 4,
- "url": "https://www.singlefeed.com/"
+ "url": "https://www.singlefeed.com/",
+ "companyId": "singlefeed"
},
"sirdata": {
"name": "Sirdata",
"categoryId": 6,
- "url": "http://www.sirdata.com/home/"
+ "url": "http://www.sirdata.com/home/",
+ "companyId": "sirdata"
},
"site24x7": {
"name": "Site24x7",
"categoryId": 6,
- "url": "https://www.site24x7.com/"
+ "url": "https://www.site24x7.com/",
+ "companyId": "zoho_corp"
},
"site_booster": {
"name": "Site Booster",
"categoryId": 7,
- "url": "https://sitebooster.com/"
+ "url": "https://sitebooster.com/",
+ "companyId": "site_booster"
},
"site_stratos": {
"name": "Site Stratos",
"categoryId": 4,
- "url": "http://www.infocube.co.jp/"
+ "url": "http://www.infocube.co.jp/",
+ "companyId": "infocube"
},
"siteapps": {
"name": "SiteApps",
"categoryId": 2,
- "url": "http://siteapps.com"
+ "url": "http://siteapps.com",
+ "companyId": "siteapps"
},
"sitebro": {
"name": "SiteBro",
"categoryId": 6,
- "url": "http://www.sitebro.net/"
+ "url": "http://www.sitebro.net/",
+ "companyId": "sitebro"
},
"siteheart": {
"name": "SiteHeart",
"categoryId": 2,
- "url": "http://siteheart.com/"
+ "url": "http://siteheart.com/",
+ "companyId": "siteheart"
},
"siteimprove": {
"name": "Siteimprove",
"categoryId": 6,
- "url": "http://siteimprove.com"
+ "url": "http://siteimprove.com",
+ "companyId": "siteimprove"
},
"siteimprove_analytics": {
"name": "SiteImprove Analytics",
"categoryId": 6,
- "url": "http://siteimprove.com"
+ "url": "http://siteimprove.com",
+ "companyId": "siteimprove"
},
"sitelabweb.com": {
"name": "sitelabweb.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"sitemeter": {
"name": "SiteMeter",
"categoryId": 6,
- "url": "http://www.sitemeter.com/"
+ "url": "http://www.sitemeter.com/",
+ "companyId": "sitemeter,_inc."
},
"sitescout": {
"name": "SiteScout by Centro",
"categoryId": 4,
- "url": "http://www.sitescout.com"
+ "url": "http://www.sitescout.com",
+ "companyId": "centro"
},
"sitetag": {
"name": "SiteTag",
"categoryId": 2,
- "url": "http://www.sitetag.us/"
+ "url": "http://www.sitetag.us/",
+ "companyId": "sitetag"
},
"sitewit": {
"name": "SiteWit",
"categoryId": 4,
- "url": "http://www.sitewit.com/"
+ "url": "http://www.sitewit.com/",
+ "companyId": "sitewit"
},
"six_apart_advertising": {
"name": "Six Apart Advertising",
"categoryId": 4,
- "url": "http://www.sixapart.com/advertising/"
+ "url": "http://www.sixapart.com/advertising/",
+ "companyId": "six_apart"
},
"sixt-neuwagen.de": {
"name": "sixt-neuwagen.de",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"skadtec.com": {
"name": "GP One GmbH",
"categoryId": 6,
- "url": "http://www.gp-one.com/"
+ "url": "http://www.gp-one.com/",
+ "companyId": null
},
"skimlinks": {
"name": "SkimLinks",
"categoryId": 4,
- "url": "http://www.skimlinks.com/"
+ "url": "http://www.skimlinks.com/",
+ "companyId": "skimlinks"
},
"skroutz": {
"name": "Skroutz",
"categoryId": 6,
- "url": "https://www.skroutz.gr/"
+ "url": "https://www.skroutz.gr/",
+ "companyId": "skroutz"
},
"skyglue": {
"name": "SkyGlue",
"categoryId": 6,
- "url": "http://www.skyglue.com/"
+ "url": "http://www.skyglue.com/",
+ "companyId": "skyglue_technology"
},
"skype": {
"name": "Skype",
"categoryId": 2,
- "url": "http://www.skype.com"
+ "url": "http://www.skype.com",
+ "companyId": "microsoft"
},
"skysa": {
"name": "Skysa",
"categoryId": 2,
- "url": "http://www.skysa.com/"
+ "url": "http://www.skysa.com/",
+ "companyId": "skysa"
},
"skyscnr.com": {
"name": "Skyscanner CDN",
"categoryId": 9,
- "url": "https://www.skyscanner.net/"
+ "url": "https://www.skyscanner.net/",
+ "companyId": null
},
"slashdot_widget": {
"name": "Slashdot Widget",
"categoryId": 2,
- "url": "http://slashdot.org"
+ "url": "http://slashdot.org",
+ "companyId": "slashdot"
},
"sleeknote": {
"name": "Sleeknote",
"categoryId": 2,
- "url": "https://sleeknote.com/"
+ "url": "https://sleeknote.com/",
+ "companyId": "sleeknote"
},
"sli_systems": {
"name": "SLI Systems",
"categoryId": 2,
- "url": "http://www.sli-systems.com"
+ "url": "http://www.sli-systems.com",
+ "companyId": "sli_systems"
},
"slice_factory": {
"name": "Slice Factory",
"categoryId": 2,
- "url": "http://www.slicefactory.com/"
+ "url": "http://www.slicefactory.com/",
+ "companyId": "slice_factory"
},
"slimcutmedia": {
"name": "SlimCutMedia",
"categoryId": 6,
- "url": "http://www.slimcutmedia.com/"
+ "url": "http://www.slimcutmedia.com/",
+ "companyId": "slimcutmedia"
},
"slingpic": {
"name": "Slingpic",
"categoryId": 4,
- "url": "http://slingpic.com/"
+ "url": "http://slingpic.com/",
+ "companyId": "affectv"
},
"smaato": {
"name": "Smaato",
"categoryId": 4,
- "url": "http://www.smaato.com/"
+ "url": "http://www.smaato.com/",
+ "companyId": "smaato"
},
"smart4ads": {
"name": "smart4ads",
"categoryId": 4,
- "url": "http://www.smart4ads.com"
+ "url": "http://www.smart4ads.com",
+ "companyId": "smart4ads"
},
"smart_adserver": {
"name": "SMART AdServer",
"categoryId": 4,
- "url": "https://smartadserver.com/"
+ "url": "https://smartadserver.com/",
+ "companyId": "smart_adserver"
},
"smart_call": {
"name": "Smart Call",
"categoryId": 2,
- "url": "https://smartcall.kz/"
+ "url": "https://smartcall.kz/",
+ "companyId": "smart_call"
},
"smart_content": {
"name": "Smart Content",
"categoryId": 4,
- "url": "http://www.getsmartcontent.com"
+ "url": "http://www.getsmartcontent.com",
+ "companyId": "get_smart_content"
},
"smart_device_media": {
"name": "Smart Device Media",
"categoryId": 4,
- "url": "http://www.smartdevicemedia.com/"
+ "url": "http://www.smartdevicemedia.com/",
+ "companyId": "smart_device_media"
},
"smart_leads": {
"name": "Smart Leads",
"categoryId": 4,
- "url": "http://www.cnt.my/"
+ "url": "http://www.cnt.my/",
+ "companyId": "smart_leads"
},
"smart_selling": {
"name": "Smart Selling",
"categoryId": 2,
- "url": "https://smartselling.cz/"
+ "url": "https://smartselling.cz/",
+ "companyId": "smart_selling"
},
"smartad": {
"name": "smartAD",
"categoryId": 4,
- "url": "http://smartad.eu/"
+ "url": "http://smartad.eu/",
+ "companyId": "smartad"
},
"smartbn": {
"name": "SmartBN",
"categoryId": 4,
- "url": "http://smartbn.ru/"
+ "url": "http://smartbn.ru/",
+ "companyId": "smartbn"
},
"smartclick.net": {
"name": "SmartClick",
"categoryId": 4,
- "url": "http://smartclick.net/"
+ "url": "http://smartclick.net/",
+ "companyId": null
},
"smartclip": {
"name": "SmartClip",
"categoryId": 4,
- "url": "http://www.smartclip.com/"
+ "url": "http://www.smartclip.com/",
+ "companyId": "smartclip"
},
"smartcontext": {
"name": "SmartContext",
"categoryId": 4,
- "url": "http://smartcontext.pl/"
+ "url": "http://smartcontext.pl/",
+ "companyId": "smartcontext"
},
"smarter_remarketer": {
"name": "SmarterHQ",
"categoryId": 4,
- "url": "https://smarterhq.com"
+ "url": "https://smarterhq.com",
+ "companyId": "smarterhq"
},
"smarter_travel": {
"name": "Smarter Travel Media",
"categoryId": 4,
- "url": "https://www.smartertravel.com/"
+ "url": "https://www.smartertravel.com/",
+ "companyId": "iac_apps"
},
"smarterclick": {
"name": "Smarterclick",
"categoryId": 4,
- "url": "http://www.smarterclick.co.uk/"
+ "url": "http://www.smarterclick.co.uk/",
+ "companyId": "smarter_click"
},
"smartertrack": {
"name": "SmarterTrack",
"categoryId": 4,
- "url": "http://www.smartertrack.com/"
+ "url": "http://www.smartertrack.com/",
+ "companyId": "smartertrack"
},
"smartlink.cool": {
"name": "smartlink.cool",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"smartlook": {
"name": "Smartlook",
"categoryId": 2,
- "url": "https://www.smartlook.com/"
+ "url": "https://www.smartlook.com/",
+ "companyId": "smartlook"
},
"smartstream.tv": {
"name": "SmartStream.TV",
"categoryId": 4,
- "url": "https://www.smartstream.tv/en"
+ "url": "https://www.smartstream.tv/en",
+ "companyId": "smartstream"
},
"smartsupp_chat": {
"name": "Smartsupp Chat",
"categoryId": 2,
- "url": "https://www.smartsupp.com/"
+ "url": "https://www.smartsupp.com/",
+ "companyId": "smartsuppp"
},
"smi2.ru": {
"name": "smi2.ru",
"categoryId": 6,
- "url": "https://smi2.net/"
+ "url": "https://smi2.net/",
+ "companyId": "media2_stat.media"
},
"smooch": {
"name": "Smooch",
"categoryId": 2,
- "url": "https://smooch.io/"
+ "url": "https://smooch.io/",
+ "companyId": "smooch"
},
"smowtion": {
"name": "Smowtion",
"categoryId": 4,
- "url": "http://www.smowtion.com/"
+ "url": "http://www.smowtion.com/",
+ "companyId": "smowtion"
},
"smx_ventures": {
"name": "SMX Ventures",
"categoryId": 6,
- "url": "http://smxeventures.com/"
+ "url": "http://smxeventures.com/",
+ "companyId": "smx_ventures"
},
"smyte": {
"name": "Smyte",
"categoryId": 6,
- "url": "https://www.smyte.com/"
+ "url": "https://www.smyte.com/",
+ "companyId": "smyte"
},
"snacktv": {
"name": "SnackTV",
"categoryId": 6,
- "url": "https://www.verizon.com/"
+ "url": "https://www.verizon.com/",
+ "companyId": "verizon"
},
"snacktv_player": {
"name": "SnackTV-Player",
"categoryId": 0,
- "url": "https://www.verizon.com/"
+ "url": "https://www.verizon.com/",
+ "companyId": "verizon"
},
"snap": {
"name": "Snap",
"categoryId": 2,
- "url": "http://www.snap.com/"
+ "url": "http://www.snap.com/",
+ "companyId": "snap_technologies"
},
"snap_engage": {
"name": "Snap Engage",
"categoryId": 2,
- "url": "https://snapengage.com/"
+ "url": "https://snapengage.com/",
+ "companyId": "snap_engage"
},
"snapchat": {
"name": "Snapchat For Business",
"categoryId": 4,
- "url": "https://www.snapchat.com/"
+ "url": "https://www.snapchat.com/",
+ "companyId": "snap_technologies"
},
"snigelweb": {
"name": "SnigelWeb, Inc.",
"categoryId": 4,
- "url": "http://www.snigelweb.com/"
+ "url": "http://www.snigelweb.com/",
+ "companyId": "snigelweb_inc"
},
"snoobi": {
"name": "Snoobi",
"categoryId": 6,
- "url": "http://www.snoobi.eu/"
+ "url": "http://www.snoobi.eu/",
+ "companyId": "snoobi"
},
"snoobi_analytics": {
"name": "Snoobi Analytics",
"categoryId": 6,
- "url": "http://www.snoobi.com/"
+ "url": "http://www.snoobi.com/",
+ "companyId": "snoobi_oy"
},
"snowplow": {
"name": "Snowplow",
"categoryId": 6,
- "url": "http://snowplowanalytics.com/"
+ "url": "http://snowplowanalytics.com/",
+ "companyId": "snowplow"
},
"soasta_mpulse": {
"name": "SOASTA mPulse",
"categoryId": 6,
- "url": "http://www.soasta.com/"
+ "url": "http://www.soasta.com/",
+ "companyId": "akamai"
},
"sociable_labs": {
"name": "Sociable Labs",
"categoryId": 4,
- "url": "http://www.sociablelabs.com/"
+ "url": "http://www.sociablelabs.com/",
+ "companyId": "sociable_labs"
},
"social_amp": {
"name": "Social Amp",
"categoryId": 4,
- "url": "http://www.merkleinc.com/"
+ "url": "http://www.merkleinc.com/",
+ "companyId": "dentsu_aegis_network"
},
"social_annex": {
"name": "Social Annex",
"categoryId": 4,
- "url": "http://www.socialannex.com"
+ "url": "http://www.socialannex.com",
+ "companyId": "social_annex"
},
"social_miner": {
"name": "Social Miner",
"categoryId": 7,
- "url": "https://socialminer.com/"
+ "url": "https://socialminer.com/",
+ "companyId": "social_miner"
},
"socialbeat": {
"name": "socialbeat",
"categoryId": 4,
- "url": "http://www.socialbeat.it/"
+ "url": "http://www.socialbeat.it/",
+ "companyId": "socialbeat"
},
"socialrms": {
"name": "SocialRMS",
"categoryId": 7,
- "url": "http://socialinterface.com/socialrms/"
+ "url": "http://socialinterface.com/socialrms/",
+ "companyId": "socialinterface"
},
"sociaplus.com": {
"name": "SociaPlus",
"categoryId": 6,
- "url": "https://sociaplus.com/"
+ "url": "https://sociaplus.com/",
+ "companyId": null
},
"sociomantic": {
"name": "Sociomantic",
"categoryId": 4,
- "url": "http://www.sociomantic.com/"
+ "url": "http://www.sociomantic.com/",
+ "companyId": "sociomantic_labs_gmbh"
},
"sohu": {
"name": "Sohu",
"categoryId": 7,
- "url": "http://www.sohu.com"
+ "url": "http://www.sohu.com",
+ "companyId": "sohu"
},
"sojern": {
"name": "Sojern",
"categoryId": 4,
- "url": "http://www.sojern.com/"
+ "url": "http://www.sojern.com/",
+ "companyId": "sojern"
},
"sokrati": {
"name": "Sokrati",
"categoryId": 4,
- "url": "http://sokrati.com/"
+ "url": "http://sokrati.com/",
+ "companyId": "sokrati"
},
"solads.media": {
"name": "solads.media",
"categoryId": 4,
- "url": "http://solads.media/"
+ "url": "http://solads.media/",
+ "companyId": null
},
"solidopinion": {
"name": "SolidOpinion",
"categoryId": 2,
- "url": "https://solidopinion.com/"
+ "url": "https://solidopinion.com/",
+ "companyId": "solidopinion"
},
"solve_media": {
"name": "Solve Media",
"categoryId": 4,
- "url": "http://solvemedia.com/"
+ "url": "http://solvemedia.com/",
+ "companyId": "solve_media"
},
"soma_2": {
"name": "SOMA 2",
"categoryId": 4,
- "url": "http://www.webcombi.de/"
+ "url": "http://www.webcombi.de/",
+ "companyId": "soma_2_gmbh"
},
"somoaudience": {
"name": "SoMo Audience",
"categoryId": 4,
- "url": "https://somoaudience.com/"
+ "url": "https://somoaudience.com/",
+ "companyId": "somoaudience"
},
"sonobi": {
"name": "Sonobi",
"categoryId": 4,
- "url": "http://sonobi.com/"
+ "url": "http://sonobi.com/",
+ "companyId": "sonobi"
},
"sophus3": {
"name": "Sophus3",
"categoryId": 4,
- "url": "http://www.sophus3.com/"
+ "url": "http://www.sophus3.com/",
+ "companyId": "sophus3"
},
"sortable": {
"name": "Sortable",
"categoryId": 4,
- "url": "https://sortable.com/"
+ "url": "https://sortable.com/",
+ "companyId": "sortable"
},
"soundcloud": {
"name": "SoundCloud",
"categoryId": 0,
- "url": "http://soundcloud.com/"
+ "url": "http://soundcloud.com/",
+ "companyId": "soundcloud"
},
"sourceknowledge_pixel": {
"name": "SourceKnowledge Pixel",
"categoryId": 4,
- "url": "http://www.provenpixel.com/"
+ "url": "http://www.provenpixel.com/",
+ "companyId": "sourceknowledge"
},
"sourcepoint": {
"name": "Sourcepoint",
"categoryId": 4,
- "url": "https://www.sourcepoint.com/"
+ "url": "https://www.sourcepoint.com/",
+ "companyId": "sourcepoint"
},
"sovrn": {
"name": "sovrn",
"categoryId": 4,
- "url": "https://www.sovrn.com/"
+ "url": "https://www.sovrn.com/",
+ "companyId": "sovrn"
},
"sovrn_viewability_solutions": {
"name": "Sovrn Signal",
"categoryId": 4,
- "url": "https://www.sovrn.com/publishers/signal/"
+ "url": "https://www.sovrn.com/publishers/signal/",
+ "companyId": "sovrn"
},
"spark_studios": {
"name": "Spark Studios",
"categoryId": 0,
- "url": "http://www.sparkstudios.com/"
+ "url": "http://www.sparkstudios.com/",
+ "companyId": "spark_studios"
},
"sparkasse.de": {
"name": "sparkasse.de",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"speakpipe": {
"name": "SpeakPipe",
"categoryId": 2,
- "url": "http://www.speakpipe.com/"
+ "url": "http://www.speakpipe.com/",
+ "companyId": "speakpipe"
},
"specific_media": {
"name": "Specific Media",
"categoryId": 4,
- "url": "http://www.specificmedia.com"
+ "url": "http://www.specificmedia.com",
+ "companyId": "specific_media"
},
"spectate": {
"name": "Spectate",
"categoryId": 6,
- "url": "http://spectate.com/"
+ "url": "http://spectate.com/",
+ "companyId": "spectate"
},
"speed_shift_media": {
"name": "Speed Shift Media",
"categoryId": 4,
- "url": "http://www.speedshiftmedia.com/"
+ "url": "http://www.speedshiftmedia.com/",
+ "companyId": "speed_shift_media"
},
"speedcurve": {
"name": "SpeedCurve",
"categoryId": 6,
- "url": "https://speedcurve.com/"
+ "url": "https://speedcurve.com/",
+ "companyId": null
},
"speedyads": {
"name": "SpeedyAds",
"categoryId": 4,
- "url": "http://www.entireweb.com/speedyads/"
+ "url": "http://www.entireweb.com/speedyads/",
+ "companyId": "entireweb"
},
"speee": {
"name": "Speee",
"categoryId": 4,
- "url": "https://speee.jp"
+ "url": "https://speee.jp",
+ "companyId": "speee"
},
"sphere": {
"name": "Sphere",
"categoryId": 4,
- "url": "http://www.sphere.com/"
+ "url": "http://www.sphere.com/",
+ "companyId": "verizon"
},
"spheremall": {
"name": "SphereMall",
"categoryId": 6,
- "url": "https://spheremall.com"
+ "url": "https://spheremall.com",
+ "companyId": "spheremall"
},
"sphereup": {
"name": "SphereUp",
"categoryId": 2,
- "url": "http://zoomd.com/"
+ "url": "http://zoomd.com/",
+ "companyId": "zoomd"
},
"spicy": {
"name": "Spicy",
"categoryId": 4,
- "url": "http://sspicy.ru/#main"
+ "url": "http://sspicy.ru/#main",
+ "companyId": "spicy_ssp"
},
"spider.ad": {
"name": "Spider.Ad",
"categoryId": 4,
- "url": "http://spider.ad/"
+ "url": "http://spider.ad/",
+ "companyId": "spider.ad"
},
"spider_ads": {
"name": "Spider Ads",
"categoryId": 4,
- "url": "http://www.spiderads.eu/"
+ "url": "http://www.spiderads.eu/",
+ "companyId": "spiderads"
},
"spinnakr": {
"name": "Spinnakr",
"categoryId": 6,
- "url": "http://spinnakr.com/"
+ "url": "http://spinnakr.com/",
+ "companyId": "spinnakr"
},
"spokenlayer": {
"name": "SpokenLayer",
"categoryId": 0,
- "url": "http://www.spokenlayer.com"
+ "url": "http://www.spokenlayer.com",
+ "companyId": "spokenlayer"
},
"spongecell": {
"name": "Spongecell",
"categoryId": 4,
- "url": "http://www.spongecell.com/"
+ "url": "http://www.spongecell.com/",
+ "companyId": "spongecell"
},
"sponsorads.de": {
"name": "SponsorAds.de",
"categoryId": 4,
- "url": "http://sponsorads.de"
+ "url": "http://sponsorads.de",
+ "companyId": "sponsorads.de"
},
"sportsbet_affiliates": {
"name": "Sportsbet Affiliates",
"categoryId": 4,
- "url": "http://www.sportsbetaffiliates.com.au/"
+ "url": "http://www.sportsbetaffiliates.com.au/",
+ "companyId": "sportsbet_affiliates"
},
"spot.im": {
"name": "Spot.IM",
"categoryId": 7,
- "url": "https://www.spot.im/"
+ "url": "https://www.spot.im/",
+ "companyId": "spot.im"
},
"spoteffect": {
"name": "Spoteffect",
"categoryId": 6,
- "url": "http://www.spoteffects.com/home/"
+ "url": "http://www.spoteffects.com/home/",
+ "companyId": "spoteffect"
},
"spotify": {
"name": "Spotify",
"categoryId": 0,
- "url": "https://www.spotify.com/"
+ "url": "https://www.spotify.com/",
+ "companyId": "spotify"
},
"spotify_embed": {
"name": "Spotify Embed",
"categoryId": 0,
- "url": "https://www.spotify.com"
+ "url": "https://www.spotify.com",
+ "companyId": "spotify"
},
"spotscenered.info": {
"name": "spotscenered.info",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"spotxchange": {
"name": "SpotX",
"categoryId": 4,
- "url": "https://www.spotx.tv/"
+ "url": "https://www.spotx.tv/",
+ "companyId": "rtl_group"
},
"spoutable": {
"name": "Spoutable",
"categoryId": 4,
- "url": "http://spoutable.com/"
+ "url": "http://spoutable.com/",
+ "companyId": "spoutable"
},
"springboard": {
"name": "SpringBoard",
"categoryId": 4,
- "url": "http://home.springboardplatform.com/"
+ "url": "http://home.springboardplatform.com/",
+ "companyId": "springboard"
},
"springserve": {
"name": "SpringServe",
"categoryId": 4,
- "url": "http://springserve.com/"
+ "url": "http://springserve.com/",
+ "companyId": "springserve"
},
"sprinklr": {
"name": "Sprinklr",
"categoryId": 4,
- "url": "https://www.sprinklr.com/"
+ "url": "https://www.sprinklr.com/",
+ "companyId": "sprinklr"
},
"sputnik": {
"name": "Sputnik",
"categoryId": 6,
- "url": "https://cnt.sputnik.ru/"
+ "url": "https://cnt.sputnik.ru/",
+ "companyId": "sputnik"
},
"squadata": {
"name": "Squadata",
"categoryId": 4,
- "url": "http://www.email-match.net/"
+ "url": "http://www.email-match.net/",
+ "companyId": "squadata"
},
"squarespace.com": {
"name": "Squarespace",
"categoryId": 6,
- "url": "https://www.squarespace.com/"
+ "url": "https://www.squarespace.com/",
+ "companyId": null
},
"srvtrck.com": {
"name": "srvtrck.com",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"srvvtrk.com": {
"name": "srvvtrk.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"sstatic.net": {
"name": "Stack Exchange",
"categoryId": 9,
- "url": "https://sstatic.net/"
+ "url": "https://sstatic.net/",
+ "companyId": null
},
"st-hatena": {
"name": "Hatena",
"categoryId": 7,
- "url": "http://www.hatena.ne.jp/"
+ "url": "http://www.hatena.ne.jp/",
+ "companyId": "hatena_jp"
},
"stackadapt": {
"name": "StackAdapt",
"categoryId": 4,
- "url": "http://www.stackadapt.com/"
+ "url": "http://www.stackadapt.com/",
+ "companyId": "stackadapt"
},
"stackpathdns.com": {
"name": "StackPath",
"categoryId": 9,
- "url": "https://www.stackpath.com/"
+ "url": "https://www.stackpath.com/",
+ "companyId": null
},
"stailamedia_com": {
"name": "stailamedia.com",
"categoryId": 4,
- "url": "http://stailamedia.com/"
+ "url": "http://stailamedia.com/",
+ "companyId": null
},
"stalluva.pro": {
"name": "stalluva.pro",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"startapp": {
"name": "StartApp",
"categoryId": 4,
- "url": "https://www.startapp.com/"
+ "url": "https://www.startapp.com/",
+ "companyId": null
},
"stat24": {
"name": "Stat24",
"categoryId": 6,
- "url": "http://www.stat24.com/en/"
+ "url": "http://www.stat24.com/en/",
+ "companyId": "stat24"
},
"stat4u": {
"name": "stat4u",
"categoryId": 6,
- "url": "http://stat.4u.pl/"
+ "url": "http://stat.4u.pl/",
+ "companyId": "stat4u"
},
"statcounter": {
"name": "Statcounter",
"categoryId": 6,
- "url": "http://www.statcounter.com/"
+ "url": "http://www.statcounter.com/",
+ "companyId": "statcounter"
},
"stathat": {
"name": "StatHat",
"categoryId": 6,
- "url": "http://www.stathat.com/"
+ "url": "http://www.stathat.com/",
+ "companyId": "stathat"
},
"statisfy": {
"name": "Statisfy",
"categoryId": 6,
- "url": "http://www.statisfy.com/"
+ "url": "http://www.statisfy.com/",
+ "companyId": "statisfy"
},
"statsy.net": {
"name": "statsy.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"statuscake": {
"name": "StatusCake",
"categoryId": 6,
- "url": "https://www.statuscake.com/"
+ "url": "https://www.statuscake.com/",
+ "companyId": "statuscake"
},
"statuspage.io": {
"name": "Statuspage",
"categoryId": 2,
- "url": "https://www.statuspage.io/"
+ "url": "https://www.statuspage.io/",
+ "companyId": "atlassian"
},
"stayfriends.de": {
"name": "stayfriends.de",
"categoryId": 8,
- "url": "https://www.stayfriends.de/"
+ "url": "https://www.stayfriends.de/",
+ "companyId": null
},
"steelhouse": {
"name": "Steel House Media",
"categoryId": 4,
- "url": "https://steelhouse.com/"
+ "url": "https://steelhouse.com/",
+ "companyId": "steelhouse"
},
"steepto.com": {
"name": "Steepto",
"categoryId": 4,
- "url": "https://www.steepto.com/"
+ "url": "https://www.steepto.com/",
+ "companyId": null
},
"stepstone.com": {
"name": "StepStone",
"categoryId": 8,
- "url": "https://www.stepstone.com/"
+ "url": "https://www.stepstone.com/",
+ "companyId": null
},
"stetic": {
"name": "Stetic",
"categoryId": 6,
- "url": "https://www.stetic.com/"
+ "url": "https://www.stetic.com/",
+ "companyId": "stetic"
},
"stickyads": {
"name": "StickyAds",
"categoryId": 4,
- "url": "http://corporate.comcast.com/"
+ "url": "http://corporate.comcast.com/",
+ "companyId": "comcast"
},
"stocktwits": {
"name": "StockTwits",
"categoryId": 2,
- "url": "http://stocktwits.com"
+ "url": "http://stocktwits.com",
+ "companyId": "stocktwits"
},
"storify": {
"name": "Storify",
"categoryId": 4,
- "url": "https://storify.com/"
+ "url": "https://storify.com/",
+ "companyId": "adobe"
},
"storygize": {
"name": "Storygize",
"categoryId": 4,
- "url": "http://www.storygize.com/"
+ "url": "http://www.storygize.com/",
+ "companyId": null
},
"strands_recommender": {
"name": "Strands Recommender",
"categoryId": 4,
- "url": "http://recommender.strands.com"
+ "url": "http://recommender.strands.com",
+ "companyId": "strands"
},
"strava": {
"name": "Strava",
"categoryId": 6,
- "url": "https://strava.com"
+ "url": "https://strava.com",
+ "companyId": "strava"
},
"streak": {
"name": "Streak",
"categoryId": 2,
- "url": "http://www.streak.com/"
+ "url": "http://www.streak.com/",
+ "companyId": "streak"
},
"streamrail.com": {
"name": "StreamRail",
"categoryId": 4,
- "url": "https://www.streamrail.com/"
+ "url": "https://www.streamrail.com/",
+ "companyId": "ironsource"
},
"stride": {
"name": "Stride",
"categoryId": 6,
- "url": "https://www.getstride.com/"
+ "url": "https://www.getstride.com/",
+ "companyId": "stride_software"
},
"stripchat.com": {
"name": "stripchat.com",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"stripe.com": {
"name": "Stripe",
"categoryId": 2,
- "url": "https://stripe.com/"
+ "url": "https://stripe.com/",
+ "companyId": null
},
"stripst.com": {
"name": "stripst.com",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"stroer_digital_media": {
"name": "Stroer Digital Media",
"categoryId": 4,
- "url": "http://www.stroeer.de/"
+ "url": "http://www.stroeer.de/",
+ "companyId": "stroer"
},
"strossle": {
"name": "Strossle",
"categoryId": 4,
- "url": "https://strossle.com/"
+ "url": "https://strossle.com/",
+ "companyId": "strossle"
},
"struq": {
"name": "Struq",
"categoryId": 4,
- "url": "http://www.struq.com/"
+ "url": "http://www.struq.com/",
+ "companyId": "quantcast"
},
"stumbleupon_widgets": {
"name": "StumbleUpon Widgets",
"categoryId": 7,
- "url": "http://www.stumbleupon.com/"
+ "url": "http://www.stumbleupon.com/",
+ "companyId": "stumbleupon"
},
"sub2": {
"name": "Sub2",
"categoryId": 4,
- "url": "http://www.sub2tech.com/"
+ "url": "http://www.sub2tech.com/",
+ "companyId": "sub2"
},
"sublime_skinz": {
"name": "Sublime",
"categoryId": 4,
- "url": "https://sublimeskinz.com/home"
+ "url": "https://sublimeskinz.com/home",
+ "companyId": "sublime_skinz"
},
"suggest.io": {
"name": "Suggest.io",
"categoryId": 4,
- "url": "https://suggest.io/"
+ "url": "https://suggest.io/",
+ "companyId": "suggest.io"
},
"sumologic.com": {
"name": "Sumologic",
"categoryId": 6,
- "url": "https://www.sumologic.com/"
+ "url": "https://www.sumologic.com/",
+ "companyId": null
},
"sumome": {
"name": "Sumo",
"categoryId": 6,
- "url": "https://sumo.com/"
+ "url": "https://sumo.com/",
+ "companyId": "sumome"
},
"sundaysky": {
"name": "SundaySky",
"categoryId": 4,
- "url": "http://www.sundaysky.com/"
+ "url": "http://www.sundaysky.com/",
+ "companyId": "sundaysky"
},
"supercounters": {
"name": "SuperCounters",
"categoryId": 6,
- "url": "http://www.supercounters.com/"
+ "url": "http://www.supercounters.com/",
+ "companyId": "supercounters"
},
"superfastcdn.com": {
"name": "superfastcdn.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"supership": {
"name": "Supership",
"categoryId": 4,
- "url": "https://supership.jp/en/"
+ "url": "https://supership.jp/en/",
+ "companyId": "supership"
},
"supplyframe": {
"name": "SupplyFrame",
"categoryId": 4,
- "url": "https://supplyframe.com/"
+ "url": "https://supplyframe.com/",
+ "companyId": "supplyframe"
},
"surf_by_surfingbird": {
"name": "Surf by Surfingbird",
"categoryId": 2,
- "url": "http://surfingbird.ru/"
+ "url": "http://surfingbird.ru/",
+ "companyId": "surfingbird"
},
"survata": {
"name": "Survata",
"categoryId": 4,
- "url": "https://www.survata.com/"
+ "url": "https://www.survata.com/",
+ "companyId": "survata"
},
"sweettooth": {
"name": "Sweettooth",
"categoryId": 2,
- "url": "https://www.sweettoothrewards.com/"
+ "url": "https://www.sweettoothrewards.com/",
+ "companyId": "sweet_tooth_rewards"
},
"swiftype": {
"name": "Swiftype",
"categoryId": 9,
- "url": "https://swiftype.com/"
+ "url": "https://swiftype.com/",
+ "companyId": "elastic"
},
"swisscom": {
"name": "Swisscom",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"switch_concepts": {
"name": "Switch Concepts",
"categoryId": 4,
- "url": "http://www.switchconcepts.co.uk/"
+ "url": "http://www.switchconcepts.co.uk/",
+ "companyId": "switch_concepts"
},
"swoop": {
"name": "Swoop",
"categoryId": 4,
- "url": "http://swoop.com/"
+ "url": "http://swoop.com/",
+ "companyId": "swoop"
},
"sykes": {
"name": "Sykes",
"categoryId": 6,
- "url": "http://www.sykescottages.co.uk/"
+ "url": "http://www.sykescottages.co.uk/",
+ "companyId": "sykes_cottages"
},
"symantec": {
"name": "Symantec (Norton Secured Seal)",
"categoryId": 5,
- "url": "https://www.symantec.com/page.jsp?id=ssl-resources&tabID=3#"
+ "url": "https://www.symantec.com/page.jsp?id=ssl-resources&tabID=3#",
+ "companyId": "symantec"
},
"symphony_talent": {
"name": "Symphony Talent",
"categoryId": 2,
- "url": "http://www.symphonytalent.com/"
+ "url": "http://www.symphonytalent.com/",
+ "companyId": "symphony_talent"
},
"synacor": {
"name": "Synacor",
"categoryId": 4,
- "url": "https://www.synacor.com/"
+ "url": "https://www.synacor.com/",
+ "companyId": "synacor"
},
"syncapse": {
"name": "Syncapse",
"categoryId": 4,
- "url": "http://www.clickable.com/"
+ "url": "http://www.clickable.com/",
+ "companyId": "syncapse"
},
"synergy-e": {
"name": "Synergy-E",
"categoryId": 4,
- "url": "http://synergy-e.com/"
+ "url": "http://synergy-e.com/",
+ "companyId": "synergy-e"
},
"t-mobile": {
"name": "Deutsche Telekom",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"t8cdn.com": {
"name": "t8cdn.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"tableteducation.com": {
"name": "tableteducation.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"taboola": {
"name": "Taboola",
"categoryId": 4,
- "url": "http://www.taboola.com"
+ "url": "http://www.taboola.com",
+ "companyId": "taboola"
},
"tacoda": {
"name": "Tacoda",
"categoryId": 4,
- "url": "http://www.tacoda.com/"
+ "url": "http://www.tacoda.com/",
+ "companyId": "verizon"
},
"tag_commander": {
"name": "Commanders Act",
"categoryId": 5,
- "url": "https://www.commandersact.com/en/"
+ "url": "https://www.commandersact.com/en/",
+ "companyId": "tag_commander"
},
"tagcade": {
"name": "Tagcade",
"categoryId": 4,
- "url": "https://www.pubvantage.com/"
+ "url": "https://www.pubvantage.com/",
+ "companyId": "pubvantage"
},
"taggify": {
"name": "Taggify",
"categoryId": 4,
- "url": "http://new.taggify.net/"
+ "url": "http://new.taggify.net/",
+ "companyId": "taggify"
},
"taggy": {
"name": "TAGGY",
"categoryId": 4,
- "url": "http://taggy.jp/"
+ "url": "http://taggy.jp/",
+ "companyId": "taggy"
},
"tagman": {
"name": "TagMan",
"categoryId": 5,
- "url": "http://www.tagman.com/"
+ "url": "http://www.tagman.com/",
+ "companyId": "ensighten"
},
"tail_target": {
"name": "Tail",
"categoryId": 6,
- "url": "https://www.tail.digital/"
+ "url": "https://www.tail.digital/",
+ "companyId": "tail.digital"
},
"tailsweep": {
"name": "Tailsweep",
"categoryId": 4,
- "url": "http://www.tailsweep.se/"
+ "url": "http://www.tailsweep.se/",
+ "companyId": "tailsweep"
},
"tamedia.ch": {
"name": "Tamedia",
"categoryId": 4,
- "url": "https://www.tamedia.ch/"
+ "url": "https://www.tamedia.ch/",
+ "companyId": null
},
"tanx": {
"name": "Tanx",
"categoryId": 4,
- "url": "http://tanx.com/"
+ "url": "http://tanx.com/",
+ "companyId": "tanx"
},
"taobao": {
"name": "Taobao",
"categoryId": 4,
- "url": "https://world.taobao.com/"
+ "url": "https://world.taobao.com/",
+ "companyId": "alibaba"
},
"tapad": {
"name": "Tapad",
"categoryId": 4,
- "url": "http://www.tapad.com/"
+ "url": "http://www.tapad.com/",
+ "companyId": "telenor"
},
"tapinfluence": {
"name": "TapInfluence",
"categoryId": 4,
- "url": "http://theblogfrog.com/"
+ "url": "http://theblogfrog.com/",
+ "companyId": "tapinfluence"
},
"tarafdari": {
"name": "Tarafdari",
"categoryId": 4,
- "url": "https://www.tarafdari.com/"
+ "url": "https://www.tarafdari.com/",
+ "companyId": "tarafdari"
},
"target_2_sell": {
"name": "Target 2 Sell",
"categoryId": 4,
- "url": "http://www.target2sell.com/en/"
+ "url": "http://www.target2sell.com/en/",
+ "companyId": "target_2_sell"
},
"target_circle": {
"name": "Target Circle",
"categoryId": 6,
- "url": "http://targetcircle.com"
+ "url": "http://targetcircle.com",
+ "companyId": "target_circle"
},
"target_fuel": {
"name": "Target Fuel",
"categoryId": 6,
- "url": "http://targetfuel.com/"
+ "url": "http://targetfuel.com/",
+ "companyId": "target_fuel"
},
"tawk": {
"name": "Tawk",
"categoryId": 2,
- "url": "https://www.tawk.to/"
+ "url": "https://www.tawk.to/",
+ "companyId": "tawk"
},
"tbn.ru": {
"name": "TBN.ru",
"categoryId": 4,
- "url": "http://www.agava.ru"
+ "url": "http://www.agava.ru",
+ "companyId": "agava"
},
"tchibo_de": {
"name": "tchibo.de",
"categoryId": 8,
- "url": "http://tchibo.de/"
+ "url": "http://tchibo.de/",
+ "companyId": null
},
"tdsrmbl_net": {
"name": "tdsrmbl.net",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"teads": {
"name": "Teads",
"categoryId": 4,
- "url": "http://teads.tv/"
+ "url": "http://teads.tv/",
+ "companyId": "teads"
},
"tealeaf": {
"name": "Tealeaf",
"categoryId": 6,
- "url": "https://www.ibm.com/digital-marketing"
+ "url": "https://www.ibm.com/digital-marketing",
+ "companyId": "ibm"
},
"tealium": {
"name": "Tealium",
"categoryId": 5,
- "url": "http://www.tealium.com/"
+ "url": "http://www.tealium.com/",
+ "companyId": "tealium"
},
"teaser.cc": {
"name": "Teaser.cc",
"categoryId": 4,
- "url": "http://www.teaser.cc/"
+ "url": "http://www.teaser.cc/",
+ "companyId": "teaser.cc"
},
"tedemis": {
"name": "Tedemis",
"categoryId": 4,
- "url": "http://www.tedemis.com"
+ "url": "http://www.tedemis.com",
+ "companyId": "tedemis"
},
"teletech": {
"name": "TeleTech",
"categoryId": 4,
- "url": "http://www.webmetro.com/whoweare/technology.aspx"
+ "url": "http://www.webmetro.com/whoweare/technology.aspx",
+ "companyId": "teletech"
},
"tender": {
"name": "Tender",
"categoryId": 2,
- "url": "http://www.tenderapp.com/"
+ "url": "http://www.tenderapp.com/",
+ "companyId": "tender"
},
"tensitionschoo.club": {
"name": "tensitionschoo.club",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"teroti": {
"name": "Teroti",
"categoryId": 4,
- "url": "http://www.teroti.com/"
+ "url": "http://www.teroti.com/",
+ "companyId": "teroti"
},
"terren": {
"name": "Terren",
"categoryId": 4,
- "url": "http://www.webterren.com/"
+ "url": "http://www.webterren.com/",
+ "companyId": "terren"
},
"teufel.de": {
"name": "teufel.de",
"categoryId": 8,
- "url": "https://www.teufel.de/"
+ "url": "https://www.teufel.de/",
+ "companyId": null
},
"the_adex": {
"name": "The ADEX",
"categoryId": 4,
- "url": "http://www.theadex.com/"
+ "url": "http://www.theadex.com/",
+ "companyId": "prosieben_sat1"
},
"the_deck": {
"name": "The DECK",
"categoryId": 4,
- "url": "http://decknetwork.net/"
+ "url": "http://decknetwork.net/",
+ "companyId": "the_deck"
},
"the_guardian": {
"name": "The Guardian",
"categoryId": 8,
- "url": "https://www.theguardian.com/"
+ "url": "https://www.theguardian.com/",
+ "companyId": "the_guardian"
},
"the_reach_group": {
"name": "The Reach Group",
"categoryId": 4,
- "url": "http://www.redvertisment.com"
+ "url": "http://www.redvertisment.com",
+ "companyId": "the_reach_group"
},
"the_search_agency": {
"name": "The Search Agency",
"categoryId": 4,
- "url": "http://www.thesearchagency.com/"
+ "url": "http://www.thesearchagency.com/",
+ "companyId": "the_search_agency"
},
"the_sun": {
"name": "The Sun",
"categoryId": 8,
- "url": "https://www.thesun.co.uk/"
+ "url": "https://www.thesun.co.uk/",
+ "companyId": "the_sun"
},
"the_weather_company": {
"name": "The Weather Company",
"categoryId": 4,
- "url": "http://www.theweathercompany.com/"
+ "url": "http://www.theweathercompany.com/",
+ "companyId": "ibm"
},
"themoviedb": {
"name": "The Movie DB",
"categoryId": 8,
- "url": "https://www.themoviedb.org/"
+ "url": "https://www.themoviedb.org/",
+ "companyId": "themoviedb"
},
"thinglink": {
"name": "ThingLink",
"categoryId": 4,
- "url": "http://www.thinglink.com/"
+ "url": "http://www.thinglink.com/",
+ "companyId": "thinglink"
},
"threatmetrix": {
"name": "ThreatMetrix",
"categoryId": 6,
- "url": "http://threatmetrix.com/"
+ "url": "http://threatmetrix.com/",
+ "companyId": "threatmetrix"
},
"tidbit": {
"name": "Tidbit",
"categoryId": 2,
- "url": "http://tidbit.co.in/"
+ "url": "http://tidbit.co.in/",
+ "companyId": "tidbit"
},
"tidio": {
"name": "Tidio",
"categoryId": 2,
- "url": "https://www.tidio.com/"
+ "url": "https://www.tidio.com/",
+ "companyId": "tidio_chat"
+ },
+ "tiktok_analytics": {
+ "name": "TikTok Analytics",
+ "categoryId": 6,
+ "url": "https://analytics.tiktok.com",
+ "companyId": "bytedance_inc"
},
"tiller": {
"name": "Tiller",
"categoryId": 4,
- "url": "https://www.tiller.com/"
+ "url": "https://www.tiller.com/",
+ "companyId": "tiller"
},
"timezondb": {
"name": "TimezonDB",
"categoryId": 4,
- "url": "https://timezonedb.com/"
+ "url": "https://timezonedb.com/",
+ "companyId": "timezonedb"
},
"tinypass": {
"name": "Piano",
"categoryId": 5,
- "url": "https://piano.io/"
+ "url": "https://piano.io/",
+ "companyId": "piano"
},
"tisoomi": {
"name": "Tisoomi",
"categoryId": 4,
- "url": "https://tisoomi-services.com/"
+ "url": "https://tisoomi-services.com/",
+ "companyId": null
},
"tlv_media": {
"name": "TLV Media",
"categoryId": 4,
- "url": "http://www.tlvmedia.com"
+ "url": "http://www.tlvmedia.com",
+ "companyId": "tlvmedia"
},
"tns": {
"name": "TNS",
"categoryId": 6,
- "url": "http://www.tnsglobal.com/"
+ "url": "http://www.tnsglobal.com/",
+ "companyId": "wpp"
},
"tomnewsupdate.info": {
"name": "tomnewsupdate.info",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"tomorrow_focus": {
"name": "Tomorrow Focus",
"categoryId": 4,
- "url": "http://www.tomorrow-focus.com"
+ "url": "http://www.tomorrow-focus.com",
+ "companyId": "hubert_burda_media"
},
"tonefuse": {
"name": "ToneFuse",
"categoryId": 4,
- "url": "http://www.tonefuse.com/"
+ "url": "http://www.tonefuse.com/",
+ "companyId": "tonefuse"
},
"top_mail": {
"name": "Top Mail",
"categoryId": 6,
- "url": "https://corp.megafon.com/"
+ "url": "https://corp.megafon.com/",
+ "companyId": "megafon"
},
"toplist.cz": {
"name": "toplist.cz",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"toponclick_com": {
"name": "toponclick.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"topsy": {
"name": "Topsy",
"categoryId": 4,
- "url": "http://topsy.com/"
+ "url": "http://topsy.com/",
+ "companyId": "topsy"
},
"torbit": {
"name": "Torbit",
"categoryId": 6,
- "url": "http://torbit.com/"
+ "url": "http://torbit.com/",
+ "companyId": "torbit"
},
"toro": {
"name": "TORO",
"categoryId": 4,
- "url": "http://toroadvertising.com/"
+ "url": "http://toroadvertising.com/",
+ "companyId": "toro_advertising"
},
"tororango.com": {
"name": "tororango.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"total_media": {
"name": "Total Media",
"categoryId": 4,
- "url": "http://www.totalmedia.co.il/eng/"
+ "url": "http://www.totalmedia.co.il/eng/",
+ "companyId": "total_media"
},
"touchcommerce": {
"name": "Nuance",
"categoryId": 2,
- "url": "https://www.nuance.com/omni-channel-customer-engagement/digital.html"
+ "url": "https://www.nuance.com/omni-channel-customer-engagement/digital.html",
+ "companyId": "touchcommerce"
},
"tovarro.com": {
"name": "Tovarro",
"categoryId": 4,
- "url": "https://www.tovarro.com/"
+ "url": "https://www.tovarro.com/",
+ "companyId": null
},
"tp-cdn.com": {
"name": "TrialPay",
"categoryId": 4,
- "url": "https://www.trialpay.com/"
+ "url": "https://www.trialpay.com/",
+ "companyId": null
},
"tracc.it": {
"name": "Kiwe.io",
"categoryId": 6,
- "url": "https://www.kiwe.io/"
+ "url": "https://www.kiwe.io/",
+ "companyId": null
},
"tracemyip": {
"name": "TraceMyIP",
"categoryId": 4,
- "url": "http://www.tracemyip.org/"
+ "url": "http://www.tracemyip.org/",
+ "companyId": "tracemyip"
},
"traceview": {
"name": "TraceView",
"categoryId": 6,
- "url": "http://www.appneta.com/"
+ "url": "http://www.appneta.com/",
+ "companyId": "appneta"
},
"track_duck": {
"name": "Track Duck",
"categoryId": 6,
- "url": "https://trackduck.com/"
+ "url": "https://trackduck.com/",
+ "companyId": "track_duck"
},
"trackjs": {
"name": "TrackJS",
"categoryId": 6,
- "url": "http://www.trackjs.com/"
+ "url": "http://www.trackjs.com/",
+ "companyId": "trackjs"
},
"trackset_conversionlab": {
"name": "Trackset ConversionLab",
"categoryId": 4,
- "url": "http://www.trackset.com/"
+ "url": "http://www.trackset.com/",
+ "companyId": "trackset"
},
"trackuity": {
"name": "Trackuity",
"categoryId": 2,
- "url": "http://www.trackuity.com/"
+ "url": "http://www.trackuity.com/",
+ "companyId": "trackuity"
},
"tradedesk": {
"name": "TradeDesk",
"categoryId": 4,
- "url": "http://www.thetradedesk.com/"
+ "url": "http://www.thetradedesk.com/",
+ "companyId": "the_trade_desk"
},
"tradedoubler": {
"name": "TradeDoubler",
"categoryId": 4,
- "url": "http://www.tradedoubler.com/"
+ "url": "http://www.tradedoubler.com/",
+ "companyId": "tradedoubler"
},
"tradelab": {
"name": "Tradelab",
"categoryId": 4,
- "url": "http://www.tradelab.fr/"
+ "url": "http://www.tradelab.fr/",
+ "companyId": "tradelab"
},
"tradetracker": {
"name": "TradeTracker",
"categoryId": 4,
- "url": "http://www.tradetracker.com"
+ "url": "http://www.tradetracker.com",
+ "companyId": "tradetracker"
},
"traffective": {
"name": "Traffective",
"categoryId": 4,
- "url": "https://traffective.com/"
+ "url": "https://traffective.com/",
+ "companyId": null
},
"traffic_fuel": {
"name": "Traffic Fuel",
"categoryId": 4,
- "url": "https://trafficfuel.com/"
+ "url": "https://trafficfuel.com/",
+ "companyId": "traffic_fuel"
},
"traffic_revenue": {
"name": "Traffic Revenue",
"categoryId": 4,
- "url": "http://www.trafficrevenue.net/"
+ "url": "http://www.trafficrevenue.net/",
+ "companyId": "traffic_revenue"
},
"traffic_stars": {
"name": "Traffic Stars",
"categoryId": 3,
- "url": "https://trafficstars.com/#index_page"
+ "url": "https://trafficstars.com/#index_page",
+ "companyId": "traffic_stars"
},
"trafficbroker": {
"name": "TrafficBroker",
"categoryId": 4,
- "url": "http://trafficbroker.com/"
+ "url": "http://trafficbroker.com/",
+ "companyId": "trafficbroker"
},
"trafficfabrik.com": {
"name": "Traffic Fabrik",
"categoryId": 3,
- "url": "https://www.trafficfabrik.com/"
+ "url": "https://www.trafficfabrik.com/",
+ "companyId": null
},
"trafficfactory": {
"name": "Traffic Factory",
"categoryId": 4,
- "url": "https://www.trafficfactory.biz/"
+ "url": "https://www.trafficfactory.biz/",
+ "companyId": null
},
"trafficforce": {
"name": "TrafficForce",
"categoryId": 4,
- "url": "http://www.trafficforce.com/"
+ "url": "http://www.trafficforce.com/",
+ "companyId": "trafficforce"
},
"traffichaus": {
"name": "TrafficHaus",
"categoryId": 3,
- "url": "http://www.traffichaus.com"
+ "url": "http://www.traffichaus.com",
+ "companyId": "traffichaus"
},
"trafficjunky": {
"name": "TrafficJunky",
"categoryId": 3,
- "url": "http://www.trafficjunky.net/"
+ "url": "http://www.trafficjunky.net/",
+ "companyId": "trafficjunky"
},
"traffiliate": {
"name": "Traffiliate",
"categoryId": 4,
- "url": "http://www.traffiliate.com/"
+ "url": "http://www.traffiliate.com/",
+ "companyId": "dsnr_media_group"
},
"trafic": {
"name": "Trafic",
"categoryId": 6,
- "url": "http://www.trafic.ro/"
+ "url": "http://www.trafic.ro/",
+ "companyId": "trafic"
},
"trafmag.com": {
"name": "TrafMag",
"categoryId": 4,
- "url": "https://trafmag.com/"
+ "url": "https://trafmag.com/",
+ "companyId": "trafmag"
+ },
+ "transcend": {
+ "name": "Transcend Consent",
+ "categoryId": 14,
+ "url": "https://transcend.io/consent/",
+ "companyId": "transcend"
+ },
+ "transcend_telemetry": {
+ "name": "Transcend Telemetry",
+ "categoryId": 6,
+ "url": "https://transcend.io",
+ "companyId": "transcend"
},
"transmatic": {
"name": "Transmatic",
"categoryId": 6,
- "url": "http://www.transmatico.com/en/"
+ "url": "http://www.transmatico.com/en/",
+ "companyId": "transmatico"
},
"travel_audience": {
"name": "Travel Audience",
"categoryId": 6,
- "url": "https://travelaudience.com/"
+ "url": "https://travelaudience.com/",
+ "companyId": "travel_audience"
},
"trbo": {
"name": "trbo",
"categoryId": 4,
- "url": "http://www.trbo.com/"
+ "url": "http://www.trbo.com/",
+ "companyId": "trbo"
},
"treasuredata": {
"name": "Treasure Data",
"categoryId": 6,
- "url": "https://www.treasuredata.com/"
+ "url": "https://www.treasuredata.com/",
+ "companyId": "arm"
},
"tremor_video": {
"name": "Tremor Video",
"categoryId": 0,
- "url": "http://www.tremormedia.com/"
+ "url": "http://www.tremormedia.com/",
+ "companyId": "tremor_video"
},
"trendcounter": {
"name": "trendcounter",
"categoryId": 6,
- "url": "http://www.trendcounter.com/"
+ "url": "http://www.trendcounter.com/",
+ "companyId": "trendcounter"
},
"trendemon": {
"name": "TrenDemon",
"categoryId": 6,
- "url": "http://trendemon.com"
+ "url": "http://trendemon.com",
+ "companyId": "trendemon"
},
"tribal_fusion": {
"name": "Tribal Fusion",
"categoryId": 4,
- "url": "http://www.tribalfusion.com/"
+ "url": "http://www.tribalfusion.com/",
+ "companyId": "exponential_interactive"
},
"tribal_fusion_notice": {
"name": "Tribal Fusion Notice",
"categoryId": 4,
- "url": "http://www.tribalfusion.com"
+ "url": "http://www.tribalfusion.com",
+ "companyId": "exponential_interactive"
},
"triblio": {
"name": "Triblio",
"categoryId": 6,
- "url": "https://triblio.com/"
+ "url": "https://triblio.com/",
+ "companyId": "triblio"
},
"trigger_mail_marketing": {
"name": "Trigger Mail Marketing",
"categoryId": 4,
- "url": "http://www.triggeremailmarketing.com/"
+ "url": "http://www.triggeremailmarketing.com/",
+ "companyId": "trigger_mail_marketing"
},
"triggerbee": {
"name": "Triggerbee",
"categoryId": 2,
- "url": "https://triggerbee.com/"
+ "url": "https://triggerbee.com/",
+ "companyId": "triggerbee"
},
"tripadvisor": {
"name": "TripAdvisor",
"categoryId": 8,
- "url": "http://iac.com/"
+ "url": "http://iac.com/",
+ "companyId": "iac_apps"
},
"triplelift": {
"name": "TripleLift",
"categoryId": 4,
- "url": "http://triplelift.com/"
+ "url": "http://triplelift.com/",
+ "companyId": "triplelift"
},
"triptease": {
"name": "Triptease",
"categoryId": 2,
- "url": "https://www.triptease.com"
+ "url": "https://www.triptease.com",
+ "companyId": "triptease"
},
"triton_digital": {
"name": "Triton Digital",
"categoryId": 0,
- "url": "http://www.tritondigital.com/"
+ "url": "http://www.tritondigital.com/",
+ "companyId": "triton_digital"
},
"trovus_revelations": {
"name": "Trovus Revelations",
"categoryId": 4,
- "url": "http://www.trovus.co.uk/"
+ "url": "http://www.trovus.co.uk/",
+ "companyId": "trovus_revelations"
},
"trsv3.com": {
"name": "trsv3.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"true_fit": {
"name": "True Fit",
"categoryId": 4,
- "url": "https://www.truefit.com/"
+ "url": "https://www.truefit.com/",
+ "companyId": "true_fit"
},
"trueanthem": {
"name": "True Anthem",
"categoryId": 4,
- "url": "https://www.trueanthem.com/"
+ "url": "https://www.trueanthem.com/",
+ "companyId": "trueanthem"
},
"trueffect": {
"name": "TruEffect",
"categoryId": 4,
- "url": "http://www.trueffect.com/"
+ "url": "http://www.trueffect.com/",
+ "companyId": "trueffect"
},
"truehits.net": {
"name": "Truehits.net",
"categoryId": 6,
- "url": "http://truehits.net/"
+ "url": "http://truehits.net/",
+ "companyId": "truehits.net"
},
"trumba": {
"name": "Trumba",
"categoryId": 4,
- "url": "http://www.trumba.com"
+ "url": "http://www.trumba.com",
+ "companyId": "trumba"
},
"truoptik": {
"name": "Tru Optik",
"categoryId": 6,
- "url": "http://truoptik.com/"
+ "url": "http://truoptik.com/",
+ "companyId": null
},
"trustarc": {
"name": "TrustArc",
"categoryId": 5,
- "url": "http://www.trustarc.com/"
+ "url": "http://www.trustarc.com/",
+ "companyId": "trustarc"
},
"truste_consent": {
"name": "Truste Consent",
"categoryId": 5,
- "url": "http://www.trustarc.com/"
+ "url": "http://www.trustarc.com/",
+ "companyId": "trustarc"
},
"truste_notice": {
"name": "TRUSTe Notice",
"categoryId": 5,
- "url": "http://www.truste.com/"
+ "url": "http://www.truste.com/",
+ "companyId": "trustarc"
},
"truste_seal": {
"name": "TRUSTe Seal",
"categoryId": 5,
- "url": "http://www.truste.com/"
+ "url": "http://www.truste.com/",
+ "companyId": "trustarc"
},
"trusted_shops": {
"name": "Trusted Shops",
"categoryId": 5,
- "url": "http://www.trustedshops.com/"
+ "url": "http://www.trustedshops.com/",
+ "companyId": "trusted_shops"
},
"trustev": {
"name": "Trustev",
"categoryId": 6,
- "url": "http://www.trustev.com/"
+ "url": "http://www.trustev.com/",
+ "companyId": "trustev"
},
"trustlogo": {
"name": "TrustLogo",
"categoryId": 5,
- "url": "http://www.comodo.com/"
+ "url": "http://www.comodo.com/",
+ "companyId": "comodo"
},
"trustpilot": {
"name": "Trustpilot",
"categoryId": 2,
- "url": "http://www.trustpilot.com"
+ "url": "http://www.trustpilot.com",
+ "companyId": "trustpilot"
},
"trustwave.com": {
"name": "Trustwave",
"categoryId": 8,
- "url": "https://www.trustwave.com/home/"
+ "url": "https://www.trustwave.com/home/",
+ "companyId": null
},
"tubecorporate": {
"name": "Tube Corporate",
"categoryId": 3,
- "url": "https://tubecorporate.com/"
+ "url": "https://tubecorporate.com/",
+ "companyId": null
},
"tubecup.org": {
"name": "tubecup.org",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"tubemogul": {
"name": "TubeMogul",
"categoryId": 4,
- "url": "http://tubemogul.com/"
+ "url": "http://tubemogul.com/",
+ "companyId": "tubemogul"
},
"tumblr_analytics": {
"name": "Tumblr Analytics",
"categoryId": 6,
- "url": "https://www.verizon.com/"
+ "url": "https://www.verizon.com/",
+ "companyId": "verizon"
},
"tumblr_buttons": {
"name": "Tumblr Buttons",
"categoryId": 7,
- "url": "http://www.tumblr.com/"
+ "url": "http://www.tumblr.com/",
+ "companyId": "verizon"
},
"tumblr_dashboard": {
"name": "Tumblr Dashboard",
"categoryId": 7,
- "url": "http://www.tumblr.com/"
+ "url": "http://www.tumblr.com/",
+ "companyId": "verizon"
},
"tune_in": {
"name": "Tune In",
"categoryId": 0,
- "url": "http://tunein.com/"
+ "url": "http://tunein.com/",
+ "companyId": "tunein"
},
"turbo": {
"name": "Turbo",
"categoryId": 4,
- "url": "http://www.turboadv.com/"
+ "url": "http://www.turboadv.com/",
+ "companyId": "turbo"
},
"turn_inc.": {
"name": "Turn Inc.",
"categoryId": 4,
- "url": "https://www.amobee.com/company/"
+ "url": "https://www.amobee.com/company/",
+ "companyId": "singtel"
},
"turner": {
"name": "Warner Media",
"categoryId": 6,
- "url": "https://www.warnermedia.com/"
+ "url": "https://www.warnermedia.com/",
+ "companyId": "turner"
},
"turnsocial": {
"name": "TurnSocial",
"categoryId": 7,
- "url": "http://turnsocial.com/"
+ "url": "http://turnsocial.com/",
+ "companyId": "turnsocial"
},
"turnto": {
"name": "TurnTo",
"categoryId": 2,
- "url": "http://www.turntonetworks.com/"
+ "url": "http://www.turntonetworks.com/",
+ "companyId": "turnto_networks"
},
"tvsquared.com": {
"name": "TVSquared",
"categoryId": 4,
- "url": "http://tvsquared.com/"
+ "url": "http://tvsquared.com/",
+ "companyId": "tvsquared"
},
"tweetboard": {
"name": "Tweetboard",
"categoryId": 7,
- "url": "http://tweetboard.com/alpha/"
+ "url": "http://tweetboard.com/alpha/",
+ "companyId": "tweetboard"
},
"tweetmeme": {
"name": "TweetMeme",
"categoryId": 7,
- "url": "http://tweetmeme.com/"
+ "url": "http://tweetmeme.com/",
+ "companyId": "tweetmeme"
},
"twenga": {
"name": "Twenga Solutions",
"categoryId": 4,
- "url": "https://www.twenga-solutions.com/"
+ "url": "https://www.twenga-solutions.com/",
+ "companyId": null
},
"twiago": {
"name": "Twiago",
"categoryId": 4,
- "url": "https://www.twiago.com/"
+ "url": "https://www.twiago.com/",
+ "companyId": "twiago"
},
"twine": {
"name": "Twine",
"categoryId": 6,
- "url": "http://twinedigital.com/"
+ "url": "http://twinedigital.com/",
+ "companyId": "twine_digital"
},
"twitch.tv": {
"name": "Twitch",
"categoryId": 0,
- "url": "https://www.twitch.tv/"
+ "url": "https://www.twitch.tv/",
+ "companyId": "amazon_associates"
},
"twitch_cdn": {
"name": "Twitch CDN",
"categoryId": 0,
- "url": "https://www.twitch.tv/"
+ "url": "https://www.twitch.tv/",
+ "companyId": "amazon_associates"
},
"twitter": {
"name": "Twitter",
"categoryId": 7,
- "url": "https://twitter.com"
+ "url": "https://twitter.com",
+ "companyId": "twitter"
},
"twitter_ads": {
"name": "Twitter Advertising",
"categoryId": 4,
- "url": "http://twitter.com/widgets"
+ "url": "http://twitter.com/widgets",
+ "companyId": "twitter"
},
"twitter_analytics": {
"name": "Twitter Analytics",
"categoryId": 6,
- "url": "https://twitter.com"
+ "url": "https://twitter.com",
+ "companyId": "twitter"
},
"twitter_badge": {
"name": "Twitter Badge",
"categoryId": 7,
- "url": "http://twitter.com/widgets"
+ "url": "http://twitter.com/widgets",
+ "companyId": "twitter"
},
"twitter_button": {
"name": "Twitter Button",
"categoryId": 7,
- "url": "http://twitter.com"
+ "url": "http://twitter.com",
+ "companyId": "twitter"
},
"twitter_conversion_tracking": {
"name": "Twitter Conversion Tracking",
"categoryId": 4,
- "url": "https://twitter.com/"
+ "url": "https://twitter.com/",
+ "companyId": "twitter"
},
"twitter_for_business": {
"name": "Twitter for Business",
"categoryId": 4,
- "url": "https://business.twitter.com/"
+ "url": "https://business.twitter.com/",
+ "companyId": "twitter"
},
"twitter_syndication": {
"name": "Twitter Syndication",
"categoryId": 7,
- "url": "https://twitter.com"
+ "url": "https://twitter.com",
+ "companyId": "twitter"
},
"twittercounter": {
"name": "TwitterCounter",
"categoryId": 6,
- "url": "http://twittercounter.com/"
+ "url": "http://twittercounter.com/",
+ "companyId": "twitter_counter"
},
"twyn": {
"name": "Twyn",
"categoryId": 4,
- "url": "http://www.twyn.com"
+ "url": "http://www.twyn.com",
+ "companyId": "twyn"
},
"txxx.com": {
"name": "txxx.com",
"categoryId": 8,
- "url": "https://txxx.com"
+ "url": "https://txxx.com",
+ "companyId": null
},
"tynt": {
"name": "33Across",
"categoryId": 4,
- "url": "http://www.tynt.com/"
+ "url": "http://www.tynt.com/",
+ "companyId": "33across"
},
"typeform": {
"name": "Typeform",
"categoryId": 2,
- "url": "https://www.typeform.com/"
+ "url": "https://www.typeform.com/",
+ "companyId": null
},
"typepad_stats": {
"name": "Typepad Stats",
"categoryId": 6,
- "url": "http://www.typepad.com/features/statistics.ht"
+ "url": "http://www.typepad.com/features/statistics.ht",
+ "companyId": "typepad"
},
"typography.com": {
"name": "Webfonts by Hoefler&Co",
"categoryId": 9,
- "url": "https://www.typography.com/"
+ "url": "https://www.typography.com/",
+ "companyId": null
},
"tyroo": {
"name": "Tyroo",
"categoryId": 7,
- "url": "http://www.tyroo.com/"
+ "url": "http://www.tyroo.com/",
+ "companyId": "tyroo"
},
"tzetze": {
"name": "TzeTze",
"categoryId": 2,
- "url": "http://www.tzetze.it/"
+ "url": "http://www.tzetze.it/",
+ "companyId": "tzetze"
},
"ubersetzung-app.com": {
"name": "ubersetzung-app.com",
"categoryId": 12,
- "url": "https://www.ubersetzung-app.com/"
+ "url": "https://www.ubersetzung-app.com/",
+ "companyId": null
},
"ucfunnel": {
"name": "ucfunnel",
"categoryId": 4,
- "url": "https://www.ucfunnel.com/"
+ "url": "https://www.ucfunnel.com/",
+ "companyId": "ucfunnel"
},
"ucoz": {
"name": "uCoz",
"categoryId": 6,
- "url": "http://www.ucoz.net/"
+ "url": "http://www.ucoz.net/",
+ "companyId": "ucoz"
},
"uliza": {
"name": "Uliza",
"categoryId": 4,
- "url": "http://uliza.jp/index.html"
+ "url": "http://uliza.jp/index.html",
+ "companyId": "uliza"
},
"umbel": {
"name": "Umbel",
"categoryId": 6,
- "url": "http://umbel.com"
+ "url": "http://umbel.com",
+ "companyId": "umbel"
},
"umebiggestern.club": {
"name": "umebiggestern.club",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"unanimis": {
"name": "Unanimis",
"categoryId": 4,
- "url": "http://www.unanimis.co.uk/"
+ "url": "http://www.unanimis.co.uk/",
+ "companyId": "switch_concepts"
},
"unbounce": {
"name": "Unbounce",
"categoryId": 6,
- "url": "http://unbounce.com/"
+ "url": "http://unbounce.com/",
+ "companyId": "unbounce"
},
"unbxd": {
"name": "UNBXD",
"categoryId": 6,
- "url": "http://unbxd.com/"
+ "url": "http://unbxd.com/",
+ "companyId": "unbxd"
},
"under-box.com": {
"name": "under-box.com",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"undercomputer.com": {
"name": "undercomputer.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"underdog_media": {
"name": "Underdog Media",
"categoryId": 4,
- "url": "http://www.underdogmedia.com"
+ "url": "http://www.underdogmedia.com",
+ "companyId": "underdog_media"
},
"undertone": {
"name": "Undertone",
"categoryId": 4,
- "url": "https://www.undertone.com/"
+ "url": "https://www.undertone.com/",
+ "companyId": "perion"
},
"unica": {
"name": "Unica",
"categoryId": 2,
- "url": "http://www.unica.com/"
+ "url": "http://www.unica.com/",
+ "companyId": "ibm"
},
"unister": {
"name": "Unister",
"categoryId": 6,
- "url": "http://www.unister.de/"
+ "url": "http://www.unister.de/",
+ "companyId": "unister"
},
"unite": {
"name": "Unite",
"categoryId": 4,
- "url": "http://unite.me/#"
+ "url": "http://unite.me/#",
+ "companyId": "unite"
},
"united_digital_group": {
"name": "United Digital Group",
"categoryId": 4,
- "url": "https://www.udg.de/"
+ "url": "https://www.udg.de/",
+ "companyId": "united_digital_group"
},
"united_internet_media_gmbh": {
"name": "United Internet Media GmbH",
"categoryId": 4,
- "url": "https://www.united-internet.de/"
+ "url": "https://www.united-internet.de/",
+ "companyId": "united_internet"
},
"univide": {
"name": "Univide",
"categoryId": 4,
- "url": "http://www.oracle.com/"
+ "url": "http://www.oracle.com/",
+ "companyId": "oracle"
},
"unpkg.com": {
"name": "unpkg",
"categoryId": 9,
- "url": "https://unpkg.com/#/"
+ "url": "https://unpkg.com/#/",
+ "companyId": null
},
"unruly_media": {
"name": "Unruly Media",
"categoryId": 4,
- "url": "http://www.unrulymedia.com/"
+ "url": "http://www.unrulymedia.com/",
+ "companyId": "unruly"
},
"untriel_finger_printing": {
"name": "Untriel Finger Printing",
"categoryId": 6,
- "url": "https://www.untriel.nl/"
+ "url": "https://www.untriel.nl/",
+ "companyId": "untriel"
},
"upland_clickability_beacon": {
"name": "Upland Clickability Beacon",
"categoryId": 4,
- "url": "http://www.clickability.com/"
+ "url": "http://www.clickability.com/",
+ "companyId": "upland_software"
},
"uppr.de": {
"name": "uppr GmbH",
"categoryId": 4,
- "url": "https://uppr.de/"
+ "url": "https://uppr.de/",
+ "companyId": null
},
"upravel.com": {
"name": "upravel.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"upsellit": {
"name": "UpSellit",
"categoryId": 2,
- "url": "http://www.upsellit.com"
+ "url": "http://www.upsellit.com",
+ "companyId": "upsellit"
},
"upsight": {
"name": "Upsight",
"categoryId": 6,
- "url": "http://www.upsight.com/"
+ "url": "http://www.upsight.com/",
+ "companyId": "upsight"
},
"uptain": {
"name": "Uptain",
"categoryId": 6,
- "url": "http://www.uptain.de/en/regaining-lost-customers/"
+ "url": "http://www.uptain.de/en/regaining-lost-customers/",
+ "companyId": "uptain"
},
"uptolike.com": {
"name": "Uptolike",
"categoryId": 7,
- "url": "https://www.uptolike.com/"
+ "url": "https://www.uptolike.com/",
+ "companyId": "uptolike"
},
"uptrends": {
"name": "Uptrends",
"categoryId": 6,
- "url": "http://www.uptrends.com/"
+ "url": "http://www.uptrends.com/",
+ "companyId": "uptrends"
},
"urban-media.com": {
"name": "Urban Media GmbH",
"categoryId": 4,
- "url": "https://www.urban-media.com/"
+ "url": "https://www.urban-media.com/",
+ "companyId": null
},
"urban_airship": {
"name": "Urban Airship",
"categoryId": 6,
- "url": "https://www.urbanairship.com/"
+ "url": "https://www.urbanairship.com/",
+ "companyId": "urban_airship"
},
"usability_tools": {
"name": "Usability Tools",
"categoryId": 6,
- "url": "http://usabilitytools.com/"
+ "url": "http://usabilitytools.com/",
+ "companyId": "usability_tools"
},
"usabilla": {
"name": "Usabilla",
"categoryId": 2,
- "url": "https://usabilla.com/"
+ "url": "https://usabilla.com/",
+ "companyId": "usabilla"
},
"usemax": {
"name": "Usemax",
"categoryId": 4,
- "url": "http://www.usemax.de"
+ "url": "http://www.usemax.de",
+ "companyId": "usemax"
},
"usemessages.com": {
"name": "usemessages.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"usercycle": {
"name": "USERcycle",
"categoryId": 6,
- "url": "http://usercycle.com/"
+ "url": "http://usercycle.com/",
+ "companyId": "usercycle"
},
"userdive": {
"name": "USERDIVE",
"categoryId": 6,
- "url": "http://userdive.com/"
+ "url": "http://userdive.com/",
+ "companyId": "userdive"
},
"userecho": {
"name": "UserEcho",
"categoryId": 2,
- "url": "http://userecho.com"
+ "url": "http://userecho.com",
+ "companyId": "userecho"
},
"userlike.com": {
"name": "Userlike",
"categoryId": 2,
- "url": "https://www.userlike.com/"
+ "url": "https://www.userlike.com/",
+ "companyId": "userlike"
},
"userpulse": {
"name": "UserPulse",
"categoryId": 2,
- "url": "http://www.userpulse.com/"
+ "url": "http://www.userpulse.com/",
+ "companyId": "userpulse"
},
"userreplay": {
"name": "UserReplay",
"categoryId": 6,
- "url": "https://www.userreplay.com/"
+ "url": "https://www.userreplay.com/",
+ "companyId": "userreplay"
},
"userreport": {
"name": "UserReport",
"categoryId": 2,
- "url": "http://www.userreport.com/"
+ "url": "http://www.userreport.com/",
+ "companyId": "userreport"
},
"userrules": {
"name": "UserRules",
"categoryId": 2,
- "url": "http://www.userrules.com/"
+ "url": "http://www.userrules.com/",
+ "companyId": "userrules_software"
},
"usersnap": {
"name": "Usersnap",
"categoryId": 2,
- "url": "http://usersnap.com/"
+ "url": "http://usersnap.com/",
+ "companyId": "usersnap"
},
"uservoice": {
"name": "UserVoice",
"categoryId": 2,
- "url": "http://uservoice.com/"
+ "url": "http://uservoice.com/",
+ "companyId": "uservoice"
},
"userzoom.com": {
"name": "UserZoom",
"categoryId": 2,
- "url": "https://www.userzoom.com/"
+ "url": "https://www.userzoom.com/",
+ "companyId": "userzoom"
},
"usocial": {
"name": "Usocial",
"categoryId": 7,
- "url": "https://usocial.pro/en"
+ "url": "https://usocial.pro/en",
+ "companyId": "usocial"
},
"utarget": {
"name": "uTarget",
"categoryId": 4,
- "url": "http://utarget.ru/"
+ "url": "http://utarget.ru/",
+ "companyId": "utarget"
},
"uuidksinc.net": {
"name": "uuidksinc.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"v12_group": {
"name": "V12 Group",
"categoryId": 6,
- "url": null
+ "url": null,
+ "companyId": null
},
"vacaneedasap.com": {
"name": "vacaneedasap.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"valassis": {
"name": "Valassis",
"categoryId": 4,
- "url": "http://www.brand.net/"
+ "url": "http://www.brand.net/",
+ "companyId": "valassis"
},
"validclick": {
"name": "ValidClick",
"categoryId": 4,
- "url": "http://inuvo.com/"
+ "url": "http://inuvo.com/",
+ "companyId": "inuvo"
},
"valiton": {
"name": "Valiton",
"categoryId": 4,
- "url": "https://www.valiton.com/"
+ "url": "https://www.valiton.com/",
+ "companyId": "hubert_burda_media"
},
"valueclick_media": {
"name": "ValueClick Media",
"categoryId": 4,
- "url": "https://www.conversantmedia.eu/"
+ "url": "https://www.conversantmedia.eu/",
+ "companyId": "conversant"
},
"valuecommerce": {
"name": "ValueCommerce",
"categoryId": 4,
- "url": "https://www.valuecommerce.ne.jp"
+ "url": "https://www.valuecommerce.ne.jp",
+ "companyId": "valuecommerce"
},
"valued_opinions": {
"name": "Valued Opinions",
"categoryId": 4,
- "url": "http://valuedopinions.com"
+ "url": "http://valuedopinions.com",
+ "companyId": "valued_opinions"
},
"vanksen": {
"name": "Vanksen",
"categoryId": 4,
- "url": "http://www.buzzparadise.com/"
+ "url": "http://www.buzzparadise.com/",
+ "companyId": "vanksen"
},
"varick_media_management": {
"name": "Varick Media Management",
"categoryId": 4,
- "url": "http://www.varickmm.com/"
+ "url": "http://www.varickmm.com/",
+ "companyId": "varick_media_management"
},
"vcita": {
"name": "Vcita",
"categoryId": 6,
- "url": "https://www.vcita.com/"
+ "url": "https://www.vcita.com/",
+ "companyId": "vcita"
},
"vcommission": {
"name": "vCommission",
"categoryId": 4,
- "url": "http://www.vcommission.com/"
+ "url": "http://www.vcommission.com/",
+ "companyId": "vcommission"
},
"vdopia": {
"name": "Vdopia",
"categoryId": 4,
- "url": "http://mobile.vdopia.com/"
+ "url": "http://mobile.vdopia.com/",
+ "companyId": "vdopia"
},
"ve_interactive": {
"name": "Ve Interactive",
"categoryId": 4,
- "url": "https://www.veinteractive.com"
+ "url": "https://www.veinteractive.com",
+ "companyId": "ve_interactive"
},
"vee24": {
"name": "VEE24",
"categoryId": 0,
- "url": "https://www.vee24.com/"
+ "url": "https://www.vee24.com/",
+ "companyId": "vee24"
},
"velocecdn.com": {
"name": "velocecdn.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"velti_mgage_visualize": {
"name": "Velti mGage Visualize",
"categoryId": 4,
- "url": "http://www.velti.com/"
+ "url": "http://www.velti.com/",
+ "companyId": "velti"
},
"vendemore": {
"name": "Vendemore",
"categoryId": 1,
- "url": "https://vendemore.com/"
+ "url": "https://vendemore.com/",
+ "companyId": "ratos"
},
"venturead.com": {
"name": "venturead.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"venyoo": {
"name": "Venyoo",
"categoryId": 2,
- "url": "http://venyoo.ru/"
+ "url": "http://venyoo.ru/",
+ "companyId": "venyoo"
},
"veoxa": {
"name": "Veoxa",
"categoryId": 4,
- "url": "http://www.veoxa.com/"
+ "url": "http://www.veoxa.com/",
+ "companyId": "veoxa"
},
"vergic.com": {
"name": "Vergic",
"categoryId": 1,
- "url": "https://www.vergic.com/"
+ "url": "https://www.vergic.com/",
+ "companyId": null
},
"vero": {
"name": "Vero",
"categoryId": 4,
- "url": "http://www.getvero.com/"
+ "url": "http://www.getvero.com/",
+ "companyId": "vero"
},
"vertical_acuity": {
"name": "Vertical Acuity",
"categoryId": 4,
- "url": "http://www.verticalacuity.com/"
+ "url": "http://www.verticalacuity.com/",
+ "companyId": "outbrain"
},
"vertical_leap": {
"name": "Vertical Leap",
"categoryId": 4,
- "url": "http://www.vertical-leap.co.uk/"
+ "url": "http://www.vertical-leap.co.uk/",
+ "companyId": "vertical_leap"
},
"verticalresponse": {
"name": "VerticalResponse",
"categoryId": 4,
- "url": "http://www.verticalresponse.com"
+ "url": "http://www.verticalresponse.com",
+ "companyId": "verticalresponse"
},
"verticalscope": {
"name": "VerticalScope",
"categoryId": 4,
- "url": "http://www.verticalscope.com"
+ "url": "http://www.verticalscope.com",
+ "companyId": "verticalscope"
},
"vertoz": {
"name": "Vertoz",
"categoryId": 4,
- "url": "http://www.vertoz.com/"
+ "url": "http://www.vertoz.com/",
+ "companyId": "vertoz"
},
"veruta": {
"name": "Veruta",
"categoryId": 4,
- "url": "http://www.veruta.com/"
+ "url": "http://www.veruta.com/",
+ "companyId": "veruta"
},
"verve_mobile": {
"name": "Verve Mobile",
"categoryId": 4,
- "url": "http://www.vervemobile.com/"
+ "url": "http://www.vervemobile.com/",
+ "companyId": "verve_mobile"
},
"vg_wort": {
"name": "VG Wort",
"categoryId": 6,
- "url": "https://tom.vgwort.de/portal/showHelp"
+ "url": "https://tom.vgwort.de/portal/showHelp",
+ "companyId": "vg_wort"
},
"vi": {
"name": "Vi",
"categoryId": 4,
- "url": "http://www.vi.ru/"
+ "url": "http://www.vi.ru/",
+ "companyId": "vi"
},
"viacom_tag_container": {
"name": "Viacom Tag Container",
"categoryId": 4,
- "url": "http://www.viacom.com/"
+ "url": "http://www.viacom.com/",
+ "companyId": "viacom"
},
"viafoura": {
"name": "Viafoura",
"categoryId": 4,
- "url": "http://www.viafoura.com/"
+ "url": "http://www.viafoura.com/",
+ "companyId": "viafoura"
},
"vibrant_ads": {
"name": "Vibrant Ads",
"categoryId": 4,
- "url": "http://www.vibrantmedia.com/"
+ "url": "http://www.vibrantmedia.com/",
+ "companyId": "vibrant_media"
},
"vicomi.com": {
"name": "Vicomi",
"categoryId": 6,
- "url": "http://www.vicomi.com/"
+ "url": "http://www.vicomi.com/",
+ "companyId": "vicomi"
},
"vidazoo.com": {
"name": "Vidazoo",
"categoryId": 4,
- "url": "https://www.vidazoo.com/"
+ "url": "https://www.vidazoo.com/",
+ "companyId": null
},
"video_desk": {
"name": "Video Desk",
"categoryId": 0,
- "url": "https://www.videodesk.com/"
+ "url": "https://www.videodesk.com/",
+ "companyId": "video_desk"
},
"video_potok": {
"name": "Video Potok",
"categoryId": 0,
- "url": "http://videopotok.pro/"
+ "url": "http://videopotok.pro/",
+ "companyId": "videopotok"
},
"videoadex.com": {
"name": "VideoAdX",
"categoryId": 4,
- "url": "https://www.videoadex.com/"
+ "url": "https://www.videoadex.com/",
+ "companyId": "digiteka"
},
"videology": {
"name": "Videology",
"categoryId": 4,
- "url": "https://videologygroup.com/"
+ "url": "https://videologygroup.com/",
+ "companyId": "singtel"
},
"videonow": {
"name": "VideoNow",
"categoryId": 4,
- "url": "https://videonow.ru/"
+ "url": "https://videonow.ru/",
+ "companyId": "videonow"
},
"videoplayerhub.com": {
"name": "videoplayerhub.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"videoplaza": {
"name": "Videoplaza",
"categoryId": 4,
- "url": "http://www.videoplaza.com/"
+ "url": "http://www.videoplaza.com/",
+ "companyId": "videoplaza"
},
"videostep": {
"name": "VideoStep",
"categoryId": 4,
- "url": "https://www.videostep.com/"
+ "url": "https://www.videostep.com/",
+ "companyId": "videostep"
},
"vidgyor": {
"name": "Vidgyor",
"categoryId": 0,
- "url": "http://vidgyor.com/"
+ "url": "http://vidgyor.com/",
+ "companyId": "vidgyor"
},
"vidible": {
"name": "Vidible",
"categoryId": 4,
- "url": "http://vidible.tv/"
+ "url": "http://vidible.tv/",
+ "companyId": "verizon"
},
"vidora": {
"name": "Vidora",
"categoryId": 0,
- "url": "https://www.vidora.com/"
+ "url": "https://www.vidora.com/",
+ "companyId": "vidora"
},
"vietad": {
"name": "VietAd",
"categoryId": 4,
- "url": "http://vietad.vn/"
+ "url": "http://vietad.vn/",
+ "companyId": "vietad"
},
"viglink": {
"name": "VigLink",
"categoryId": 4,
- "url": "http://www.viglink.com"
+ "url": "http://www.viglink.com",
+ "companyId": "viglink"
},
"vigo": {
"name": "Vigo",
"categoryId": 6,
- "url": "https://vigo.one/"
+ "url": "https://vigo.one/",
+ "companyId": "vigo"
},
"vimeo": {
"name": "Vimeo",
"categoryId": 0,
- "url": "http://vimeo.com/"
+ "url": "http://vimeo.com/",
+ "companyId": "vimeo"
},
"vindico_group": {
"name": "Vindico Group",
"categoryId": 4,
- "url": "http://www.vindicogroup.com/"
+ "url": "http://www.vindicogroup.com/",
+ "companyId": "vindico_group"
},
"vinted": {
"name": "Vinted",
"categoryId": 8,
- "url": "https://www.vinted.com/"
+ "url": "https://www.vinted.com/",
+ "companyId": null
},
"viral_ad_network": {
"name": "Viral Ad Network",
"categoryId": 4,
- "url": "http://viraladnetwork.joinvan.com/"
+ "url": "http://viraladnetwork.joinvan.com/",
+ "companyId": "viral_ad_network"
},
"viral_loops": {
"name": "Viral Loops",
"categoryId": 2,
- "url": "https://viral-loops.com/"
+ "url": "https://viral-loops.com/",
+ "companyId": "viral-loops"
},
"viralgains": {
"name": "ViralGains",
"categoryId": 4,
- "url": "https://www.viralgains.com/"
+ "url": "https://www.viralgains.com/",
+ "companyId": null
},
"viralmint": {
"name": "ViralMint",
"categoryId": 7,
- "url": "http://www.viralmint.com"
+ "url": "http://www.viralmint.com",
+ "companyId": "viralmint"
},
"virgul": {
"name": "Virgul",
"categoryId": 4,
- "url": "http://www.virgul.com/"
+ "url": "http://www.virgul.com/",
+ "companyId": "virgul"
},
"virool_player": {
"name": "Virool Player",
"categoryId": 4,
- "url": "https://www.virool.com/"
+ "url": "https://www.virool.com/",
+ "companyId": "virool"
},
"virtusize": {
"name": "Virtusize",
"categoryId": 5,
- "url": "http://www.virtusize.com/"
+ "url": "http://www.virtusize.com/",
+ "companyId": "virtusize"
},
"visible_measures": {
"name": "Visible Measures",
"categoryId": 4,
- "url": "http://www.visiblemeasures.com/"
+ "url": "http://www.visiblemeasures.com/",
+ "companyId": "visible_measures"
},
"vision_critical": {
"name": "Vision Critical",
"categoryId": 6,
- "url": "http://visioncritical.com/"
+ "url": "http://visioncritical.com/",
+ "companyId": "vision_critical"
},
"visit_streamer": {
"name": "Visit Streamer",
"categoryId": 6,
- "url": "http://www.visitstreamer.com/"
+ "url": "http://www.visitstreamer.com/",
+ "companyId": "visit_streamer"
},
"visitortrack": {
"name": "VisitorTrack",
"categoryId": 4,
- "url": "http://www.netfactor.com/"
+ "url": "http://www.netfactor.com/",
+ "companyId": "netfactor"
},
"visitorville": {
"name": "VisitorVille",
"categoryId": 6,
- "url": "http://www.visitorville.com"
+ "url": "http://www.visitorville.com",
+ "companyId": "visitorville"
},
"visscore": {
"name": "VisScore",
"categoryId": 4,
- "url": "http://withcubed.com/"
+ "url": "http://withcubed.com/",
+ "companyId": "cubed_attribution"
},
"visual_iq": {
"name": "Visual IQ",
"categoryId": 6,
- "url": "http://visualiq.com/"
+ "url": "http://visualiq.com/",
+ "companyId": "visualiq"
},
"visual_revenue": {
"name": "Visual Revenue",
"categoryId": 6,
- "url": "http://visualrevenue.com/"
+ "url": "http://visualrevenue.com/",
+ "companyId": "outbrain"
},
"visual_website_optimizer": {
"name": "VWO",
"categoryId": 6,
- "url": "https://vwo.com/"
+ "url": "https://vwo.com/",
+ "companyId": "wingify"
},
"visualdna": {
"name": "VisualDNA",
"categoryId": 4,
- "url": "http://www.visualdna.com/"
+ "url": "http://www.visualdna.com/",
+ "companyId": "nielsen"
},
"visualstudio.com": {
"name": "Visualstudio.com",
"categoryId": 8,
- "url": "https://www.visualstudio.com/"
+ "url": "https://www.visualstudio.com/",
+ "companyId": "microsoft"
},
"visualvisitor": {
"name": "VisualVisitor",
"categoryId": 6,
- "url": "http://www.visualvisitor.com/"
+ "url": "http://www.visualvisitor.com/",
+ "companyId": "visualvisitor"
},
"vivalu": {
"name": "VIVALU",
"categoryId": 4,
- "url": "https://www.vivalu.com/"
+ "url": "https://www.vivalu.com/",
+ "companyId": "vivalu"
},
"vivistats": {
"name": "ViviStats",
"categoryId": 6,
- "url": "http://en.vivistats.com/"
+ "url": "http://en.vivistats.com/",
+ "companyId": "vivistats"
},
"vizury": {
"name": "Vizury",
"categoryId": 4,
- "url": "http://www.vizury.com/website/"
+ "url": "http://www.vizury.com/website/",
+ "companyId": "vizury"
},
"vizzit": {
"name": "Vizzit",
"categoryId": 4,
- "url": "http://www.vizzit.se/h/en/"
+ "url": "http://www.vizzit.se/h/en/",
+ "companyId": "vizzit"
},
"vk.com": {
"name": "Vk.com",
"categoryId": 7,
- "url": "https://vk.com/"
+ "url": "https://vk.com/",
+ "companyId": "megafon"
},
"vkontakte": {
"name": "VKontakte",
"categoryId": 7,
- "url": "https://vk.com/"
+ "url": "https://vk.com/",
+ "companyId": "megafon"
},
"vkontakte_widgets": {
"name": "VKontakte Widgets",
"categoryId": 7,
- "url": "http://vk.com/developers.php"
+ "url": "http://vk.com/developers.php",
+ "companyId": "megafon"
},
"vntsm.com": {
"name": "Venatus Media",
"categoryId": 4,
- "url": "https://www.venatusmedia.com/"
+ "url": "https://www.venatusmedia.com/",
+ "companyId": "venatus"
},
"vodafone.de": {
"name": "vodafone.de",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"voicefive": {
"name": "VoiceFive",
"categoryId": 6,
- "url": "https://www.voicefive.com"
+ "url": "https://www.voicefive.com",
+ "companyId": "comscore"
},
"volusion_chat": {
"name": "Volusion Chat",
"categoryId": 2,
- "url": "https://www.volusion.com/"
+ "url": "https://www.volusion.com/",
+ "companyId": "volusion"
},
"voluum": {
"name": "Voluum",
"categoryId": 4,
- "url": "https://voluum.com/"
+ "url": "https://voluum.com/",
+ "companyId": "codewise"
},
"vooxe.com": {
"name": "vooxe.com",
"categoryId": 8,
- "url": "http://www.vooxe.com/"
+ "url": "http://www.vooxe.com/",
+ "companyId": null
},
"vorwerk.de": {
"name": "vorwerk.de",
"categoryId": 8,
- "url": "https://corporate.vorwerk.de/home/"
+ "url": "https://corporate.vorwerk.de/home/",
+ "companyId": null
},
"vox": {
"name": "Vox",
"categoryId": 2,
- "url": "https://www.voxmedia.com/"
+ "url": "https://www.voxmedia.com/",
+ "companyId": "vox"
},
"voxus": {
"name": "Voxus",
"categoryId": 4,
- "url": "http://www.voxus.tv/"
+ "url": "http://www.voxus.tv/",
+ "companyId": "voxus"
},
"vpon": {
"name": "VPON",
"categoryId": 4,
- "url": "http://www.vpon.com/en/"
+ "url": "http://www.vpon.com/en/",
+ "companyId": "vpon"
},
"vpscash": {
"name": "VPSCash",
"categoryId": 4,
- "url": "http://vpscash.nl/home"
+ "url": "http://vpscash.nl/home",
+ "companyId": "vps_cash"
},
"vtracy.de": {
"name": "vtracy.de",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"vuukle": {
"name": "Vuukle",
"categoryId": 6,
- "url": "http://vuukle.com/"
+ "url": "http://vuukle.com/",
+ "companyId": "vuukle"
},
"vzaar": {
"name": "Vzaar",
"categoryId": 0,
- "url": "http://vzaar.com/"
+ "url": "http://vzaar.com/",
+ "companyId": "vzaar"
},
"w3counter": {
"name": "W3Counter",
"categoryId": 6,
- "url": "http://www.w3counter.com/"
+ "url": "http://www.w3counter.com/",
+ "companyId": "awio_web_services"
},
"w3roi": {
"name": "w3roi",
"categoryId": 6,
- "url": "http://www.w3roi.com/"
+ "url": "http://www.w3roi.com/",
+ "companyId": "w3roi"
},
"wahoha": {
"name": "Wahoha",
"categoryId": 2,
- "url": "http://wahoha.com/"
+ "url": "http://wahoha.com/",
+ "companyId": "wahoha"
},
"walkme.com": {
"name": "WalkMe",
"categoryId": 2,
- "url": "https://www.walkme.com/"
+ "url": "https://www.walkme.com/",
+ "companyId": "walkme"
},
"wall_street_on_demand": {
"name": "Wall Street on Demand",
"categoryId": 4,
- "url": "http://www.wallst.com"
+ "url": "http://www.wallst.com",
+ "companyId": "markit_on_demand"
},
"walmart": {
"name": "Walmart",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"wamcash": {
"name": "Wamcash",
"categoryId": 3,
- "url": "http://wamcash.com/"
+ "url": "http://wamcash.com/",
+ "companyId": "wamcash"
},
"wanelo": {
"name": "Wanelo",
"categoryId": 2,
- "url": "https://wanelo.com/"
+ "url": "https://wanelo.com/",
+ "companyId": "wanelo"
},
"warp.ly": {
"name": "Warp.ly",
"categoryId": 6,
- "url": "https://warp.ly/"
+ "url": "https://warp.ly/",
+ "companyId": "warp.ly"
},
"way2traffic": {
"name": "Way2traffic",
"categoryId": 4,
- "url": "http://www.way2traffic.com/"
+ "url": "http://www.way2traffic.com/",
+ "companyId": "way2traffic"
},
"wayfair_com": {
"name": "Wayfair",
"categoryId": 8,
- "url": "https://www.wayfair.com/"
+ "url": "https://www.wayfair.com/",
+ "companyId": null
},
"wdr.de": {
"name": "wdr.de",
"categoryId": 8,
- "url": "https://www1.wdr.de/index.html"
+ "url": "https://www1.wdr.de/index.html",
+ "companyId": null
},
"web-stat": {
"name": "Web-Stat",
"categoryId": 6,
- "url": "http://www.web-stat.net/"
+ "url": "http://www.web-stat.net/",
+ "companyId": "web-stat"
},
"web.de": {
"name": "web.de",
"categoryId": 8,
- "url": "https://web.de/"
+ "url": "https://web.de/",
+ "companyId": null
},
"web.stat": {
"name": "Web.STAT",
"categoryId": 6,
- "url": "http://webstat.net/"
+ "url": "http://webstat.net/",
+ "companyId": "web.stat"
},
"web_service_award": {
"name": "Web Service Award",
"categoryId": 6,
- "url": "http://webserviceaward.com/english/"
+ "url": "http://webserviceaward.com/english/",
+ "companyId": "web_service_award"
},
"web_traxs": {
"name": "Web Traxs",
"categoryId": 6,
- "url": "http://websolutions.thomasnet.com/web-traxs-analytics.php"
+ "url": "http://websolutions.thomasnet.com/web-traxs-analytics.php",
+ "companyId": "thomasnet_websolutions"
},
"web_wipe_analytics": {
"name": "Web Wipe Analytics",
"categoryId": 6,
- "url": "http://tensquare.de"
+ "url": "http://tensquare.de",
+ "companyId": "tensquare"
},
"webads": {
"name": "WebAds",
"categoryId": 4,
- "url": "http://www.webads.co.uk/"
+ "url": "http://www.webads.co.uk/",
+ "companyId": "webads"
},
"webantenna": {
"name": "WebAntenna",
"categoryId": 6,
- "url": "http://www.bebit.co.jp/webantenna/"
+ "url": "http://www.bebit.co.jp/webantenna/",
+ "companyId": "webantenna"
},
"webclicks24_com": {
"name": "webclicks24.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"webclose.net": {
"name": "webclose.net",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"webcollage": {
"name": "Webcollage",
"categoryId": 2,
- "url": "http://www.webcollage.com/"
+ "url": "http://www.webcollage.com/",
+ "companyId": "webcollage"
},
"webedia": {
"name": "Webedia",
"categoryId": 4,
- "url": "http://fr.webedia-group.com/"
+ "url": "http://fr.webedia-group.com/",
+ "companyId": "fimalac_group"
},
"webeffective": {
"name": "WebEffective",
"categoryId": 6,
- "url": "http://www.keynote.com/"
+ "url": "http://www.keynote.com/",
+ "companyId": "keynote_systems"
},
"webengage": {
"name": "WebEngage",
"categoryId": 2,
- "url": "http://webengage.com/"
+ "url": "http://webengage.com/",
+ "companyId": "webengage"
},
"webgains": {
"name": "Webgains",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"webgozar": {
"name": "WebGozar",
"categoryId": 6,
- "url": "http://webgozar.com/"
+ "url": "http://webgozar.com/",
+ "companyId": "webgozar"
},
"webhelpje": {
"name": "Webhelpje",
"categoryId": 2,
- "url": "http://www.webhelpje.nl/"
+ "url": "http://www.webhelpje.nl/",
+ "companyId": "webhelpje"
},
"webleads_tracker": {
"name": "Webleads Tracker",
"categoryId": 6,
- "url": "http://www.webleads-tracker.fr/"
+ "url": "http://www.webleads-tracker.fr/",
+ "companyId": "webleads_tracker"
},
"webmecanik": {
"name": "Webmecanik",
"categoryId": 6,
- "url": "http://www.webmecanik.com/en/"
+ "url": "http://www.webmecanik.com/en/",
+ "companyId": "webmecanik"
},
"weborama": {
"name": "Weborama",
"categoryId": 4,
- "url": "https://weborama.com/"
+ "url": "https://weborama.com/",
+ "companyId": "weborama"
},
"webprospector": {
"name": "WebProspector",
"categoryId": 6,
- "url": "http://www.webprospector.de/"
+ "url": "http://www.webprospector.de/",
+ "companyId": "webprospector"
},
"webstat": {
"name": "WebSTAT",
"categoryId": 6,
- "url": "http://www.webstat.com/"
+ "url": "http://www.webstat.com/",
+ "companyId": "webstat"
},
"webstat.se": {
"name": "Webstat.se",
"categoryId": 6,
- "url": "http://www.webstat.se/"
+ "url": "http://www.webstat.se/",
+ "companyId": "webstat.se"
},
"webtrack": {
"name": "webtrack",
"categoryId": 6,
- "url": "http://www.webtrack.biz/"
+ "url": "http://www.webtrack.biz/",
+ "companyId": "webtrack"
},
"webtraffic": {
"name": "Webtraffic",
"categoryId": 6,
- "url": "http://www.webtraffic.se/"
+ "url": "http://www.webtraffic.se/",
+ "companyId": "schibsted_asa"
},
"webtrekk": {
"name": "Webtrekk",
"categoryId": 6,
- "url": "http://www.webtrekk.com/"
+ "url": "http://www.webtrekk.com/",
+ "companyId": "webtrekk"
},
"webtrekk_cc": {
"name": "Webtrek Control Cookie",
"categoryId": 6,
- "url": "https://www.webtrekk.com/en/home/"
+ "url": "https://www.webtrekk.com/en/home/",
+ "companyId": "webtrekk"
},
"webtrends": {
"name": "Webtrends",
"categoryId": 6,
- "url": "http://www.webtrends.com/"
+ "url": "http://www.webtrends.com/",
+ "companyId": "webtrends"
},
"webtrends_ads": {
"name": "Webtrends Ads",
"categoryId": 4,
- "url": "http://www.webtrends.com"
+ "url": "http://www.webtrends.com",
+ "companyId": "webtrends"
},
"webvisor": {
"name": "WebVisor",
"categoryId": 6,
- "url": "http://webvisor.ru"
+ "url": "http://webvisor.ru",
+ "companyId": "yandex"
},
"wedcs": {
"name": "WEDCS",
"categoryId": 4,
- "url": "https://www.microsoft.com/"
+ "url": "https://www.microsoft.com/",
+ "companyId": "microsoft"
},
"weebly_ads": {
"name": "Weebly Ads",
"categoryId": 4,
- "url": "http://www.weebly.com"
+ "url": "http://www.weebly.com",
+ "companyId": "weebly"
},
"weibo_widget": {
"name": "Weibo Widget",
"categoryId": 4,
- "url": "http://www.sina.com/"
+ "url": "http://www.sina.com/",
+ "companyId": "sina"
},
"westlotto_com": {
"name": "westlotto.com",
"categoryId": 8,
- "url": "http://westlotto.com/"
+ "url": "http://westlotto.com/",
+ "companyId": null
},
"wetter_com": {
"name": "Wetter.com",
"categoryId": 8,
- "url": "http://www.wetter.com/"
+ "url": "http://www.wetter.com/",
+ "companyId": null
},
"whatbroadcast": {
"name": "Whatbroadcast",
"categoryId": 2,
- "url": "https://www.whatsbroadcast.com/"
+ "url": "https://www.whatsbroadcast.com/",
+ "companyId": "whatsbroadcast"
},
"whos.amung.us": {
"name": "Whos.amung.us",
"categoryId": 6,
- "url": "http://whos.amung.us/"
+ "url": "http://whos.amung.us/",
+ "companyId": "whos.amung.us"
},
"whoson": {
"name": "WhosOn",
"categoryId": 6,
- "url": "http://www.whoson.com/"
+ "url": "http://www.whoson.com/",
+ "companyId": "whoson"
},
"wibbitz": {
"name": "Wibbitz",
"categoryId": 0,
- "url": "http://www.wibbitz.com/"
+ "url": "http://www.wibbitz.com/",
+ "companyId": "wibbitz"
},
"wibiya_toolbar": {
"name": "Wibiya Toolbar",
"categoryId": 7,
- "url": "http://www.wibiya.com/"
+ "url": "http://www.wibiya.com/",
+ "companyId": "wibiya"
},
"widdit": {
"name": "Widdit",
"categoryId": 2,
- "url": "http://www.predictad.com/"
+ "url": "http://www.predictad.com/",
+ "companyId": "widdit"
},
"widerplanet": {
"name": "WiderPlanet",
"categoryId": 4,
- "url": "http://widerplanet.com/"
+ "url": "http://widerplanet.com/",
+ "companyId": "wider_planet"
},
"widespace": {
"name": "Widespace",
"categoryId": 4,
- "url": "https://www.widespace.com/"
+ "url": "https://www.widespace.com/",
+ "companyId": "widespace"
},
"widgetbox": {
"name": "WidgetBox",
"categoryId": 2,
- "url": "http://www.widgetbox.com/"
+ "url": "http://www.widgetbox.com/",
+ "companyId": "widgetbox"
},
"wiget_media": {
"name": "Wiget Media",
"categoryId": 4,
- "url": "http://wigetmedia.com"
+ "url": "http://wigetmedia.com",
+ "companyId": "wiget_media"
},
"wigzo": {
"name": "Wigzo",
"categoryId": 4,
- "url": "https://www.wigzo.com/"
+ "url": "https://www.wigzo.com/",
+ "companyId": "wigzo"
},
"wikia-services.com": {
"name": "Wikia Services",
"categoryId": 8,
- "url": "http://www.wikia.com/fandom"
+ "url": "http://www.wikia.com/fandom",
+ "companyId": "wikia"
},
"wikia_beacon": {
"name": "Wikia Beacon",
"categoryId": 6,
- "url": "http://www.wikia.com/"
+ "url": "http://www.wikia.com/",
+ "companyId": "wikia"
},
"wikia_cdn": {
"name": "Wikia CDN",
"categoryId": 9,
- "url": "http://www.wikia.com/fandom"
+ "url": "http://www.wikia.com/fandom",
+ "companyId": "wikia"
},
"wikimedia.org": {
"name": "WikiMedia",
"categoryId": 9,
- "url": "https://wikimediafoundation.org/"
+ "url": "https://wikimediafoundation.org/",
+ "companyId": "wikimedia_foundation"
},
"winaffiliates": {
"name": "Winaffiliates",
"categoryId": 6,
- "url": "http://www.winaffiliates.com/"
+ "url": "http://www.winaffiliates.com/",
+ "companyId": "winaffiliates"
},
"wipmania": {
"name": "WIPmania",
"categoryId": 6,
- "url": "http://www.wipmania.com/"
+ "url": "http://www.wipmania.com/",
+ "companyId": "wipmania"
},
"wiqhit": {
"name": "WiQhit",
"categoryId": 6,
- "url": "https://wiqhit.com/nl/"
+ "url": "https://wiqhit.com/nl/",
+ "companyId": "wiqhit"
},
"wirecard": {
"name": "Wirecard",
"categoryId": 2,
- "url": "https://www.wirecard.com/"
+ "url": "https://www.wirecard.com/",
+ "companyId": null
},
"wiredminds": {
"name": "WiredMinds",
"categoryId": 6,
- "url": "http://www.wiredminds.de/"
+ "url": "http://www.wiredminds.de/",
+ "companyId": "wiredminds"
},
"wirtualna_polska": {
"name": "Wirtualna Polska",
"categoryId": 4,
- "url": "http://reklama.wp.pl/"
+ "url": "http://reklama.wp.pl/",
+ "companyId": "wirtualna_polska"
},
"wisepops": {
"name": "WisePops",
"categoryId": 4,
- "url": "http://wisepops.com/"
+ "url": "http://wisepops.com/",
+ "companyId": "wisepops"
},
"wishpond": {
"name": "Wishpond",
"categoryId": 2,
- "url": "http://wishpond.com"
+ "url": "http://wishpond.com",
+ "companyId": "wishpond"
},
"wistia": {
"name": "Wistia",
"categoryId": 6,
- "url": "http://wistia.com/"
+ "url": "http://wistia.com/",
+ "companyId": "wistia"
},
"wix.com": {
"name": "Wix",
"categoryId": 8,
- "url": "https://www.wix.com/"
+ "url": "https://www.wix.com/",
+ "companyId": "wix"
},
"wixab": {
"name": "Wixab",
"categoryId": 6,
- "url": "http://wixab.com/en/"
+ "url": "http://wixab.com/en/",
+ "companyId": "wixab"
},
"wixmp": {
"name": "Wix Media Platform",
"categoryId": 9,
- "url": "https://www.wixmp.com/"
+ "url": "https://www.wixmp.com/",
+ "companyId": "wix"
},
"wnzmauurgol.com": {
"name": "wnzmauurgol.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"wonderpush": {
"name": "WonderPush",
"categoryId": 2,
- "url": "https://www.wonderpush.com/"
+ "url": "https://www.wonderpush.com/",
+ "companyId": "wonderpush"
},
"woopic.com": {
"name": "woopic.com",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"woopra": {
"name": "Woopra",
"categoryId": 6,
- "url": "http://www.woopra.com/"
+ "url": "http://www.woopra.com/",
+ "companyId": "woopra"
},
"wordpress_ads": {
"name": "Wordpress Ads",
"categoryId": 4,
- "url": "https://wordpress.com/"
+ "url": "https://wordpress.com/",
+ "companyId": "automattic"
},
"wordpress_stats": {
"name": "WordPress Stats",
"categoryId": 6,
- "url": "http://wordpress.org/extend/plugins/stats/"
+ "url": "http://wordpress.org/extend/plugins/stats/",
+ "companyId": "automattic"
},
"wordstream": {
"name": "WordStream",
"categoryId": 6,
- "url": "http://www.wordstream.com/"
+ "url": "http://www.wordstream.com/",
+ "companyId": "wordstream"
},
"worldnaturenet_xyz": {
"name": "worldnaturenet.xyz",
"categoryId": 12,
- "url": null
+ "url": null,
+ "companyId": null
},
"wp.pl": {
"name": "Wirtualna Polska ",
"categoryId": 4,
- "url": "https://www.wp.pl/"
+ "url": "https://www.wp.pl/",
+ "companyId": "wp"
},
"wp_engine": {
"name": "WP Engine",
"categoryId": 5,
- "url": "https://wpengine.com/"
+ "url": "https://wpengine.com/",
+ "companyId": "wp_engine"
},
"writeup_clickanalyzer": {
"name": "WriteUp ClickAnalyzer",
"categoryId": 6,
- "url": "http://www.writeup.co.jp/"
+ "url": "http://www.writeup.co.jp/",
+ "companyId": "writeup"
},
"wurfl": {
"name": "WURFL",
"categoryId": 6,
- "url": "https://web.wurfl.io/"
+ "url": "https://web.wurfl.io/",
+ "companyId": "scientiamobile"
},
"wwwpromoter": {
"name": "WWWPromoter",
"categoryId": 4,
- "url": "http://wwwpromoter.com/"
+ "url": "http://wwwpromoter.com/",
+ "companyId": "wwwpromoter"
},
"wykop": {
"name": "Wykop",
"categoryId": 7,
- "url": "http://www.wykop.pl"
+ "url": "http://www.wykop.pl",
+ "companyId": "wykop"
},
"wysistat.com": {
"name": "WysiStat",
"categoryId": 6,
- "url": "https://www.wysistat.net/"
+ "url": "https://www.wysistat.net/",
+ "companyId": "wysistat"
},
"wywy.com": {
"name": "wywy",
"categoryId": 4,
- "url": "http://wywy.com/"
+ "url": "http://wywy.com/",
+ "companyId": "tvsquared"
},
"x-lift": {
"name": "X-lift",
"categoryId": 4,
- "url": "https://www.x-lift.jp/"
+ "url": "https://www.x-lift.jp/",
+ "companyId": "x-lift"
},
"xapads": {
"name": "Xapads",
"categoryId": 4,
- "url": "http://www.xapads.com/"
+ "url": "http://www.xapads.com/",
+ "companyId": "xapads"
},
"xen-media.com": {
"name": "xen-media.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"xfreeservice.com": {
"name": "xfreeservice.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"xhamster": {
"name": "xHamster",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"xing": {
"name": "Xing",
"categoryId": 6,
- "url": "http://www.xing.com/"
+ "url": "http://www.xing.com/",
+ "companyId": "xing"
},
"xmediaclicks": {
"name": "XmediaClicks",
"categoryId": 3,
- "url": "http://exoclick.com/"
+ "url": "http://exoclick.com/",
+ "companyId": "exoclick"
},
"xnxx_cdn": {
"name": "xnxx CDN",
"categoryId": 9,
- "url": "https://www.xnxx.com"
+ "url": "https://www.xnxx.com",
+ "companyId": null
},
"xplosion": {
"name": "xplosion",
"categoryId": 4,
- "url": "http://www.xplosion.de/"
+ "url": "http://www.xplosion.de/",
+ "companyId": "xplosion_interactive"
},
"xtend": {
"name": "XTEND",
"categoryId": 4,
- "url": "http://www.xtendmedia.com/"
+ "url": "http://www.xtendmedia.com/",
+ "companyId": "matomy_media"
},
"xvideos_com": {
"name": "xvideos.com",
"categoryId": 8,
- "url": null
+ "url": null,
+ "companyId": null
},
"xxxlshop.de": {
"name": "xxxlshop.de",
"categoryId": 8,
- "url": "https://www.xxxlshop.de/"
+ "url": "https://www.xxxlshop.de/",
+ "companyId": null
},
"xxxlutz": {
"name": "XXXLutz",
"categoryId": 8,
- "url": "https://www.xxxlutz.de/"
+ "url": "https://www.xxxlutz.de/",
+ "companyId": "xxxlutz"
},
"yabbi": {
"name": "Yabbi",
"categoryId": 4,
- "url": "https://yabbi.me/"
+ "url": "https://yabbi.me/",
+ "companyId": null
},
"yabuka": {
"name": "Yabuka",
"categoryId": 4,
- "url": "http://www.yabuka.com/"
+ "url": "http://www.yabuka.com/",
+ "companyId": "yabuka"
},
"yahoo": {
"name": "Yahoo!",
"categoryId": 6,
- "url": "https://yahoo.com"
+ "url": "https://yahoo.com",
+ "companyId": "verizon"
},
"yahoo_ad_exchange": {
"name": "Yahoo! Ad Exchange",
"categoryId": 4,
- "url": "https://www.verizonmedia.com/advertising"
+ "url": "https://www.verizonmedia.com/advertising",
+ "companyId": "verizon"
},
"yahoo_ad_manager": {
"name": "Yahoo! Ad Manager Plus",
"categoryId": 4,
- "url": "https://developer.yahoo.com/analytics/"
+ "url": "https://developer.yahoo.com/analytics/",
+ "companyId": "verizon"
},
"yahoo_analytics": {
"name": "Yahoo! Analytics",
"categoryId": 6,
- "url": "http://web.analytics.yahoo.com/"
+ "url": "http://web.analytics.yahoo.com/",
+ "companyId": "verizon"
},
"yahoo_commerce_central": {
"name": "Yahoo! Commerce Central",
"categoryId": 4,
- "url": "http://lexity.com/"
+ "url": "http://lexity.com/",
+ "companyId": "verizon"
},
"yahoo_dot_tag": {
"name": "Yahoo! DOT tag",
"categoryId": 4,
- "url": "https://www.verizon.com/"
+ "url": "https://www.verizon.com/",
+ "companyId": "verizon"
},
"yahoo_japan_retargeting": {
"name": "Yahoo! Japan Retargeting",
"categoryId": 4,
- "url": "http://www.yahoo.com/"
+ "url": "http://www.yahoo.com/",
+ "companyId": "yahoo_japan"
},
"yahoo_overture": {
"name": "Yahoo! Overture",
"categoryId": 4,
- "url": "http://searchmarketing.yahoo.com"
+ "url": "http://searchmarketing.yahoo.com",
+ "companyId": "verizon"
},
"yahoo_small_business": {
"name": "Yahoo! Small Business",
"categoryId": 4,
- "url": "http://www.pixazza.com/"
+ "url": "http://www.pixazza.com/",
+ "companyId": "verizon"
},
"yandex": {
"name": "Yandex",
"categoryId": 4,
- "url": "https://www.yandex.com/"
+ "url": "https://www.yandex.com/",
+ "companyId": "yandex"
},
"yandex.api": {
"name": "Yandex.API",
"categoryId": 2,
- "url": "http://api.yandex.ru/"
+ "url": "http://api.yandex.ru/",
+ "companyId": "yandex"
},
"yandex_adexchange": {
"name": "Yandex AdExchange",
"categoryId": 4,
- "url": "https://www.yandex.com/"
+ "url": "https://www.yandex.com/",
+ "companyId": "yandex"
},
"yandex_advisor": {
"name": "Yandex.Advisor",
"categoryId": 12,
- "url": "https://sovetnik.yandex.ru/"
+ "url": "https://sovetnik.yandex.ru/",
+ "companyId": "yandex"
},
"yandex_direct": {
"name": "Yandex.Direct",
"categoryId": 6,
- "url": "https://direct.yandex.com/"
+ "url": "https://direct.yandex.com/",
+ "companyId": "yandex"
},
"yandex_metrika": {
"name": "Yandex Metrika",
"categoryId": 6,
- "url": "https://metrica.yandex.com/"
+ "url": "https://metrica.yandex.com/",
+ "companyId": "yandex"
},
"yandex_passport": {
"name": "Yandex Passport",
"categoryId": 2,
- "url": "https://www.yandex.com/"
+ "url": "https://www.yandex.com/",
+ "companyId": "yandex"
},
"yapfiles.ru": {
"name": "yapfiles.ru",
"categoryId": 8,
- "url": "https://www.yapfiles.ru/"
+ "url": "https://www.yapfiles.ru/",
+ "companyId": null
},
"yashi": {
"name": "Yashi",
"categoryId": 4,
- "url": "http://www.yashi.com/"
+ "url": "http://www.yashi.com/",
+ "companyId": "mass2"
},
"ybrant_media": {
"name": "Ybrant Media",
"categoryId": 4,
- "url": "http://www.addynamix.com/index.html"
+ "url": "http://www.addynamix.com/index.html",
+ "companyId": "ybrant_media"
},
"ycontent": {
"name": "Ycontent",
"categoryId": 0,
- "url": "http://ycontent.com.br/"
+ "url": "http://ycontent.com.br/",
+ "companyId": "ycontent"
},
"yektanet": {
"name": "Yektanet",
"categoryId": 4,
- "url": "https://yektanet.com/"
+ "url": "https://yektanet.com/",
+ "companyId": "yektanet"
},
"yengo": {
"name": "Yengo",
"categoryId": 4,
- "url": "http://www.yengo.com/"
+ "url": "http://www.yengo.com/",
+ "companyId": "yengo"
},
"yesmail": {
"name": "Yesmail",
"categoryId": 4,
- "url": "http://www.yesmail.com/"
+ "url": "http://www.yesmail.com/",
+ "companyId": "yes_mail"
},
"yesup_advertising": {
"name": "YesUp Advertising",
"categoryId": 4,
- "url": "http://yesup.net/"
+ "url": "http://yesup.net/",
+ "companyId": "yesup"
},
"yesware": {
"name": "Yesware",
"categoryId": 2,
- "url": "http://www.yesware.com/"
+ "url": "http://www.yesware.com/",
+ "companyId": "yesware"
},
"yieldbot": {
"name": "Yieldbot",
"categoryId": 6,
- "url": "https://www.yieldbot.com/"
+ "url": "https://www.yieldbot.com/",
+ "companyId": "yieldbot"
},
"yieldify": {
"name": "Yieldify",
"categoryId": 4,
- "url": "http://www.yieldify.com/"
+ "url": "http://www.yieldify.com/",
+ "companyId": "yieldify"
},
"yieldlab": {
"name": "Yieldlab",
"categoryId": 4,
- "url": "http://www.yieldlab.de/"
+ "url": "http://www.yieldlab.de/",
+ "companyId": "prosieben_sat1"
},
"yieldlove": {
"name": "Yieldlove",
"categoryId": 4,
- "url": "https://www.yieldlove.com/"
+ "url": "https://www.yieldlove.com/",
+ "companyId": "yieldlove"
},
"yieldmo": {
"name": "Yieldmo",
"categoryId": 4,
- "url": "https://www.yieldmo.com/"
+ "url": "https://www.yieldmo.com/",
+ "companyId": "yieldmo"
},
"yieldr": {
"name": "Yieldr Ads",
"categoryId": 4,
- "url": "https://www.yieldr.com/"
+ "url": "https://www.yieldr.com/",
+ "companyId": "yieldr"
},
"yieldr_air": {
"name": "Yieldr Air",
"categoryId": 6,
- "url": "https://www.yieldr.com/"
+ "url": "https://www.yieldr.com/",
+ "companyId": "yieldr"
},
"yieldsquare": {
"name": "YieldSquare",
"categoryId": 4,
- "url": "http://www.yieldsquare.com/"
+ "url": "http://www.yieldsquare.com/",
+ "companyId": "yieldsquare"
},
"yle": {
"name": "YLE",
"categoryId": 6,
- "url": "http://yle.fi/"
+ "url": "http://yle.fi/",
+ "companyId": "yle"
},
"yllixmedia": {
"name": "YllixMedia",
"categoryId": 4,
- "url": "http://yllix.com/"
+ "url": "http://yllix.com/",
+ "companyId": "yllixmedia"
},
"ymetrica1.com": {
"name": "ymetrica1.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"ymzrrizntbhde.com": {
"name": "ymzrrizntbhde.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"yo_button": {
"name": "Yo Button",
"categoryId": 2,
- "url": "http://www.justyo.co/"
+ "url": "http://www.justyo.co/",
+ "companyId": "yo"
},
"yodle": {
"name": "Yodle",
"categoryId": 4,
- "url": "http://www.yodle.com/"
+ "url": "http://www.yodle.com/",
+ "companyId": "yodle"
},
"yola_analytics": {
"name": "Yola Analytics",
"categoryId": 6,
- "url": "https://www.yola.com/"
+ "url": "https://www.yola.com/",
+ "companyId": "yola"
},
"yomedia": {
"name": "Yomedia",
"categoryId": 4,
- "url": "http://www.pinetech.vn/"
+ "url": "http://www.pinetech.vn/",
+ "companyId": "yomedia"
},
"yoochoose.net": {
"name": "YOOCHOOSE",
"categoryId": 4,
- "url": "https://yoochoose.com/"
+ "url": "https://yoochoose.com/",
+ "companyId": null
},
"yotpo": {
"name": "Yotpo",
"categoryId": 1,
- "url": "https://www.yotpo.com/"
+ "url": "https://www.yotpo.com/",
+ "companyId": "yotpo"
},
"yottaa": {
"name": "Yottaa",
"categoryId": 6,
- "url": "https://www.yottaa.com/"
+ "url": "https://www.yottaa.com/",
+ "companyId": "yottaa"
},
"yottly": {
"name": "Yottly",
"categoryId": 4,
- "url": "https://yottly.com/"
+ "url": "https://yottly.com/",
+ "companyId": "yottly"
},
"youcanbookme": {
"name": "YouCanBookMe",
"categoryId": 2,
- "url": "https://youcanbook.me/"
+ "url": "https://youcanbook.me/",
+ "companyId": "youcanbookme"
},
"youku": {
"name": "Youku",
"categoryId": 0,
- "url": "http://www.youku.com/"
+ "url": "http://www.youku.com/",
+ "companyId": "youku"
},
"youporn": {
"name": "YouPorn",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"youtube": {
"name": "YouTube",
"categoryId": 0,
- "url": "https://www.youtube.com/"
+ "url": "https://www.youtube.com/",
+ "companyId": "google"
},
"youtube_subscription": {
"name": "YouTube Subscription",
"categoryId": 2,
- "url": "http://www.youtube.com/"
+ "url": "http://www.youtube.com/",
+ "companyId": "google"
},
"yp": {
"name": "YellowPages",
"categoryId": 4,
- "url": "https://www.yellowpages.com/"
+ "url": "https://www.yellowpages.com/",
+ "companyId": "thryv"
},
"ysance": {
"name": "YSance",
"categoryId": 4,
- "url": "http://www.ysance.com/en/index.html"
+ "url": "http://www.ysance.com/en/index.html",
+ "companyId": "ysance"
},
"yume": {
"name": "YuMe",
"categoryId": 4,
- "url": "http://www.yume.com/"
+ "url": "http://www.yume.com/",
+ "companyId": "yume"
},
"yume,_inc.": {
"name": "YuMe, Inc.",
"categoryId": 4,
- "url": "http://www.yume.com/"
+ "url": "http://www.yume.com/",
+ "companyId": "yume"
},
"yusp": {
"name": "Yusp",
"categoryId": 6,
- "url": "https://www.yusp.com/"
+ "url": "https://www.yusp.com/",
+ "companyId": "yusp"
},
"zadarma": {
"name": "Zadarma",
"categoryId": 2,
- "url": "https://zadarma.com/"
+ "url": "https://zadarma.com/",
+ "companyId": "zadarma"
},
"zalando_de": {
"name": "zalando.de",
"categoryId": 8,
- "url": "https://zalando.de/"
+ "url": "https://zalando.de/",
+ "companyId": "zalando"
},
"zalo": {
"name": "Zalo",
"categoryId": 2,
- "url": "https://zaloapp.com/"
+ "url": "https://zaloapp.com/",
+ "companyId": "zalo"
},
"zanox": {
"name": "Zanox",
"categoryId": 4,
- "url": "http://www.zanox.com/us/"
+ "url": "http://www.zanox.com/us/",
+ "companyId": "axel_springer"
},
"zaparena": {
"name": "zaparena",
"categoryId": 4,
- "url": "http://www.zaparena.com/"
+ "url": "http://www.zaparena.com/",
+ "companyId": "zapunited"
},
"zappos": {
"name": "Zappos",
"categoryId": 4,
- "url": "http://www.zappos.com/"
+ "url": "http://www.zappos.com/",
+ "companyId": "zappos"
},
"zdassets.com": {
"name": "Zendesk CDN",
"categoryId": 8,
- "url": "http://www.zendesk.com/"
+ "url": "http://www.zendesk.com/",
+ "companyId": "zendesk"
},
"zebestof.com": {
"name": "Zebestof",
"categoryId": 4,
- "url": "http://www.zebestof.com/en/home/"
+ "url": "http://www.zebestof.com/en/home/",
+ "companyId": "zebestof"
},
"zedo": {
"name": "Zedo",
"categoryId": 4,
- "url": "http://www.zedo.com/"
+ "url": "http://www.zedo.com/",
+ "companyId": "zedo"
},
"zemanta": {
"name": "Zemanta",
"categoryId": 2,
- "url": "http://www.zemanta.com/"
+ "url": "http://www.zemanta.com/",
+ "companyId": "zemanta"
},
"zencoder": {
"name": "Zencoder",
"categoryId": 0,
- "url": "https://zencoder.com/en/"
+ "url": "https://zencoder.com/en/",
+ "companyId": "zencoder"
},
"zendesk": {
"name": "Zendesk",
"categoryId": 2,
- "url": "http://www.zendesk.com/"
+ "url": "http://www.zendesk.com/",
+ "companyId": "zendesk"
},
"zergnet": {
"name": "ZergNet",
"categoryId": 2,
- "url": "http://www.zergnet.com/info"
+ "url": "http://www.zergnet.com/info",
+ "companyId": "zergnet"
},
"zero.kz": {
"name": "ZERO.kz",
"categoryId": 6,
- "url": "http://zero.kz/"
+ "url": "http://zero.kz/",
+ "companyId": "neolabs_zero"
},
"zeta": {
"name": "Zeta",
"categoryId": 2,
- "url": "https://zetaglobal.com/"
+ "url": "https://zetaglobal.com/",
+ "companyId": "zeta"
},
"zeusclicks": {
"name": "ZeusClicks",
"categoryId": 4,
- "url": "http://zeusclicks.com/"
+ "url": "http://zeusclicks.com/",
+ "companyId": null
},
"ziff_davis": {
"name": "Ziff Davis",
"categoryId": 4,
- "url": "https://www.ziffdavis.com/"
+ "url": "https://www.ziffdavis.com/",
+ "companyId": "ziff_davis"
},
"zift_solutions": {
"name": "Zift Solutions",
"categoryId": 6,
- "url": "https://ziftsolutions.com/"
+ "url": "https://ziftsolutions.com/",
+ "companyId": "zift_solutions"
},
"zimbio.com": {
"name": "Zimbio",
"categoryId": 8,
- "url": "http://www.zimbio.com/"
+ "url": "http://www.zimbio.com/",
+ "companyId": null
},
"zippyshare_widget": {
"name": "Zippyshare Widget",
"categoryId": 2,
- "url": "http://www.zippyshare.com"
+ "url": "http://www.zippyshare.com",
+ "companyId": "zippyshare"
},
"zmags": {
"name": "Zmags",
"categoryId": 6,
- "url": "https://zmags.com/"
+ "url": "https://zmags.com/",
+ "companyId": "zmags"
},
"zmctrack.net": {
"name": "zmctrack.net",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"zog.link": {
"name": "zog.link",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"zoho": {
"name": "Zoho",
"categoryId": 6,
- "url": "https://www.zohocorp.com/index.html"
+ "url": "https://www.zohocorp.com/index.html",
+ "companyId": "zoho_corp"
},
"zononi.com": {
"name": "zononi.com",
"categoryId": 3,
- "url": null
+ "url": null,
+ "companyId": null
},
"zopim": {
"name": "Zopim",
"categoryId": 2,
- "url": "http://www.zopim.com/"
+ "url": "http://www.zopim.com/",
+ "companyId": "zendesk"
},
"zukxd6fkxqn.com": {
"name": "zukxd6fkxqn.com",
"categoryId": 11,
- "url": null
+ "url": null,
+ "companyId": null
},
"zwaar": {
"name": "Zwaar",
"categoryId": 4,
- "url": "http://www.zwaar.org"
+ "url": "http://www.zwaar.org",
+ "companyId": "zwaar"
},
"zypmedia": {
"name": "ZypMedia",
"categoryId": 4,
- "url": "http://www.zypmedia.com/"
+ "url": "http://www.zypmedia.com/",
+ "companyId": "zypmedia"
}
},
"trackerDomains": {
@@ -16525,6 +19754,7 @@
"amazon.it": "amazon",
"d3io1k5o0zdpqr.cloudfront.net": "amazon",
"amazon-adsystem.com": "amazon_adsystem",
+ "serving-sys.com": "amazon_adsystem",
"sizmek.com": "amazon_adsystem",
"assoc-amazon.ca": "amazon_associates",
"assoc-amazon.co.uk": "amazon_associates",
@@ -17751,6 +20981,7 @@
"googleadservices.com": "google_adservices",
"google-analytics.com": "google_analytics",
"appspot.com": "google_appspot",
+ "beacons-google.com": "google_beacons",
"adsensecustomsearchads.com": "google_custom_search",
"mail-ads.google.com": "google_email",
"fonts.googleapis.com": "google_fonts",
@@ -18411,6 +21642,7 @@
"nzaza.com": "matiro",
"matomo.cloud": "matomo",
"matomo.org": "matomo",
+ "piwik.org": "matomo",
"adsmarket.com": "matomy_market",
"m2pub.com": "matomy_market",
"mb01.com": "maxbounty",
@@ -18508,6 +21740,18 @@
"trouter.io": "microsoft",
"windows.net": "microsoft",
"analytics.live.com": "microsoft_analytics",
+ "a.clarity.ms": "microsoft_clarity",
+ "b.clarity.ms": "microsoft_clarity",
+ "c.clarity.ms": "microsoft_clarity",
+ "d.clarity.ms": "microsoft_clarity",
+ "e.clarity.ms": "microsoft_clarity",
+ "f.clarity.ms": "microsoft_clarity",
+ "g.clarity.ms": "microsoft_clarity",
+ "h.clarity.ms": "microsoft_clarity",
+ "i.clarity.ms": "microsoft_clarity",
+ "j.clarity.ms": "microsoft_clarity",
+ "log.clarity.ms": "microsoft_clarity",
+ "www.clarity.ms": "microsoft_clarity",
"mmismm.com": "mindset_media",
"imgfarm.com": "mindspark",
"mindspark.com": "mindspark",
@@ -18865,8 +22109,8 @@
"pinimg.com": "pinterest",
"pinterest.com": "pinterest",
"app.pipz.io": "pipz",
- "piwik.org": "piwik",
- "piwik.pro": "piwik",
+ "disabled.invalid": "piwik",
+ "piwik.pro": "piwik_pro_analytics_suite",
"adrta.com": "pixalate",
"app.pixelpop.co": "pixel_union",
"pixfuture.net": "pixfuture",
@@ -19666,6 +22910,7 @@
"tidbit.co.in": "tidbit",
"code.tidio.co": "tidio",
"widget-v4.tidiochat.com": "tidio",
+ "analytics.tiktok.com": "tiktok_analytics",
"optimized.by.tiller.co": "tiller",
"vip.timezonedb.com": "timezondb",
"npttech.com": "tinypass",
@@ -19727,6 +22972,9 @@
"traffiliate.com": "traffiliate",
"storage.trafic.ro": "trafic",
"trafmag.com": "trafmag.com",
+ "api.transcend.io": "transcend",
+ "cdn.transcend.io": "transcend",
+ "telemetry.transcend.io": "transcend_telemetry",
"backoffice.transmatico.com": "transmatic",
"travelaudience.com": "travel_audience",
"trbo.com": "trbo",
@@ -20261,7 +23509,6 @@
"rfihub.com": "zeta",
"rfihub.net": "zeta",
"ru4.com": "zeta",
- "serving-sys.com": "zeta",
"xplusone.com": "zeta",
"zeusclicks.com": "zeusclicks",
"webtest.net": "ziff_davis",
diff --git a/client/src/reducers/services.js b/client/src/reducers/services.js
index 7c478366..c0663c8e 100644
--- a/client/src/reducers/services.js
+++ b/client/src/reducers/services.js
@@ -12,6 +12,14 @@ const services = handleActions(
processing: false,
}),
+ [actions.getAllBlockedServicesRequest]: (state) => ({ ...state, processingAll: true }),
+ [actions.getAllBlockedServicesFailure]: (state) => ({ ...state, processingAll: false }),
+ [actions.getAllBlockedServicesSuccess]: (state, { payload }) => ({
+ ...state,
+ allServices: payload.blocked_services,
+ processingAll: false,
+ }),
+
[actions.setBlockedServicesRequest]: (state) => ({ ...state, processingSet: true }),
[actions.setBlockedServicesFailure]: (state) => ({ ...state, processingSet: false }),
[actions.setBlockedServicesSuccess]: (state) => ({
@@ -21,8 +29,10 @@ const services = handleActions(
},
{
processing: true,
+ processingAll: true,
processingSet: false,
list: [],
+ allServices: [],
},
);
diff --git a/go.mod b/go.mod
index b7acd047..15cf4397 100644
--- a/go.mod
+++ b/go.mod
@@ -3,24 +3,24 @@ module github.com/AdguardTeam/AdGuardHome
go 1.18
require (
- github.com/AdguardTeam/dnsproxy v0.45.2
- github.com/AdguardTeam/golibs v0.10.9
+ github.com/AdguardTeam/dnsproxy v0.46.2
+ github.com/AdguardTeam/golibs v0.11.2
github.com/AdguardTeam/urlfilter v0.16.0
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.2.5
github.com/digineo/go-ipset/v2 v2.2.1
- github.com/dimfeld/httptreemux/v5 v5.4.0
- github.com/fsnotify/fsnotify v1.5.4
+ github.com/dimfeld/httptreemux/v5 v5.5.0
+ github.com/fsnotify/fsnotify v1.6.0
github.com/go-ping/ping v1.1.0
- github.com/google/go-cmp v0.5.8
+ github.com/google/go-cmp v0.5.9
github.com/google/gopacket v1.1.19
github.com/google/renameio v1.0.1
github.com/google/uuid v1.3.0
- github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84
- github.com/kardianos/service v1.2.1
- github.com/lucas-clemente/quic-go v0.29.1
+ github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c
+ github.com/kardianos/service v1.2.2
+ github.com/lucas-clemente/quic-go v0.29.2
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
- github.com/mdlayher/netlink v1.6.0
+ github.com/mdlayher/netlink v1.6.2
// TODO(a.garipov): This package is deprecated; find a new one or use
// our own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0
@@ -28,10 +28,10 @@ require (
github.com/stretchr/testify v1.8.0
github.com/ti-mo/netfilter v0.4.0
go.etcd.io/bbolt v1.3.6
- golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be
- golang.org/x/exp v0.0.0-20220929160808-de9c53c655b9
- golang.org/x/net v0.0.0-20220927171203-f486391704dc
- golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec
+ golang.org/x/crypto v0.1.0
+ golang.org/x/exp v0.0.0-20221026153819-32f3d567a233
+ golang.org/x/net v0.1.0
+ golang.org/x/sys v0.1.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.0
@@ -48,20 +48,22 @@ require (
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/josharian/native v1.0.0 // indirect
- github.com/marten-seemann/qpack v0.2.1 // indirect
- github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
- github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
+ github.com/marten-seemann/qpack v0.3.0 // indirect
+ github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
+ github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
github.com/mdlayher/packet v1.0.0 // indirect
github.com/mdlayher/socket v0.2.3 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
+ github.com/onsi/ginkgo/v2 v2.4.0 // indirect
+ github.com/onsi/gomega v1.22.1 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect
- golang.org/x/mod v0.6.0-dev.0.20220922195421-2adab6b8c60e // indirect
- golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
- golang.org/x/text v0.3.7 // indirect
- golang.org/x/tools v0.1.12 // indirect
+ golang.org/x/mod v0.6.0 // indirect
+ golang.org/x/sync v0.1.0 // indirect
+ golang.org/x/text v0.4.0 // indirect
+ golang.org/x/tools v0.2.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)
diff --git a/go.sum b/go.sum
index 458cb14d..23d31a79 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,9 @@
-github.com/AdguardTeam/dnsproxy v0.45.2 h1:K9BXkQAfAKjrzbWbczpA2IA1owLe/edv0nG0e2+Esko=
-github.com/AdguardTeam/dnsproxy v0.45.2/go.mod h1:h+0r4GDvHHY2Wu6r7oqva+O37h00KofYysfzy1TEXFE=
+github.com/AdguardTeam/dnsproxy v0.46.2 h1:ZUKM713Ts5meYQqk6cJkUBMCFSWqFPXTgjXkN4RI1Vo=
+github.com/AdguardTeam/dnsproxy v0.46.2/go.mod h1:PAmRzFqls0E92XTglyY2ESAqMAzZJhHKErG1ZpRnpjA=
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
-github.com/AdguardTeam/golibs v0.10.9 h1:F9oP2da0dQ9RQDM1lGR7LxUTfUWu8hEFOs4icwAkKM0=
-github.com/AdguardTeam/golibs v0.10.9/go.mod h1:W+5rznZa1cSNSFt+gPS7f4Wytnr9fOrd5ZYqwadPw14=
+github.com/AdguardTeam/golibs v0.11.2 h1:JbQB1Dg2JWStXgHh1QqBbOLWnP4t9oDjppoBH6TVXSE=
+github.com/AdguardTeam/golibs v0.11.2/go.mod h1:87bN2x4VsTritptE3XZg9l8T6gznWsIxHBcQ1DeRIXA=
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
github.com/AdguardTeam/urlfilter v0.16.0 h1:IO29m+ZyQuuOnPLTzHuXj35V1DZOp1Dcryl576P2syg=
github.com/AdguardTeam/urlfilter v0.16.0/go.mod h1:46YZDOV1+qtdRDuhZKVPSSp7JWWes0KayqHrKAFBdEI=
@@ -31,13 +31,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/digineo/go-ipset/v2 v2.2.1 h1:k6skY+0fMqeUjjeWO/m5OuWPSZUAn7AucHMnQ1MX77g=
github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkqw1uo8Qxe0VU=
-github.com/dimfeld/httptreemux/v5 v5.4.0 h1:IiHYEjh+A7pYbhWyjmGnj5HZK6gpOOvyBXCJ+BE8/Gs=
-github.com/dimfeld/httptreemux/v5 v5.4.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
+github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ=
+github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
-github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
@@ -52,8 +53,8 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -61,8 +62,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU=
@@ -74,8 +75,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
-github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI=
-github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
+github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ=
+github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@@ -84,20 +85,20 @@ github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGu
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/kardianos/service v1.2.1 h1:AYndMsehS+ywIS6RB9KOlcXzteWUzxgMgBymJD7+BYk=
-github.com/kardianos/service v1.2.1/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
+github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
+github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/lucas-clemente/quic-go v0.29.1 h1:Z+WMJ++qMLhvpFkRZA+jl3BTxUjm415YBmWanXB8zP0=
-github.com/lucas-clemente/quic-go v0.29.1/go.mod h1:CTcNfLYJS2UuRNB+zcNlgvkjBhxX6Hm3WUxxAQx2mgE=
-github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
-github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
-github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
-github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
-github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU=
-github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
+github.com/lucas-clemente/quic-go v0.29.2 h1:O8Mt0O6LpvEW+wfC40vZdcw0DngwYzoxq5xULZNzSI8=
+github.com/lucas-clemente/quic-go v0.29.2/go.mod h1:g6/h9YMmLuU54tL1gW25uIi3VlBp3uv+sBihplIuskE=
+github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
+github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
+github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
+github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
+github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
+github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
@@ -107,15 +108,14 @@ github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqc
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/netlink v1.1.2-0.20201013204415-ded538f7f4be/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
-github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
-github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
+github.com/mdlayher/netlink v1.6.2 h1:D2zGSkvYsJ6NreeED3JiVTu1lj2sIYATqSaZlhPzUgQ=
+github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU=
github.com/mdlayher/packet v1.0.0 h1:InhZJbdShQYt6XV2GPj5XHxChzOfhJJOMbvnGAmOfQ8=
github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.1.0 h1:K4PFMVy+AFsp0Zdlrts7yNhxc/uXoPVHi9RzRvtZF2Y=
github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5s9Sg=
-github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM=
github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=
@@ -129,12 +129,14 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
+github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
+github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
+github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
+github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -173,16 +175,16 @@ go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A=
-golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/exp v0.0.0-20220929160808-de9c53c655b9 h1:lNtcVz/3bOstm7Vebox+5m3nLh/BYWnhmc3AhXOW6oI=
-golang.org/x/exp v0.0.0-20220929160808-de9c53c655b9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
+golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
+golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
+golang.org/x/exp v0.0.0-20221026153819-32f3d567a233 h1:9bNbSKT4RPLEzne0Xh1v3NaNecsa1DKjkOuTbY6V9rI=
+golang.org/x/exp v0.0.0-20221026153819-32f3d567a233/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220922195421-2adab6b8c60e h1:WhB000cGjOfbJiedMGvJkMTclI18VD69w27k+sceql8=
-golang.org/x/mod v0.6.0-dev.0.20220922195421-2adab6b8c60e/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -194,7 +196,6 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -203,17 +204,17 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
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-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
-golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
-golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -230,7 +231,6 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -250,20 +250,21 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
-golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/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/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -271,8 +272,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -282,8 +283,8 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/aghchan/aghchan.go b/internal/aghchan/aghchan.go
new file mode 100644
index 00000000..1da1790a
--- /dev/null
+++ b/internal/aghchan/aghchan.go
@@ -0,0 +1,33 @@
+// Package aghchan contains channel utilities.
+package aghchan
+
+import (
+ "fmt"
+ "time"
+)
+
+// Receive returns an error if it cannot receive a value form c before timeout
+// runs out.
+func Receive[T any](c <-chan T, timeout time.Duration) (v T, ok bool, err error) {
+ var zero T
+ timeoutCh := time.After(timeout)
+ select {
+ case <-timeoutCh:
+ // TODO(a.garipov): Consider implementing [errors.Aser] for
+ // os.ErrTimeout.
+ return zero, false, fmt.Errorf("did not receive after %s", timeout)
+ case v, ok = <-c:
+ return v, ok, nil
+ }
+}
+
+// MustReceive panics if it cannot receive a value form c before timeout runs
+// out.
+func MustReceive[T any](c <-chan T, timeout time.Duration) (v T, ok bool) {
+ v, ok, err := Receive(c, timeout)
+ if err != nil {
+ panic(err)
+ }
+
+ return v, ok
+}
diff --git a/internal/aghhttp/aghhttp.go b/internal/aghhttp/aghhttp.go
index f03ebf7d..bde0112a 100644
--- a/internal/aghhttp/aghhttp.go
+++ b/internal/aghhttp/aghhttp.go
@@ -62,9 +62,16 @@ func WriteTextPlainDeprecated(w http.ResponseWriter, r *http.Request) (isPlainTe
}
// WriteJSONResponse sets the content-type header in w.Header() to
-// "application/json", encodes resp to w, calls Error on any returned error, and
-// returns it as well.
+// "application/json", writes a header with a "200 OK" status, encodes resp to
+// w, calls [Error] on any returned error, and returns it as well.
func WriteJSONResponse(w http.ResponseWriter, r *http.Request, resp any) (err error) {
+ return WriteJSONResponseCode(w, r, http.StatusOK, resp)
+}
+
+// WriteJSONResponseCode is like [WriteJSONResponse] but adds the ability to
+// redefine the status code.
+func WriteJSONResponseCode(w http.ResponseWriter, r *http.Request, code int, resp any) (err error) {
+ w.WriteHeader(code)
w.Header().Set(HdrNameContentType, HdrValApplicationJSON)
err = json.NewEncoder(w).Encode(resp)
if err != nil {
diff --git a/internal/aghhttp/header.go b/internal/aghhttp/header.go
index a0b79425..0bfbcff2 100644
--- a/internal/aghhttp/header.go
+++ b/internal/aghhttp/header.go
@@ -8,11 +8,14 @@ package aghhttp
const (
HdrNameAcceptEncoding = "Accept-Encoding"
HdrNameAccessControlAllowOrigin = "Access-Control-Allow-Origin"
+ HdrNameAltSvc = "Alt-Svc"
HdrNameContentEncoding = "Content-Encoding"
HdrNameContentType = "Content-Type"
+ HdrNameOrigin = "Origin"
HdrNameServer = "Server"
HdrNameTrailer = "Trailer"
HdrNameUserAgent = "User-Agent"
+ HdrNameVary = "Vary"
)
// HTTP header value constants.
diff --git a/internal/aghnet/arpdb.go b/internal/aghnet/arpdb.go
index 4909af5f..a63e5cd2 100644
--- a/internal/aghnet/arpdb.go
+++ b/internal/aghnet/arpdb.go
@@ -5,10 +5,11 @@ import (
"bytes"
"fmt"
"net"
+ "net/netip"
"sync"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/netutil"
+ "golang.org/x/exp/slices"
)
// ARPDB: The Network Neighborhood Database
@@ -54,7 +55,7 @@ type Neighbor struct {
Name string
// IP contains either IPv4 or IPv6.
- IP net.IP
+ IP netip.Addr
// MAC contains the hardware address.
MAC net.HardwareAddr
@@ -64,8 +65,8 @@ type Neighbor struct {
func (n Neighbor) Clone() (clone Neighbor) {
return Neighbor{
Name: n.Name,
- IP: netutil.CloneIP(n.IP),
- MAC: netutil.CloneMAC(n.MAC),
+ IP: n.IP,
+ MAC: slices.Clone(n.MAC),
}
}
diff --git a/internal/aghnet/arpdb_bsd.go b/internal/aghnet/arpdb_bsd.go
index 9519eeec..c4048939 100644
--- a/internal/aghnet/arpdb_bsd.go
+++ b/internal/aghnet/arpdb_bsd.go
@@ -5,6 +5,7 @@ package aghnet
import (
"bufio"
"net"
+ "net/netip"
"strings"
"sync"
@@ -47,22 +48,28 @@ func parseArpA(sc *bufio.Scanner, lenHint int) (ns []Neighbor) {
if ipStr := fields[1]; len(ipStr) < 2 {
continue
- } else if ip := net.ParseIP(ipStr[1 : len(ipStr)-1]); ip == nil {
+ } else if ip, err := netip.ParseAddr(ipStr[1 : len(ipStr)-1]); err != nil {
+ log.Debug("arpdb: parsing arp output: ip: %s", err)
+
continue
} else {
n.IP = ip
}
hwStr := fields[3]
- if mac, err := net.ParseMAC(hwStr); err != nil {
+ mac, err := net.ParseMAC(hwStr)
+ if err != nil {
+ log.Debug("arpdb: parsing arp output: mac: %s", err)
+
continue
} else {
n.MAC = mac
}
host := fields[0]
- if err := netutil.ValidateDomainName(host); err != nil {
- log.Debug("parsing arp output: %s", err)
+ err = netutil.ValidateDomainName(host)
+ if err != nil {
+ log.Debug("arpdb: parsing arp output: host: %s", err)
} else {
n.Name = host
}
diff --git a/internal/aghnet/arpdb_bsd_test.go b/internal/aghnet/arpdb_bsd_test.go
index 9933c721..b6bd6b9d 100644
--- a/internal/aghnet/arpdb_bsd_test.go
+++ b/internal/aghnet/arpdb_bsd_test.go
@@ -4,6 +4,7 @@ package aghnet
import (
"net"
+ "net/netip"
)
const arpAOutput = `
@@ -17,14 +18,14 @@ hostname.two (::ffff:ffff) at ef:cd:ab:ef:cd:ab on em0 expires in 1198 seconds [
var wantNeighs = []Neighbor{{
Name: "hostname.one",
- IP: net.IPv4(192, 168, 1, 2),
+ IP: netip.MustParseAddr("192.168.1.2"),
MAC: net.HardwareAddr{0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF},
}, {
Name: "hostname.two",
- IP: net.ParseIP("::ffff:ffff"),
+ IP: netip.MustParseAddr("::ffff:ffff"),
MAC: net.HardwareAddr{0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB},
}, {
Name: "",
- IP: net.ParseIP("::1234"),
+ IP: netip.MustParseAddr("::1234"),
MAC: net.HardwareAddr{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
}}
diff --git a/internal/aghnet/arpdb_linux.go b/internal/aghnet/arpdb_linux.go
index 82f83adf..e6e51feb 100644
--- a/internal/aghnet/arpdb_linux.go
+++ b/internal/aghnet/arpdb_linux.go
@@ -7,6 +7,7 @@ import (
"fmt"
"io/fs"
"net"
+ "net/netip"
"strings"
"sync"
@@ -94,7 +95,8 @@ func (arp *fsysARPDB) Refresh() (err error) {
}
n := Neighbor{}
- if n.IP = net.ParseIP(fields[0]); n.IP == nil || n.IP.IsUnspecified() {
+ n.IP, err = netip.ParseAddr(fields[0])
+ if err != nil || n.IP.IsUnspecified() {
continue
} else if n.MAC, err = net.ParseMAC(fields[3]); err != nil {
continue
@@ -135,15 +137,19 @@ func parseArpAWrt(sc *bufio.Scanner, lenHint int) (ns []Neighbor) {
n := Neighbor{}
- if ip := net.ParseIP(fields[0]); ip == nil || n.IP.IsUnspecified() {
+ ip, err := netip.ParseAddr(fields[0])
+ if err != nil || n.IP.IsUnspecified() {
+ log.Debug("arpdb: parsing arp output: ip: %s", err)
+
continue
} else {
n.IP = ip
}
hwStr := fields[3]
- if mac, err := net.ParseMAC(hwStr); err != nil {
- log.Debug("parsing arp output: %s", err)
+ mac, err := net.ParseMAC(hwStr)
+ if err != nil {
+ log.Debug("arpdb: parsing arp output: mac: %s", err)
continue
} else {
@@ -174,7 +180,9 @@ func parseArpA(sc *bufio.Scanner, lenHint int) (ns []Neighbor) {
if ipStr := fields[1]; len(ipStr) < 2 {
continue
- } else if ip := net.ParseIP(ipStr[1 : len(ipStr)-1]); ip == nil {
+ } else if ip, err := netip.ParseAddr(ipStr[1 : len(ipStr)-1]); err != nil {
+ log.Debug("arpdb: parsing arp output: ip: %s", err)
+
continue
} else {
n.IP = ip
@@ -182,7 +190,7 @@ func parseArpA(sc *bufio.Scanner, lenHint int) (ns []Neighbor) {
hwStr := fields[3]
if mac, err := net.ParseMAC(hwStr); err != nil {
- log.Debug("parsing arp output: %s", err)
+ log.Debug("arpdb: parsing arp output: mac: %s", err)
continue
} else {
@@ -191,7 +199,7 @@ func parseArpA(sc *bufio.Scanner, lenHint int) (ns []Neighbor) {
host := fields[0]
if verr := netutil.ValidateDomainName(host); verr != nil {
- log.Debug("parsing arp output: %s", verr)
+ log.Debug("arpdb: parsing arp output: host: %s", verr)
} else {
n.Name = host
}
@@ -218,14 +226,18 @@ func parseIPNeigh(sc *bufio.Scanner, lenHint int) (ns []Neighbor) {
n := Neighbor{}
- if ip := net.ParseIP(fields[0]); ip == nil {
+ ip, err := netip.ParseAddr(fields[0])
+ if err != nil {
+ log.Debug("arpdb: parsing arp output: ip: %s", err)
+
continue
} else {
n.IP = ip
}
- if mac, err := net.ParseMAC(fields[4]); err != nil {
- log.Debug("parsing arp output: %s", err)
+ mac, err := net.ParseMAC(fields[4])
+ if err != nil {
+ log.Debug("arpdb: parsing arp output: mac: %s", err)
continue
} else {
diff --git a/internal/aghnet/arpdb_linux_test.go b/internal/aghnet/arpdb_linux_test.go
index 22fe7135..d07c654d 100644
--- a/internal/aghnet/arpdb_linux_test.go
+++ b/internal/aghnet/arpdb_linux_test.go
@@ -4,6 +4,7 @@ package aghnet
import (
"net"
+ "net/netip"
"sync"
"testing"
"testing/fstest"
@@ -33,10 +34,10 @@ const ipNeighOutput = `
::ffff:ffff dev enp0s3 lladdr ef:cd:ab:ef:cd:ab router STALE`
var wantNeighs = []Neighbor{{
- IP: net.IPv4(192, 168, 1, 2),
+ IP: netip.MustParseAddr("192.168.1.2"),
MAC: net.HardwareAddr{0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF},
}, {
- IP: net.ParseIP("::ffff:ffff"),
+ IP: netip.MustParseAddr("::ffff:ffff"),
MAC: net.HardwareAddr{0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB},
}}
diff --git a/internal/aghnet/arpdb_openbsd.go b/internal/aghnet/arpdb_openbsd.go
index 5590f335..2b356d06 100644
--- a/internal/aghnet/arpdb_openbsd.go
+++ b/internal/aghnet/arpdb_openbsd.go
@@ -5,6 +5,7 @@ package aghnet
import (
"bufio"
"net"
+ "net/netip"
"strings"
"sync"
@@ -50,14 +51,18 @@ func parseArpA(sc *bufio.Scanner, lenHint int) (ns []Neighbor) {
n := Neighbor{}
- if ip := net.ParseIP(fields[0]); ip == nil {
+ ip, err := netip.ParseAddr(fields[0])
+ if err != nil {
+ log.Debug("arpdb: parsing arp output: ip: %s", err)
+
continue
} else {
n.IP = ip
}
- if mac, err := net.ParseMAC(fields[1]); err != nil {
- log.Debug("parsing arp output: %s", err)
+ mac, err := net.ParseMAC(fields[1])
+ if err != nil {
+ log.Debug("arpdb: parsing arp output: mac: %s", err)
continue
} else {
diff --git a/internal/aghnet/arpdb_openbsd_test.go b/internal/aghnet/arpdb_openbsd_test.go
index 0a45514a..a324ed9c 100644
--- a/internal/aghnet/arpdb_openbsd_test.go
+++ b/internal/aghnet/arpdb_openbsd_test.go
@@ -4,6 +4,7 @@ package aghnet
import (
"net"
+ "net/netip"
)
const arpAOutput = `
@@ -15,9 +16,9 @@ Host Ethernet Address Netif Expire Flags
`
var wantNeighs = []Neighbor{{
- IP: net.IPv4(192, 168, 1, 2),
+ IP: netip.MustParseAddr("192.168.1.2"),
MAC: net.HardwareAddr{0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF},
}, {
- IP: net.ParseIP("::ffff:ffff"),
+ IP: netip.MustParseAddr("::ffff:ffff"),
MAC: net.HardwareAddr{0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB},
}}
diff --git a/internal/aghnet/arpdb_test.go b/internal/aghnet/arpdb_test.go
index d6971448..ab40ab6f 100644
--- a/internal/aghnet/arpdb_test.go
+++ b/internal/aghnet/arpdb_test.go
@@ -2,6 +2,7 @@ package aghnet
import (
"net"
+ "net/netip"
"sync"
"testing"
@@ -35,7 +36,7 @@ func (arp *TestARPDB) Neighbors() (ns []Neighbor) {
}
func TestARPDBS(t *testing.T) {
- knownIP := net.IP{1, 2, 3, 4}
+ knownIP := netip.MustParseAddr("1.2.3.4")
knownMAC := net.HardwareAddr{0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF}
succRefrCount, failRefrCount := 0, 0
diff --git a/internal/aghnet/arpdb_windows.go b/internal/aghnet/arpdb_windows.go
index f6e27b5b..ed4c8682 100644
--- a/internal/aghnet/arpdb_windows.go
+++ b/internal/aghnet/arpdb_windows.go
@@ -5,6 +5,7 @@ package aghnet
import (
"bufio"
"net"
+ "net/netip"
"strings"
"sync"
)
@@ -43,13 +44,15 @@ func parseArpA(sc *bufio.Scanner, lenHint int) (ns []Neighbor) {
n := Neighbor{}
- if ip := net.ParseIP(fields[0]); ip == nil {
+ ip, err := netip.ParseAddr(fields[0])
+ if err != nil {
continue
} else {
n.IP = ip
}
- if mac, err := net.ParseMAC(fields[1]); err != nil {
+ mac, err := net.ParseMAC(fields[1])
+ if err != nil {
continue
} else {
n.MAC = mac
diff --git a/internal/aghnet/arpdb_windows_test.go b/internal/aghnet/arpdb_windows_test.go
index bb75c988..c3dcfe04 100644
--- a/internal/aghnet/arpdb_windows_test.go
+++ b/internal/aghnet/arpdb_windows_test.go
@@ -4,6 +4,7 @@ package aghnet
import (
"net"
+ "net/netip"
)
const arpAOutput = `
@@ -14,9 +15,9 @@ Interface: 192.168.1.1 --- 0x7
::ffff:ffff ef-cd-ab-ef-cd-ab static`
var wantNeighs = []Neighbor{{
- IP: net.IPv4(192, 168, 1, 2),
+ IP: netip.MustParseAddr("192.168.1.2"),
MAC: net.HardwareAddr{0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF},
}, {
- IP: net.ParseIP("::ffff:ffff"),
+ IP: netip.MustParseAddr("::ffff:ffff"),
MAC: net.HardwareAddr{0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB},
}}
diff --git a/internal/aghnet/dhcp_unix.go b/internal/aghnet/dhcp_unix.go
index 464e5aa9..16c3c87a 100644
--- a/internal/aghnet/dhcp_unix.go
+++ b/internal/aghnet/dhcp_unix.go
@@ -6,6 +6,7 @@ import (
"bytes"
"fmt"
"net"
+ "net/netip"
"os"
"time"
@@ -38,48 +39,44 @@ func checkOtherDHCP(ifaceName string) (ok4, ok6 bool, err4, err6 error) {
}
// ifaceIPv4Subnet returns the first suitable IPv4 subnetwork iface has.
-func ifaceIPv4Subnet(iface *net.Interface) (subnet *net.IPNet, err error) {
+func ifaceIPv4Subnet(iface *net.Interface) (subnet netip.Prefix, err error) {
var addrs []net.Addr
if addrs, err = iface.Addrs(); err != nil {
- return nil, err
+ return netip.Prefix{}, err
}
for _, a := range addrs {
+ var ip net.IP
+ var maskLen int
switch a := a.(type) {
case *net.IPAddr:
- subnet = &net.IPNet{
- IP: a.IP,
- Mask: a.IP.DefaultMask(),
- }
+ ip = a.IP
+ maskLen, _ = ip.DefaultMask().Size()
case *net.IPNet:
- subnet = a
+ ip = a.IP
+ maskLen, _ = a.Mask.Size()
default:
continue
}
- if ip4 := subnet.IP.To4(); ip4 != nil {
- subnet.IP = ip4
-
- return subnet, nil
+ if ip = ip.To4(); ip != nil {
+ return netip.PrefixFrom(netip.AddrFrom4(*(*[4]byte)(ip)), maskLen), nil
}
}
- return nil, fmt.Errorf("interface %s has no ipv4 addresses", iface.Name)
+ return netip.Prefix{}, fmt.Errorf("interface %s has no ipv4 addresses", iface.Name)
}
// checkOtherDHCPv4 sends a DHCP request to the specified network interface, and
// waits for a response for a period defined by defaultDiscoverTime.
func checkOtherDHCPv4(iface *net.Interface) (ok bool, err error) {
- var subnet *net.IPNet
+ var subnet netip.Prefix
if subnet, err = ifaceIPv4Subnet(iface); err != nil {
return false, err
}
// Resolve broadcast addr.
- dst := netutil.IPPort{
- IP: BroadcastFromIPNet(subnet),
- Port: 67,
- }.String()
+ dst := netip.AddrPortFrom(BroadcastFromPref(subnet), 67).String()
var dstAddr *net.UDPAddr
if dstAddr, err = net.ResolveUDPAddr("udp4", dst); err != nil {
return false, fmt.Errorf("couldn't resolve UDP address %s: %w", dst, err)
diff --git a/internal/aghnet/hostscontainer.go b/internal/aghnet/hostscontainer.go
index 5d750945..f74ce1f9 100644
--- a/internal/aghnet/hostscontainer.go
+++ b/internal/aghnet/hostscontainer.go
@@ -5,7 +5,7 @@ import (
"fmt"
"io"
"io/fs"
- "net"
+ "net/netip"
"path"
"strings"
"sync"
@@ -19,6 +19,7 @@ import (
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
+ "golang.org/x/exp/maps"
)
// DefaultHostsPaths returns the slice of paths default for the operating system
@@ -106,10 +107,10 @@ type HostsContainer struct {
done chan struct{}
// updates is the channel for receiving updated hosts.
- updates chan *netutil.IPMap
+ updates chan HostsRecords
// last is the set of hosts that was cached within last detected change.
- last *netutil.IPMap
+ last HostsRecords
// fsys is the working file system to read hosts files from.
fsys fs.FS
@@ -124,6 +125,25 @@ type HostsContainer struct {
listID int
}
+// HostsRecords is a mapping of an IP address to its hosts data.
+type HostsRecords map[netip.Addr]*HostsRecord
+
+// HostsRecord represents a single hosts file record.
+type HostsRecord struct {
+ Aliases *stringutil.Set
+ Canonical string
+}
+
+// equal returns true if all fields of rec are equal to field in other or they
+// both are nil.
+func (rec *HostsRecord) equal(other *HostsRecord) (ok bool) {
+ if rec == nil {
+ return other == nil
+ }
+
+ return rec.Canonical == other.Canonical && rec.Aliases.Equal(other.Aliases)
+}
+
// ErrNoHostsPaths is returned when there are no valid paths to watch passed to
// the HostsContainer.
const ErrNoHostsPaths errors.Error = "no valid paths to hosts files provided"
@@ -158,7 +178,7 @@ func NewHostsContainer(
},
listID: listID,
done: make(chan struct{}, 1),
- updates: make(chan *netutil.IPMap, 1),
+ updates: make(chan HostsRecords, 1),
fsys: fsys,
w: w,
patterns: patterns,
@@ -196,9 +216,8 @@ func (hc *HostsContainer) Close() (err error) {
return nil
}
-// Upd returns the channel into which the updates are sent. The receivable
-// map's values are guaranteed to be of type of *HostsRecord.
-func (hc *HostsContainer) Upd() (updates <-chan *netutil.IPMap) {
+// Upd returns the channel into which the updates are sent.
+func (hc *HostsContainer) Upd() (updates <-chan HostsRecords) {
return hc.updates
}
@@ -264,7 +283,7 @@ type hostsParser struct {
// table stores only the unique IP-hostname pairs. It's also sent to the
// updates channel afterwards.
- table *netutil.IPMap
+ table HostsRecords
}
// newHostsParser creates a new *hostsParser with buffers of size taken from the
@@ -273,7 +292,7 @@ func (hc *HostsContainer) newHostsParser() (hp *hostsParser) {
return &hostsParser{
rulesBuilder: &strings.Builder{},
translations: map[string]string{},
- table: netutil.NewIPMap(hc.last.Len()),
+ table: make(HostsRecords, len(hc.last)),
}
}
@@ -285,7 +304,7 @@ func (hp *hostsParser) parseFile(r io.Reader) (patterns []string, cont bool, err
s := bufio.NewScanner(r)
for s.Scan() {
ip, hosts := hp.parseLine(s.Text())
- if ip == nil || len(hosts) == 0 {
+ if ip == (netip.Addr{}) || len(hosts) == 0 {
continue
}
@@ -296,14 +315,15 @@ func (hp *hostsParser) parseFile(r io.Reader) (patterns []string, cont bool, err
}
// parseLine parses the line having the hosts syntax ignoring invalid ones.
-func (hp *hostsParser) parseLine(line string) (ip net.IP, hosts []string) {
+func (hp *hostsParser) parseLine(line string) (ip netip.Addr, hosts []string) {
fields := strings.Fields(line)
if len(fields) < 2 {
- return nil, nil
+ return netip.Addr{}, nil
}
- if ip = net.ParseIP(fields[0]); ip == nil {
- return nil, nil
+ ip, err := netip.ParseAddr(fields[0])
+ if err != nil {
+ return netip.Addr{}, nil
}
for _, f := range fields[1:] {
@@ -321,7 +341,7 @@ func (hp *hostsParser) parseLine(line string) (ip net.IP, hosts []string) {
// See https://github.com/AdguardTeam/AdGuardHome/issues/3946.
//
// TODO(e.burkov): Investigate if hosts may contain DNS-SD domains.
- err := netutil.ValidateDomainName(f)
+ err = netutil.ValidateDomainName(f)
if err != nil {
log.Error("%s: host %q is invalid, ignoring", hostsContainerPref, f)
@@ -334,30 +354,13 @@ func (hp *hostsParser) parseLine(line string) (ip net.IP, hosts []string) {
return ip, hosts
}
-// HostsRecord represents a single hosts file record.
-type HostsRecord struct {
- Aliases *stringutil.Set
- Canonical string
-}
-
-// Equal returns true if all fields of rec are equal to field in other or they
-// both are nil.
-func (rec *HostsRecord) Equal(other *HostsRecord) (ok bool) {
- if rec == nil {
- return other == nil
- }
-
- return rec.Canonical == other.Canonical && rec.Aliases.Equal(other.Aliases)
-}
-
// addRecord puts the record for the IP address to the rules builder if needed.
// The first host is considered to be the canonical name for the IP address.
// hosts must have at least one name.
-func (hp *hostsParser) addRecord(ip net.IP, hosts []string) {
+func (hp *hostsParser) addRecord(ip netip.Addr, hosts []string) {
line := strings.Join(append([]string{ip.String()}, hosts...), " ")
- var rec *HostsRecord
- v, ok := hp.table.Get(ip)
+ rec, ok := hp.table[ip]
if !ok {
rec = &HostsRecord{
Aliases: stringutil.NewSet(),
@@ -365,14 +368,7 @@ func (hp *hostsParser) addRecord(ip net.IP, hosts []string) {
rec.Canonical, hosts = hosts[0], hosts[1:]
hp.addRules(ip, rec.Canonical, line)
- hp.table.Set(ip, rec)
- } else {
- rec, ok = v.(*HostsRecord)
- if !ok {
- log.Error("%s: adding pairs: unexpected type %T", hostsContainerPref, v)
-
- return
- }
+ hp.table[ip] = rec
}
for _, host := range hosts {
@@ -387,7 +383,7 @@ func (hp *hostsParser) addRecord(ip net.IP, hosts []string) {
}
// addRules adds rules and rule translations for the line.
-func (hp *hostsParser) addRules(ip net.IP, host, line string) {
+func (hp *hostsParser) addRules(ip netip.Addr, host, line string) {
rule, rulePtr := hp.writeRules(host, ip)
hp.translations[rule], hp.translations[rulePtr] = line, line
@@ -396,8 +392,9 @@ func (hp *hostsParser) addRules(ip net.IP, host, line string) {
// writeRules writes the actual rule for the qtype and the PTR for the host-ip
// pair into internal builders.
-func (hp *hostsParser) writeRules(host string, ip net.IP) (rule, rulePtr string) {
- arpa, err := netutil.IPToReversedAddr(ip)
+func (hp *hostsParser) writeRules(host string, ip netip.Addr) (rule, rulePtr string) {
+ // TODO(a.garipov): Add a netip.Addr version to netutil.
+ arpa, err := netutil.IPToReversedAddr(ip.AsSlice())
if err != nil {
return "", ""
}
@@ -415,7 +412,7 @@ func (hp *hostsParser) writeRules(host string, ip net.IP) (rule, rulePtr string)
var qtype string
// The validation of the IP address has been performed earlier so it is
// guaranteed to be either an IPv4 or an IPv6.
- if ip.To4() != nil {
+ if ip.Is4() {
qtype = "A"
} else {
qtype = "AAAA"
@@ -442,51 +439,8 @@ func (hp *hostsParser) writeRules(host string, ip net.IP) (rule, rulePtr string)
return rule, rulePtr
}
-// equalSet returns true if the internal hosts table just parsed equals target.
-// target's values must be of type *HostsRecord.
-func (hp *hostsParser) equalSet(target *netutil.IPMap) (ok bool) {
- if target == nil {
- // hp.table shouldn't appear nil since it's initialized on each refresh.
- return target == hp.table
- }
-
- if hp.table.Len() != target.Len() {
- return false
- }
-
- hp.table.Range(func(ip net.IP, recVal any) (cont bool) {
- var targetVal any
- targetVal, ok = target.Get(ip)
- if !ok {
- return false
- }
-
- var rec *HostsRecord
- rec, ok = recVal.(*HostsRecord)
- if !ok {
- log.Error("%s: comparing: unexpected type %T", hostsContainerPref, recVal)
-
- return false
- }
-
- var targetRec *HostsRecord
- targetRec, ok = targetVal.(*HostsRecord)
- if !ok {
- log.Error("%s: comparing: target: unexpected type %T", hostsContainerPref, targetVal)
-
- return false
- }
-
- ok = rec.Equal(targetRec)
-
- return ok
- })
-
- return ok
-}
-
// sendUpd tries to send the parsed data to the ch.
-func (hp *hostsParser) sendUpd(ch chan *netutil.IPMap) {
+func (hp *hostsParser) sendUpd(ch chan HostsRecords) {
log.Debug("%s: sending upd", hostsContainerPref)
upd := hp.table
@@ -524,14 +478,14 @@ func (hc *HostsContainer) refresh() (err error) {
return fmt.Errorf("refreshing : %w", err)
}
- if hp.equalSet(hc.last) {
+ if maps.EqualFunc(hp.table, hc.last, (*HostsRecord).equal) {
log.Debug("%s: no changes detected", hostsContainerPref)
return nil
}
defer hp.sendUpd(hc.updates)
- hc.last = hp.table.ShallowClone()
+ hc.last = maps.Clone(hp.table)
var rulesStrg *filterlist.RuleStorage
if rulesStrg, err = hp.newStrg(hc.listID); err != nil {
diff --git a/internal/aghnet/hostscontainer_test.go b/internal/aghnet/hostscontainer_test.go
index 1f75a3c9..a5822530 100644
--- a/internal/aghnet/hostscontainer_test.go
+++ b/internal/aghnet/hostscontainer_test.go
@@ -3,6 +3,7 @@ package aghnet
import (
"io/fs"
"net"
+ "net/netip"
"path"
"strings"
"sync/atomic"
@@ -10,6 +11,7 @@ import (
"testing/fstest"
"time"
+ "github.com/AdguardTeam/AdGuardHome/internal/aghchan"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
@@ -135,7 +137,7 @@ func TestNewHostsContainer(t *testing.T) {
func TestHostsContainer_refresh(t *testing.T) {
// TODO(e.burkov): Test the case with no actual updates.
- ip := net.IP{127, 0, 0, 1}
+ ip := netutil.IPv4Localhost()
ipStr := ip.String()
testFS := fstest.MapFS{"dir/file1": &fstest.MapFile{Data: []byte(ipStr + ` hostname` + nl)}}
@@ -163,27 +165,17 @@ func TestHostsContainer_refresh(t *testing.T) {
checkRefresh := func(t *testing.T, want *HostsRecord) {
t.Helper()
- var ok bool
- var upd *netutil.IPMap
- select {
- case upd, ok = <-hc.Upd():
- require.True(t, ok)
- require.NotNil(t, upd)
- case <-time.After(1 * time.Second):
- t.Fatal("did not receive after 1s")
- }
-
- assert.Equal(t, 1, upd.Len())
-
- v, ok := upd.Get(ip)
+ upd, ok := aghchan.MustReceive(hc.Upd(), 1*time.Second)
require.True(t, ok)
+ require.NotNil(t, upd)
- require.IsType(t, (*HostsRecord)(nil), v)
+ assert.Len(t, upd, 1)
- rec, _ := v.(*HostsRecord)
+ rec, ok := upd[ip]
+ require.True(t, ok)
require.NotNil(t, rec)
- assert.Truef(t, rec.Equal(want), "%+v != %+v", rec, want)
+ assert.Truef(t, rec.equal(want), "%+v != %+v", rec, want)
}
t.Run("initial_refresh", func(t *testing.T) {
@@ -568,13 +560,13 @@ func TestHostsContainer(t *testing.T) {
}
func TestUniqueRules_ParseLine(t *testing.T) {
- ip := net.IP{127, 0, 0, 1}
+ ip := netutil.IPv4Localhost()
ipStr := ip.String()
testCases := []struct {
name string
line string
- wantIP net.IP
+ wantIP netip.Addr
wantHosts []string
}{{
name: "simple",
@@ -589,7 +581,7 @@ func TestUniqueRules_ParseLine(t *testing.T) {
}, {
name: "invalid_line",
line: ipStr,
- wantIP: nil,
+ wantIP: netip.Addr{},
wantHosts: nil,
}, {
name: "invalid_line_hostname",
@@ -604,7 +596,7 @@ func TestUniqueRules_ParseLine(t *testing.T) {
}, {
name: "whole_comment",
line: `# ` + ipStr + ` hostname`,
- wantIP: nil,
+ wantIP: netip.Addr{},
wantHosts: nil,
}, {
name: "partial_comment",
@@ -614,7 +606,7 @@ func TestUniqueRules_ParseLine(t *testing.T) {
}, {
name: "empty",
line: ``,
- wantIP: nil,
+ wantIP: netip.Addr{},
wantHosts: nil,
}}
@@ -622,7 +614,7 @@ func TestUniqueRules_ParseLine(t *testing.T) {
hp := hostsParser{}
t.Run(tc.name, func(t *testing.T) {
got, hosts := hp.parseLine(tc.line)
- assert.True(t, tc.wantIP.Equal(got))
+ assert.Equal(t, tc.wantIP, got)
assert.Equal(t, tc.wantHosts, hosts)
})
}
diff --git a/internal/aghnet/net.go b/internal/aghnet/net.go
index 2de9c630..bdcc6e4f 100644
--- a/internal/aghnet/net.go
+++ b/internal/aghnet/net.go
@@ -7,12 +7,12 @@ import (
"fmt"
"io"
"net"
+ "net/netip"
"syscall"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
- "github.com/AdguardTeam/golibs/netutil"
)
// Variables and functions to substitute in tests.
@@ -31,6 +31,12 @@ var (
// the IP being static is available.
const ErrNoStaticIPInfo errors.Error = "no information about static ip"
+// IPv4Localhost returns 127.0.0.1, which returns true for [netip.Addr.Is4].
+func IPv4Localhost() (ip netip.Addr) { return netip.AddrFrom4([4]byte{127, 0, 0, 1}) }
+
+// IPv6Localhost returns ::1, which returns true for [netip.Addr.Is6].
+func IPv6Localhost() (ip netip.Addr) { return netip.AddrFrom16([16]byte{15: 1}) }
+
// IfaceHasStaticIP checks if interface is configured to have static IP address.
// If it can't give a definitive answer, it returns false and an error for which
// errors.Is(err, ErrNoStaticIPInfo) is true.
@@ -47,26 +53,31 @@ func IfaceSetStaticIP(ifaceName string) (err error) {
//
// TODO(e.burkov): Investigate if the gateway address may be fetched in another
// way since not every machine has the software installed.
-func GatewayIP(ifaceName string) (ip net.IP) {
+func GatewayIP(ifaceName string) (ip netip.Addr) {
code, out, err := aghosRunCommand("ip", "route", "show", "dev", ifaceName)
if err != nil {
log.Debug("%s", err)
- return nil
+ return netip.Addr{}
} else if code != 0 {
log.Debug("fetching gateway ip: unexpected exit code: %d", code)
- return nil
+ return netip.Addr{}
}
fields := bytes.Fields(out)
// The meaningful "ip route" command output should contain the word
// "default" at first field and default gateway IP address at third field.
if len(fields) < 3 || string(fields[0]) != "default" {
- return nil
+ return netip.Addr{}
}
- return net.ParseIP(string(fields[2]))
+ ip, err = netip.ParseAddr(string(fields[2]))
+ if err != nil {
+ return netip.Addr{}
+ }
+
+ return ip
}
// CanBindPrivilegedPorts checks if current process can bind to privileged
@@ -78,9 +89,9 @@ func CanBindPrivilegedPorts() (can bool, err error) {
// NetInterface represents an entry of network interfaces map.
type NetInterface struct {
// Addresses are the network interface addresses.
- Addresses []net.IP `json:"ip_addresses,omitempty"`
+ Addresses []netip.Addr `json:"ip_addresses,omitempty"`
// Subnets are the IP networks for this network interface.
- Subnets []*net.IPNet `json:"-"`
+ Subnets []netip.Prefix `json:"-"`
Name string `json:"name"`
HardwareAddr net.HardwareAddr `json:"hardware_address"`
Flags net.Flags `json:"flags"`
@@ -101,57 +112,79 @@ func (iface NetInterface) MarshalJSON() ([]byte, error) {
})
}
+func NetInterfaceFrom(iface *net.Interface) (niface *NetInterface, err error) {
+ niface = &NetInterface{
+ Name: iface.Name,
+ HardwareAddr: iface.HardwareAddr,
+ Flags: iface.Flags,
+ MTU: iface.MTU,
+ }
+
+ addrs, err := iface.Addrs()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get addresses for interface %s: %w", iface.Name, err)
+ }
+
+ // Collect network interface addresses.
+ for _, addr := range addrs {
+ n, ok := addr.(*net.IPNet)
+ if !ok {
+ // Should be *net.IPNet, this is weird.
+ return nil, fmt.Errorf("expected %[2]s to be %[1]T, got %[2]T", n, addr)
+ } else if ip4 := n.IP.To4(); ip4 != nil {
+ n.IP = ip4
+ }
+
+ ip, ok := netip.AddrFromSlice(n.IP)
+ if !ok {
+ return nil, fmt.Errorf("bad address %s", n.IP)
+ }
+
+ ip = ip.Unmap()
+ if ip.IsLinkLocalUnicast() {
+ // Ignore link-local IPv4.
+ if ip.Is4() {
+ continue
+ }
+
+ ip = ip.WithZone(iface.Name)
+ }
+
+ ones, _ := n.Mask.Size()
+ p := netip.PrefixFrom(ip, ones)
+
+ niface.Addresses = append(niface.Addresses, ip)
+ niface.Subnets = append(niface.Subnets, p)
+ }
+
+ return niface, nil
+}
+
// GetValidNetInterfacesForWeb returns interfaces that are eligible for DNS and
// WEB only we do not return link-local addresses here.
//
// TODO(e.burkov): Can't properly test the function since it's nontrivial to
// substitute net.Interface.Addrs and the net.InterfaceAddrs can't be used.
-func GetValidNetInterfacesForWeb() (netIfaces []*NetInterface, err error) {
+func GetValidNetInterfacesForWeb() (nifaces []*NetInterface, err error) {
ifaces, err := net.Interfaces()
if err != nil {
- return nil, fmt.Errorf("couldn't get interfaces: %w", err)
+ return nil, fmt.Errorf("getting interfaces: %w", err)
} else if len(ifaces) == 0 {
- return nil, errors.Error("couldn't find any legible interface")
+ return nil, errors.Error("no legible interfaces")
}
- for _, iface := range ifaces {
- var addrs []net.Addr
- addrs, err = iface.Addrs()
+ for i := range ifaces {
+ var niface *NetInterface
+ niface, err = NetInterfaceFrom(&ifaces[i])
if err != nil {
- return nil, fmt.Errorf("failed to get addresses for interface %s: %w", iface.Name, err)
- }
-
- netIface := &NetInterface{
- MTU: iface.MTU,
- Name: iface.Name,
- HardwareAddr: iface.HardwareAddr,
- Flags: iface.Flags,
- }
-
- // Collect network interface addresses.
- for _, addr := range addrs {
- ipNet, ok := addr.(*net.IPNet)
- if !ok {
- // Should be net.IPNet, this is weird.
- return nil, fmt.Errorf("got %s that is not net.IPNet, it is %T", addr, addr)
- }
-
- // Ignore link-local.
- if ipNet.IP.IsLinkLocalUnicast() {
- continue
- }
-
- netIface.Addresses = append(netIface.Addresses, ipNet.IP)
- netIface.Subnets = append(netIface.Subnets, ipNet)
- }
-
- // Discard interfaces with no addresses.
- if len(netIface.Addresses) != 0 {
- netIfaces = append(netIfaces, netIface)
+ return nil, err
+ } else if len(niface.Addresses) != 0 {
+ // Discard interfaces with no addresses.
+ nifaces = append(nifaces, niface)
}
}
- return netIfaces, nil
+ return nifaces, nil
}
// InterfaceByIP returns the name of the interface bound to ip.
@@ -160,7 +193,7 @@ func GetValidNetInterfacesForWeb() (netIfaces []*NetInterface, err error) {
// IP address can be shared by multiple interfaces in some configurations.
//
// TODO(e.burkov): See TODO on GetValidNetInterfacesForWeb.
-func InterfaceByIP(ip net.IP) (ifaceName string) {
+func InterfaceByIP(ip netip.Addr) (ifaceName string) {
ifaces, err := GetValidNetInterfacesForWeb()
if err != nil {
return ""
@@ -168,7 +201,7 @@ func InterfaceByIP(ip net.IP) (ifaceName string) {
for _, iface := range ifaces {
for _, addr := range iface.Addresses {
- if ip.Equal(addr) {
+ if ip == addr {
return iface.Name
}
}
@@ -177,15 +210,16 @@ func InterfaceByIP(ip net.IP) (ifaceName string) {
return ""
}
-// GetSubnet returns pointer to net.IPNet for the specified interface or nil if
+// GetSubnet returns the subnet corresponding to the interface of zero prefix if
// the search fails.
//
// TODO(e.burkov): See TODO on GetValidNetInterfacesForWeb.
-func GetSubnet(ifaceName string) *net.IPNet {
+func GetSubnet(ifaceName string) (p netip.Prefix) {
netIfaces, err := GetValidNetInterfacesForWeb()
if err != nil {
log.Error("Could not get network interfaces info: %v", err)
- return nil
+
+ return p
}
for _, netIface := range netIfaces {
@@ -194,14 +228,14 @@ func GetSubnet(ifaceName string) *net.IPNet {
}
}
- return nil
+ return p
}
// CheckPort checks if the port is available for binding. network is expected
// to be one of "udp" and "tcp".
-func CheckPort(network string, ip net.IP, port int) (err error) {
+func CheckPort(network string, ipp netip.AddrPort) (err error) {
var c io.Closer
- addr := netutil.IPPort{IP: ip, Port: port}.String()
+ addr := ipp.String()
switch network {
case "tcp":
c, err = net.Listen(network, addr)
@@ -251,18 +285,23 @@ func CollectAllIfacesAddrs() (addrs []string, err error) {
return addrs, nil
}
-// BroadcastFromIPNet calculates the broadcast IP address for n.
-func BroadcastFromIPNet(n *net.IPNet) (dc net.IP) {
- dc = netutil.CloneIP(n.IP)
-
- mask := n.Mask
- if mask == nil {
- mask = dc.DefaultMask()
+// BroadcastFromPref calculates the broadcast IP address for p.
+func BroadcastFromPref(p netip.Prefix) (bc netip.Addr) {
+ bc = p.Addr().Unmap()
+ if !bc.IsValid() {
+ return netip.Addr{}
}
- for i, b := range mask {
- dc[i] |= ^b
+ maskLen, addrLen := p.Bits(), bc.BitLen()
+ if maskLen == addrLen {
+ return bc
}
- return dc
+ ipBytes := bc.AsSlice()
+ for i := maskLen; i < addrLen; i++ {
+ ipBytes[i/8] |= 1 << (7 - (i % 8))
+ }
+ bc, _ = netip.AddrFromSlice(ipBytes)
+
+ return bc
}
diff --git a/internal/aghnet/net_linux.go b/internal/aghnet/net_linux.go
index d0c3f7fd..fc83d56a 100644
--- a/internal/aghnet/net_linux.go
+++ b/internal/aghnet/net_linux.go
@@ -6,7 +6,7 @@ import (
"bufio"
"fmt"
"io"
- "net"
+ "net/netip"
"os"
"strings"
@@ -151,7 +151,7 @@ func findIfaceLine(s *bufio.Scanner, name string) (ok bool) {
// interface through dhcpcd.conf.
func ifaceSetStaticIP(ifaceName string) (err error) {
ipNet := GetSubnet(ifaceName)
- if ipNet.IP == nil {
+ if !ipNet.Addr().IsValid() {
return errors.Error("can't get IP address")
}
@@ -174,7 +174,7 @@ func ifaceSetStaticIP(ifaceName string) (err error) {
// dhcpcdConfIface returns configuration lines for the dhcpdc.conf files that
// configure the interface to have a static IP.
-func dhcpcdConfIface(ifaceName string, ipNet *net.IPNet, gwIP net.IP) (conf string) {
+func dhcpcdConfIface(ifaceName string, subnet netip.Prefix, gateway netip.Addr) (conf string) {
b := &strings.Builder{}
stringutil.WriteToBuilder(
b,
@@ -183,15 +183,15 @@ func dhcpcdConfIface(ifaceName string, ipNet *net.IPNet, gwIP net.IP) (conf stri
" added by AdGuard Home.\ninterface ",
ifaceName,
"\nstatic ip_address=",
- ipNet.String(),
+ subnet.String(),
"\n",
)
- if gwIP != nil {
- stringutil.WriteToBuilder(b, "static routers=", gwIP.String(), "\n")
+ if gateway != (netip.Addr{}) {
+ stringutil.WriteToBuilder(b, "static routers=", gateway.String(), "\n")
}
- stringutil.WriteToBuilder(b, "static domain_name_servers=", ipNet.IP.String(), "\n\n")
+ stringutil.WriteToBuilder(b, "static domain_name_servers=", subnet.Addr().String(), "\n\n")
return b.String()
}
diff --git a/internal/aghnet/net_test.go b/internal/aghnet/net_test.go
index d4ee59ee..f4275c6c 100644
--- a/internal/aghnet/net_test.go
+++ b/internal/aghnet/net_test.go
@@ -6,11 +6,11 @@ import (
"fmt"
"io/fs"
"net"
+ "net/netip"
"os"
"strings"
"testing"
- "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -19,7 +19,7 @@ import (
)
func TestMain(m *testing.M) {
- aghtest.DiscardLogOutput(m)
+ testutil.DiscardLogOutput(m)
}
// testdata is the filesystem containing data for testing the package.
@@ -93,34 +93,29 @@ func TestGatewayIP(t *testing.T) {
const cmd = "ip route show dev " + ifaceName
testCases := []struct {
- name string
shell mapShell
- want net.IP
+ want netip.Addr
+ name string
}{{
- name: "success_v4",
shell: theOnlyCmd(cmd, 0, `default via 1.2.3.4 onlink`, nil),
- want: net.IP{1, 2, 3, 4}.To16(),
+ want: netip.MustParseAddr("1.2.3.4"),
+ name: "success_v4",
}, {
- name: "success_v6",
shell: theOnlyCmd(cmd, 0, `default via ::ffff onlink`, nil),
- want: net.IP{
- 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0xFF, 0xFF,
- },
+ want: netip.MustParseAddr("::ffff"),
+ name: "success_v6",
}, {
- name: "bad_output",
shell: theOnlyCmd(cmd, 0, `non-default via 1.2.3.4 onlink`, nil),
- want: nil,
+ want: netip.Addr{},
+ name: "bad_output",
}, {
- name: "err_runcmd",
shell: theOnlyCmd(cmd, 0, "", errors.Error("can't run command")),
- want: nil,
+ want: netip.Addr{},
+ name: "err_runcmd",
}, {
- name: "bad_code",
shell: theOnlyCmd(cmd, 1, "", nil),
- want: nil,
+ want: netip.Addr{},
+ name: "bad_code",
}}
for _, tc := range testCases {
@@ -150,65 +145,61 @@ func TestInterfaceByIP(t *testing.T) {
}
func TestBroadcastFromIPNet(t *testing.T) {
- known6 := net.IP{
- 1, 2, 3, 4,
- 5, 6, 7, 8,
- 9, 10, 11, 12,
- 13, 14, 15, 16,
- }
+ known4 := netip.MustParseAddr("192.168.0.1")
+ fullBroadcast4 := netip.MustParseAddr("255.255.255.255")
+
+ known6 := netip.MustParseAddr("102:304:506:708:90a:b0c:d0e:f10")
testCases := []struct {
- name string
- subnet *net.IPNet
- want net.IP
+ pref netip.Prefix
+ want netip.Addr
+ name string
}{{
+ pref: netip.PrefixFrom(known4, 0),
+ want: fullBroadcast4,
name: "full",
- subnet: &net.IPNet{
- IP: net.IP{192, 168, 0, 1},
- Mask: net.IPMask{255, 255, 15, 0},
- },
- want: net.IP{192, 168, 240, 255},
}, {
- name: "ipv6_no_mask",
- subnet: &net.IPNet{
- IP: known6,
- },
+ pref: netip.PrefixFrom(known4, 20),
+ want: netip.MustParseAddr("192.168.15.255"),
+ name: "full",
+ }, {
+ pref: netip.PrefixFrom(known6, netutil.IPv6BitLen),
want: known6,
+ name: "ipv6_no_mask",
}, {
+ pref: netip.PrefixFrom(known4, netutil.IPv4BitLen),
+ want: known4,
name: "ipv4_no_mask",
- subnet: &net.IPNet{
- IP: net.IP{192, 168, 1, 2},
- },
- want: net.IP{192, 168, 1, 255},
}, {
+ pref: netip.PrefixFrom(netip.IPv4Unspecified(), 0),
+ want: fullBroadcast4,
name: "unspecified",
- subnet: &net.IPNet{
- IP: net.IP{0, 0, 0, 0},
- Mask: net.IPMask{0, 0, 0, 0},
- },
- want: net.IPv4bcast,
+ }, {
+ pref: netip.Prefix{},
+ want: netip.Addr{},
+ name: "invalid",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- bc := BroadcastFromIPNet(tc.subnet)
- assert.True(t, bc.Equal(tc.want), bc)
+ assert.Equal(t, tc.want, BroadcastFromPref(tc.pref))
})
}
}
func TestCheckPort(t *testing.T) {
+ laddr := netip.AddrPortFrom(IPv4Localhost(), 0)
+
t.Run("tcp_bound", func(t *testing.T) {
- l, err := net.Listen("tcp", "127.0.0.1:")
+ l, err := net.Listen("tcp", laddr.String())
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, l.Close)
- ipp := netutil.IPPortFromAddr(l.Addr())
- require.NotNil(t, ipp)
- require.NotNil(t, ipp.IP)
- require.NotZero(t, ipp.Port)
+ ipp := testutil.RequireTypeAssert[*net.TCPAddr](t, l.Addr()).AddrPort()
+ require.Equal(t, laddr.Addr(), ipp.Addr())
+ require.NotZero(t, ipp.Port())
- err = CheckPort("tcp", ipp.IP, ipp.Port)
+ err = CheckPort("tcp", ipp)
target := &net.OpError{}
require.ErrorAs(t, err, &target)
@@ -216,16 +207,15 @@ func TestCheckPort(t *testing.T) {
})
t.Run("udp_bound", func(t *testing.T) {
- conn, err := net.ListenPacket("udp", "127.0.0.1:")
+ conn, err := net.ListenPacket("udp", laddr.String())
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, conn.Close)
- ipp := netutil.IPPortFromAddr(conn.LocalAddr())
- require.NotNil(t, ipp)
- require.NotNil(t, ipp.IP)
- require.NotZero(t, ipp.Port)
+ ipp := testutil.RequireTypeAssert[*net.UDPAddr](t, conn.LocalAddr()).AddrPort()
+ require.Equal(t, laddr.Addr(), ipp.Addr())
+ require.NotZero(t, ipp.Port())
- err = CheckPort("udp", ipp.IP, ipp.Port)
+ err = CheckPort("udp", ipp)
target := &net.OpError{}
require.ErrorAs(t, err, &target)
@@ -233,12 +223,12 @@ func TestCheckPort(t *testing.T) {
})
t.Run("bad_network", func(t *testing.T) {
- err := CheckPort("bad_network", nil, 0)
+ err := CheckPort("bad_network", netip.AddrPortFrom(netip.Addr{}, 0))
assert.NoError(t, err)
})
t.Run("can_bind", func(t *testing.T) {
- err := CheckPort("udp", net.IP{0, 0, 0, 0}, 0)
+ err := CheckPort("udp", netip.AddrPortFrom(netip.IPv4Unspecified(), 0))
assert.NoError(t, err)
})
}
@@ -322,18 +312,18 @@ func TestNetInterface_MarshalJSON(t *testing.T) {
`"mtu":1500` +
`}` + "\n"
- ip4, ip6 := net.IP{1, 2, 3, 4}, net.IP{0xAA, 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
- mask4, mask6 := net.CIDRMask(24, netutil.IPv4BitLen), net.CIDRMask(8, netutil.IPv6BitLen)
+ ip4, ok := netip.AddrFromSlice([]byte{1, 2, 3, 4})
+ require.True(t, ok)
+
+ ip6, ok := netip.AddrFromSlice([]byte{0xAA, 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
+ require.True(t, ok)
+
+ net4 := netip.PrefixFrom(ip4, 24)
+ net6 := netip.PrefixFrom(ip6, 8)
iface := &NetInterface{
- Addresses: []net.IP{ip4, ip6},
- Subnets: []*net.IPNet{{
- IP: ip4.Mask(mask4),
- Mask: mask4,
- }, {
- IP: ip6.Mask(mask6),
- Mask: mask6,
- }},
+ Addresses: []netip.Addr{ip4, ip6},
+ Subnets: []netip.Prefix{net4, net6},
Name: "iface0",
HardwareAddr: net.HardwareAddr{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
Flags: net.FlagUp | net.FlagMulticast,
diff --git a/internal/aghnet/systemresolvers_others_test.go b/internal/aghnet/systemresolvers_others_test.go
index a9974e0a..8bc506a8 100644
--- a/internal/aghnet/systemresolvers_others_test.go
+++ b/internal/aghnet/systemresolvers_others_test.go
@@ -6,6 +6,7 @@ import (
"context"
"testing"
+ "github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -17,9 +18,8 @@ func createTestSystemResolversImpl(
t.Helper()
sr := createTestSystemResolvers(t, hostGenFunc)
- require.IsType(t, (*systemResolvers)(nil), sr)
- return sr.(*systemResolvers)
+ return testutil.RequireTypeAssert[*systemResolvers](t, sr)
}
func TestSystemResolvers_Refresh(t *testing.T) {
diff --git a/internal/aghos/aghos_test.go b/internal/aghos/aghos_test.go
index 684f646e..3916d98e 100644
--- a/internal/aghos/aghos_test.go
+++ b/internal/aghos/aghos_test.go
@@ -3,9 +3,9 @@ package aghos_test
import (
"testing"
- "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
+ "github.com/AdguardTeam/golibs/testutil"
)
func TestMain(m *testing.M) {
- aghtest.DiscardLogOutput(m)
+ testutil.DiscardLogOutput(m)
}
diff --git a/internal/aghos/filewalker_internal_test.go b/internal/aghos/filewalker_internal_test.go
index bb162812..732afc9b 100644
--- a/internal/aghos/filewalker_internal_test.go
+++ b/internal/aghos/filewalker_internal_test.go
@@ -15,11 +15,11 @@ import (
// errFSOpen.
type errFS struct{}
-// errFSOpen is returned from errGlobFS.Open.
+// errFSOpen is returned from errFS.Open.
const errFSOpen errors.Error = "test open error"
-// Open implements the fs.FS interface for *errGlobFS. fsys is always nil and
-// err is always errFSOpen.
+// Open implements the fs.FS interface for *errFS. fsys is always nil and err
+// is always errFSOpen.
func (efs *errFS) Open(name string) (fsys fs.File, err error) {
return nil, errFSOpen
}
diff --git a/internal/aghos/os.go b/internal/aghos/os.go
index b39ecbbd..26201df2 100644
--- a/internal/aghos/os.go
+++ b/internal/aghos/os.go
@@ -175,11 +175,21 @@ func RootDirFS() (fsys fs.FS) {
return os.DirFS("")
}
+// NotifyReconfigureSignal notifies c on receiving reconfigure signals.
+func NotifyReconfigureSignal(c chan<- os.Signal) {
+ notifyReconfigureSignal(c)
+}
+
// NotifyShutdownSignal notifies c on receiving shutdown signals.
func NotifyShutdownSignal(c chan<- os.Signal) {
notifyShutdownSignal(c)
}
+// IsReconfigureSignal returns true if sig is a reconfigure signal.
+func IsReconfigureSignal(sig os.Signal) (ok bool) {
+ return isReconfigureSignal(sig)
+}
+
// IsShutdownSignal returns true if sig is a shutdown signal.
func IsShutdownSignal(sig os.Signal) (ok bool) {
return isShutdownSignal(sig)
diff --git a/internal/aghos/os_unix.go b/internal/aghos/os_unix.go
index da8ee912..7e04f0c0 100644
--- a/internal/aghos/os_unix.go
+++ b/internal/aghos/os_unix.go
@@ -9,10 +9,18 @@ import (
"golang.org/x/sys/unix"
)
+func notifyReconfigureSignal(c chan<- os.Signal) {
+ signal.Notify(c, unix.SIGHUP)
+}
+
func notifyShutdownSignal(c chan<- os.Signal) {
signal.Notify(c, unix.SIGINT, unix.SIGQUIT, unix.SIGTERM)
}
+func isReconfigureSignal(sig os.Signal) (ok bool) {
+ return sig == unix.SIGHUP
+}
+
func isShutdownSignal(sig os.Signal) (ok bool) {
switch sig {
case
diff --git a/internal/aghos/os_windows.go b/internal/aghos/os_windows.go
index c79a603f..616fcf46 100644
--- a/internal/aghos/os_windows.go
+++ b/internal/aghos/os_windows.go
@@ -39,17 +39,23 @@ func isOpenWrt() (ok bool) {
return false
}
+func notifyReconfigureSignal(c chan<- os.Signal) {
+ signal.Notify(c, windows.SIGHUP)
+}
+
func notifyShutdownSignal(c chan<- os.Signal) {
// syscall.SIGTERM is processed automatically. See go doc os/signal,
// section Windows.
signal.Notify(c, os.Interrupt)
}
+func isReconfigureSignal(sig os.Signal) (ok bool) {
+ return sig == windows.SIGHUP
+}
+
func isShutdownSignal(sig os.Signal) (ok bool) {
switch sig {
- case
- os.Interrupt,
- syscall.SIGTERM:
+ case os.Interrupt, syscall.SIGTERM:
return true
default:
return false
diff --git a/internal/aghtest/aghtest.go b/internal/aghtest/aghtest.go
index 878ef178..850446e0 100644
--- a/internal/aghtest/aghtest.go
+++ b/internal/aghtest/aghtest.go
@@ -3,21 +3,11 @@ package aghtest
import (
"io"
- "os"
"testing"
"github.com/AdguardTeam/golibs/log"
)
-// DiscardLogOutput runs tests with discarded logger output.
-func DiscardLogOutput(m *testing.M) {
- // TODO(e.burkov): Refactor code and tests to not use the global mutable
- // logger.
- log.SetOutput(io.Discard)
-
- os.Exit(m.Run())
-}
-
// ReplaceLogWriter moves logger output to w and uses Cleanup method of t to
// revert changes.
func ReplaceLogWriter(t testing.TB, w io.Writer) {
diff --git a/internal/aghtest/interface.go b/internal/aghtest/interface.go
index 2de9d372..7dec58e5 100644
--- a/internal/aghtest/interface.go
+++ b/internal/aghtest/interface.go
@@ -15,6 +15,8 @@ import (
// Standard Library
+// Package fs
+
// type check
var _ fs.FS = &FS{}
@@ -58,6 +60,8 @@ func (fsys *StatFS) Stat(name string) (fs.FileInfo, error) {
return fsys.OnStat(name)
}
+// Package net
+
// type check
var _ net.Listener = (*Listener)(nil)
@@ -83,31 +87,9 @@ func (l *Listener) Close() (err error) {
return l.OnClose()
}
-// Module dnsproxy
+// Module adguard-home
-// type check
-var _ upstream.Upstream = (*UpstreamMock)(nil)
-
-// UpstreamMock is a mock [upstream.Upstream] implementation for tests.
-//
-// TODO(a.garipov): Replace with all uses of Upstream with UpstreamMock and
-// rename it to just Upstream.
-type UpstreamMock struct {
- OnAddress func() (addr string)
- OnExchange func(req *dns.Msg) (resp *dns.Msg, err error)
-}
-
-// Address implements the [upstream.Upstream] interface for *UpstreamMock.
-func (u *UpstreamMock) Address() (addr string) {
- return u.OnAddress()
-}
-
-// Exchange implements the [upstream.Upstream] interface for *UpstreamMock.
-func (u *UpstreamMock) Exchange(req *dns.Msg) (resp *dns.Msg, err error) {
- return u.OnExchange(req)
-}
-
-// Module AdGuardHome
+// Package aghos
// type check
var _ aghos.FSWatcher = (*FSWatcher)(nil)
@@ -133,3 +115,35 @@ func (w *FSWatcher) Add(name string) (err error) {
func (w *FSWatcher) Close() (err error) {
return w.OnClose()
}
+
+// Module dnsproxy
+
+// Package upstream
+
+// type check
+var _ upstream.Upstream = (*UpstreamMock)(nil)
+
+// UpstreamMock is a mock [upstream.Upstream] implementation for tests.
+//
+// TODO(a.garipov): Replace with all uses of Upstream with UpstreamMock and
+// rename it to just Upstream.
+type UpstreamMock struct {
+ OnAddress func() (addr string)
+ OnExchange func(req *dns.Msg) (resp *dns.Msg, err error)
+ OnClose func() (err error)
+}
+
+// Address implements the [upstream.Upstream] interface for *UpstreamMock.
+func (u *UpstreamMock) Address() (addr string) {
+ return u.OnAddress()
+}
+
+// Exchange implements the [upstream.Upstream] interface for *UpstreamMock.
+func (u *UpstreamMock) Exchange(req *dns.Msg) (resp *dns.Msg, err error) {
+ return u.OnExchange(req)
+}
+
+// Close implements the [upstream.Upstream] interface for *UpstreamMock.
+func (u *UpstreamMock) Close() (err error) {
+ return u.OnClose()
+}
diff --git a/internal/aghtest/interface_test.go b/internal/aghtest/interface_test.go
index 5a465c2c..9141d132 100644
--- a/internal/aghtest/interface_test.go
+++ b/internal/aghtest/interface_test.go
@@ -1,9 +1,3 @@
package aghtest_test
-import (
- "github.com/AdguardTeam/AdGuardHome/internal/aghos"
- "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
-)
-
-// type check
-var _ aghos.FSWatcher = (*aghtest.FSWatcher)(nil)
+// Put interface checks that cause import cycles here.
diff --git a/internal/aghtest/upstream.go b/internal/aghtest/upstream.go
index 699c14b9..be6bbdbe 100644
--- a/internal/aghtest/upstream.go
+++ b/internal/aghtest/upstream.go
@@ -5,12 +5,12 @@ import (
"encoding/hex"
"fmt"
"net"
+ "net/netip"
"strings"
- "testing"
+ "github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/miekg/dns"
- "github.com/stretchr/testify/require"
)
// Additional Upstream Testing Utilities
@@ -25,50 +25,11 @@ type Upstream struct {
IPv4 map[string][]net.IP
// IPv6 is a map of hostname to IPv6.
IPv6 map[string][]net.IP
- // Reverse is a map of address to domain name.
- Reverse map[string][]string
- // Addr is the address for Address method.
- Addr string
}
-// RespondTo returns a response with answer if req has class cl, question type
-// qt, and target targ.
-func RespondTo(t testing.TB, req *dns.Msg, cl, qt uint16, targ, answer string) (resp *dns.Msg) {
- t.Helper()
+var _ upstream.Upstream = (*Upstream)(nil)
- require.NotNil(t, req)
- require.Len(t, req.Question, 1)
-
- q := req.Question[0]
- targ = dns.Fqdn(targ)
- if q.Qclass != cl || q.Qtype != qt || q.Name != targ {
- return nil
- }
-
- respHdr := dns.RR_Header{
- Name: targ,
- Rrtype: qt,
- Class: cl,
- Ttl: 60,
- }
-
- resp = new(dns.Msg).SetReply(req)
- switch qt {
- case dns.TypePTR:
- resp.Answer = []dns.RR{
- &dns.PTR{
- Hdr: respHdr,
- Ptr: answer,
- },
- }
- default:
- t.Fatalf("unsupported question type: %s", dns.Type(qt))
- }
-
- return resp
-}
-
-// Exchange implements the upstream.Upstream interface for *Upstream.
+// Exchange implements the [upstream.Upstream] interface for *Upstream.
//
// TODO(a.garipov): Split further into handlers.
func (u *Upstream) Exchange(m *dns.Msg) (resp *dns.Msg, err error) {
@@ -102,10 +63,6 @@ func (u *Upstream) Exchange(m *dns.Msg) (resp *dns.Msg, err error) {
for _, ip := range u.IPv6[name] {
resp.Answer = append(resp.Answer, &dns.AAAA{Hdr: hdr, AAAA: ip})
}
- case dns.TypePTR:
- for _, name := range u.Reverse[name] {
- resp.Answer = append(resp.Answer, &dns.PTR{Hdr: hdr, Ptr: name})
- }
}
if len(resp.Answer) == 0 {
resp.SetRcode(m, dns.RcodeNameError)
@@ -114,9 +71,106 @@ func (u *Upstream) Exchange(m *dns.Msg) (resp *dns.Msg, err error) {
return resp, nil
}
-// Address implements upstream.Upstream interface for *Upstream.
+// Address implements [upstream.Upstream] interface for *Upstream.
func (u *Upstream) Address() string {
- return u.Addr
+ return "todo.upstream.example"
+}
+
+// Close implements [upstream.Upstream] interface for *Upstream.
+func (u *Upstream) Close() (err error) {
+ return nil
+}
+
+// MatchedResponse is a test helper that returns a response with answer if req
+// has question type qt, and target targ. Otherwise, it returns nil.
+//
+// req must not be nil and req.Question must have a length of 1. Answer is
+// interpreted in the following ways:
+//
+// - For A and AAAA queries, answer must be an IP address of the corresponding
+// protocol version.
+//
+// - For PTR queries, answer should be a domain name in the response.
+//
+// If the answer does not correspond to the question type, MatchedResponse panics.
+// Panics are used instead of [testing.TB], because the helper is intended to
+// use in [UpstreamMock.OnExchange] callbacks, which are usually called in a
+// separate goroutine.
+//
+// TODO(a.garipov): Consider adding version with DNS class as well.
+func MatchedResponse(req *dns.Msg, qt uint16, targ, answer string) (resp *dns.Msg) {
+ if req == nil || len(req.Question) != 1 {
+ panic(fmt.Errorf("bad req: %+v", req))
+ }
+
+ q := req.Question[0]
+ targ = dns.Fqdn(targ)
+ if q.Qclass != dns.ClassINET || q.Qtype != qt || q.Name != targ {
+ return nil
+ }
+
+ respHdr := dns.RR_Header{
+ Name: targ,
+ Rrtype: qt,
+ Class: dns.ClassINET,
+ Ttl: 60,
+ }
+
+ resp = new(dns.Msg).SetReply(req)
+ switch qt {
+ case dns.TypeA:
+ resp.Answer = mustAnsA(respHdr, answer)
+ case dns.TypeAAAA:
+ resp.Answer = mustAnsAAAA(respHdr, answer)
+ case dns.TypePTR:
+ resp.Answer = []dns.RR{&dns.PTR{
+ Hdr: respHdr,
+ Ptr: answer,
+ }}
+ default:
+ panic(fmt.Errorf("aghtest: bad question type: %s", dns.Type(qt)))
+ }
+
+ return resp
+}
+
+// mustAnsA returns valid answer records if s is a valid IPv4 address.
+// Otherwise, mustAnsA panics.
+func mustAnsA(respHdr dns.RR_Header, s string) (ans []dns.RR) {
+ ip, err := netip.ParseAddr(s)
+ if err != nil || !ip.Is4() {
+ panic(fmt.Errorf("aghtest: bad A answer: %+v", s))
+ }
+
+ return []dns.RR{&dns.A{
+ Hdr: respHdr,
+ A: ip.AsSlice(),
+ }}
+}
+
+// mustAnsAAAA returns valid answer records if s is a valid IPv6 address.
+// Otherwise, mustAnsAAAA panics.
+func mustAnsAAAA(respHdr dns.RR_Header, s string) (ans []dns.RR) {
+ ip, err := netip.ParseAddr(s)
+ if err != nil || !ip.Is6() {
+ panic(fmt.Errorf("aghtest: bad AAAA answer: %+v", s))
+ }
+
+ return []dns.RR{&dns.AAAA{
+ Hdr: respHdr,
+ AAAA: ip.AsSlice(),
+ }}
+}
+
+// NewUpstreamMock returns an [*UpstreamMock], fields OnAddress and OnClose of
+// which are set to stubs that return "upstream.example" and nil respectively.
+// The field OnExchange is set to onExc.
+func NewUpstreamMock(onExc func(req *dns.Msg) (resp *dns.Msg, err error)) (u *UpstreamMock) {
+ return &UpstreamMock{
+ OnAddress: func() (addr string) { return "upstream.example" },
+ OnExchange: onExc,
+ OnClose: func() (err error) { return nil },
+ }
}
// NewBlockUpstream returns an [*UpstreamMock] that works like an upstream that
@@ -144,9 +198,7 @@ func NewBlockUpstream(hostname string, shouldBlock bool) (u *UpstreamMock) {
}
return &UpstreamMock{
- OnAddress: func() (addr string) {
- return "sbpc.upstream.example"
- },
+ OnAddress: func() (addr string) { return "sbpc.upstream.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
resp = respTmpl.Copy()
resp.SetReply(req)
@@ -154,6 +206,7 @@ func NewBlockUpstream(hostname string, shouldBlock bool) (u *UpstreamMock) {
return resp, nil
},
+ OnClose: func() (err error) { return nil },
}
}
@@ -165,11 +218,10 @@ const ErrUpstream errors.Error = "test upstream error"
// its Exchange method.
func NewErrorUpstream() (u *UpstreamMock) {
return &UpstreamMock{
- OnAddress: func() (addr string) {
- return "error.upstream.example"
- },
+ OnAddress: func() (addr string) { return "error.upstream.example" },
OnExchange: func(_ *dns.Msg) (resp *dns.Msg, err error) {
return nil, errors.Error("test upstream error")
},
+ OnClose: func() (err error) { return nil },
}
}
diff --git a/internal/aghtls/aghtls.go b/internal/aghtls/aghtls.go
index 5dc7a382..7aa35b0c 100644
--- a/internal/aghtls/aghtls.go
+++ b/internal/aghtls/aghtls.go
@@ -1,7 +1,50 @@
// Package aghtls contains utilities for work with TLS.
package aghtls
-import "crypto/tls"
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "net/netip"
+
+ "github.com/AdguardTeam/golibs/log"
+)
+
+// init makes sure that the cipher name map is filled.
+//
+// TODO(a.garipov): Propose a similar API to crypto/tls.
+func init() {
+ suites := tls.CipherSuites()
+ cipherSuites = make(map[string]uint16, len(suites))
+ for _, s := range suites {
+ cipherSuites[s.Name] = s.ID
+ }
+
+ log.Debug("tls: known ciphers: %q", cipherSuites)
+}
+
+// cipherSuites are a name-to-ID mapping of cipher suites from crypto/tls. It
+// is filled by init. It must not be modified.
+var cipherSuites map[string]uint16
+
+// ParseCiphers parses a slice of cipher suites from cipher names.
+func ParseCiphers(cipherNames []string) (cipherIDs []uint16, err error) {
+ if cipherNames == nil {
+ return nil, nil
+ }
+
+ cipherIDs = make([]uint16, 0, len(cipherNames))
+ for _, name := range cipherNames {
+ id, ok := cipherSuites[name]
+ if !ok {
+ return nil, fmt.Errorf("unknown cipher %q", name)
+ }
+
+ cipherIDs = append(cipherIDs, id)
+ }
+
+ return cipherIDs, nil
+}
// SaferCipherSuites returns a set of default cipher suites with vulnerable and
// weak cipher suites removed.
@@ -28,3 +71,19 @@ func SaferCipherSuites() (safe []uint16) {
return safe
}
+
+// CertificateHasIP returns true if cert has at least a single IP address among
+// its subjectAltNames.
+func CertificateHasIP(cert *x509.Certificate) (ok bool) {
+ if len(cert.IPAddresses) > 0 {
+ return true
+ }
+
+ for _, name := range cert.DNSNames {
+ if _, err := netip.ParseAddr(name); err == nil {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/internal/aghtls/aghtls_test.go b/internal/aghtls/aghtls_test.go
new file mode 100644
index 00000000..7e5b99f9
--- /dev/null
+++ b/internal/aghtls/aghtls_test.go
@@ -0,0 +1,56 @@
+package aghtls_test
+
+import (
+ "crypto/tls"
+ "testing"
+
+ "github.com/AdguardTeam/AdGuardHome/internal/aghtls"
+ "github.com/AdguardTeam/golibs/testutil"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMain(m *testing.M) {
+ testutil.DiscardLogOutput(m)
+}
+
+func TestParseCiphers(t *testing.T) {
+ testCases := []struct {
+ name string
+ wantErrMsg string
+ want []uint16
+ in []string
+ }{{
+ name: "nil",
+ wantErrMsg: "",
+ want: nil,
+ in: nil,
+ }, {
+ name: "empty",
+ wantErrMsg: "",
+ want: []uint16{},
+ in: []string{},
+ }, {}, {
+ name: "one",
+ wantErrMsg: "",
+ want: []uint16{tls.TLS_AES_128_GCM_SHA256},
+ in: []string{"TLS_AES_128_GCM_SHA256"},
+ }, {
+ name: "several",
+ wantErrMsg: "",
+ want: []uint16{tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384},
+ in: []string{"TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384"},
+ }, {
+ name: "bad",
+ wantErrMsg: `unknown cipher "bad_cipher"`,
+ want: nil,
+ in: []string{"bad_cipher"},
+ }}
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ got, err := aghtls.ParseCiphers(tc.in)
+ testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
+ assert.Equal(t, tc.want, got)
+ })
+ }
+}
diff --git a/internal/aghtls/root.go b/internal/aghtls/root.go
new file mode 100644
index 00000000..d81db143
--- /dev/null
+++ b/internal/aghtls/root.go
@@ -0,0 +1,14 @@
+package aghtls
+
+import (
+ "crypto/x509"
+)
+
+// SystemRootCAs tries to load root certificates from the operating system. It
+// returns nil in case nothing is found so that Go' crypto/x509 can use its
+// default algorithm to find system root CA list.
+//
+// See https://github.com/AdguardTeam/AdGuardHome/issues/1311.
+func SystemRootCAs() (roots *x509.CertPool) {
+ return rootCAs()
+}
diff --git a/internal/aghtls/root_linux.go b/internal/aghtls/root_linux.go
new file mode 100644
index 00000000..0805f198
--- /dev/null
+++ b/internal/aghtls/root_linux.go
@@ -0,0 +1,56 @@
+//go:build linux
+
+package aghtls
+
+import (
+ "crypto/x509"
+ "os"
+ "path/filepath"
+
+ "github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/log"
+)
+
+func rootCAs() (roots *x509.CertPool) {
+ // Directories with the system root certificates, which aren't supported by
+ // Go's crypto/x509.
+ dirs := []string{
+ // Entware.
+ "/opt/etc/ssl/certs",
+ }
+
+ roots = x509.NewCertPool()
+ for _, dir := range dirs {
+ dirEnts, err := os.ReadDir(dir)
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ continue
+ }
+
+ // TODO(a.garipov): Improve error handling here and in other places.
+ log.Error("aghtls: opening directory %q: %s", dir, err)
+ }
+
+ var rootsAdded bool
+ for _, de := range dirEnts {
+ var certData []byte
+ rootFile := filepath.Join(dir, de.Name())
+ certData, err = os.ReadFile(rootFile)
+ if err != nil {
+ log.Error("aghtls: reading root cert: %s", err)
+ } else {
+ if roots.AppendCertsFromPEM(certData) {
+ rootsAdded = true
+ } else {
+ log.Error("aghtls: could not add root from %q", rootFile)
+ }
+ }
+ }
+
+ if rootsAdded {
+ return roots
+ }
+ }
+
+ return nil
+}
diff --git a/internal/aghtls/root_others.go b/internal/aghtls/root_others.go
new file mode 100644
index 00000000..38a50630
--- /dev/null
+++ b/internal/aghtls/root_others.go
@@ -0,0 +1,9 @@
+//go:build !linux
+
+package aghtls
+
+import "crypto/x509"
+
+func rootCAs() (roots *x509.CertPool) {
+ return nil
+}
diff --git a/internal/dhcpd/config.go b/internal/dhcpd/config.go
index 9d8ef057..718d567f 100644
--- a/internal/dhcpd/config.go
+++ b/internal/dhcpd/config.go
@@ -3,12 +3,12 @@ package dhcpd
import (
"fmt"
"net"
+ "net/netip"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/netutil"
)
// ServerConfig is the configuration for the DHCP server. The order of YAML
@@ -65,16 +65,16 @@ type V4ServerConf struct {
Enabled bool `yaml:"-" json:"-"`
InterfaceName string `yaml:"-" json:"-"`
- GatewayIP net.IP `yaml:"gateway_ip" json:"gateway_ip"`
- SubnetMask net.IP `yaml:"subnet_mask" json:"subnet_mask"`
+ GatewayIP netip.Addr `yaml:"gateway_ip" json:"gateway_ip"`
+ SubnetMask netip.Addr `yaml:"subnet_mask" json:"subnet_mask"`
// broadcastIP is the broadcasting address pre-calculated from the
// configured gateway IP and subnet mask.
- broadcastIP net.IP
+ broadcastIP netip.Addr
// The first & the last IP address for dynamic leases
// Bytes [0..2] of the last allowed IP address must match the first IP
- RangeStart net.IP `yaml:"range_start" json:"range_start"`
- RangeEnd net.IP `yaml:"range_end" json:"range_end"`
+ RangeStart netip.Addr `yaml:"range_start" json:"range_start"`
+ RangeEnd netip.Addr `yaml:"range_end" json:"range_end"`
LeaseDuration uint32 `yaml:"lease_duration" json:"lease_duration"` // in seconds
@@ -95,11 +95,11 @@ type V4ServerConf struct {
ipRange *ipRange
leaseTime time.Duration // the time during which a dynamic lease is considered valid
- dnsIPAddrs []net.IP // IPv4 addresses to return to DHCP clients as DNS server addresses
+ dnsIPAddrs []netip.Addr // IPv4 addresses to return to DHCP clients as DNS server addresses
// subnet contains the DHCP server's subnet. The IP is the IP of the
// gateway.
- subnet *net.IPNet
+ subnet netip.Prefix
// notify is a way to signal to other components that leases have been
// changed. notify must be called outside of locked sections, since the
@@ -113,16 +113,12 @@ type V4ServerConf struct {
// errNilConfig is an error returned by validation method if the config is nil.
const errNilConfig errors.Error = "nil config"
-// ensureV4 returns a 4-byte version of ip. An error is returned if the passed
-// ip is not an IPv4.
-func ensureV4(ip net.IP) (ip4 net.IP, err error) {
- if ip == nil {
- return nil, fmt.Errorf("%v is not an IP address", ip)
- }
-
- ip4 = ip.To4()
- if ip4 == nil {
- return nil, fmt.Errorf("%v is not an IPv4 address", ip)
+// ensureV4 returns an unmapped version of ip. An error is returned if the
+// passed ip is not an IPv4.
+func ensureV4(ip netip.Addr, kind string) (ip4 netip.Addr, err error) {
+ ip4 = ip.Unmap()
+ if !ip4.IsValid() || !ip4.Is4() {
+ return netip.Addr{}, fmt.Errorf("%v is not an IPv4 %s", ip, kind)
}
return ip4, nil
@@ -139,33 +135,45 @@ func (c *V4ServerConf) Validate() (err error) {
return errNilConfig
}
- var gatewayIP net.IP
- gatewayIP, err = ensureV4(c.GatewayIP)
+ gatewayIP, err := ensureV4(c.GatewayIP, "address")
if err != nil {
- // Don't wrap an errors since it's inforative enough as is and there is
+ // Don't wrap an errors since it's informative enough as is and there is
// an annotation deferred already.
return err
}
- if c.SubnetMask == nil {
- return fmt.Errorf("invalid subnet mask: %v", c.SubnetMask)
- }
-
- subnetMask := net.IPMask(netutil.CloneIP(c.SubnetMask.To4()))
- c.subnet = &net.IPNet{
- IP: gatewayIP,
- Mask: subnetMask,
- }
- c.broadcastIP = aghnet.BroadcastFromIPNet(c.subnet)
-
- c.ipRange, err = newIPRange(c.RangeStart, c.RangeEnd)
+ subnetMask, err := ensureV4(c.SubnetMask, "subnet mask")
if err != nil {
- // Don't wrap an errors since it's inforative enough as is and there is
+ // Don't wrap an errors since it's informative enough as is and there is
+ // an annotation deferred already.
+ return err
+ }
+ maskLen, _ := net.IPMask(subnetMask.AsSlice()).Size()
+
+ c.subnet = netip.PrefixFrom(gatewayIP, maskLen)
+ c.broadcastIP = aghnet.BroadcastFromPref(c.subnet)
+
+ rangeStart, err := ensureV4(c.RangeStart, "address")
+ if err != nil {
+ // Don't wrap an errors since it's informative enough as is and there is
+ // an annotation deferred already.
+ return err
+ }
+ rangeEnd, err := ensureV4(c.RangeEnd, "address")
+ if err != nil {
+ // Don't wrap an errors since it's informative enough as is and there is
// an annotation deferred already.
return err
}
- if c.ipRange.contains(gatewayIP) {
+ c.ipRange, err = newIPRange(rangeStart.AsSlice(), rangeEnd.AsSlice())
+ if err != nil {
+ // Don't wrap an errors since it's informative enough as is and there is
+ // an annotation deferred already.
+ return err
+ }
+
+ if c.ipRange.contains(gatewayIP.AsSlice()) {
return fmt.Errorf("gateway ip %v in the ip range: %v-%v",
gatewayIP,
c.RangeStart,
@@ -173,14 +181,14 @@ func (c *V4ServerConf) Validate() (err error) {
)
}
- if !c.subnet.Contains(c.RangeStart) {
+ if !c.subnet.Contains(rangeStart) {
return fmt.Errorf("range start %v is outside network %v",
c.RangeStart,
c.subnet,
)
}
- if !c.subnet.Contains(c.RangeEnd) {
+ if !c.subnet.Contains(rangeEnd) {
return fmt.Errorf("range end %v is outside network %v",
c.RangeEnd,
c.subnet,
diff --git a/internal/dhcpd/conn_unix.go b/internal/dhcpd/conn_unix.go
index ec58afda..efcbc8aa 100644
--- a/internal/dhcpd/conn_unix.go
+++ b/internal/dhcpd/conn_unix.go
@@ -73,10 +73,10 @@ func (s *v4Server) newDHCPConn(iface *net.Interface) (c net.PacketConn, err erro
return &dhcpConn{
udpConn: bcast,
- bcastIP: s.conf.broadcastIP,
+ bcastIP: s.conf.broadcastIP.AsSlice(),
rawConn: ucast,
srcMAC: iface.HardwareAddr,
- srcIP: s.conf.dnsIPAddrs[0],
+ srcIP: s.conf.dnsIPAddrs[0].AsSlice(),
}, nil
}
diff --git a/internal/dhcpd/dhcpd.go b/internal/dhcpd/dhcpd.go
index 875b3d79..ea7725a9 100644
--- a/internal/dhcpd/dhcpd.go
+++ b/internal/dhcpd/dhcpd.go
@@ -9,8 +9,8 @@ import (
"time"
"github.com/AdguardTeam/golibs/log"
- "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
+ "golang.org/x/exp/slices"
)
const (
@@ -54,8 +54,8 @@ func (l *Lease) Clone() (clone *Lease) {
return &Lease{
Expiry: l.Expiry,
Hostname: l.Hostname,
- HWAddr: netutil.CloneMAC(l.HWAddr),
- IP: netutil.CloneIP(l.IP),
+ HWAddr: slices.Clone(l.HWAddr),
+ IP: slices.Clone(l.IP),
}
}
@@ -242,7 +242,7 @@ func Create(conf *ServerConfig) (s *server, err error) {
v4conf := conf.Conf4
v4conf.InterfaceName = s.conf.InterfaceName
v4conf.notify = s.onNotify
- v4conf.Enabled = s.conf.Enabled && len(v4conf.RangeStart) != 0
+ v4conf.Enabled = s.conf.Enabled && v4conf.RangeStart.IsValid()
s.srv4, err = v4Create(&v4conf)
if err != nil {
diff --git a/internal/dhcpd/dhcpd_unix_test.go b/internal/dhcpd/dhcpd_unix_test.go
index cd1ca39a..0bf516cb 100644
--- a/internal/dhcpd/dhcpd_unix_test.go
+++ b/internal/dhcpd/dhcpd_unix_test.go
@@ -4,19 +4,19 @@ package dhcpd
import (
"net"
+ "net/netip"
"os"
"testing"
"time"
- "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
- "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "golang.org/x/exp/slices"
)
func TestMain(m *testing.M) {
- aghtest.DiscardLogOutput(m)
+ testutil.DiscardLogOutput(m)
}
func testNotify(flags uint32) {
@@ -33,10 +33,10 @@ func TestDB(t *testing.T) {
s.srv4, err = v4Create(&V4ServerConf{
Enabled: true,
- RangeStart: net.IP{192, 168, 10, 100},
- RangeEnd: net.IP{192, 168, 10, 200},
- GatewayIP: net.IP{192, 168, 10, 1},
- SubnetMask: net.IP{255, 255, 255, 0},
+ RangeStart: netip.MustParseAddr("192.168.10.100"),
+ RangeEnd: netip.MustParseAddr("192.168.10.200"),
+ GatewayIP: netip.MustParseAddr("192.168.10.1"),
+ SubnetMask: netip.MustParseAddr("255.255.255.0"),
notify: testNotify,
})
require.NoError(t, err)
@@ -113,35 +113,35 @@ func TestNormalizeLeases(t *testing.T) {
func TestV4Server_badRange(t *testing.T) {
testCases := []struct {
name string
+ gatewayIP netip.Addr
+ subnetMask netip.Addr
wantErrMsg string
- gatewayIP net.IP
- subnetMask net.IP
}{{
- name: "gateway_in_range",
+ name: "gateway_in_range",
+ gatewayIP: netip.MustParseAddr("192.168.10.120"),
+ subnetMask: netip.MustParseAddr("255.255.255.0"),
wantErrMsg: "dhcpv4: gateway ip 192.168.10.120 in the ip range: " +
"192.168.10.20-192.168.10.200",
- gatewayIP: net.IP{192, 168, 10, 120},
- subnetMask: net.IP{255, 255, 255, 0},
}, {
- name: "outside_range_start",
+ name: "outside_range_start",
+ gatewayIP: netip.MustParseAddr("192.168.10.1"),
+ subnetMask: netip.MustParseAddr("255.255.255.240"),
wantErrMsg: "dhcpv4: range start 192.168.10.20 is outside network " +
"192.168.10.1/28",
- gatewayIP: net.IP{192, 168, 10, 1},
- subnetMask: net.IP{255, 255, 255, 240},
}, {
- name: "outside_range_end",
+ name: "outside_range_end",
+ gatewayIP: netip.MustParseAddr("192.168.10.1"),
+ subnetMask: netip.MustParseAddr("255.255.255.224"),
wantErrMsg: "dhcpv4: range end 192.168.10.200 is outside network " +
"192.168.10.1/27",
- gatewayIP: net.IP{192, 168, 10, 1},
- subnetMask: net.IP{255, 255, 255, 224},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
conf := V4ServerConf{
Enabled: true,
- RangeStart: net.IP{192, 168, 10, 20},
- RangeEnd: net.IP{192, 168, 10, 200},
+ RangeStart: netip.MustParseAddr("192.168.10.20"),
+ RangeEnd: netip.MustParseAddr("192.168.10.200"),
GatewayIP: tc.gatewayIP,
SubnetMask: tc.subnetMask,
notify: testNotify,
@@ -156,7 +156,7 @@ func TestV4Server_badRange(t *testing.T) {
// cloneUDPAddr returns a deep copy of a.
func cloneUDPAddr(a *net.UDPAddr) (clone *net.UDPAddr) {
return &net.UDPAddr{
- IP: netutil.CloneIP(a.IP),
+ IP: slices.Clone(a.IP),
Port: a.Port,
Zone: a.Zone,
}
diff --git a/internal/dhcpd/http_unix.go b/internal/dhcpd/http_unix.go
index de06431f..2e7ef57d 100644
--- a/internal/dhcpd/http_unix.go
+++ b/internal/dhcpd/http_unix.go
@@ -7,6 +7,7 @@ import (
"fmt"
"net"
"net/http"
+ "net/netip"
"os"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
@@ -17,11 +18,11 @@ import (
)
type v4ServerConfJSON struct {
- GatewayIP net.IP `json:"gateway_ip"`
- SubnetMask net.IP `json:"subnet_mask"`
- RangeStart net.IP `json:"range_start"`
- RangeEnd net.IP `json:"range_end"`
- LeaseDuration uint32 `json:"lease_duration"`
+ GatewayIP netip.Addr `json:"gateway_ip"`
+ SubnetMask netip.Addr `json:"subnet_mask"`
+ RangeStart netip.Addr `json:"range_start"`
+ RangeEnd netip.Addr `json:"range_end"`
+ LeaseDuration uint32 `json:"lease_duration"`
}
func (j *v4ServerConfJSON) toServerConf() *V4ServerConf {
@@ -39,8 +40,8 @@ func (j *v4ServerConfJSON) toServerConf() *V4ServerConf {
}
type v6ServerConfJSON struct {
- RangeStart net.IP `json:"range_start"`
- LeaseDuration uint32 `json:"lease_duration"`
+ RangeStart netip.Addr `json:"range_start"`
+ LeaseDuration uint32 `json:"lease_duration"`
}
func v6JSONToServerConf(j *v6ServerConfJSON) V6ServerConf {
@@ -49,7 +50,7 @@ func v6JSONToServerConf(j *v6ServerConfJSON) V6ServerConf {
}
return V6ServerConf{
- RangeStart: j.RangeStart,
+ RangeStart: j.RangeStart.AsSlice(),
LeaseDuration: j.LeaseDuration,
}
}
@@ -78,18 +79,7 @@ func (s *server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
status.Leases = s.Leases(LeasesDynamic)
status.StaticLeases = s.Leases(LeasesStatic)
- w.Header().Set("Content-Type", "application/json")
-
- err := json.NewEncoder(w).Encode(status)
- if err != nil {
- aghhttp.Error(
- r,
- w,
- http.StatusInternalServerError,
- "Unable to marshal DHCP status json: %s",
- err,
- )
- }
+ _ = aghhttp.WriteJSONResponse(w, r, status)
}
func (s *server) enableDHCP(ifaceName string) (code int, err error) {
@@ -155,7 +145,7 @@ func (s *server) handleDHCPSetConfigV4(
v4Conf := conf.V4.toServerConf()
v4Conf.Enabled = conf.Enabled == aghalg.NBTrue
- if len(v4Conf.RangeStart) == 0 {
+ if !v4Conf.RangeStart.IsValid() {
v4Conf.Enabled = false
}
@@ -246,22 +236,7 @@ func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
return
}
- if conf.Enabled != aghalg.NBNull {
- s.conf.Enabled = conf.Enabled == aghalg.NBTrue
- }
-
- if conf.InterfaceName != "" {
- s.conf.InterfaceName = conf.InterfaceName
- }
-
- if srv4 != nil {
- s.srv4 = srv4
- }
-
- if srv6 != nil {
- s.srv6 = srv6
- }
-
+ s.setConfFromJSON(conf, srv4, srv6)
s.conf.ConfigModified()
err = s.dbLoad()
@@ -280,13 +255,33 @@ func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
}
}
+// setConfFromJSON sets configuration parameters in s from the new configuration
+// decoded from JSON.
+func (s *server) setConfFromJSON(conf *dhcpServerConfigJSON, srv4, srv6 DHCPServer) {
+ if conf.Enabled != aghalg.NBNull {
+ s.conf.Enabled = conf.Enabled == aghalg.NBTrue
+ }
+
+ if conf.InterfaceName != "" {
+ s.conf.InterfaceName = conf.InterfaceName
+ }
+
+ if srv4 != nil {
+ s.srv4 = srv4
+ }
+
+ if srv6 != nil {
+ s.srv6 = srv6
+ }
+}
+
type netInterfaceJSON struct {
- Name string `json:"name"`
- HardwareAddr string `json:"hardware_address"`
- Flags string `json:"flags"`
- GatewayIP net.IP `json:"gateway_ip"`
- Addrs4 []net.IP `json:"ipv4_addresses"`
- Addrs6 []net.IP `json:"ipv6_addresses"`
+ Name string `json:"name"`
+ HardwareAddr string `json:"hardware_address"`
+ Flags string `json:"flags"`
+ GatewayIP netip.Addr `json:"gateway_ip"`
+ Addrs4 []netip.Addr `json:"ipv4_addresses"`
+ Addrs6 []netip.Addr `json:"ipv6_addresses"`
}
func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
@@ -347,13 +342,18 @@ func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
return
}
// ignore link-local
+ //
+ // TODO(e.burkov): Try to listen DHCP on LLA as well.
if ipnet.IP.IsLinkLocalUnicast() {
continue
}
- if ipnet.IP.To4() != nil {
- jsonIface.Addrs4 = append(jsonIface.Addrs4, ipnet.IP)
+
+ if ip4 := ipnet.IP.To4(); ip4 != nil {
+ addr := netip.AddrFrom4(*(*[4]byte)(ip4))
+ jsonIface.Addrs4 = append(jsonIface.Addrs4, addr)
} else {
- jsonIface.Addrs6 = append(jsonIface.Addrs6, ipnet.IP)
+ addr := netip.AddrFrom16(*(*[16]byte)(ipnet.IP))
+ jsonIface.Addrs6 = append(jsonIface.Addrs6, addr)
}
}
if len(jsonIface.Addrs4)+len(jsonIface.Addrs6) != 0 {
diff --git a/internal/dhcpd/http_windows.go b/internal/dhcpd/http_windows.go
index 5f7f73c1..fda72d48 100644
--- a/internal/dhcpd/http_windows.go
+++ b/internal/dhcpd/http_windows.go
@@ -3,11 +3,10 @@
package dhcpd
import (
- "encoding/json"
"net/http"
+ "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
- "github.com/AdguardTeam/golibs/log"
)
// jsonError is a generic JSON error response.
@@ -25,15 +24,9 @@ type jsonError struct {
// TODO(a.garipov): Either take the logger from the server after we've
// refactored logging or make this not a method of *Server.
func (s *server) notImplemented(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusNotImplemented)
-
- err := json.NewEncoder(w).Encode(&jsonError{
+ _ = aghhttp.WriteJSONResponseCode(w, r, http.StatusNotImplemented, &jsonError{
Message: aghos.Unsupported("dhcp").Error(),
})
- if err != nil {
- log.Debug("writing 501 json response: %s", err)
- }
}
// registerHandlers sets the handlers for DHCP HTTP API that always respond with
diff --git a/internal/dhcpd/iprange.go b/internal/dhcpd/iprange.go
index 45422957..6cf7d01f 100644
--- a/internal/dhcpd/iprange.go
+++ b/internal/dhcpd/iprange.go
@@ -27,6 +27,8 @@ const maxRangeLen = math.MaxUint32
// newIPRange creates a new IP address range. start must be less than end. The
// resulting range must not be greater than maxRangeLen.
+//
+// TODO(e.burkov): Use netip.Addr.
func newIPRange(start, end net.IP) (r *ipRange, err error) {
defer func() { err = errors.Annotate(err, "invalid ip range: %w") }()
diff --git a/internal/dhcpd/options_unix.go b/internal/dhcpd/options_unix.go
index 6950604d..082826d5 100644
--- a/internal/dhcpd/options_unix.go
+++ b/internal/dhcpd/options_unix.go
@@ -372,12 +372,9 @@ func (s *v4Server) prepareOptions() {
dhcpv4.OptGeneric(dhcpv4.OptionTCPKeepaliveGarbage, []byte{0x01}),
// Values From Configuration
+ dhcpv4.OptRouter(s.conf.GatewayIP.AsSlice()),
- // Set the Router Option to working subnet's IP since it's initialized
- // with the address of the gateway.
- dhcpv4.OptRouter(s.conf.subnet.IP),
-
- dhcpv4.OptSubnetMask(s.conf.subnet.Mask),
+ dhcpv4.OptSubnetMask(s.conf.SubnetMask.AsSlice()),
)
// Set values for explicitly configured options.
diff --git a/internal/dhcpd/options_unix_test.go b/internal/dhcpd/options_unix_test.go
index 2b5a5cb0..a4ab7dc8 100644
--- a/internal/dhcpd/options_unix_test.go
+++ b/internal/dhcpd/options_unix_test.go
@@ -251,8 +251,6 @@ func TestPrepareOptions(t *testing.T) {
for _, tc := range testCases {
s := &v4Server{
conf: &V4ServerConf{
- // Just to avoid nil pointer dereference.
- subnet: &net.IPNet{},
Options: tc.opts,
},
}
diff --git a/internal/dhcpd/v4_unix.go b/internal/dhcpd/v4_unix.go
index 3735ffdc..d83e0125 100644
--- a/internal/dhcpd/v4_unix.go
+++ b/internal/dhcpd/v4_unix.go
@@ -6,6 +6,7 @@ import (
"bytes"
"fmt"
"net"
+ "net/netip"
"strings"
"sync"
"time"
@@ -295,7 +296,8 @@ func (s *v4Server) addLease(l *Lease) (err error) {
if l.IsStatic() {
// TODO(a.garipov, d.seregin): Subnet can be nil when dhcp server is
// disabled.
- if sn := s.conf.subnet; !sn.Contains(l.IP) {
+ addr := netip.AddrFrom4(*(*[4]byte)(l.IP.To4()))
+ if sn := s.conf.subnet; !sn.Contains(addr) {
return fmt.Errorf("subnet %s does not contain the ip %q", sn, l.IP)
}
} else if !inOffset {
@@ -353,7 +355,7 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
ip := l.IP.To4()
if ip == nil {
return fmt.Errorf("invalid ip %q, only ipv4 is supported", l.IP)
- } else if gwIP := s.conf.GatewayIP; gwIP.Equal(ip) {
+ } else if gwIP := s.conf.GatewayIP; gwIP == netip.AddrFrom4(*(*[4]byte)(ip)) {
return fmt.Errorf("can't assign the gateway IP %s to the lease", gwIP)
}
@@ -701,7 +703,8 @@ func (s *v4Server) handleSelecting(
// Client inserts the address of the selected server in server identifier,
// ciaddr MUST be zero.
mac := req.ClientHWAddr
- if !sid.Equal(s.conf.dnsIPAddrs[0]) {
+
+ if !sid.Equal(s.conf.dnsIPAddrs[0].AsSlice()) {
log.Debug("dhcpv4: bad server identifier in req msg for %s: %s", mac, sid)
return nil, false
@@ -733,7 +736,8 @@ func (s *v4Server) handleSelecting(
func (s *v4Server) handleInitReboot(req *dhcpv4.DHCPv4, reqIP net.IP) (l *Lease, needsReply bool) {
mac := req.ClientHWAddr
- if ip4 := reqIP.To4(); ip4 == nil {
+ ip4 := reqIP.To4()
+ if ip4 == nil {
log.Debug("dhcpv4: bad requested address in req msg for %s: %s", mac, reqIP)
return nil, false
@@ -747,7 +751,7 @@ func (s *v4Server) handleInitReboot(req *dhcpv4.DHCPv4, reqIP net.IP) (l *Lease,
return nil, false
}
- if !s.conf.subnet.Contains(reqIP) {
+ if !s.conf.subnet.Contains(netip.AddrFrom4(*(*[4]byte)(ip4))) {
// If the DHCP server detects that the client is on the wrong net then
// the server SHOULD send a DHCPNAK message to the client.
log.Debug("dhcpv4: wrong subnet in init-reboot req msg for %s: %s", mac, reqIP)
@@ -972,7 +976,7 @@ func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) int {
// Include server's identifier option since any reply should contain it.
//
// See https://datatracker.ietf.org/doc/html/rfc2131#page-29.
- resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0]))
+ resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0].AsSlice()))
// TODO(a.garipov): Refactor this into handlers.
var l *Lease
@@ -1014,7 +1018,7 @@ func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) int {
}
if l != nil {
- resp.YourIPAddr = netutil.CloneIP(l.IP)
+ resp.YourIPAddr = slices.Clone(l.IP)
}
s.updateOptions(req, resp)
@@ -1188,7 +1192,14 @@ func (s *v4Server) Start() (err error) {
s.implicitOpts.Update(dhcpv4.OptDNS(dnsIPAddrs...))
}
- s.conf.dnsIPAddrs = dnsIPAddrs
+ for _, ip := range dnsIPAddrs {
+ ip = ip.To4()
+ if ip == nil {
+ continue
+ }
+
+ s.conf.dnsIPAddrs = append(s.conf.dnsIPAddrs, netip.AddrFrom4(*(*[4]byte)(ip)))
+ }
var c net.PacketConn
if c, err = s.newDHCPConn(iface); err != nil {
diff --git a/internal/dhcpd/v4_unix_test.go b/internal/dhcpd/v4_unix_test.go
index c73009a2..411e36d9 100644
--- a/internal/dhcpd/v4_unix_test.go
+++ b/internal/dhcpd/v4_unix_test.go
@@ -5,6 +5,7 @@ package dhcpd
import (
"fmt"
"net"
+ "net/netip"
"strings"
"testing"
"time"
@@ -22,11 +23,11 @@ import (
)
var (
- DefaultRangeStart = net.IP{192, 168, 10, 100}
- DefaultRangeEnd = net.IP{192, 168, 10, 200}
- DefaultGatewayIP = net.IP{192, 168, 10, 1}
- DefaultSelfIP = net.IP{192, 168, 10, 2}
- DefaultSubnetMask = net.IP{255, 255, 255, 0}
+ DefaultRangeStart = netip.MustParseAddr("192.168.10.100")
+ DefaultRangeEnd = netip.MustParseAddr("192.168.10.200")
+ DefaultGatewayIP = netip.MustParseAddr("192.168.10.1")
+ DefaultSelfIP = netip.MustParseAddr("192.168.10.2")
+ DefaultSubnetMask = netip.MustParseAddr("255.255.255.0")
)
// defaultV4ServerConf returns the default configuration for *v4Server to use in
@@ -39,7 +40,7 @@ func defaultV4ServerConf() (conf *V4ServerConf) {
GatewayIP: DefaultGatewayIP,
SubnetMask: DefaultSubnetMask,
notify: testNotify,
- dnsIPAddrs: []net.IP{DefaultSelfIP},
+ dnsIPAddrs: []netip.Addr{DefaultSelfIP},
}
}
@@ -82,7 +83,7 @@ func TestV4Server_leasing(t *testing.T) {
Expiry: time.Unix(leaseExpireStatic, 0),
Hostname: staticName,
HWAddr: anotherMAC,
- IP: anotherIP,
+ IP: anotherIP.AsSlice(),
})
assert.ErrorIs(t, err, ErrDupHostname)
})
@@ -96,7 +97,7 @@ func TestV4Server_leasing(t *testing.T) {
Expiry: time.Unix(leaseExpireStatic, 0),
Hostname: anotherName,
HWAddr: staticMAC,
- IP: anotherIP,
+ IP: anotherIP.AsSlice(),
})
testutil.AssertErrorMsg(t, wantErrMsg, err)
})
@@ -135,7 +136,7 @@ func TestV4Server_leasing(t *testing.T) {
dhcpv4.WithOption(dhcpv4.OptHostName(name)),
dhcpv4.WithOption(dhcpv4.OptRequestedIPAddress(ip)),
dhcpv4.WithOption(dhcpv4.OptClientIdentifier([]byte{1, 2, 3, 4, 5, 6, 8})),
- dhcpv4.WithGatewayIP(DefaultGatewayIP),
+ dhcpv4.WithGatewayIP(DefaultGatewayIP.AsSlice()),
)
require.NoError(t, err)
@@ -150,7 +151,7 @@ func TestV4Server_leasing(t *testing.T) {
}
t.Run("same_name", func(t *testing.T) {
- resp := discoverAnOffer(t, staticName, anotherIP, anotherMAC)
+ resp := discoverAnOffer(t, staticName, anotherIP.AsSlice(), anotherMAC)
req, err := dhcpv4.NewRequestFromOffer(resp, dhcpv4.WithOption(
dhcpv4.OptHostName(staticName),
@@ -164,7 +165,7 @@ func TestV4Server_leasing(t *testing.T) {
})
t.Run("same_mac", func(t *testing.T) {
- resp := discoverAnOffer(t, anotherName, anotherIP, staticMAC)
+ resp := discoverAnOffer(t, anotherName, anotherIP.AsSlice(), staticMAC)
req, err := dhcpv4.NewRequestFromOffer(resp, dhcpv4.WithOption(
dhcpv4.OptHostName(anotherName),
@@ -219,7 +220,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
lease: &Lease{
Hostname: "probably-router.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
- IP: DefaultGatewayIP,
+ IP: DefaultGatewayIP.AsSlice(),
},
name: "with_gateway_ip",
wantErrMsg: "dhcpv4: adding static lease: " +
@@ -326,7 +327,7 @@ func TestV4_AddReplace(t *testing.T) {
}
func TestV4Server_handle_optionsPriority(t *testing.T) {
- defaultIP := net.IP{192, 168, 1, 1}
+ defaultIP := netip.MustParseAddr("192.168.1.1")
knownIP := net.IP{1, 2, 3, 4}
// prepareSrv creates a *v4Server and sets the opt6IPs in the initial
@@ -343,14 +344,14 @@ func TestV4Server_handle_optionsPriority(t *testing.T) {
}
conf.Options = []string{b.String()}
} else {
- defer func() { s.implicitOpts.Update(dhcpv4.OptDNS(defaultIP)) }()
+ defer func() { s.implicitOpts.Update(dhcpv4.OptDNS(defaultIP.AsSlice())) }()
}
var err error
s, err = v4Create(conf)
require.NoError(t, err)
- s.conf.dnsIPAddrs = []net.IP{defaultIP}
+ s.conf.dnsIPAddrs = []netip.Addr{defaultIP}
return s
}
@@ -386,7 +387,7 @@ func TestV4Server_handle_optionsPriority(t *testing.T) {
t.Run("default", func(t *testing.T) {
s := prepareSrv(t, nil)
- checkResp(t, s, []net.IP{defaultIP})
+ checkResp(t, s, []net.IP{defaultIP.AsSlice()})
})
t.Run("explicitly_configured", func(t *testing.T) {
@@ -481,7 +482,6 @@ func TestV4Server_updateOptions(t *testing.T) {
s, err := v4Create(conf)
require.NoError(t, err)
-
require.IsType(t, (*v4Server)(nil), s)
t.Run(tc.name, func(t *testing.T) {
@@ -506,8 +506,9 @@ func TestV4StaticLease_Get(t *testing.T) {
s, ok := sIface.(*v4Server)
require.True(t, ok)
- s.conf.dnsIPAddrs = []net.IP{{192, 168, 10, 1}}
- s.implicitOpts.Update(dhcpv4.OptDNS(s.conf.dnsIPAddrs...))
+ dnsAddr := netip.MustParseAddr("192.168.10.1")
+ s.conf.dnsIPAddrs = []netip.Addr{dnsAddr}
+ s.implicitOpts.Update(dhcpv4.OptDNS(dnsAddr.AsSlice()))
l := &Lease{
Hostname: "static-1.local",
@@ -539,9 +540,12 @@ func TestV4StaticLease_Get(t *testing.T) {
assert.Equal(t, dhcpv4.MessageTypeOffer, resp.MessageType())
assert.Equal(t, mac, resp.ClientHWAddr)
assert.True(t, l.IP.Equal(resp.YourIPAddr))
- assert.True(t, s.conf.GatewayIP.Equal(resp.Router()[0]))
- assert.True(t, s.conf.GatewayIP.Equal(resp.ServerIdentifier()))
- assert.Equal(t, s.conf.subnet.Mask, resp.SubnetMask())
+
+ assert.True(t, resp.Router()[0].Equal(s.conf.GatewayIP.AsSlice()))
+ assert.True(t, resp.ServerIdentifier().Equal(s.conf.GatewayIP.AsSlice()))
+
+ ones, _ := resp.SubnetMask().Size()
+ assert.Equal(t, s.conf.subnet.Bits(), ones)
assert.Equal(t, s.conf.leaseTime.Seconds(), resp.IPAddressLeaseTime(-1).Seconds())
})
@@ -561,16 +565,19 @@ func TestV4StaticLease_Get(t *testing.T) {
assert.Equal(t, dhcpv4.MessageTypeAck, resp.MessageType())
assert.Equal(t, mac, resp.ClientHWAddr)
assert.True(t, l.IP.Equal(resp.YourIPAddr))
- assert.True(t, s.conf.GatewayIP.Equal(resp.Router()[0]))
- assert.True(t, s.conf.GatewayIP.Equal(resp.ServerIdentifier()))
- assert.Equal(t, s.conf.subnet.Mask, resp.SubnetMask())
+
+ assert.True(t, resp.Router()[0].Equal(s.conf.GatewayIP.AsSlice()))
+ assert.True(t, resp.ServerIdentifier().Equal(s.conf.GatewayIP.AsSlice()))
+
+ ones, _ := resp.SubnetMask().Size()
+ assert.Equal(t, s.conf.subnet.Bits(), ones)
assert.Equal(t, s.conf.leaseTime.Seconds(), resp.IPAddressLeaseTime(-1).Seconds())
})
dnsAddrs := resp.DNS()
require.Len(t, dnsAddrs, 1)
- assert.True(t, s.conf.GatewayIP.Equal(dnsAddrs[0]))
+ assert.True(t, dnsAddrs[0].Equal(s.conf.GatewayIP.AsSlice()))
t.Run("check_lease", func(t *testing.T) {
ls := s.GetLeases(LeasesStatic)
@@ -591,8 +598,8 @@ func TestV4DynamicLease_Get(t *testing.T) {
s, err := v4Create(conf)
require.NoError(t, err)
- s.conf.dnsIPAddrs = []net.IP{{192, 168, 10, 1}}
- s.implicitOpts.Update(dhcpv4.OptDNS(s.conf.dnsIPAddrs...))
+ s.conf.dnsIPAddrs = []netip.Addr{netip.MustParseAddr("192.168.10.1")}
+ s.implicitOpts.Update(dhcpv4.OptDNS(s.conf.dnsIPAddrs[0].AsSlice()))
var req, resp *dhcpv4.DHCPv4
mac := net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}
@@ -617,15 +624,16 @@ func TestV4DynamicLease_Get(t *testing.T) {
assert.Equal(t, dhcpv4.MessageTypeOffer, resp.MessageType())
assert.Equal(t, mac, resp.ClientHWAddr)
- assert.Equal(t, s.conf.RangeStart, resp.YourIPAddr)
- assert.Equal(t, s.conf.GatewayIP, resp.ServerIdentifier())
+ assert.True(t, resp.YourIPAddr.Equal(s.conf.RangeStart.AsSlice()))
+ assert.True(t, resp.ServerIdentifier().Equal(s.conf.GatewayIP.AsSlice()))
router := resp.Router()
require.Len(t, router, 1)
- assert.Equal(t, s.conf.GatewayIP, router[0])
+ assert.True(t, router[0].Equal(s.conf.GatewayIP.AsSlice()))
- assert.Equal(t, s.conf.subnet.Mask, resp.SubnetMask())
+ ones, _ := resp.SubnetMask().Size()
+ assert.Equal(t, s.conf.subnet.Bits(), ones)
assert.Equal(t, s.conf.leaseTime.Seconds(), resp.IPAddressLeaseTime(-1).Seconds())
assert.Equal(t, []byte("012"), resp.Options.Get(dhcpv4.OptionFQDN))
@@ -649,15 +657,17 @@ func TestV4DynamicLease_Get(t *testing.T) {
t.Run("ack", func(t *testing.T) {
assert.Equal(t, dhcpv4.MessageTypeAck, resp.MessageType())
assert.Equal(t, mac, resp.ClientHWAddr)
- assert.True(t, s.conf.RangeStart.Equal(resp.YourIPAddr))
+ assert.True(t, resp.YourIPAddr.Equal(s.conf.RangeStart.AsSlice()))
router := resp.Router()
require.Len(t, router, 1)
- assert.Equal(t, s.conf.GatewayIP, router[0])
+ assert.True(t, router[0].Equal(s.conf.GatewayIP.AsSlice()))
- assert.True(t, s.conf.GatewayIP.Equal(resp.ServerIdentifier()))
- assert.Equal(t, s.conf.subnet.Mask, resp.SubnetMask())
+ assert.True(t, resp.ServerIdentifier().Equal(s.conf.GatewayIP.AsSlice()))
+
+ ones, _ := resp.SubnetMask().Size()
+ assert.Equal(t, s.conf.subnet.Bits(), ones)
assert.Equal(t, s.conf.leaseTime.Seconds(), resp.IPAddressLeaseTime(-1).Seconds())
})
diff --git a/internal/dnsforward/access.go b/internal/dnsforward/access.go
index 23ec1137..6d45a6d5 100644
--- a/internal/dnsforward/access.go
+++ b/internal/dnsforward/access.go
@@ -3,24 +3,26 @@ package dnsforward
import (
"encoding/json"
"fmt"
- "net"
"net/http"
+ "net/netip"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/log"
- "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
)
-// accessCtx controls IP and client blocking that takes place before all other
-// processing. An accessCtx is safe for concurrent use.
-type accessCtx struct {
- allowedIPs *netutil.IPMap
- blockedIPs *netutil.IPMap
+// unit is a convenient alias for struct{}
+type unit = struct{}
+
+// accessManager controls IP and client blocking that takes place before all
+// other processing. An accessManager is safe for concurrent use.
+type accessManager struct {
+ allowedIPs map[netip.Addr]unit
+ blockedIPs map[netip.Addr]unit
allowedClientIDs *stringutil.Set
blockedClientIDs *stringutil.Set
@@ -28,36 +30,29 @@ type accessCtx struct {
blockedHostsEng *urlfilter.DNSEngine
// TODO(a.garipov): Create a type for a set of IP networks.
- // netutil.IPNetSet?
- allowedNets []*net.IPNet
- blockedNets []*net.IPNet
+ allowedNets []netip.Prefix
+ blockedNets []netip.Prefix
}
-// unit is a convenient alias for struct{}
-type unit = struct{}
-
// processAccessClients is a helper for processing a list of client strings,
// which may be an IP address, a CIDR, or a ClientID.
func processAccessClients(
clientStrs []string,
- ips *netutil.IPMap,
- nets *[]*net.IPNet,
+ ips map[netip.Addr]unit,
+ nets *[]netip.Prefix,
clientIDs *stringutil.Set,
) (err error) {
for i, s := range clientStrs {
- if ip := net.ParseIP(s); ip != nil {
- ips.Set(ip, unit{})
- } else if cidrIP, ipnet, cidrErr := net.ParseCIDR(s); cidrErr == nil {
- ipnet.IP = cidrIP
+ var ip netip.Addr
+ var ipnet netip.Prefix
+ if ip, err = netip.ParseAddr(s); err == nil {
+ ips[ip] = unit{}
+ } else if ipnet, err = netip.ParsePrefix(s); err == nil {
*nets = append(*nets, ipnet)
} else {
- idErr := ValidateClientID(s)
- if idErr != nil {
- return fmt.Errorf(
- "value %q at index %d: bad ip, cidr, or clientid",
- s,
- i,
- )
+ err = ValidateClientID(s)
+ if err != nil {
+ return fmt.Errorf("value %q at index %d: bad ip, cidr, or clientid", s, i)
}
clientIDs.Add(s)
@@ -68,10 +63,10 @@ func processAccessClients(
}
// newAccessCtx creates a new accessCtx.
-func newAccessCtx(allowed, blocked, blockedHosts []string) (a *accessCtx, err error) {
- a = &accessCtx{
- allowedIPs: netutil.NewIPMap(0),
- blockedIPs: netutil.NewIPMap(0),
+func newAccessCtx(allowed, blocked, blockedHosts []string) (a *accessManager, err error) {
+ a = &accessManager{
+ allowedIPs: map[netip.Addr]unit{},
+ blockedIPs: map[netip.Addr]unit{},
allowedClientIDs: stringutil.NewSet(),
blockedClientIDs: stringutil.NewSet(),
@@ -111,12 +106,12 @@ func newAccessCtx(allowed, blocked, blockedHosts []string) (a *accessCtx, err er
}
// allowlistMode returns true if this *accessCtx is in the allowlist mode.
-func (a *accessCtx) allowlistMode() (ok bool) {
- return a.allowedIPs.Len() != 0 || a.allowedClientIDs.Len() != 0 || len(a.allowedNets) != 0
+func (a *accessManager) allowlistMode() (ok bool) {
+ return len(a.allowedIPs) != 0 || a.allowedClientIDs.Len() != 0 || len(a.allowedNets) != 0
}
// isBlockedClientID returns true if the ClientID should be blocked.
-func (a *accessCtx) isBlockedClientID(id string) (ok bool) {
+func (a *accessManager) isBlockedClientID(id string) (ok bool) {
allowlistMode := a.allowlistMode()
if id == "" {
// In allowlist mode, consider requests without ClientIDs blocked by
@@ -132,7 +127,7 @@ func (a *accessCtx) isBlockedClientID(id string) (ok bool) {
}
// isBlockedHost returns true if host should be blocked.
-func (a *accessCtx) isBlockedHost(host string) (ok bool) {
+func (a *accessManager) isBlockedHost(host string) (ok bool) {
_, ok = a.blockedHostsEng.Match(strings.ToLower(host))
return ok
@@ -140,7 +135,7 @@ func (a *accessCtx) isBlockedHost(host string) (ok bool) {
// isBlockedIP returns the status of the IP address blocking as well as the rule
// that blocked it.
-func (a *accessCtx) isBlockedIP(ip net.IP) (blocked bool, rule string) {
+func (a *accessManager) isBlockedIP(ip netip.Addr) (blocked bool, rule string) {
blocked = true
ips := a.blockedIPs
ipnets := a.blockedNets
@@ -152,7 +147,7 @@ func (a *accessCtx) isBlockedIP(ip net.IP) (blocked bool, rule string) {
ipnets = a.allowedNets
}
- if _, ok := ips.Get(ip); ok {
+ if _, ok := ips[ip]; ok {
return blocked, ip.String()
}
@@ -240,7 +235,7 @@ func (s *Server) handleAccessSet(w http.ResponseWriter, r *http.Request) {
return
}
- var a *accessCtx
+ var a *accessManager
a, err = newAccessCtx(list.AllowedClients, list.DisallowedClients, list.BlockedHosts)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "creating access ctx: %s", err)
diff --git a/internal/dnsforward/access_test.go b/internal/dnsforward/access_test.go
index 7f9c4e79..7889cdad 100644
--- a/internal/dnsforward/access_test.go
+++ b/internal/dnsforward/access_test.go
@@ -1,7 +1,7 @@
package dnsforward
import (
- "net"
+ "net/netip"
"testing"
"github.com/stretchr/testify/assert"
@@ -95,27 +95,27 @@ func TestIsBlockedIP(t *testing.T) {
testCases := []struct {
name string
wantRule string
- ip net.IP
+ ip netip.Addr
wantBlocked bool
}{{
name: "match_ip",
wantRule: "1.2.3.4",
- ip: net.IP{1, 2, 3, 4},
+ ip: netip.MustParseAddr("1.2.3.4"),
wantBlocked: true,
}, {
name: "match_cidr",
wantRule: "5.6.7.8/24",
- ip: net.IP{5, 6, 7, 100},
+ ip: netip.MustParseAddr("5.6.7.100"),
wantBlocked: true,
}, {
name: "no_match_ip",
wantRule: "",
- ip: net.IP{9, 2, 3, 4},
+ ip: netip.MustParseAddr("9.2.3.4"),
wantBlocked: false,
}, {
name: "no_match_cidr",
wantRule: "",
- ip: net.IP{9, 6, 7, 100},
+ ip: netip.MustParseAddr("9.6.7.100"),
wantBlocked: false,
}}
diff --git a/internal/dnsforward/clientid.go b/internal/dnsforward/clientid.go
index 16bac881..6a111c47 100644
--- a/internal/dnsforward/clientid.go
+++ b/internal/dnsforward/clientid.go
@@ -123,7 +123,14 @@ type quicConnection interface {
func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string, err error) {
proto := pctx.Proto
if proto == proxy.ProtoHTTPS {
- return clientIDFromDNSContextHTTPS(pctx)
+ clientID, err = clientIDFromDNSContextHTTPS(pctx)
+ if err != nil {
+ return "", fmt.Errorf("checking url: %w", err)
+ } else if clientID != "" {
+ return clientID, nil
+ }
+
+ // Go on and check the domain name as well.
} else if proto != proxy.ProtoTLS && proto != proxy.ProtoQUIC {
return "", nil
}
@@ -133,31 +140,9 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
return "", nil
}
- cliSrvName := ""
- switch proto {
- case proxy.ProtoTLS:
- conn := pctx.Conn
- tc, ok := conn.(tlsConn)
- if !ok {
- return "", fmt.Errorf(
- "proxy ctx conn of proto %s is %T, want *tls.Conn",
- proto,
- conn,
- )
- }
-
- cliSrvName = tc.ConnectionState().ServerName
- case proxy.ProtoQUIC:
- conn, ok := pctx.QUICConnection.(quicConnection)
- if !ok {
- return "", fmt.Errorf(
- "proxy ctx quic conn of proto %s is %T, want quic.Connection",
- proto,
- pctx.QUICConnection,
- )
- }
-
- cliSrvName = conn.ConnectionState().TLS.ServerName
+ cliSrvName, err := clientServerName(pctx, proto)
+ if err != nil {
+ return "", err
}
clientID, err = clientIDFromClientServerName(
@@ -171,3 +156,47 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
return clientID, nil
}
+
+// clientServerName returns the TLS server name based on the protocol.
+func clientServerName(pctx *proxy.DNSContext, proto proxy.Proto) (srvName string, err error) {
+ switch proto {
+ case proxy.ProtoHTTPS:
+ // github.com/lucas-clemente/quic-go seems to not populate the TLS
+ // field. So, if the request comes over HTTP/3, use the Host header
+ // value as the server name.
+ //
+ // See https://github.com/lucas-clemente/quic-go/issues/2879.
+ //
+ // TODO(a.garipov): Remove this crutch once they fix it.
+ r := pctx.HTTPRequest
+ if r.ProtoAtLeast(3, 0) {
+ var host string
+ host, err = netutil.SplitHost(r.Host)
+ if err != nil {
+ return "", fmt.Errorf("parsing host: %w", err)
+ }
+
+ srvName = host
+ } else if connState := r.TLS; connState != nil {
+ srvName = r.TLS.ServerName
+ }
+ case proxy.ProtoQUIC:
+ qConn := pctx.QUICConnection
+ conn, ok := qConn.(quicConnection)
+ if !ok {
+ return "", fmt.Errorf("pctx conn of proto %s is %T, want quic.Connection", proto, qConn)
+ }
+
+ srvName = conn.ConnectionState().TLS.ServerName
+ case proxy.ProtoTLS:
+ conn := pctx.Conn
+ tc, ok := conn.(tlsConn)
+ if !ok {
+ return "", fmt.Errorf("pctx conn of proto %s is %T, want *tls.Conn", proto, conn)
+ }
+
+ srvName = tc.ConnectionState().ServerName
+ }
+
+ return srvName, nil
+}
diff --git a/internal/dnsforward/clientid_test.go b/internal/dnsforward/clientid_test.go
index 31c55fcd..b52f2ad0 100644
--- a/internal/dnsforward/clientid_test.go
+++ b/internal/dnsforward/clientid_test.go
@@ -47,8 +47,6 @@ func (c testQUICConnection) ConnectionState() (cs quic.ConnectionState) {
}
func TestServer_clientIDFromDNSContext(t *testing.T) {
- // TODO(a.garipov): Consider moving away from the text-based error
- // checks and onto a more structured approach.
testCases := []struct {
name string
proto proxy.Proto
@@ -57,6 +55,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID string
wantErrMsg string
strictSNI bool
+ useHTTP3 bool
}{{
name: "udp",
proto: proxy.ProtoUDP,
@@ -65,6 +64,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "",
wantErrMsg: "",
strictSNI: false,
+ useHTTP3: false,
}, {
name: "tls_no_clientid",
proto: proxy.ProtoTLS,
@@ -73,6 +73,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "",
wantErrMsg: "",
strictSNI: true,
+ useHTTP3: false,
}, {
name: "tls_no_client_server_name",
proto: proxy.ProtoTLS,
@@ -82,6 +83,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantErrMsg: `clientid check: client server name "" ` +
`doesn't match host server name "example.com"`,
strictSNI: true,
+ useHTTP3: false,
}, {
name: "tls_no_client_server_name_no_strict",
proto: proxy.ProtoTLS,
@@ -90,6 +92,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "",
wantErrMsg: "",
strictSNI: false,
+ useHTTP3: false,
}, {
name: "tls_clientid",
proto: proxy.ProtoTLS,
@@ -98,6 +101,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "cli",
wantErrMsg: "",
strictSNI: true,
+ useHTTP3: false,
}, {
name: "tls_clientid_hostname_error",
proto: proxy.ProtoTLS,
@@ -107,6 +111,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantErrMsg: `clientid check: client server name "cli.example.net" ` +
`doesn't match host server name "example.com"`,
strictSNI: true,
+ useHTTP3: false,
}, {
name: "tls_invalid_clientid",
proto: proxy.ProtoTLS,
@@ -116,6 +121,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantErrMsg: `clientid check: invalid clientid "!!!": ` +
`bad domain name label rune '!'`,
strictSNI: true,
+ useHTTP3: false,
}, {
name: "tls_clientid_too_long",
proto: proxy.ProtoTLS,
@@ -127,6 +133,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
`pqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789": ` +
`domain name label is too long: got 72, max 63`,
strictSNI: true,
+ useHTTP3: false,
}, {
name: "quic_clientid",
proto: proxy.ProtoQUIC,
@@ -135,6 +142,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "cli",
wantErrMsg: "",
strictSNI: true,
+ useHTTP3: false,
}, {
name: "tls_clientid_issue3437",
proto: proxy.ProtoTLS,
@@ -144,6 +152,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantErrMsg: `clientid check: client server name "cli.myexample.com" ` +
`doesn't match host server name "example.com"`,
strictSNI: true,
+ useHTTP3: false,
}, {
name: "tls_case",
proto: proxy.ProtoTLS,
@@ -152,6 +161,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "insensitive",
wantErrMsg: ``,
strictSNI: true,
+ useHTTP3: false,
}, {
name: "quic_case",
proto: proxy.ProtoQUIC,
@@ -160,6 +170,34 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "insensitive",
wantErrMsg: ``,
strictSNI: true,
+ useHTTP3: false,
+ }, {
+ name: "https_no_clientid",
+ proto: proxy.ProtoHTTPS,
+ hostSrvName: "example.com",
+ cliSrvName: "example.com",
+ wantClientID: "",
+ wantErrMsg: "",
+ strictSNI: true,
+ useHTTP3: false,
+ }, {
+ name: "https_clientid",
+ proto: proxy.ProtoHTTPS,
+ hostSrvName: "example.com",
+ cliSrvName: "cli.example.com",
+ wantClientID: "cli",
+ wantErrMsg: "",
+ strictSNI: true,
+ useHTTP3: false,
+ }, {
+ name: "https_clientid_quic",
+ proto: proxy.ProtoHTTPS,
+ hostSrvName: "example.com",
+ cliSrvName: "cli.example.com",
+ wantClientID: "cli",
+ wantErrMsg: "",
+ strictSNI: true,
+ useHTTP3: true,
}}
for _, tc := range testCases {
@@ -173,16 +211,21 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
conf: ServerConfig{TLSConfig: tlsConf},
}
- var conn net.Conn
- if tc.proto == proxy.ProtoTLS {
- conn = testTLSConn{
+ var (
+ conn net.Conn
+ qconn quic.Connection
+ httpReq *http.Request
+ )
+
+ switch tc.proto {
+ case proxy.ProtoHTTPS:
+ httpReq = newHTTPReq(tc.cliSrvName, tc.useHTTP3)
+ case proxy.ProtoQUIC:
+ qconn = testQUICConnection{
serverName: tc.cliSrvName,
}
- }
-
- var qconn quic.Connection
- if tc.proto == proxy.ProtoQUIC {
- qconn = testQUICConnection{
+ case proxy.ProtoTLS:
+ conn = testTLSConn{
serverName: tc.cliSrvName,
}
}
@@ -190,6 +233,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
pctx := &proxy.DNSContext{
Proto: tc.proto,
Conn: conn,
+ HTTPRequest: httpReq,
QUICConnection: qconn,
}
@@ -201,60 +245,107 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
}
}
+// newHTTPReq is a helper to create HTTP requests for tests.
+func newHTTPReq(cliSrvName string, useHTTP3 bool) (r *http.Request) {
+ u := &url.URL{
+ Path: "/dns-query",
+ }
+
+ if useHTTP3 {
+ return &http.Request{
+ ProtoMajor: 3,
+ ProtoMinor: 0,
+ URL: u,
+ Host: cliSrvName,
+ TLS: &tls.ConnectionState{},
+ }
+ }
+
+ return &http.Request{
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ URL: u,
+ Host: cliSrvName,
+ TLS: &tls.ConnectionState{
+ ServerName: cliSrvName,
+ },
+ }
+}
+
func TestClientIDFromDNSContextHTTPS(t *testing.T) {
testCases := []struct {
name string
path string
+ cliSrvName string
wantClientID string
wantErrMsg string
}{{
name: "no_clientid",
path: "/dns-query",
+ cliSrvName: "example.com",
wantClientID: "",
wantErrMsg: "",
}, {
name: "no_clientid_slash",
path: "/dns-query/",
+ cliSrvName: "example.com",
wantClientID: "",
wantErrMsg: "",
}, {
name: "clientid",
path: "/dns-query/cli",
+ cliSrvName: "example.com",
wantClientID: "cli",
wantErrMsg: "",
}, {
name: "clientid_slash",
path: "/dns-query/cli/",
+ cliSrvName: "example.com",
wantClientID: "cli",
wantErrMsg: "",
}, {
name: "clientid_case",
path: "/dns-query/InSeNsItIvE",
+ cliSrvName: "example.com",
wantClientID: "insensitive",
wantErrMsg: ``,
}, {
name: "bad_url",
path: "/foo",
+ cliSrvName: "example.com",
wantClientID: "",
wantErrMsg: `clientid check: invalid path "/foo"`,
}, {
name: "extra",
path: "/dns-query/cli/foo",
+ cliSrvName: "example.com",
wantClientID: "",
wantErrMsg: `clientid check: invalid path "/dns-query/cli/foo": extra parts`,
}, {
name: "invalid_clientid",
path: "/dns-query/!!!",
+ cliSrvName: "example.com",
wantClientID: "",
wantErrMsg: `clientid check: invalid clientid "!!!": bad domain name label rune '!'`,
+ }, {
+ name: "both_ids",
+ path: "/dns-query/right",
+ cliSrvName: "wrong.example.com",
+ wantClientID: "right",
+ wantErrMsg: "",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
+ connState := &tls.ConnectionState{
+ ServerName: tc.cliSrvName,
+ }
+
r := &http.Request{
URL: &url.URL{
Path: tc.path,
},
+ TLS: connState,
}
pctx := &proxy.DNSContext{
diff --git a/internal/dnsforward/config.go b/internal/dnsforward/config.go
index f8e51ff0..6357d681 100644
--- a/internal/dnsforward/config.go
+++ b/internal/dnsforward/config.go
@@ -97,9 +97,16 @@ type FilteringConfig struct {
// Access settings
// --
- AllowedClients []string `yaml:"allowed_clients"` // IP addresses of whitelist clients
- DisallowedClients []string `yaml:"disallowed_clients"` // IP addresses of clients that should be blocked
- BlockedHosts []string `yaml:"blocked_hosts"` // hosts that should be blocked
+ // AllowedClients is the slice of IP addresses, CIDR networks, and ClientIDs
+ // of allowed clients. If not empty, only these clients are allowed, and
+ // [FilteringConfig.DisallowedClients] are ignored.
+ AllowedClients []string `yaml:"allowed_clients"`
+
+ // DisallowedClients is the slice of IP addresses, CIDR networks, and
+ // ClientIDs of disallowed clients.
+ DisallowedClients []string `yaml:"disallowed_clients"`
+
+ BlockedHosts []string `yaml:"blocked_hosts"` // hosts that should be blocked
// TrustedProxies is the list of IP addresses and CIDR networks to detect
// proxy servers addresses the DoH requests from which should be handled.
// The value of nil or an empty slice for this field makes Proxy not trust
@@ -140,13 +147,12 @@ type FilteringConfig struct {
// TLSConfig is the TLS configuration for HTTPS, DNS-over-HTTPS, and DNS-over-TLS
type TLSConfig struct {
+ cert tls.Certificate
+
TLSListenAddrs []*net.TCPAddr `yaml:"-" json:"-"`
QUICListenAddrs []*net.UDPAddr `yaml:"-" json:"-"`
HTTPSListenAddrs []*net.TCPAddr `yaml:"-" json:"-"`
- // Reject connection if the client uses server name (in SNI) that doesn't match the certificate
- StrictSNICheck bool `yaml:"strict_sni_check" json:"-"`
-
// PEM-encoded certificates chain
CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"`
// PEM-encoded private key
@@ -162,9 +168,20 @@ type TLSConfig struct {
// used for ClientID checking and Discovery of Designated Resolvers (DDR).
ServerName string `yaml:"-" json:"-"`
- cert tls.Certificate
// DNS names from certificate (SAN) or CN value from Subject
dnsNames []string
+
+ // OverrideTLSCiphers, when set, contains the names of the cipher suites to
+ // use. If the slice is empty, the default safe suites are used.
+ OverrideTLSCiphers []string `yaml:"override_tls_ciphers,omitempty" json:"-"`
+
+ // StrictSNICheck controls if the connections with SNI mismatching the
+ // certificate's ones should be rejected.
+ StrictSNICheck bool `yaml:"strict_sni_check" json:"-"`
+
+ // hasIPAddrs is set during the certificate parsing and is true if the
+ // configured certificate contains at least a single IP address.
+ hasIPAddrs bool
}
// DNSCryptConfig is the DNSCrypt server configuration struct.
@@ -193,7 +210,9 @@ type ServerConfig struct {
UpstreamTimeout time.Duration
TLSv12Roots *x509.CertPool // list of root CAs for TLSv1.2
- TLSCiphers []uint16 // list of TLS ciphers to use
+
+ // TLSCiphers are the IDs of TLS cipher suites to use.
+ TLSCiphers []uint16
// Called when the configuration is changed by HTTP request
ConfigModified func()
@@ -348,17 +367,13 @@ func UpstreamHTTPVersions(http3 bool) (v []upstream.HTTPVersion) {
// prepareUpstreamSettings - prepares upstream DNS server settings
func (s *Server) prepareUpstreamSettings() error {
- // We're setting a customized set of RootCAs
- // The reason is that Go default mechanism of loading TLS roots
- // does not always work properly on some routers so we're
- // loading roots manually and pass it here.
- // See "util.LoadSystemRootCAs"
+ // We're setting a customized set of RootCAs. The reason is that Go default
+ // mechanism of loading TLS roots does not always work properly on some
+ // routers so we're loading roots manually and pass it here.
+ //
+ // See [aghtls.SystemRootCAs].
upstream.RootCAs = s.conf.TLSv12Roots
-
- // See util.InitTLSCiphers -- removed unsafe ciphers
- if len(s.conf.TLSCiphers) > 0 {
- upstream.CipherSuites = s.conf.TLSCiphers
- }
+ upstream.CipherSuites = s.conf.TLSCiphers
// Load upstreams either from the file, or from the settings
var upstreams []string
@@ -451,7 +466,7 @@ func (s *Server) prepareIpsetListSettings() (err error) {
}
// prepareTLS - prepares TLS configuration for the DNS proxy
-func (s *Server) prepareTLS(proxyConfig *proxy.Config) error {
+func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
if len(s.conf.CertificateChainData) == 0 || len(s.conf.PrivateKeyData) == 0 {
return nil
}
@@ -470,31 +485,32 @@ func (s *Server) prepareTLS(proxyConfig *proxy.Config) error {
proxyConfig.QUICListenAddr,
)
- var err error
s.conf.cert, err = tls.X509KeyPair(s.conf.CertificateChainData, s.conf.PrivateKeyData)
if err != nil {
return fmt.Errorf("failed to parse TLS keypair: %w", err)
}
+ cert, err := x509.ParseCertificate(s.conf.cert.Certificate[0])
+ if err != nil {
+ return fmt.Errorf("x509.ParseCertificate(): %w", err)
+ }
+
+ s.conf.hasIPAddrs = aghtls.CertificateHasIP(cert)
+
if s.conf.StrictSNICheck {
- var x *x509.Certificate
- x, err = x509.ParseCertificate(s.conf.cert.Certificate[0])
- if err != nil {
- return fmt.Errorf("x509.ParseCertificate(): %w", err)
- }
- if len(x.DNSNames) != 0 {
- s.conf.dnsNames = x.DNSNames
- log.Debug("dns: using DNS names from certificate's SAN: %v", x.DNSNames)
+ if len(cert.DNSNames) != 0 {
+ s.conf.dnsNames = cert.DNSNames
+ log.Debug("dnsforward: using certificate's SAN as DNS names: %v", cert.DNSNames)
sort.Strings(s.conf.dnsNames)
} else {
- s.conf.dnsNames = append(s.conf.dnsNames, x.Subject.CommonName)
- log.Debug("dns: using DNS name from certificate's CN: %s", x.Subject.CommonName)
+ s.conf.dnsNames = append(s.conf.dnsNames, cert.Subject.CommonName)
+ log.Debug("dnsforward: using certificate's CN as DNS name: %s", cert.Subject.CommonName)
}
}
proxyConfig.TLSConfig = &tls.Config{
GetCertificate: s.onGetCertificate,
- CipherSuites: aghtls.SaferCipherSuites(),
+ CipherSuites: s.conf.TLSCiphers,
MinVersion: tls.VersionTLS12,
}
diff --git a/internal/dnsforward/dns.go b/internal/dnsforward/dns.go
index 947625e3..e4ca6520 100644
--- a/internal/dnsforward/dns.go
+++ b/internal/dnsforward/dns.go
@@ -3,6 +3,7 @@ package dnsforward
import (
"encoding/binary"
"net"
+ "net/netip"
"strings"
"time"
@@ -194,7 +195,7 @@ func (s *Server) setTableHostToIP(t hostToIPTable) {
s.tableHostToIP = t
}
-func (s *Server) setTableIPToHost(t *netutil.IPMap) {
+func (s *Server) setTableIPToHost(t ipToHostTable) {
s.tableIPToHostLock.Lock()
defer s.tableIPToHostLock.Unlock()
@@ -202,52 +203,54 @@ func (s *Server) setTableIPToHost(t *netutil.IPMap) {
}
func (s *Server) onDHCPLeaseChanged(flags int) {
- var err error
-
- add := true
switch flags {
case dhcpd.LeaseChangedAdded,
dhcpd.LeaseChangedAddedStatic,
dhcpd.LeaseChangedRemovedStatic:
// Go on.
case dhcpd.LeaseChangedRemovedAll:
- add = false
+ s.setTableHostToIP(nil)
+ s.setTableIPToHost(nil)
+
+ return
default:
return
}
- var hostToIP hostToIPTable
- var ipToHost *netutil.IPMap
- if add {
- ll := s.dhcpServer.Leases(dhcpd.LeasesAll)
+ ll := s.dhcpServer.Leases(dhcpd.LeasesAll)
+ hostToIP := make(hostToIPTable, len(ll))
+ ipToHost := make(ipToHostTable, len(ll))
- hostToIP = make(hostToIPTable, len(ll))
- ipToHost = netutil.NewIPMap(len(ll))
+ for _, l := range ll {
+ // TODO(a.garipov): Remove this after we're finished with the client
+ // hostname validations in the DHCP server code.
+ err := netutil.ValidateDomainName(l.Hostname)
+ if err != nil {
+ log.Debug("dnsforward: skipping invalid hostname %q from dhcp: %s", l.Hostname, err)
- for _, l := range ll {
- // TODO(a.garipov): Remove this after we're finished with the client
- // hostname validations in the DHCP server code.
- err = netutil.ValidateDomainName(l.Hostname)
- if err != nil {
- log.Debug(
- "dns: skipping invalid hostname %q from dhcp: %s",
- l.Hostname,
- err,
- )
- }
-
- lowhost := strings.ToLower(l.Hostname + "." + s.localDomainSuffix)
- ip := netutil.CloneIP(l.IP)
-
- ipToHost.Set(ip, lowhost)
- hostToIP[lowhost] = ip
+ continue
}
- log.Debug("dns: added %d A/PTR entries from DHCP", ipToHost.Len())
+ lowhost := strings.ToLower(l.Hostname + "." + s.localDomainSuffix)
+
+ // Assume that we only process IPv4 now.
+ //
+ // TODO(a.garipov): Remove once we switch to netip.Addr more fully.
+ ip, err := netutil.IPToAddr(l.IP, netutil.AddrFamilyIPv4)
+ if err != nil {
+ log.Debug("dnsforward: skipping invalid ip %v from dhcp: %s", l.IP, err)
+
+ continue
+ }
+
+ ipToHost[ip] = lowhost
+ hostToIP[lowhost] = ip
}
s.setTableHostToIP(hostToIP)
s.setTableIPToHost(ipToHost)
+
+ log.Debug("dnsforward: added %d a and ptr entries from dhcp", len(ipToHost))
}
// processDDRQuery responds to Discovery of Designated Resolvers (DDR) SVCB
@@ -256,21 +259,13 @@ func (s *Server) onDHCPLeaseChanged(flags int) {
//
// See https://www.ietf.org/archive/id/draft-ietf-add-ddr-10.html.
func (s *Server) processDDRQuery(dctx *dnsContext) (rc resultCode) {
- pctx := dctx.proxyCtx
- q := pctx.Req.Question[0]
-
if !s.conf.HandleDDR {
return resultCodeSuccess
}
+ pctx := dctx.proxyCtx
+ q := pctx.Req.Question[0]
if q.Name == ddrHostFQDN {
- if s.dnsProxy.TLSListenAddr == nil && s.conf.HTTPSListenAddrs == nil &&
- s.dnsProxy.QUICListenAddr == nil || q.Qtype != dns.TypeSVCB {
- pctx.Res = s.makeResponse(pctx.Req)
-
- return resultCodeFinish
- }
-
pctx.Res = s.makeDDRResponse(pctx.Req)
return resultCodeFinish
@@ -288,6 +283,10 @@ func (s *Server) processDDRQuery(dctx *dnsContext) (rc resultCode) {
// [draft standard]: https://www.ietf.org/archive/id/draft-ietf-add-ddr-10.html.
func (s *Server) makeDDRResponse(req *dns.Msg) (resp *dns.Msg) {
resp = s.makeResponse(req)
+ if req.Question[0].Qtype != dns.TypeSVCB {
+ return resp
+ }
+
// TODO(e.burkov): Think about storing the FQDN version of the server's
// name somewhere.
domainName := dns.Fqdn(s.conf.ServerName)
@@ -309,20 +308,26 @@ func (s *Server) makeDDRResponse(req *dns.Msg) (resp *dns.Msg) {
resp.Answer = append(resp.Answer, ans)
}
- for _, addr := range s.dnsProxy.TLSListenAddr {
- values := []dns.SVCBKeyValue{
- &dns.SVCBAlpn{Alpn: []string{"dot"}},
- &dns.SVCBPort{Port: uint16(addr.Port)},
- }
+ if s.conf.hasIPAddrs {
+ // Only add DNS-over-TLS resolvers in case the certificate contains IP
+ // addresses.
+ //
+ // See https://github.com/AdguardTeam/AdGuardHome/issues/4927.
+ for _, addr := range s.dnsProxy.TLSListenAddr {
+ values := []dns.SVCBKeyValue{
+ &dns.SVCBAlpn{Alpn: []string{"dot"}},
+ &dns.SVCBPort{Port: uint16(addr.Port)},
+ }
- ans := &dns.SVCB{
- Hdr: s.hdr(req, dns.TypeSVCB),
- Priority: 1,
- Target: domainName,
- Value: values,
- }
+ ans := &dns.SVCB{
+ Hdr: s.hdr(req, dns.TypeSVCB),
+ Priority: 1,
+ Target: domainName,
+ Value: values,
+ }
- resp.Answer = append(resp.Answer, ans)
+ resp.Answer = append(resp.Answer, ans)
+ }
}
for _, addr := range s.dnsProxy.QUICListenAddr {
@@ -362,24 +367,13 @@ func (s *Server) processDetermineLocal(dctx *dnsContext) (rc resultCode) {
// dhcpHostToIP tries to get an IP leased by DHCP and returns the copy of
// address since the data inside the internal table may be changed while request
// processing. It's safe for concurrent use.
-func (s *Server) dhcpHostToIP(host string) (ip net.IP, ok bool) {
+func (s *Server) dhcpHostToIP(host string) (ip netip.Addr, ok bool) {
s.tableHostToIPLock.Lock()
defer s.tableHostToIPLock.Unlock()
- if s.tableHostToIP == nil {
- return nil, false
- }
+ ip, ok = s.tableHostToIP[host]
- var ipFromTable net.IP
- ipFromTable, ok = s.tableHostToIP[host]
- if !ok {
- return nil, false
- }
-
- ip = make(net.IP, len(ipFromTable))
- copy(ip, ipFromTable)
-
- return ip, true
+ return ip, ok
}
// processDHCPHosts respond to A requests if the target hostname is known to
@@ -396,7 +390,7 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
}
if !dctx.isLocalClient {
- log.Debug("dns: %q requests for dhcp host %q", pctx.Addr, reqHost)
+ log.Debug("dnsforward: %q requests for dhcp host %q", pctx.Addr, reqHost)
pctx.Res = s.genNXDomain(req)
// Do not even put into query log.
@@ -407,18 +401,18 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
if !ok {
// Go on and process them with filters, including dnsrewrite ones, and
// possibly route them to a domain-specific upstream.
- log.Debug("dns: no dhcp record for %q", reqHost)
+ log.Debug("dnsforward: no dhcp record for %q", reqHost)
return resultCodeSuccess
}
- log.Debug("dns: dhcp record for %q is %s", reqHost, ip)
+ log.Debug("dnsforward: dhcp record for %q is %s", reqHost, ip)
resp := s.makeResponse(req)
if q.Qtype == dns.TypeA {
a := &dns.A{
Hdr: s.hdr(req, dns.TypeA),
- A: ip,
+ A: ip.AsSlice(),
}
resp.Answer = append(resp.Answer, a)
}
@@ -440,7 +434,7 @@ func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
ip, err := netutil.IPFromReversedAddr(q.Name)
if err != nil {
- log.Debug("dns: parsing reversed addr: %s", err)
+ log.Debug("dnsforward: parsing reversed addr: %s", err)
// DNS-Based Service Discovery uses PTR records having not an ARPA
// format of the domain name in question. Those shouldn't be
@@ -448,12 +442,12 @@ func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
// RFC 2782.
name := strings.TrimSuffix(q.Name, ".")
if err = netutil.ValidateSRVDomainName(name); err != nil {
- log.Debug("dns: validating service domain: %s", err)
+ log.Debug("dnsforward: validating service domain: %s", err)
return resultCodeError
}
- log.Debug("dns: request is for a service domain")
+ log.Debug("dnsforward: request is for a service domain")
return resultCodeSuccess
}
@@ -462,13 +456,13 @@ func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
// assume that all the DHCP leases we give are locally-served or at least
// don't need to be accessible externally.
if !s.privateNets.Contains(ip) {
- log.Debug("dns: addr %s is not from locally-served network", ip)
+ log.Debug("dnsforward: addr %s is not from locally-served network", ip)
return resultCodeSuccess
}
if !dctx.isLocalClient {
- log.Debug("dns: %q requests an internal ip", pctx.Addr)
+ log.Debug("dnsforward: %q requests an internal ip", pctx.Addr)
pctx.Res = s.genNXDomain(req)
// Do not even put into query log.
@@ -492,27 +486,13 @@ func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
// ipToDHCPHost tries to get a hostname leased by DHCP. It's safe for
// concurrent use.
-func (s *Server) ipToDHCPHost(ip net.IP) (host string, ok bool) {
+func (s *Server) ipToDHCPHost(ip netip.Addr) (host string, ok bool) {
s.tableIPToHostLock.Lock()
defer s.tableIPToHostLock.Unlock()
- if s.tableIPToHost == nil {
- return "", false
- }
+ host, ok = s.tableIPToHost[ip]
- var v any
- v, ok = s.tableIPToHost.Get(ip)
- if !ok {
- return "", false
- }
-
- if host, ok = v.(string); !ok {
- log.Error("dns: bad type %T in tableIPToHost for %s", v, ip)
-
- return "", false
- }
-
- return host, true
+ return host, ok
}
// processDHCPAddrs responds to PTR requests if the target IP is leased by the
@@ -528,12 +508,20 @@ func (s *Server) processDHCPAddrs(dctx *dnsContext) (rc resultCode) {
return resultCodeSuccess
}
- host, ok := s.ipToDHCPHost(ip)
+ // TODO(a.garipov): Remove once we switch to netip.Addr more fully.
+ ipAddr, err := netutil.IPToAddrNoMapped(ip)
+ if err != nil {
+ log.Debug("dnsforward: bad reverse ip %v from dhcp: %s", ip, err)
+
+ return resultCodeSuccess
+ }
+
+ host, ok := s.ipToDHCPHost(ipAddr)
if !ok {
return resultCodeSuccess
}
- log.Debug("dns: dhcp reverse record for %s is %q", ip, host)
+ log.Debug("dnsforward: dhcp reverse record for %s is %q", ip, host)
req := pctx.Req
resp := s.makeResponse(req)
@@ -638,7 +626,7 @@ func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
//
// TODO(a.garipov): Route such queries to a custom upstream for the
// local domain name if there is one.
- log.Debug("dns: dhcp client hostname %q was not filtered", reqHost)
+ log.Debug("dnsforward: dhcp client hostname %q was not filtered", reqHost)
pctx.Res = s.genNXDomain(req)
return resultCodeFinish
@@ -711,13 +699,13 @@ func (s *Server) setCustomUpstream(pctx *proxy.DNSContext, clientID string) {
id := stringutil.Coalesce(clientID, ipStringFromAddr(pctx.Addr))
upsConf, err := customUpsByClient(id)
if err != nil {
- log.Error("dns: getting custom upstreams for client %s: %s", id, err)
+ log.Error("dnsforward: getting custom upstreams for client %s: %s", id, err)
return
}
if upsConf != nil {
- log.Debug("dns: using custom upstreams for client %s", id)
+ log.Debug("dnsforward: using custom upstreams for client %s", id)
}
pctx.CustomUpstreamConfig = upsConf
diff --git a/internal/dnsforward/dns_test.go b/internal/dnsforward/dns_test.go
index da7c8ae6..d07c30dc 100644
--- a/internal/dnsforward/dns_test.go
+++ b/internal/dnsforward/dns_test.go
@@ -2,13 +2,16 @@ package dnsforward
import (
"net"
+ "net/netip"
"testing"
+ "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/netutil"
+ "github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -154,19 +157,9 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
func prepareTestServer(t *testing.T, portDoH, portDoT, portDoQ int, ddrEnabled bool) (s *Server) {
t.Helper()
- proxyConf := proxy.Config{}
-
- if portDoT > 0 {
- proxyConf.TLSListenAddr = []*net.TCPAddr{{Port: portDoT}}
- }
-
- if portDoQ > 0 {
- proxyConf.QUICListenAddr = []*net.UDPAddr{{Port: portDoQ}}
- }
-
s = &Server{
dnsProxy: &proxy.Proxy{
- Config: proxyConf,
+ Config: proxy.Config{},
},
conf: ServerConfig{
FilteringConfig: FilteringConfig{
@@ -178,8 +171,17 @@ func prepareTestServer(t *testing.T, portDoH, portDoT, portDoQ int, ddrEnabled b
},
}
+ if portDoT > 0 {
+ s.dnsProxy.TLSListenAddr = []*net.TCPAddr{{Port: portDoT}}
+ s.conf.hasIPAddrs = true
+ }
+
+ if portDoQ > 0 {
+ s.dnsProxy.QUICListenAddr = []*net.UDPAddr{{Port: portDoQ}}
+ }
+
if portDoH > 0 {
- s.conf.TLSConfig.HTTPSListenAddrs = []*net.TCPAddr{{Port: portDoH}}
+ s.conf.HTTPSListenAddrs = []*net.TCPAddr{{Port: portDoH}}
}
return s
@@ -230,12 +232,11 @@ func TestServer_ProcessDetermineLocal(t *testing.T) {
}
func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
- knownIP := net.IP{1, 2, 3, 4}
-
+ knownIP := netip.MustParseAddr("1.2.3.4")
testCases := []struct {
name string
host string
- wantIP net.IP
+ wantIP netip.Addr
wantRes resultCode
isLocalCli bool
}{{
@@ -247,19 +248,19 @@ func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
}, {
name: "local_client_unknown_host",
host: "wronghost.lan",
- wantIP: nil,
+ wantIP: netip.Addr{},
wantRes: resultCodeSuccess,
isLocalCli: true,
}, {
name: "external_client_known_host",
host: "example.lan",
- wantIP: nil,
+ wantIP: netip.Addr{},
wantRes: resultCodeFinish,
isLocalCli: false,
}, {
name: "external_client_unknown_host",
host: "wronghost.lan",
- wantIP: nil,
+ wantIP: netip.Addr{},
wantRes: resultCodeFinish,
isLocalCli: false,
}}
@@ -304,7 +305,7 @@ func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
return
}
- if tc.wantIP == nil {
+ if tc.wantIP == (netip.Addr{}) {
assert.Nil(t, pctx.Res)
} else {
require.NotNil(t, pctx.Res)
@@ -312,7 +313,12 @@ func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
ans := pctx.Res.Answer
require.Len(t, ans, 1)
- assert.Equal(t, tc.wantIP, ans[0].(*dns.A).A)
+ a := testutil.RequireTypeAssert[*dns.A](t, ans[0])
+
+ ip, err := netutil.IPToAddr(a.A, netutil.AddrFamilyIPv4)
+ require.NoError(t, err)
+
+ assert.Equal(t, tc.wantIP, ip)
}
})
}
@@ -324,26 +330,26 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
examplelan = "example." + defaultLocalDomainSuffix
)
- knownIP := net.IP{1, 2, 3, 4}
+ knownIP := netip.MustParseAddr("1.2.3.4")
testCases := []struct {
name string
host string
suffix string
- wantIP net.IP
+ wantIP netip.Addr
wantRes resultCode
qtyp uint16
}{{
name: "success_external",
host: examplecom,
suffix: defaultLocalDomainSuffix,
- wantIP: nil,
+ wantIP: netip.Addr{},
wantRes: resultCodeSuccess,
qtyp: dns.TypeA,
}, {
name: "success_external_non_a",
host: examplecom,
suffix: defaultLocalDomainSuffix,
- wantIP: nil,
+ wantIP: netip.Addr{},
wantRes: resultCodeSuccess,
qtyp: dns.TypeCNAME,
}, {
@@ -357,14 +363,14 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
name: "success_internal_unknown",
host: "example-new.lan",
suffix: defaultLocalDomainSuffix,
- wantIP: nil,
+ wantIP: netip.Addr{},
wantRes: resultCodeSuccess,
qtyp: dns.TypeA,
}, {
name: "success_internal_aaaa",
host: examplelan,
suffix: defaultLocalDomainSuffix,
- wantIP: nil,
+ wantIP: netip.Addr{},
wantRes: resultCodeSuccess,
qtyp: dns.TypeAAAA,
}, {
@@ -423,7 +429,7 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
ans := pctx.Res.Answer
require.Len(t, ans, 0)
- } else if tc.wantIP == nil {
+ } else if tc.wantIP == (netip.Addr{}) {
assert.Nil(t, pctx.Res)
} else {
require.NotNil(t, pctx.Res)
@@ -431,19 +437,33 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
ans := pctx.Res.Answer
require.Len(t, ans, 1)
- assert.Equal(t, tc.wantIP, ans[0].(*dns.A).A)
+ a := testutil.RequireTypeAssert[*dns.A](t, ans[0])
+
+ ip, err := netutil.IPToAddr(a.A, netutil.AddrFamilyIPv4)
+ require.NoError(t, err)
+
+ assert.Equal(t, tc.wantIP, ip)
}
})
}
}
func TestServer_ProcessRestrictLocal(t *testing.T) {
- ups := &aghtest.Upstream{
- Reverse: map[string][]string{
- "251.252.253.254.in-addr.arpa.": {"host1.example.net."},
- "1.1.168.192.in-addr.arpa.": {"some.local-client."},
- },
- }
+ const (
+ extPTRQuestion = "251.252.253.254.in-addr.arpa."
+ extPTRAnswer = "host1.example.net."
+ intPTRQuestion = "1.1.168.192.in-addr.arpa."
+ intPTRAnswer = "some.local-client."
+ )
+
+ ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
+ return aghalg.Coalesce(
+ aghtest.MatchedResponse(req, dns.TypePTR, extPTRQuestion, extPTRAnswer),
+ aghtest.MatchedResponse(req, dns.TypePTR, intPTRQuestion, intPTRAnswer),
+ new(dns.Msg).SetRcode(req, dns.RcodeNameError),
+ ), nil
+ })
+
s := createTestServer(t, &filtering.Config{}, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
@@ -513,14 +533,20 @@ func TestServer_ProcessLocalPTR_usingResolvers(t *testing.T) {
const locDomain = "some.local."
const reqAddr = "1.1.168.192.in-addr.arpa."
- s := createTestServer(t, &filtering.Config{}, ServerConfig{
- UDPListenAddrs: []*net.UDPAddr{{}},
- TCPListenAddrs: []*net.TCPAddr{{}},
- }, &aghtest.Upstream{
- Reverse: map[string][]string{
- reqAddr: {locDomain},
+ s := createTestServer(
+ t,
+ &filtering.Config{},
+ ServerConfig{
+ UDPListenAddrs: []*net.UDPAddr{{}},
+ TCPListenAddrs: []*net.TCPAddr{{}},
},
- })
+ aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
+ return aghalg.Coalesce(
+ aghtest.MatchedResponse(req, dns.TypePTR, reqAddr, locDomain),
+ new(dns.Msg).SetRcode(req, dns.RcodeNameError),
+ ), nil
+ }),
+ )
var proxyCtx *proxy.DNSContext
var dnsCtx *dnsContext
diff --git a/internal/dnsforward/dnsforward.go b/internal/dnsforward/dnsforward.go
index 3806ef5b..6d107153 100644
--- a/internal/dnsforward/dnsforward.go
+++ b/internal/dnsforward/dnsforward.go
@@ -5,11 +5,13 @@ import (
"fmt"
"net"
"net/http"
+ "net/netip"
"runtime"
"strings"
"sync"
"time"
+ "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
@@ -43,8 +45,13 @@ var defaultBlockedHosts = []string{"version.bind", "id.server", "hostname.bind"}
var webRegistered bool
-// hostToIPTable is an alias for the type of Server.tableHostToIP.
-type hostToIPTable = map[string]net.IP
+// hostToIPTable is a convenient type alias for tables of host names to an IP
+// address.
+type hostToIPTable = map[string]netip.Addr
+
+// ipToHostTable is a convenient type alias for tables of IP addresses to their
+// host names. For example, for use with PTR queries.
+type ipToHostTable = map[netip.Addr]string
// Server is the main way to start a DNS server.
//
@@ -63,7 +70,7 @@ type Server struct {
dhcpServer dhcpd.Interface // DHCP server instance (optional)
queryLog querylog.QueryLog // Query log instance
stats stats.Interface
- access *accessCtx
+ access *accessManager
// localDomainSuffix is the suffix used to detect internal hosts. It
// must be a valid domain name plus dots on each side.
@@ -81,7 +88,7 @@ type Server struct {
tableHostToIP hostToIPTable
tableHostToIPLock sync.Mutex
- tableIPToHost *netutil.IPMap
+ tableIPToHost ipToHostTable
tableIPToHostLock sync.Mutex
// clientIDCache is a temporary storage for ClientIDs that were extracted
@@ -517,7 +524,7 @@ func validateBlockingMode(mode BlockingMode, blockingIPv4, blockingIPv6 net.IP)
}
// prepareInternalProxy initializes the DNS proxy that is used for internal DNS
-// queries, such at client PTR resolving and updater hostname resolving.
+// queries, such as public clients PTR resolving and updater hostname resolving.
func (s *Server) prepareInternalProxy() (err error) {
conf := &proxy.Config{
CacheEnabled: true,
@@ -557,16 +564,49 @@ func (s *Server) Stop() error {
return s.stopLocked()
}
-// stopLocked stops the DNS server without locking. For internal use only.
-func (s *Server) stopLocked() error {
+// stopLocked stops the DNS server without locking. For internal use only.
+func (s *Server) stopLocked() (err error) {
if s.dnsProxy != nil {
- err := s.dnsProxy.Stop()
+ err = s.dnsProxy.Stop()
if err != nil {
- return fmt.Errorf("could not stop the DNS server properly: %w", err)
+ return fmt.Errorf("closing primary resolvers: %w", err)
}
}
- s.isRunning = false
+ var errs []error
+
+ if upsConf := s.internalProxy.UpstreamConfig; upsConf != nil {
+ const action = "closing internal resolvers"
+
+ err = upsConf.Close()
+ if err != nil {
+ if errors.Is(err, net.ErrClosed) {
+ log.Debug("dnsforward: %s: %s", action, err)
+ } else {
+ errs = append(errs, fmt.Errorf("%s: %w", action, err))
+ }
+ }
+ }
+
+ if upsConf := s.localResolvers.UpstreamConfig; upsConf != nil {
+ const action = "closing local resolvers"
+
+ err = upsConf.Close()
+ if err != nil {
+ if errors.Is(err, net.ErrClosed) {
+ log.Debug("dnsforward: %s: %s", action, err)
+ } else {
+ errs = append(errs, fmt.Errorf("%s: %w", action, err))
+ }
+ }
+ }
+
+ if len(errs) > 0 {
+ return errors.List("stopping dns server", errs...)
+ } else {
+ s.isRunning = false
+ }
+
return nil
}
@@ -639,27 +679,35 @@ func (s *Server) IsBlockedClient(ip net.IP, clientID string) (blocked bool, rule
s.serverLock.RLock()
defer s.serverLock.RUnlock()
+ blockedByIP := false
+ if ip != nil {
+ // TODO(a.garipov): Remove once we switch to netip.Addr more fully.
+ ipAddr, err := netutil.IPToAddrNoMapped(ip)
+ if err != nil {
+ log.Error("dnsforward: bad client ip %v: %s", ip, err)
+
+ return false, ""
+ }
+
+ blockedByIP, rule = s.access.isBlockedIP(ipAddr)
+ }
+
allowlistMode := s.access.allowlistMode()
- blockedByIP, rule := s.access.isBlockedIP(ip)
blockedByClientID := s.access.isBlockedClientID(clientID)
- // Allow if at least one of the checks allows in allowlist mode, but
- // block if at least one of the checks blocks in blocklist mode.
+ // Allow if at least one of the checks allows in allowlist mode, but block
+ // if at least one of the checks blocks in blocklist mode.
if allowlistMode && blockedByIP && blockedByClientID {
- log.Debug("client %s (id %q) is not in access allowlist", ip, clientID)
+ log.Debug("client %v (id %q) is not in access allowlist", ip, clientID)
// Return now without substituting the empty rule for the
// clientID because the rule can't be empty here.
return true, rule
} else if !allowlistMode && (blockedByIP || blockedByClientID) {
- log.Debug("client %s (id %q) is in access blocklist", ip, clientID)
+ log.Debug("client %v (id %q) is in access blocklist", ip, clientID)
blocked = true
}
- if rule == "" {
- rule = clientID
- }
-
- return blocked, rule
+ return blocked, aghalg.Coalesce(rule, clientID)
}
diff --git a/internal/dnsforward/dnsforward_test.go b/internal/dnsforward/dnsforward_test.go
index 7d1ae199..56c21516 100644
--- a/internal/dnsforward/dnsforward_test.go
+++ b/internal/dnsforward/dnsforward_test.go
@@ -33,7 +33,7 @@ import (
)
func TestMain(m *testing.M) {
- aghtest.DiscardLogOutput(m)
+ testutil.DiscardLogOutput(m)
}
const (
@@ -161,8 +161,23 @@ func createTestTLS(t *testing.T, tlsConf TLSConfig) (s *Server, certPem []byte)
return s, certPem
}
+const googleDomainName = "google-public-dns-a.google.com."
+
func createGoogleATestMessage() *dns.Msg {
- return createTestMessage("google-public-dns-a.google.com.")
+ return createTestMessage(googleDomainName)
+}
+
+func newGoogleUpstream() (u upstream.Upstream) {
+ return &aghtest.UpstreamMock{
+ OnAddress: func() (addr string) { return "google.upstream.example" },
+ OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
+ return aghalg.Coalesce(
+ aghtest.MatchedResponse(req, dns.TypeA, googleDomainName, "8.8.8.8"),
+ new(dns.Msg).SetRcode(req, dns.RcodeNameError),
+ ), nil
+ },
+ OnClose: func() (err error) { return nil },
+ }
}
func createTestMessage(host string) *dns.Msg {
@@ -247,13 +262,7 @@ func TestServer(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
}, nil)
- s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
- &aghtest.Upstream{
- IPv4: map[string][]net.IP{
- "google-public-dns-a.google.com.": {{8, 8, 8, 8}},
- },
- },
- }
+ s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
testCases := []struct {
@@ -320,13 +329,7 @@ func TestServerWithProtectionDisabled(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
}, nil)
- s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
- &aghtest.Upstream{
- IPv4: map[string][]net.IP{
- "google-public-dns-a.google.com.": {{8, 8, 8, 8}},
- },
- },
- }
+ s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
// Message over UDP.
@@ -343,13 +346,7 @@ func TestDoTServer(t *testing.T) {
s, certPem := createTestTLS(t, TLSConfig{
TLSListenAddrs: []*net.TCPAddr{{}},
})
- s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
- &aghtest.Upstream{
- IPv4: map[string][]net.IP{
- "google-public-dns-a.google.com.": {{8, 8, 8, 8}},
- },
- },
- }
+ s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
// Add our self-signed generated config to roots.
@@ -373,13 +370,7 @@ func TestDoQServer(t *testing.T) {
s, _ := createTestTLS(t, TLSConfig{
QUICListenAddrs: []*net.UDPAddr{{IP: net.IP{127, 0, 0, 1}}},
})
- s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
- &aghtest.Upstream{
- IPv4: map[string][]net.IP{
- "google-public-dns-a.google.com.": {{8, 8, 8, 8}},
- },
- },
- }
+ s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
// Create a DNS-over-QUIC upstream.
@@ -417,13 +408,7 @@ func TestServerRace(t *testing.T) {
ConfigModified: func() {},
}
s := createTestServer(t, filterConf, forwardConf, nil)
- s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
- &aghtest.Upstream{
- IPv4: map[string][]net.IP{
- "google-public-dns-a.google.com.": {{8, 8, 8, 8}},
- },
- },
- }
+ s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
// Message over UDP.
@@ -557,11 +542,12 @@ func TestServerCustomClientUpstream(t *testing.T) {
}
s := createTestServer(t, &filtering.Config{}, forwardConf, nil)
s.conf.GetCustomUpstreamByClient = func(_ string) (conf *proxy.UpstreamConfig, err error) {
- ups := &aghtest.Upstream{
- IPv4: map[string][]net.IP{
- "host.": {{192, 168, 0, 1}},
- },
- }
+ ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
+ return aghalg.Coalesce(
+ aghtest.MatchedResponse(req, dns.TypeA, "host", "192.168.0.1"),
+ new(dns.Msg).SetRcode(req, dns.RcodeNameError),
+ ), nil
+ })
return &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{ups},
@@ -604,7 +590,6 @@ func TestBlockCNAMEProtectionEnabled(t *testing.T) {
testUpstm := &aghtest.Upstream{
CName: testCNAMEs,
IPv4: testIPv4,
- IPv6: nil,
}
s.conf.ProtectionEnabled = false
s.dnsProxy.UpstreamConfig = &proxy.UpstreamConfig{
@@ -931,16 +916,13 @@ func TestRewrite(t *testing.T) {
},
}))
- s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
- &aghtest.Upstream{
- CName: map[string][]string{
- "example.org": {"somename"},
- },
- IPv4: map[string][]net.IP{
- "example.org.": {{4, 3, 2, 1}},
- },
- },
- }
+ ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
+ return aghalg.Coalesce(
+ aghtest.MatchedResponse(req, dns.TypeA, "example.org", "4.3.2.1"),
+ new(dns.Msg).SetRcode(req, dns.RcodeNameError),
+ ), nil
+ })
+ s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{ups}
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
@@ -1061,11 +1043,12 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
require.Len(t, resp.Answer, 1)
- assert.Equal(t, dns.TypePTR, resp.Answer[0].Header().Rrtype)
- assert.Equal(t, "34.12.168.192.in-addr.arpa.", resp.Answer[0].Header().Name)
+ ans := resp.Answer[0]
+ assert.Equal(t, dns.TypePTR, ans.Header().Rrtype)
+ assert.Equal(t, "34.12.168.192.in-addr.arpa.", ans.Header().Name)
+
+ ptr := testutil.RequireTypeAssert[*dns.PTR](t, ans)
- ptr, ok := resp.Answer[0].(*dns.PTR)
- require.True(t, ok)
assert.Equal(t, dns.Fqdn("myhost."+localDomain), ptr.Ptr)
}
@@ -1211,12 +1194,10 @@ func TestServer_Exchange(t *testing.T) {
extUpstream := &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "external.upstream.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
- resp = aghalg.Coalesce(
- aghtest.RespondTo(t, req, dns.ClassINET, dns.TypePTR, revExtIPv4, onesHost),
+ return aghalg.Coalesce(
+ aghtest.MatchedResponse(req, dns.TypePTR, revExtIPv4, onesHost),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
- )
-
- return resp, nil
+ ), nil
},
}
@@ -1226,12 +1207,10 @@ func TestServer_Exchange(t *testing.T) {
locUpstream := &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "local.upstream.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
- resp = aghalg.Coalesce(
- aghtest.RespondTo(t, req, dns.ClassINET, dns.TypePTR, revLocIPv4, localDomainHost),
+ return aghalg.Coalesce(
+ aghtest.MatchedResponse(req, dns.TypePTR, revLocIPv4, localDomainHost),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
- )
-
- return resp, nil
+ ), nil
},
}
diff --git a/internal/dnsforward/dnsrewrite.go b/internal/dnsforward/dnsrewrite.go
index 9897449d..47eab12b 100644
--- a/internal/dnsforward/dnsrewrite.go
+++ b/internal/dnsforward/dnsrewrite.go
@@ -20,19 +20,13 @@ func (s *Server) filterDNSRewriteResponse(
v rules.RRValue,
) (ans dns.RR, err error) {
switch rr {
- case
- dns.TypeA,
- dns.TypeAAAA:
+ case dns.TypeA, dns.TypeAAAA:
return s.ansFromDNSRewriteIP(v, rr, req)
- case
- dns.TypePTR,
- dns.TypeTXT:
+ case dns.TypePTR, dns.TypeTXT:
return s.ansFromDNSRewriteText(v, rr, req)
case dns.TypeMX:
return s.ansFromDNSRewriteMX(v, rr, req)
- case
- dns.TypeHTTPS,
- dns.TypeSVCB:
+ case dns.TypeHTTPS, dns.TypeSVCB:
return s.ansFromDNSRewriteSVCB(v, rr, req)
case dns.TypeSRV:
return s.ansFromDNSRewriteSRV(v, rr, req)
diff --git a/internal/dnsforward/http.go b/internal/dnsforward/http.go
index 91e31b70..8879bb1a 100644
--- a/internal/dnsforward/http.go
+++ b/internal/dnsforward/http.go
@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/http"
+ "net/netip"
"strings"
"time"
@@ -435,22 +436,22 @@ func validateUpstream(u string, domains []string) (useDefault bool, err error) {
// TODO(e.burkov): Validate the domain name.
for _, proto := range protocols {
if strings.HasPrefix(u, proto) {
- return useDefault, nil
+ return false, nil
}
}
- if strings.Contains(u, "://") {
- return useDefault, errors.Error("wrong protocol")
+ if proto, _, ok := strings.Cut(u, "://"); ok {
+ return false, fmt.Errorf("bad protocol %q", proto)
}
// Check if upstream is either an IP or IP with port.
- if net.ParseIP(u) != nil {
- return useDefault, nil
- } else if _, err = netutil.ParseIPPort(u); err != nil {
- return useDefault, err
+ if _, err = netip.ParseAddr(u); err == nil {
+ return false, nil
+ } else if _, err = netip.ParseAddrPort(u); err == nil {
+ return false, nil
}
- return useDefault, nil
+ return false, err
}
// separateUpstream returns the upstream and the specified domains. domains is
@@ -603,6 +604,7 @@ func checkDNS(
if err != nil {
return fmt.Errorf("failed to choose upstream for %q: %w", upstreamAddr, err)
}
+ defer func() { err = errors.WithDeferred(err, u.Close()) }()
if err = healthCheck(u); err != nil {
err = fmt.Errorf("upstream %q fails to exchange: %w", upstreamAddr, err)
diff --git a/internal/dnsforward/http_test.go b/internal/dnsforward/http_test.go
index f591ac58..64ba21c4 100644
--- a/internal/dnsforward/http_test.go
+++ b/internal/dnsforward/http_test.go
@@ -188,10 +188,8 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
name: "upstream_mode_fastest_addr",
wantSet: "",
}, {
- name: "upstream_dns_bad",
- wantSet: `validating upstream servers: ` +
- `validating upstream "!!!": bad ipport address "!!!": ` +
- `address !!!: missing port in address`,
+ name: "upstream_dns_bad",
+ wantSet: `validating upstream servers: validating upstream "!!!": not an ip:port`,
}, {
name: "bootstraps_bad",
wantSet: `checking bootstrap a: invalid address: ` +
@@ -297,15 +295,15 @@ func TestValidateUpstreams(t *testing.T) {
},
}, {
name: "invalid",
- wantErr: `validating upstream "dhcp://fake.dns": wrong protocol`,
+ wantErr: `validating upstream "dhcp://fake.dns": bad protocol "dhcp"`,
set: []string{"dhcp://fake.dns"},
}, {
name: "invalid",
- wantErr: `validating upstream "1.2.3.4.5": bad ipport address "1.2.3.4.5": address 1.2.3.4.5: missing port in address`,
+ wantErr: `validating upstream "1.2.3.4.5": not an ip:port`,
set: []string{"1.2.3.4.5"},
}, {
name: "invalid",
- wantErr: `validating upstream "123.3.7m": bad ipport address "123.3.7m": address 123.3.7m: missing port in address`,
+ wantErr: `validating upstream "123.3.7m": not an ip:port`,
set: []string{"123.3.7m"},
}, {
name: "invalid",
@@ -313,7 +311,7 @@ func TestValidateUpstreams(t *testing.T) {
set: []string{"[/host.com]tls://dns.adguard.com"},
}, {
name: "invalid",
- wantErr: `validating upstream "[host.ru]#": bad ipport address "[host.ru]#": address [host.ru]#: missing port in address`,
+ wantErr: `validating upstream "[host.ru]#": not an ip:port`,
set: []string{"[host.ru]#"},
}, {
name: "valid_default",
diff --git a/internal/dnsforward/stats.go b/internal/dnsforward/stats.go
index 9a7b1ddb..4d63220d 100644
--- a/internal/dnsforward/stats.go
+++ b/internal/dnsforward/stats.go
@@ -12,6 +12,7 @@ import (
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
+ "golang.org/x/exp/slices"
)
// Write Stats data and logs
@@ -28,7 +29,7 @@ func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) {
}
ip, _ := netutil.IPAndPortFromAddr(pctx.Addr)
- ip = netutil.CloneIP(ip)
+ ip = slices.Clone(ip)
s.serverLock.RLock()
defer s.serverLock.RUnlock()
diff --git a/internal/filtering/blocked.go b/internal/filtering/blocked.go
index 489def36..4e4878aa 100644
--- a/internal/filtering/blocked.go
+++ b/internal/filtering/blocked.go
@@ -10,382 +10,6 @@ import (
"golang.org/x/exp/slices"
)
-// svc represents a single blocked service.
-type svc struct {
- name string
- rules []string
-}
-
-// servicesData contains raw blocked service data.
-//
-// Keep in sync with:
-// - client/src/helpers/constants.js
-// - client/src/components/ui/Icons.js
-var servicesData = []svc{{
- name: "whatsapp",
- rules: []string{
- "||wa.me^",
- "||whatsapp.com^",
- "||whatsapp.net^",
- },
-}, {
- name: "facebook",
- rules: []string{
- "||facebook.com^",
- "||facebook.net^",
- "||fbcdn.net^",
- "||accountkit.com^",
- "||fb.me^",
- "||fb.com^",
- "||fb.gg^",
- "||fbsbx.com^",
- "||fbwat.ch^",
- "||messenger.com^",
- "||facebookcorewwwi.onion^",
- "||fbcdn.com^",
- "||fb.watch^",
- },
-}, {
- name: "twitter",
- rules: []string{
- "||t.co^",
- "||twimg.com^",
- "||twitter.com^",
- "||twttr.com^",
- },
-}, {
- name: "youtube",
- rules: []string{
- "||googlevideo.com^",
- "||wide-youtube.l.google.com^",
- "||youtu.be^",
- "||youtube",
- "||youtube-nocookie.com^",
- "||youtube.com^",
- "||youtubei.googleapis.com^",
- "||youtubekids.com^",
- "||ytimg.com^",
- },
-}, {
- name: "twitch",
- rules: []string{
- "||jtvnw.net^",
- "||ttvnw.net^",
- "||twitch.tv^",
- "||twitchcdn.net^",
- },
-}, {
- name: "netflix",
- rules: []string{
- "||nflxext.com^",
- "||netflix.com^",
- "||nflximg.net^",
- "||nflxvideo.net^",
- "||nflxso.net^",
- },
-}, {
- name: "instagram",
- rules: []string{"||instagram.com^", "||cdninstagram.com^"},
-}, {
- name: "snapchat",
- rules: []string{
- "||snapchat.com^",
- "||sc-cdn.net^",
- "||snap-dev.net^",
- "||snapkit.co",
- "||snapads.com^",
- "||impala-media-production.s3.amazonaws.com^",
- },
-}, {
- name: "discord",
- rules: []string{
- "||discord.gg^",
- "||discordapp.net^",
- "||discordapp.com^",
- "||discord.com^",
- "||discord.gift",
- "||discord.media^",
- },
-}, {
- name: "ok",
- rules: []string{"||ok.ru^"},
-}, {
- name: "skype",
- rules: []string{
- "||edge-skype-com.s-0001.s-msedge.net^",
- "||skype-edf.akadns.net^",
- "||skype.com^",
- "||skypeassets.com^",
- "||skypedata.akadns.net^",
- },
-}, {
- name: "vk",
- rules: []string{
- "||userapi.com^",
- "||vk-cdn.net^",
- "||vk.com^",
- "||vkuservideo.net^",
- },
-}, {
- name: "origin",
- rules: []string{
- "||accounts.ea.com^",
- "||origin.com^",
- "||signin.ea.com^",
- },
-}, {
- name: "steam",
- rules: []string{
- "||steam.com^",
- "||steampowered.com^",
- "||steamcommunity.com^",
- "||steamstatic.com^",
- "||steamstore-a.akamaihd.net^",
- "||steamcdn-a.akamaihd.net^",
- },
-}, {
- name: "epic_games",
- rules: []string{"||epicgames.com^", "||easyanticheat.net^", "||easy.ac^", "||eac-cdn.com^"},
-}, {
- name: "reddit",
- rules: []string{"||reddit.com^", "||redditstatic.com^", "||redditmedia.com^", "||redd.it^"},
-}, {
- name: "mail_ru",
- rules: []string{"||mail.ru^"},
-}, {
- name: "cloudflare",
- rules: []string{
- "||1.1.1.1^",
- "||argotunnel.com^",
- "||cloudflare-dns.com^",
- "||cloudflare-ipfs.com^",
- "||cloudflare-quic.com^",
- "||cloudflare.cn^",
- "||cloudflare.com^",
- "||cloudflare.net^",
- "||cloudflareaccess.com^",
- "||cloudflareapps.com^",
- "||cloudflarebolt.com^",
- "||cloudflareclient.com^",
- "||cloudflareinsights.com^",
- "||cloudflareresolve.com^",
- "||cloudflarestatus.com^",
- "||cloudflarestream.com^",
- "||cloudflarewarp.com^",
- "||dns4torpnlfs2ifuz2s2yf3fc7rdmsbhm6rw75euj35pac6ap25zgqad.onion^",
- "||one.one^",
- "||pages.dev^",
- "||trycloudflare.com^",
- "||videodelivery.net^",
- "||warp.plus^",
- "||workers.dev^",
- },
-}, {
- name: "amazon",
- rules: []string{
- "||amazon.com^",
- "||media-amazon.com^",
- "||primevideo.com^",
- "||amazontrust.com^",
- "||images-amazon.com^",
- "||ssl-images-amazon.com^",
- "||amazonpay.com^",
- "||amazonpay.in^",
- "||amazon-adsystem.com^",
- "||a2z.com^",
- "||amazon.ae^",
- "||amazon.ca^",
- "||amazon.cn^",
- "||amazon.de^",
- "||amazon.es^",
- "||amazon.fr^",
- "||amazon.in^",
- "||amazon.it^",
- "||amazon.nl^",
- "||amazon.com.au^",
- "||amazon.com.br^",
- "||amazon.co.jp^",
- "||amazon.com.mx^",
- "||amazon.com.tr^",
- "||amazon.co.uk^",
- "||createspace.com^",
- "||aws",
- },
-}, {
- name: "ebay",
- rules: []string{
- "||ebay.com^",
- "||ebayimg.com^",
- "||ebaystatic.com^",
- "||ebaycdn.net^",
- "||ebayinc.com^",
- "||ebay.at^",
- "||ebay.be^",
- "||ebay.ca^",
- "||ebay.ch^",
- "||ebay.cn^",
- "||ebay.de^",
- "||ebay.es^",
- "||ebay.fr^",
- "||ebay.ie^",
- "||ebay.in^",
- "||ebay.it^",
- "||ebay.ph^",
- "||ebay.pl^",
- "||ebay.nl^",
- "||ebay.com.au^",
- "||ebay.com.cn^",
- "||ebay.com.hk^",
- "||ebay.com.my^",
- "||ebay.com.sg^",
- "||ebay.co.uk^",
- },
-}, {
- name: "tiktok",
- rules: []string{
- "||amemv.com^",
- "||bdurl.com^",
- "||bytecdn.cn^",
- "||bytedance.map.fastly.net^",
- "||bytedapm.com^",
- "||byteimg.com^",
- "||byteoversea.com^",
- "||douyin.com^",
- "||douyincdn.com^",
- "||douyinpic.com^",
- "||douyinstatic.com^",
- "||douyinvod.com^",
- "||ixigua.com^",
- "||ixiguavideo.com^",
- "||muscdn.com^",
- "||musical.ly^",
- "||pstatp.com^",
- "||snssdk.com^",
- "||tiktok.com^",
- "||tiktokcdn.com^",
- "||tiktokv.com^",
- "||toutiao.com^",
- "||toutiaocloud.com^",
- "||toutiaocloud.net^",
- "||toutiaovod.com^",
- },
-}, {
- name: "vimeo",
- rules: []string{
- "*vod-adaptive.akamaized.net^",
- "||vimeo.com^",
- "||vimeocdn.com^",
- },
-}, {
- name: "pinterest",
- rules: []string{
- "||pinimg.com^",
- "||pinterest.*^",
- },
-}, {
- name: "imgur",
- rules: []string{"||imgur.com^"},
-}, {
- name: "dailymotion",
- rules: []string{
- "||dailymotion.com^",
- "||dm-event.net^",
- "||dmcdn.net^",
- },
-}, {
- name: "qq",
- rules: []string{
- // Block qq.com and subdomains excluding WeChat's domains.
- "||qq.com^$denyallow=wx.qq.com|weixin.qq.com",
- "||qqzaixian.com^",
- "||qq-video.cdn-go.cn^",
- "||url.cn^",
- },
-}, {
- name: "wechat",
- rules: []string{
- "||wechat.com^",
- "||weixin.qq.com.cn^",
- "||weixin.qq.com^",
- "||weixinbridge.com^",
- "||wx.qq.com^",
- },
-}, {
- name: "viber",
- rules: []string{"||viber.com^"},
-}, {
- name: "weibo",
- rules: []string{
- "||weibo.cn^",
- "||weibo.com^",
- "||weibocdn.com^",
- },
-}, {
- name: "9gag",
- rules: []string{
- "||9cache.com^",
- "||9gag.com^",
- },
-}, {
- name: "telegram",
- rules: []string{
- "||t.me^",
- "||telegram.me^",
- "||telegram.org^",
- },
-}, {
- name: "disneyplus",
- rules: []string{
- "||disney-plus.net^",
- "||disney.playback.edge.bamgrid.com^",
- "||disneynow.com^",
- "||disneyplus.com^",
- "||hotstar.com^",
- "||media.dssott.com^",
- "||star.playback.edge.bamgrid.com^",
- "||starplus.com^",
- },
-}, {
- name: "hulu",
- rules: []string{"||hulu.com^"},
-}, {
- name: "spotify",
- rules: []string{
- "/_spotify-connect._tcp.local/",
- "||spotify.com^",
- "||scdn.co^",
- "||spotify.com.edgesuite.net^",
- "||spotify.map.fastly.net^",
- "||spotify.map.fastlylb.net^",
- "||spotifycdn.net^",
- "||audio-ak-spotify-com.akamaized.net^",
- "||audio4-ak-spotify-com.akamaized.net^",
- "||heads-ak-spotify-com.akamaized.net^",
- "||heads4-ak-spotify-com.akamaized.net^",
- },
-}, {
- name: "tinder",
- rules: []string{
- "||gotinder.com^",
- "||tinder.com^",
- "||tindersparks.com^",
- },
-}, {
- name: "bilibili",
- rules: []string{
- "||b23.tv^",
- "||biliapi.net^",
- "||bilibili.com^",
- "||bilicdn1.com^",
- "||bilicdn2.com^",
- "||biligame.com^",
- "||bilivideo.cn^",
- "||bilivideo.com^",
- "||dreamcast.hk^",
- "||hdslb.com^",
- },
-}}
-
// serviceRules maps a service ID to its filtering rules.
var serviceRules map[string][]*rules.NetworkRule
@@ -394,16 +18,16 @@ var serviceIDs []string
// initBlockedServices initializes package-level blocked service data.
func initBlockedServices() {
- l := len(servicesData)
+ l := len(blockedServices)
serviceIDs = make([]string, l)
serviceRules = make(map[string][]*rules.NetworkRule, l)
- for i, s := range servicesData {
- netRules := make([]*rules.NetworkRule, 0, len(s.rules))
- for _, text := range s.rules {
+ for i, s := range blockedServices {
+ netRules := make([]*rules.NetworkRule, 0, len(s.Rules))
+ for _, text := range s.Rules {
rule, err := rules.NewNetworkRule(text, BlockedSvcsListID)
if err != nil {
- log.Error("parsing blocked service %q rule %q: %s", s.name, text, err)
+ log.Error("parsing blocked service %q rule %q: %s", s.ID, text, err)
continue
}
@@ -411,8 +35,8 @@ func initBlockedServices() {
netRules = append(netRules, rule)
}
- serviceIDs[i] = s.name
- serviceRules[s.name] = netRules
+ serviceIDs[i] = s.ID
+ serviceRules[s.ID] = netRules
}
slices.Sort(serviceIDs)
@@ -420,7 +44,7 @@ func initBlockedServices() {
log.Debug("filtering: initialized %d services", l)
}
-// BlockedSvcKnown - return TRUE if a blocked service name is known
+// BlockedSvcKnown returns true if a blocked service ID is known.
func BlockedSvcKnown(s string) (ok bool) {
_, ok = serviceRules[s]
@@ -452,14 +76,16 @@ func (d *DNSFilter) ApplyBlockedServices(setts *Settings, list []string) {
}
}
-func (d *DNSFilter) handleBlockedServicesAvailableServices(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- err := json.NewEncoder(w).Encode(serviceIDs)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "encoding available services: %s", err)
+func (d *DNSFilter) handleBlockedServicesIDs(w http.ResponseWriter, r *http.Request) {
+ _ = aghhttp.WriteJSONResponse(w, r, serviceIDs)
+}
- return
- }
+func (d *DNSFilter) handleBlockedServicesAll(w http.ResponseWriter, r *http.Request) {
+ _ = aghhttp.WriteJSONResponse(w, r, struct {
+ BlockedServices []blockedService `json:"blocked_services"`
+ }{
+ BlockedServices: blockedServices,
+ })
}
func (d *DNSFilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Request) {
@@ -467,13 +93,7 @@ func (d *DNSFilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Req
list := d.Config.BlockedServices
d.confLock.RUnlock()
- w.Header().Set("Content-Type", "application/json")
- err := json.NewEncoder(w).Encode(list)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "encoding services: %s", err)
-
- return
- }
+ _ = aghhttp.WriteJSONResponse(w, r, list)
}
func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Request) {
@@ -491,5 +111,5 @@ func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Requ
log.Debug("Updated blocked services list: %d", len(list))
- d.ConfigModified()
+ d.Config.ConfigModified()
}
diff --git a/internal/filtering/filter.go b/internal/filtering/filter.go
index fcba11aa..070ce73c 100644
--- a/internal/filtering/filter.go
+++ b/internal/filtering/filter.go
@@ -54,97 +54,120 @@ func (filter *FilterYAML) Path(dataDir string) string {
}
const (
- statusFound = 1 << iota
- statusEnabledChanged
- statusURLChanged
- statusURLExists
- statusUpdateRequired
+ // errFilterNotExist is returned from [filterSetProperties] when there are
+ // no lists with the desired URL to update.
+ //
+ // TODO(e.burkov): Use wherever the same error is needed.
+ errFilterNotExist errors.Error = "url doesn't exist"
+
+ // errFilterExists is returned from [filterSetProperties] when there is
+ // another filter having the same URL as the one updated.
+ //
+ // TODO(e.burkov): Use wherever the same error is needed.
+ errFilterExists errors.Error = "url already exists"
)
-// Update properties for a filter specified by its URL
-// Return status* flags.
-func (d *DNSFilter) filterSetProperties(url string, newf FilterYAML, whitelist bool) int {
- r := 0
+// filterSetProperties searches for the particular filter list by url and sets
+// the values of newList to it, updating afterwards if needed. It returns true
+// if the update was performed and the filtering engine restart is required.
+func (d *DNSFilter) filterSetProperties(
+ listURL string,
+ newList FilterYAML,
+ isAllowlist bool,
+) (shouldRestart bool, err error) {
d.filtersMu.Lock()
defer d.filtersMu.Unlock()
filters := d.Filters
- if whitelist {
+ if isAllowlist {
filters = d.WhitelistFilters
}
- i := slices.IndexFunc(filters, func(filt FilterYAML) bool {
- return filt.URL == url
- })
+ i := slices.IndexFunc(filters, func(filt FilterYAML) bool { return filt.URL == listURL })
if i == -1 {
- return 0
+ return false, errFilterNotExist
}
filt := &filters[i]
+ log.Debug(
+ "filtering: set name to %q, url to %s, enabled to %t for filter %s",
+ newList.Name,
+ newList.URL,
+ newList.Enabled,
+ filt.URL,
+ )
- log.Debug("filter: set properties: %s: {%s %s %v}", filt.URL, newf.Name, newf.URL, newf.Enabled)
- filt.Name = newf.Name
+ defer func(oldURL, oldName string, oldEnabled bool, oldUpdated time.Time) {
+ if err != nil {
+ filt.URL = oldURL
+ filt.Name = oldName
+ filt.Enabled = oldEnabled
+ filt.LastUpdated = oldUpdated
+ }
+ }(filt.URL, filt.Name, filt.Enabled, filt.LastUpdated)
- if filt.URL != newf.URL {
- r |= statusURLChanged | statusUpdateRequired
- if d.filterExistsNoLock(newf.URL) {
- return statusURLExists
+ filt.Name = newList.Name
+
+ if filt.URL != newList.URL {
+ if d.filterExistsLocked(newList.URL) {
+ return false, errFilterExists
}
- filt.URL = newf.URL
- filt.unload()
+ shouldRestart = true
+
+ filt.URL = newList.URL
filt.LastUpdated = time.Time{}
- filt.checksum = 0
- filt.RulesCount = 0
+ filt.unload()
}
- if filt.Enabled != newf.Enabled {
- r |= statusEnabledChanged
- filt.Enabled = newf.Enabled
- if filt.Enabled {
- if (r & statusURLChanged) == 0 {
- err := d.load(filt)
- if err != nil {
- // TODO(e.burkov): It seems the error is only returned when
- // the file exists and couldn't be open. Investigate and
- // improve.
- log.Error("loading filter %d: %s", filt.ID, err)
+ if filt.Enabled != newList.Enabled {
+ filt.Enabled = newList.Enabled
+ shouldRestart = true
+ }
- filt.LastUpdated = time.Time{}
- filt.checksum = 0
- filt.RulesCount = 0
- r |= statusUpdateRequired
- }
- }
- } else {
- filt.unload()
+ if filt.Enabled {
+ if shouldRestart {
+ // Download the filter contents.
+ shouldRestart, err = d.update(filt)
}
+ } else {
+ // TODO(e.burkov): The validation of the contents of the new URL is
+ // currently skipped if the rule list is disabled. This makes it
+ // possible to set a bad rules source, but the validation should still
+ // kick in when the filter is enabled. Consider making changing this
+ // behavior to be stricter.
+ filt.unload()
}
- return r | statusFound
+ return shouldRestart, err
}
-// Return TRUE if a filter with this URL exists
-func (d *DNSFilter) filterExists(url string) bool {
+// filterExists returns true if a filter with the same url exists in d. It's
+// safe for concurrent use.
+func (d *DNSFilter) filterExists(url string) (ok bool) {
d.filtersMu.RLock()
defer d.filtersMu.RUnlock()
- r := d.filterExistsNoLock(url)
+ r := d.filterExistsLocked(url)
return r
}
-func (d *DNSFilter) filterExistsNoLock(url string) bool {
+// filterExistsLocked returns true if d contains the filter with the same url.
+// d.filtersMu is expected to be locked.
+func (d *DNSFilter) filterExistsLocked(url string) (ok bool) {
for _, f := range d.Filters {
if f.URL == url {
return true
}
}
+
for _, f := range d.WhitelistFilters {
if f.URL == url {
return true
}
}
+
return false
}
@@ -155,7 +178,7 @@ func (d *DNSFilter) filterAdd(flt FilterYAML) bool {
defer d.filtersMu.Unlock()
// Check for duplicates
- if d.filterExistsNoLock(flt.URL) {
+ if d.filterExistsLocked(flt.URL) {
return false
}
@@ -258,18 +281,6 @@ func (d *DNSFilter) tryRefreshFilters(block, allow, force bool) (updated int, is
return updated, isNetworkErr, ok
}
-// refreshFilters updates the lists and returns the number of updated ones.
-// It's safe for concurrent use, but blocks at least until the previous
-// refreshing is finished.
-func (d *DNSFilter) refreshFilters(block, allow, force bool) (updated int) {
- d.refreshLock.Lock()
- defer d.refreshLock.Unlock()
-
- updated, _ = d.refreshFiltersIntl(block, allow, force)
-
- return updated
-}
-
// listsToUpdate returns the slice of filter lists that could be updated.
func (d *DNSFilter) listsToUpdate(filters *[]FilterYAML, force bool) (toUpd []FilterYAML) {
now := time.Now()
@@ -279,7 +290,6 @@ func (d *DNSFilter) listsToUpdate(filters *[]FilterYAML, force bool) (toUpd []Fi
for i := range *filters {
flt := &(*filters)[i] // otherwise we will be operating on a copy
- log.Debug("checking list at index %d: %v", i, flt)
if !flt.Enabled {
continue
diff --git a/internal/filtering/filter_test.go b/internal/filtering/filter_test.go
index b37dd10e..99014bd0 100644
--- a/internal/filtering/filter_test.go
+++ b/internal/filtering/filter_test.go
@@ -4,40 +4,43 @@ import (
"io/fs"
"net"
"net/http"
+ "net/netip"
"net/url"
"os"
- "path"
"path/filepath"
"testing"
"time"
- "github.com/AdguardTeam/golibs/netutil"
+ "github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-const testFltsFileName = "1.txt"
-
-func testStartFilterListener(t *testing.T, fltContent *[]byte) (l net.Listener) {
+// serveFiltersLocally is a helper that concurrently listens on a free port to
+// respond with fltContent. It also gracefully closes the listener when the
+// test under t finishes.
+func serveFiltersLocally(t *testing.T, fltContent []byte) (ipp netip.AddrPort) {
t.Helper()
h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
- n, werr := w.Write(*fltContent)
- require.NoError(t, werr)
- require.Equal(t, len(*fltContent), n)
+ pt := testutil.PanicT{}
+
+ n, werr := w.Write(fltContent)
+ require.NoError(pt, werr)
+ require.Equal(pt, len(fltContent), n)
})
- var err error
- l, err = net.Listen("tcp", ":0")
+ l, err := net.Listen("tcp", ":0")
require.NoError(t, err)
- go func() {
- _ = http.Serve(l, h)
- }()
+ go func() { _ = http.Serve(l, h) }()
testutil.CleanupAndRequireSuccess(t, l.Close)
- return l
+ addr := l.Addr()
+ require.IsType(t, new(net.TCPAddr), addr)
+
+ return netip.AddrPortFrom(aghnet.IPv4Localhost(), uint16(addr.(*net.TCPAddr).Port))
}
func TestFilters(t *testing.T) {
@@ -49,7 +52,7 @@ func TestFilters(t *testing.T) {
fltContent := []byte(content)
- l := testStartFilterListener(t, &fltContent)
+ addr := serveFiltersLocally(t, fltContent)
tempDir := t.TempDir()
@@ -64,11 +67,7 @@ func TestFilters(t *testing.T) {
f := &FilterYAML{
URL: (&url.URL{
Scheme: "http",
- Host: (&netutil.IPPort{
- IP: net.IP{127, 0, 0, 1},
- Port: l.Addr().(*net.TCPAddr).Port,
- }).String(),
- Path: path.Join(filterDir, testFltsFileName),
+ Host: addr.String(),
}).String(),
}
@@ -101,8 +100,15 @@ func TestFilters(t *testing.T) {
})
t.Run("refresh_actually", func(t *testing.T) {
- fltContent = []byte(`||example.com^`)
- t.Cleanup(func() { fltContent = []byte(content) })
+ anotherContent := []byte(`||example.com^`)
+ oldURL := f.URL
+
+ ipp := serveFiltersLocally(t, anotherContent)
+ f.URL = (&url.URL{
+ Scheme: "http",
+ Host: ipp.String(),
+ }).String()
+ t.Cleanup(func() { f.URL = oldURL })
updateAndAssert(t, require.True, 1)
})
diff --git a/internal/filtering/filtering.go b/internal/filtering/filtering.go
index ab884056..a1de94cd 100644
--- a/internal/filtering/filtering.go
+++ b/internal/filtering/filtering.go
@@ -345,27 +345,29 @@ func (d *DNSFilter) SetFilters(blockFilters, allowFilters []Filter, async bool)
blockFilters: blockFilters,
}
- d.filtersInitializerLock.Lock() // prevent multiple writers from adding more than 1 task
+ d.filtersInitializerLock.Lock()
defer d.filtersInitializerLock.Unlock()
- // remove all pending tasks
- stop := false
- for !stop {
+ // Remove all pending tasks.
+ removeLoop:
+ for {
select {
case <-d.filtersInitializerChan:
- //
+ // Continue removing.
default:
- stop = true
+ break removeLoop
}
}
d.filtersInitializerChan <- params
+
return nil
}
err := d.initFiltering(allowFilters, blockFilters)
if err != nil {
- log.Error("Can't initialize filtering subsystem: %s", err)
+ log.Error("filtering: can't initialize filtering subsystem: %s", err)
+
return err
}
diff --git a/internal/filtering/filtering_test.go b/internal/filtering/filtering_test.go
index 4fc9182d..05869765 100644
--- a/internal/filtering/filtering_test.go
+++ b/internal/filtering/filtering_test.go
@@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -18,7 +19,7 @@ import (
)
func TestMain(m *testing.M) {
- aghtest.DiscardLogOutput(m)
+ testutil.DiscardLogOutput(m)
}
const (
diff --git a/internal/filtering/http.go b/internal/filtering/http.go
index 50890f93..0bdf9c5f 100644
--- a/internal/filtering/http.go
+++ b/internal/filtering/http.go
@@ -56,7 +56,6 @@ func (d *DNSFilter) handleFilteringAddURL(w http.ResponseWriter, r *http.Request
err = validateFilterURL(fj.URL)
if err != nil {
- err = fmt.Errorf("invalid url: %s", err)
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return
@@ -75,8 +74,10 @@ func (d *DNSFilter) handleFilteringAddURL(w http.ResponseWriter, r *http.Request
URL: fj.URL,
Name: fj.Name,
white: fj.Whitelist,
+ Filter: Filter{
+ ID: assignUniqueFilterID(),
+ },
}
- filt.ID = assignUniqueFilterID()
// Download the filter contents
ok, err := d.update(&filt)
@@ -216,32 +217,15 @@ func (d *DNSFilter) handleFilteringSetURL(w http.ResponseWriter, r *http.Request
Name: fj.Data.Name,
URL: fj.Data.URL,
}
- status := d.filterSetProperties(fj.URL, filt, fj.Whitelist)
- if (status & statusFound) == 0 {
- aghhttp.Error(r, w, http.StatusBadRequest, "URL doesn't exist")
- return
- }
- if (status & statusURLExists) != 0 {
- aghhttp.Error(r, w, http.StatusBadRequest, "URL already exists")
+ restart, err := d.filterSetProperties(fj.URL, filt, fj.Whitelist)
+ if err != nil {
+ aghhttp.Error(r, w, http.StatusBadRequest, err.Error())
return
}
d.ConfigModified()
-
- restart := (status & statusEnabledChanged) != 0
- if (status&statusUpdateRequired) != 0 && fj.Data.Enabled {
- // download new filter and apply its rules.
- nUpdated := d.refreshFilters(!fj.Whitelist, fj.Whitelist, false)
- // if at least 1 filter has been updated, refreshFilters() restarts the filtering automatically
- // if not - we restart the filtering ourselves
- restart = false
- if nUpdated == 0 {
- restart = true
- }
- }
-
if restart {
d.EnableFilters(true)
}
@@ -301,14 +285,7 @@ func (d *DNSFilter) handleFilteringRefresh(w http.ResponseWriter, r *http.Reques
return
}
- w.Header().Set("Content-Type", "application/json")
-
- err = json.NewEncoder(w).Encode(resp)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "json encode: %s", err)
-
- return
- }
+ _ = aghhttp.WriteJSONResponse(w, r, resp)
}
type filterJSON struct {
@@ -361,17 +338,7 @@ func (d *DNSFilter) handleFilteringStatus(w http.ResponseWriter, r *http.Request
resp.UserRules = d.UserRules
d.filtersMu.RUnlock()
- jsonVal, err := json.Marshal(resp)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "json encode: %s", err)
-
- return
- }
- w.Header().Set("Content-Type", "application/json")
- _, err = w.Write(jsonVal)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "http write: %s", err)
- }
+ _ = aghhttp.WriteJSONResponse(w, r, resp)
}
// Set filtering configuration
@@ -473,11 +440,7 @@ func (d *DNSFilter) handleCheckHost(w http.ResponseWriter, r *http.Request) {
}
}
- w.Header().Set("Content-Type", "application/json")
- err = json.NewEncoder(w).Encode(resp)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "encoding response: %s", err)
- }
+ _ = aghhttp.WriteJSONResponse(w, r, resp)
}
// RegisterFilteringHandlers - register handlers
@@ -503,7 +466,8 @@ func (d *DNSFilter) RegisterFilteringHandlers() {
registerHTTP(http.MethodPost, "/control/rewrite/add", d.handleRewriteAdd)
registerHTTP(http.MethodPost, "/control/rewrite/delete", d.handleRewriteDelete)
- registerHTTP(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesAvailableServices)
+ registerHTTP(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesIDs)
+ registerHTTP(http.MethodGet, "/control/blocked_services/all", d.handleBlockedServicesAll)
registerHTTP(http.MethodGet, "/control/blocked_services/list", d.handleBlockedServicesList)
registerHTTP(http.MethodPost, "/control/blocked_services/set", d.handleBlockedServicesSet)
diff --git a/internal/filtering/http_test.go b/internal/filtering/http_test.go
new file mode 100644
index 00000000..987b8a2c
--- /dev/null
+++ b/internal/filtering/http_test.go
@@ -0,0 +1,143 @@
+package filtering
+
+import (
+ "bytes"
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestDNSFilter_handleFilteringSetURL(t *testing.T) {
+ filtersDir := t.TempDir()
+
+ var goodRulesEndpoint, anotherGoodRulesEndpoint, badRulesEndpoint string
+ for _, rulesSource := range []struct {
+ endpoint *string
+ content []byte
+ }{{
+ endpoint: &goodRulesEndpoint,
+ content: []byte(`||example.org^`),
+ }, {
+ endpoint: &anotherGoodRulesEndpoint,
+ content: []byte(`||example.com^`),
+ }, {
+ endpoint: &badRulesEndpoint,
+ content: []byte(``),
+ }} {
+ ipp := serveFiltersLocally(t, rulesSource.content)
+ *rulesSource.endpoint = (&url.URL{
+ Scheme: "http",
+ Host: ipp.String(),
+ }).String()
+ }
+
+ testCases := []struct {
+ name string
+ wantBody string
+ oldURL string
+ newName string
+ newURL string
+ initial []FilterYAML
+ }{{
+ name: "success",
+ wantBody: "",
+ oldURL: goodRulesEndpoint,
+ newName: "default_one",
+ newURL: anotherGoodRulesEndpoint,
+ initial: []FilterYAML{{
+ Enabled: true,
+ URL: goodRulesEndpoint,
+ Name: "default_one",
+ white: false,
+ }},
+ }, {
+ name: "non-existing",
+ wantBody: "url doesn't exist\n",
+ oldURL: anotherGoodRulesEndpoint,
+ newName: "default_one",
+ newURL: goodRulesEndpoint,
+ initial: []FilterYAML{{
+ Enabled: true,
+ URL: goodRulesEndpoint,
+ Name: "default_one",
+ white: false,
+ }},
+ }, {
+ name: "existing",
+ wantBody: "url already exists\n",
+ oldURL: goodRulesEndpoint,
+ newName: "default_one",
+ newURL: anotherGoodRulesEndpoint,
+ initial: []FilterYAML{{
+ Enabled: true,
+ URL: goodRulesEndpoint,
+ Name: "default_one",
+ white: false,
+ }, {
+ Enabled: true,
+ URL: anotherGoodRulesEndpoint,
+ Name: "another_default_one",
+ white: false,
+ }},
+ }, {
+ name: "bad_rules",
+ wantBody: "data is HTML, not plain text\n",
+ oldURL: goodRulesEndpoint,
+ newName: "default_one",
+ newURL: badRulesEndpoint,
+ initial: []FilterYAML{{
+ Enabled: true,
+ URL: goodRulesEndpoint,
+ Name: "default_one",
+ white: false,
+ }},
+ }}
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ confModifiedCalled := false
+ d, err := New(&Config{
+ FilteringEnabled: true,
+ Filters: tc.initial,
+ HTTPClient: &http.Client{
+ Timeout: 5 * time.Second,
+ },
+ ConfigModified: func() { confModifiedCalled = true },
+ DataDir: filtersDir,
+ }, nil)
+ require.NoError(t, err)
+ t.Cleanup(d.Close)
+
+ d.Start()
+
+ reqData := &filterURLReq{
+ Data: &filterURLReqData{
+ // Leave the name of an existing list.
+ Name: tc.newName,
+ URL: tc.newURL,
+ Enabled: true,
+ },
+ URL: tc.oldURL,
+ Whitelist: false,
+ }
+ data, err := json.Marshal(reqData)
+ require.NoError(t, err)
+
+ r := httptest.NewRequest(http.MethodPost, "http://example.org", bytes.NewReader(data))
+ w := httptest.NewRecorder()
+
+ d.handleFilteringSetURL(w, r)
+ assert.Equal(t, tc.wantBody, w.Body.String())
+
+ // For the moment the non-empty response body only contains occurred
+ // error, so the configuration shouldn't be written.
+ assert.Equal(t, tc.wantBody == "", confModifiedCalled)
+ })
+ }
+}
diff --git a/internal/filtering/rewrites.go b/internal/filtering/rewrites.go
index 8f0d5ebf..675ab53e 100644
--- a/internal/filtering/rewrites.go
+++ b/internal/filtering/rewrites.go
@@ -13,8 +13,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
- "github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
+ "golang.org/x/exp/slices"
)
// LegacyRewrite is a single legacy DNS rewrite record.
@@ -41,7 +41,7 @@ func (rw *LegacyRewrite) clone() (cloneRW *LegacyRewrite) {
return &LegacyRewrite{
Domain: rw.Domain,
Answer: rw.Answer,
- IP: netutil.CloneIP(rw.IP),
+ IP: slices.Clone(rw.IP),
Type: rw.Type,
}
}
@@ -240,13 +240,7 @@ func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
}
d.confLock.Unlock()
- w.Header().Set("Content-Type", "application/json")
- err := json.NewEncoder(w).Encode(arr)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "json.Encode: %s", err)
-
- return
- }
+ _ = aghhttp.WriteJSONResponse(w, r, arr)
}
func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
diff --git a/internal/filtering/safebrowsing.go b/internal/filtering/safebrowsing.go
index fe844977..c6b7c34c 100644
--- a/internal/filtering/safebrowsing.go
+++ b/internal/filtering/safebrowsing.go
@@ -5,7 +5,6 @@ import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
- "encoding/json"
"fmt"
"net"
"net/http"
@@ -381,17 +380,13 @@ func (d *DNSFilter) handleSafeBrowsingDisable(w http.ResponseWriter, r *http.Req
}
func (d *DNSFilter) handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- err := json.NewEncoder(w).Encode(&struct {
+ resp := &struct {
Enabled bool `json:"enabled"`
}{
Enabled: d.Config.SafeBrowsingEnabled,
- })
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "Unable to write response json: %s", err)
-
- return
}
+
+ _ = aghhttp.WriteJSONResponse(w, r, resp)
}
func (d *DNSFilter) handleParentalEnable(w http.ResponseWriter, r *http.Request) {
@@ -405,13 +400,11 @@ func (d *DNSFilter) handleParentalDisable(w http.ResponseWriter, r *http.Request
}
func (d *DNSFilter) handleParentalStatus(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- err := json.NewEncoder(w).Encode(&struct {
+ resp := &struct {
Enabled bool `json:"enabled"`
}{
Enabled: d.Config.ParentalEnabled,
- })
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "Unable to write response json: %s", err)
}
+
+ _ = aghhttp.WriteJSONResponse(w, r, resp)
}
diff --git a/internal/filtering/safesearch.go b/internal/filtering/safesearch.go
index df2d2108..8b3dcb9b 100644
--- a/internal/filtering/safesearch.go
+++ b/internal/filtering/safesearch.go
@@ -5,7 +5,6 @@ import (
"context"
"encoding/binary"
"encoding/gob"
- "encoding/json"
"fmt"
"net"
"net/http"
@@ -146,21 +145,13 @@ func (d *DNSFilter) handleSafeSearchDisable(w http.ResponseWriter, r *http.Reque
}
func (d *DNSFilter) handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- err := json.NewEncoder(w).Encode(&struct {
+ resp := &struct {
Enabled bool `json:"enabled"`
}{
Enabled: d.Config.SafeSearchEnabled,
- })
- if err != nil {
- aghhttp.Error(
- r,
- w,
- http.StatusInternalServerError,
- "Unable to write response json: %s",
- err,
- )
}
+
+ _ = aghhttp.WriteJSONResponse(w, r, resp)
}
var safeSearchDomains = map[string]string{
diff --git a/internal/filtering/servicelist.go b/internal/filtering/servicelist.go
new file mode 100644
index 00000000..da6e2ca7
--- /dev/null
+++ b/internal/filtering/servicelist.go
@@ -0,0 +1,471 @@
+// Code generated by go run ./scripts/blocked-services/main.go; DO NOT EDIT.
+
+package filtering
+
+// blockedService represents a single blocked service.
+type blockedService struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ IconSVG []byte `json:"icon_svg"`
+ Rules []string `json:"rules"`
+}
+
+// blockedServices contains raw blocked service data.
+var blockedServices = []blockedService{{
+ ID: "9gag",
+ Name: "9GAG",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||9cache.com^",
+ "||9gag.com^",
+ },
+}, {
+ ID: "amazon",
+ Name: "Amazon",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||a2z.com^",
+ "||amazon-adsystem.com^",
+ "||amazon.ae^",
+ "||amazon.ca^",
+ "||amazon.cn^",
+ "||amazon.co.jp^",
+ "||amazon.co.uk^",
+ "||amazon.com.au^",
+ "||amazon.com.br^",
+ "||amazon.com.mx^",
+ "||amazon.com^",
+ "||amazon.de^",
+ "||amazon.es^",
+ "||amazon.fr^",
+ "||amazon.in^",
+ "||amazon.it^",
+ "||amazon.nl^",
+ "||amazonpay.com^",
+ "||amazonpay.in^",
+ "||amazontrust.com^",
+ "||aws",
+ "||createspace.com^",
+ "||images-amazon.com^",
+ "||media-amazon.com^",
+ "||primevideo.com^",
+ "||ssl-images-amazon.com^",
+ },
+}, {
+ ID: "bilibili",
+ Name: "Bilibili",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||b23.tv^",
+ "||biliapi.net^",
+ "||bilibili.com^",
+ "||bilicdn1.com^",
+ "||bilicdn2.com^",
+ "||biligame.com^",
+ "||bilivideo.cn^",
+ "||bilivideo.com^",
+ "||dreamcast.hk^",
+ "||hdslb.com^",
+ },
+}, {
+ ID: "cloudflare",
+ Name: "CloudFlare",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||1.1.1.1^",
+ "||cloudflare-dns.com^",
+ "||cloudflare.cn^",
+ "||cloudflare.com^",
+ "||cloudflare.net^",
+ "||cloudflarebolt.com^",
+ "||cloudflareclient.com^",
+ "||cloudflareinsights.com^",
+ "||cloudflareresolve.com^",
+ "||cloudflarestatus.com^",
+ "||cloudflarestream.com^",
+ "||dns4torpnlfs2ifuz2s2yf3fc7rdmsbhm6rw75euj35pac6ap25zgqad.onion^",
+ "||one.one^",
+ "||warp.plus^",
+ },
+}, {
+ ID: "dailymotion",
+ Name: "Dailymotion",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||dailymotion.com^",
+ "||dm-event.net^",
+ "||dmcdn.net^",
+ },
+}, {
+ ID: "deezer",
+ Name: "Deezer",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||deezer.com^",
+ "||dzcdn.net^",
+ },
+}, {
+ ID: "discord",
+ Name: "Discord",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||discord.com^",
+ "||discord.gg^",
+ "||discord.media^",
+ "||discordapp.com^",
+ "||discordapp.net^",
+ },
+}, {
+ ID: "disneyplus",
+ Name: "Disney+",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||disney-plus.net^",
+ "||disney.playback.edge.bamgrid.com^",
+ "||disneyplus.com^",
+ "||media.dssott.com^",
+ },
+}, {
+ ID: "ebay",
+ Name: "EBay",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||ebay.at^",
+ "||ebay.be^",
+ "||ebay.ca^",
+ "||ebay.ch^",
+ "||ebay.cn^",
+ "||ebay.co.uk^",
+ "||ebay.com.au^",
+ "||ebay.com.cn^",
+ "||ebay.com.hk^",
+ "||ebay.com.my^",
+ "||ebay.com.sg^",
+ "||ebay.com^",
+ "||ebay.de^",
+ "||ebay.es^",
+ "||ebay.fr^",
+ "||ebay.ie^",
+ "||ebay.in^",
+ "||ebay.it^",
+ "||ebay.nl^",
+ "||ebay.ph^",
+ "||ebay.pl^",
+ "||ebaycdn.net^",
+ "||ebayimg.com^",
+ "||ebayinc.com^",
+ "||ebaystatic.com^",
+ },
+}, {
+ ID: "epic_games",
+ Name: "Epic Games",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||eac-cdn.com^",
+ "||easy.ac^",
+ "||easyanticheat.net^",
+ "||epicgames.com^",
+ },
+}, {
+ ID: "facebook",
+ Name: "Facebook",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||accountkit.com^",
+ "||facebook.com^",
+ "||facebook.net^",
+ "||facebookcorewwwi.onion^",
+ "||fb.com^",
+ "||fb.me^",
+ "||fb.watch^",
+ "||fbcdn.com^",
+ "||fbcdn.net^",
+ "||fbsbx.com^",
+ "||messenger.com^",
+ },
+}, {
+ ID: "hulu",
+ Name: "Hulu",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||hulu.com^",
+ },
+}, {
+ ID: "icloud_private_relay",
+ Name: "iCloud Private Relay",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||mask-h2.icloud.com^$dnsrewrite=NXDOMAIN;;",
+ "||mask.icloud.com^$dnsrewrite=NXDOMAIN;;",
+ },
+}, {
+ ID: "imgur",
+ Name: "Imgur",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||imgur.com^",
+ },
+}, {
+ ID: "instagram",
+ Name: "Instagram",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||cdninstagram.com^",
+ "||instagram.com^",
+ },
+}, {
+ ID: "mail_ru",
+ Name: "Mail.ru",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||mail.ru^",
+ },
+}, {
+ ID: "minecraft",
+ Name: "Minecraft",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||minecraft.net^",
+ "||minecraftservices.com^",
+ "||mojang.com^",
+ },
+}, {
+ ID: "netflix",
+ Name: "Netflix",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||netflix.com^",
+ "||nflxext.com^",
+ "||nflximg.net^",
+ "||nflxso.net^",
+ "||nflxvideo.net^",
+ },
+}, {
+ ID: "ok",
+ Name: "OK.ru",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||ok.ru^",
+ },
+}, {
+ ID: "origin",
+ Name: "Origin",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||accounts.ea.com^",
+ "||origin.com^",
+ "||signin.ea.com^",
+ },
+}, {
+ ID: "pinterest",
+ Name: "Pinterest",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||pinimg.com^",
+ "||pinterest.*^",
+ },
+}, {
+ ID: "qq",
+ Name: "QQ",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "^(?!weixin|wx)([^.]+\\.)?qq\\.com$",
+ "||qqzaixian.com^",
+ },
+}, {
+ ID: "reddit",
+ Name: "Reddit",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||redd.it^",
+ "||reddit.com^",
+ "||redditmedia.com^",
+ "||redditstatic.com^",
+ },
+}, {
+ ID: "roblox",
+ Name: "Roblox",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||rbxadder.com^",
+ "||rbxcdn.com^",
+ "||roblox.com^",
+ "||robloxcdn.com^",
+ },
+}, {
+ ID: "skype",
+ Name: "Skype",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||skype.com^",
+ "||skypeassets.com^",
+ },
+}, {
+ ID: "snapchat",
+ Name: "Snapchat",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||impala-media-production.s3.amazonaws.com^",
+ "||sc-cdn.net^",
+ "||snap-dev.net^",
+ "||snapads.com^",
+ "||snapchat.com^",
+ "||snapkit.co",
+ },
+}, {
+ ID: "spotify",
+ Name: "Spotify",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "/_spotify-connect._tcp.local/",
+ "||audio-ak-spotify-com.akamaized.net^",
+ "||audio4-ak-spotify-com.akamaized.net^",
+ "||heads-ak-spotify-com.akamaized.net^",
+ "||heads4-ak-spotify-com.akamaized.net^",
+ "||scdn.co^",
+ "||spotify.com.edgesuite.net^",
+ "||spotify.com^",
+ "||spotify.map.fastly.net^",
+ "||spotify.map.fastlylb.net^",
+ "||spotifycdn.net^",
+ },
+}, {
+ ID: "steam",
+ Name: "Steam",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||steam.com^",
+ "||steamcdn-a.akamaihd.net^",
+ "||steamcommunity.com^",
+ "||steampowered.com^",
+ "||steamstatic.com^",
+ "||steamstore-a.akamaihd.net^",
+ },
+}, {
+ ID: "telegram",
+ Name: "Telegram",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||t.me^",
+ "||telegram.me^",
+ "||telegram.org^",
+ },
+}, {
+ ID: "tiktok",
+ Name: "TikTok",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||amemv.com^",
+ "||bdurl.com^",
+ "||bytecdn.cn^",
+ "||bytedance.map.fastly.net^",
+ "||byteimg.com^",
+ "||douyin.com^",
+ "||douyincdn.com^",
+ "||ixigua.com^",
+ "||ixigua.com^",
+ "||ixiguavideo.com^",
+ "||muscdn.com^",
+ "||musical.ly^",
+ "||pstatp.com^",
+ "||snssdk.com^",
+ "||tiktok.com^",
+ "||tiktokcdn.com^",
+ "||tiktokv.com^",
+ "||toutiao.com^",
+ "||toutiaocloud.com^",
+ "||toutiaocloud.net^",
+ },
+}, {
+ ID: "tinder",
+ Name: "Tinder",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||gotinder.com^",
+ "||tinder.com^",
+ "||tindersparks.com^",
+ },
+}, {
+ ID: "twitch",
+ Name: "Twitch",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||jtvnw.net^",
+ "||ttvnw.net^",
+ "||twitch.tv^",
+ "||twitchcdn.net^",
+ },
+}, {
+ ID: "twitter",
+ Name: "Twitter",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||t.co^",
+ "||twimg.com^",
+ "||twitter.com^",
+ "||twttr.com^",
+ },
+}, {
+ ID: "viber",
+ Name: "Viber",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||viber.com^",
+ },
+}, {
+ ID: "vimeo",
+ Name: "Vimeo",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "*vod-adaptive.akamaized.net^",
+ "||vimeo.com^",
+ "||vimeocdn.com^",
+ },
+}, {
+ ID: "vk",
+ Name: "VK.com",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||userapi.com^",
+ "||vk-cdn.net^",
+ "||vk.com^",
+ "||vkuservideo.net^",
+ },
+}, {
+ ID: "wechat",
+ Name: "WeChat",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||wechat.com^",
+ "||weixin.qq.com^",
+ "||wx.qq.com^",
+ },
+}, {
+ ID: "weibo",
+ Name: "Weibo",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||weibo.com^",
+ },
+}, {
+ ID: "whatsapp",
+ Name: "WhatsApp",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||whatsapp.com^",
+ "||whatsapp.net^",
+ },
+}, {
+ ID: "youtube",
+ Name: "YouTube",
+ IconSVG: []byte(" "),
+ Rules: []string{
+ "||googlevideo.com^",
+ "||youtu.be^",
+ "||youtube",
+ "||youtube-nocookie.com^",
+ "||youtube.com^",
+ "||youtubei.googleapis.com^",
+ "||ytimg.com^",
+ },
+}}
diff --git a/internal/home/auth_test.go b/internal/home/auth_test.go
index 1bf38753..46767f7d 100644
--- a/internal/home/auth_test.go
+++ b/internal/home/auth_test.go
@@ -12,16 +12,11 @@ import (
"testing"
"time"
- "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-func TestMain(m *testing.M) {
- aghtest.DiscardLogOutput(m)
-}
-
func TestNewSessionToken(t *testing.T) {
// Successful case.
token, err := newSessionToken()
diff --git a/internal/home/clients.go b/internal/home/clients.go
index 631d27ba..fa230178 100644
--- a/internal/home/clients.go
+++ b/internal/home/clients.go
@@ -5,6 +5,7 @@ import (
"encoding"
"fmt"
"net"
+ "net/netip"
"sort"
"strings"
"sync"
@@ -21,6 +22,8 @@ import (
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
+ "golang.org/x/exp/maps"
+ "golang.org/x/exp/slices"
)
const clientsUpdatePeriod = 10 * time.Minute
@@ -50,6 +53,18 @@ type Client struct {
UseOwnBlockedServices bool
}
+// closeUpstreams closes the client-specific upstream config of c if any.
+func (c *Client) closeUpstreams() (err error) {
+ if c.upstreamConfig != nil {
+ err = c.upstreamConfig.Close()
+ if err != nil {
+ return fmt.Errorf("closing upstreams of client %q: %w", c.Name, err)
+ }
+ }
+
+ return nil
+}
+
type clientSource uint
// Client sources. The order determines the priority.
@@ -119,7 +134,7 @@ type clientsContainer struct {
idIndex map[string]*Client // ID -> client
// ipToRC is the IP address to *RuntimeClient map.
- ipToRC *netutil.IPMap
+ ipToRC map[netip.Addr]*RuntimeClient
lock sync.Mutex
@@ -155,7 +170,7 @@ func (clients *clientsContainer) Init(
}
clients.list = make(map[string]*Client)
clients.idIndex = make(map[string]*Client)
- clients.ipToRC = netutil.NewIPMap(0)
+ clients.ipToRC = map[netip.Addr]*RuntimeClient{}
clients.allTags = stringutil.NewSet(clientTags...)
@@ -317,8 +332,8 @@ func (clients *clientsContainer) onDHCPLeaseChanged(flags int) {
}
}
-// Exists checks if client with this IP address already exists.
-func (clients *clientsContainer) Exists(ip net.IP, source clientSource) (ok bool) {
+// exists checks if client with this IP address already exists.
+func (clients *clientsContainer) exists(ip net.IP, source clientSource) (ok bool) {
clients.lock.Lock()
defer clients.lock.Unlock()
@@ -399,7 +414,7 @@ func (clients *clientsContainer) clientOrArtificial(
}
var rc *RuntimeClient
- rc, ok = clients.FindRuntimeClient(ip)
+ rc, ok = clients.findRuntimeClient(ip)
if ok {
return &querylog.Client{
Name: rc.Host,
@@ -523,24 +538,21 @@ func (clients *clientsContainer) findLocked(id string) (c *Client, ok bool) {
// findRuntimeClientLocked finds a runtime client by their IP address. For
// internal use only.
func (clients *clientsContainer) findRuntimeClientLocked(ip net.IP) (rc *RuntimeClient, ok bool) {
- var v any
- v, ok = clients.ipToRC.Get(ip)
- if !ok {
- return nil, false
- }
-
- rc, ok = v.(*RuntimeClient)
- if !ok {
- log.Error("clients: bad type %T in ipToRC for %s", v, ip)
+ // TODO(a.garipov): Remove once we switch to netip.Addr more fully.
+ ipAddr, err := netutil.IPToAddrNoMapped(ip)
+ if err != nil {
+ log.Error("clients: bad client ip %v: %s", ip, err)
return nil, false
}
- return rc, true
+ rc, ok = clients.ipToRC[ipAddr]
+
+ return rc, ok
}
-// FindRuntimeClient finds a runtime client by their IP.
-func (clients *clientsContainer) FindRuntimeClient(ip net.IP) (rc *RuntimeClient, ok bool) {
+// findRuntimeClient finds a runtime client by their IP.
+func (clients *clientsContainer) findRuntimeClient(ip net.IP) (rc *RuntimeClient, ok bool) {
if ip == nil {
return nil, false
}
@@ -649,6 +661,10 @@ func (clients *clientsContainer) Del(name string) (ok bool) {
return false
}
+ if err := c.closeUpstreams(); err != nil {
+ log.Error("client container: removing client %s: %s", name, err)
+ }
+
// update Name index
delete(clients.list, name)
@@ -707,7 +723,7 @@ func (clients *clientsContainer) Update(name string, c *Client) (err error) {
}
}
- // update ID index
+ // Update ID index.
for _, id := range prev.IDs {
delete(clients.idIndex, id)
}
@@ -716,22 +732,25 @@ func (clients *clientsContainer) Update(name string, c *Client) (err error) {
}
}
- // update Name index
+ // Update name index.
if prev.Name != c.Name {
delete(clients.list, prev.Name)
clients.list[c.Name] = prev
}
- // update upstreams cache
- c.upstreamConfig = nil
+ // Update upstreams cache.
+ err = c.closeUpstreams()
+ if err != nil {
+ return err
+ }
*prev = *c
return nil
}
-// SetWHOISInfo sets the WHOIS information for a client.
-func (clients *clientsContainer) SetWHOISInfo(ip net.IP, wi *RuntimeClientWHOISInfo) {
+// setWHOISInfo sets the WHOIS information for a client.
+func (clients *clientsContainer) setWHOISInfo(ip net.IP, wi *RuntimeClientWHOISInfo) {
clients.lock.Lock()
defer clients.lock.Unlock()
@@ -757,7 +776,15 @@ func (clients *clientsContainer) SetWHOISInfo(ip net.IP, wi *RuntimeClientWHOISI
rc.WHOISInfo = wi
- clients.ipToRC.Set(ip, rc)
+ // TODO(a.garipov): Remove once we switch to netip.Addr more fully.
+ ipAddr, err := netutil.IPToAddrNoMapped(ip)
+ if err != nil {
+ log.Error("clients: bad client ip %v: %s", ip, err)
+
+ return
+ }
+
+ clients.ipToRC[ipAddr] = rc
log.Debug("clients: set whois info for runtime client with ip %s: %+v", ip, wi)
}
@@ -768,12 +795,23 @@ func (clients *clientsContainer) AddHost(ip net.IP, host string, src clientSourc
clients.lock.Lock()
defer clients.lock.Unlock()
- return clients.addHostLocked(ip, host, src), nil
+ // TODO(a.garipov): Remove once we switch to netip.Addr more fully.
+ ipAddr, err := netutil.IPToAddrNoMapped(ip)
+ if err != nil {
+ return false, fmt.Errorf("adding host: %w", err)
+ }
+
+ return clients.addHostLocked(ipAddr, host, src), nil
}
-// addHostLocked adds a new IP-hostname pairing. For internal use only.
-func (clients *clientsContainer) addHostLocked(ip net.IP, host string, src clientSource) (ok bool) {
- rc, ok := clients.findRuntimeClientLocked(ip)
+// addHostLocked adds a new IP-hostname pairing. clients.lock is expected to be
+// locked.
+func (clients *clientsContainer) addHostLocked(
+ ip netip.Addr,
+ host string,
+ src clientSource,
+) (ok bool) {
+ rc, ok := clients.ipToRC[ip]
if ok {
if rc.Source > src {
return false
@@ -788,10 +826,10 @@ func (clients *clientsContainer) addHostLocked(ip net.IP, host string, src clien
WHOISInfo: &RuntimeClientWHOISInfo{},
}
- clients.ipToRC.Set(ip, rc)
+ clients.ipToRC[ip] = rc
}
- log.Debug("clients: added %s -> %q [%d]", ip, host, clients.ipToRC.Len())
+ log.Debug("clients: added %s -> %q [%d]", ip, host, len(clients.ipToRC))
return true
}
@@ -799,47 +837,29 @@ func (clients *clientsContainer) addHostLocked(ip net.IP, host string, src clien
// rmHostsBySrc removes all entries that match the specified source.
func (clients *clientsContainer) rmHostsBySrc(src clientSource) {
n := 0
- clients.ipToRC.Range(func(ip net.IP, v any) (cont bool) {
- rc, ok := v.(*RuntimeClient)
- if !ok {
- log.Error("clients: bad type %T in ipToRC for %s", v, ip)
-
- return true
- }
-
+ for ip, rc := range clients.ipToRC {
if rc.Source == src {
- clients.ipToRC.Del(ip)
+ delete(clients.ipToRC, ip)
n++
}
-
- return true
- })
+ }
log.Debug("clients: removed %d client aliases", n)
}
// addFromHostsFile fills the client-hostname pairing index from the system's
// hosts files.
-func (clients *clientsContainer) addFromHostsFile(hosts *netutil.IPMap) {
+func (clients *clientsContainer) addFromHostsFile(hosts aghnet.HostsRecords) {
clients.lock.Lock()
defer clients.lock.Unlock()
clients.rmHostsBySrc(ClientSourceHostsFile)
n := 0
- hosts.Range(func(ip net.IP, v any) (cont bool) {
- rec, ok := v.(*aghnet.HostsRecord)
- if !ok {
- log.Error("dns: bad type %T in ipToRC for %s", v, ip)
-
- return true
- }
-
+ for ip, rec := range hosts {
clients.addHostLocked(ip, rec.Canonical, ClientSourceHostsFile)
n++
-
- return true
- })
+ }
log.Debug("clients: added %d client aliases from system hosts file", n)
}
@@ -900,7 +920,15 @@ func (clients *clientsContainer) updateFromDHCP(add bool) {
continue
}
- ok := clients.addHostLocked(l.IP, l.Hostname, ClientSourceDHCP)
+ // TODO(a.garipov): Remove once we switch to netip.Addr more fully.
+ ipAddr, err := netutil.IPToAddrNoMapped(l.IP)
+ if err != nil {
+ log.Error("clients: bad client ip %v from dhcp: %s", l.IP, err)
+
+ continue
+ }
+
+ ok := clients.addHostLocked(ipAddr, l.Hostname, ClientSourceDHCP)
if ok {
n++
}
@@ -908,3 +936,24 @@ func (clients *clientsContainer) updateFromDHCP(add bool) {
log.Debug("clients: added %d client aliases from dhcp", n)
}
+
+// close gracefully closes all the client-specific upstream configurations of
+// the persistent clients.
+func (clients *clientsContainer) close() (err error) {
+ persistent := maps.Values(clients.list)
+ slices.SortFunc(persistent, func(a, b *Client) (less bool) { return a.Name < b.Name })
+
+ var errs []error
+
+ for _, cli := range persistent {
+ if err = cli.closeUpstreams(); err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ if len(errs) > 0 {
+ return errors.List("closing client specific upstreams", errs...)
+ }
+
+ return nil
+}
diff --git a/internal/home/clients_test.go b/internal/home/clients_test.go
index 5b4ccdd3..636971eb 100644
--- a/internal/home/clients_test.go
+++ b/internal/home/clients_test.go
@@ -2,6 +2,7 @@ package home
import (
"net"
+ "net/netip"
"os"
"runtime"
"testing"
@@ -56,9 +57,9 @@ func TestClients(t *testing.T) {
assert.Equal(t, "client2", c.Name)
- assert.False(t, clients.Exists(net.IP{1, 2, 3, 4}, ClientSourceHostsFile))
- assert.True(t, clients.Exists(net.IP{1, 1, 1, 1}, ClientSourceHostsFile))
- assert.True(t, clients.Exists(net.IP{2, 2, 2, 2}, ClientSourceHostsFile))
+ assert.False(t, clients.exists(net.IP{1, 2, 3, 4}, ClientSourceHostsFile))
+ assert.True(t, clients.exists(net.IP{1, 1, 1, 1}, ClientSourceHostsFile))
+ assert.True(t, clients.exists(net.IP{2, 2, 2, 2}, ClientSourceHostsFile))
})
t.Run("add_fail_name", func(t *testing.T) {
@@ -108,8 +109,8 @@ func TestClients(t *testing.T) {
})
require.NoError(t, err)
- assert.False(t, clients.Exists(net.IP{1, 1, 1, 1}, ClientSourceHostsFile))
- assert.True(t, clients.Exists(net.IP{1, 1, 1, 2}, ClientSourceHostsFile))
+ assert.False(t, clients.exists(net.IP{1, 1, 1, 1}, ClientSourceHostsFile))
+ assert.True(t, clients.exists(net.IP{1, 1, 1, 2}, ClientSourceHostsFile))
err = clients.Update("client1", &Client{
IDs: []string{"1.1.1.2"},
@@ -138,7 +139,7 @@ func TestClients(t *testing.T) {
ok := clients.Del("client1-renamed")
require.True(t, ok)
- assert.False(t, clients.Exists(net.IP{1, 1, 1, 2}, ClientSourceHostsFile))
+ assert.False(t, clients.exists(net.IP{1, 1, 1, 2}, ClientSourceHostsFile))
})
t.Run("del_fail", func(t *testing.T) {
@@ -164,7 +165,7 @@ func TestClients(t *testing.T) {
assert.True(t, ok)
- assert.True(t, clients.Exists(ip, ClientSourceHostsFile))
+ assert.True(t, clients.exists(ip, ClientSourceHostsFile))
})
t.Run("dhcp_replaces_arp", func(t *testing.T) {
@@ -174,13 +175,13 @@ func TestClients(t *testing.T) {
require.NoError(t, err)
assert.True(t, ok)
- assert.True(t, clients.Exists(ip, ClientSourceARP))
+ assert.True(t, clients.exists(ip, ClientSourceARP))
ok, err = clients.AddHost(ip, "from_dhcp", ClientSourceDHCP)
require.NoError(t, err)
assert.True(t, ok)
- assert.True(t, clients.Exists(ip, ClientSourceDHCP))
+ assert.True(t, clients.exists(ip, ClientSourceDHCP))
})
t.Run("addhost_fail", func(t *testing.T) {
@@ -201,38 +202,30 @@ func TestClientsWHOIS(t *testing.T) {
}
t.Run("new_client", func(t *testing.T) {
- ip := net.IP{1, 1, 1, 255}
- clients.SetWHOISInfo(ip, whois)
- v, _ := clients.ipToRC.Get(ip)
- require.NotNil(t, v)
-
- rc, ok := v.(*RuntimeClient)
- require.True(t, ok)
+ ip := netip.MustParseAddr("1.1.1.255")
+ clients.setWHOISInfo(ip.AsSlice(), whois)
+ rc := clients.ipToRC[ip]
require.NotNil(t, rc)
assert.Equal(t, rc.WHOISInfo, whois)
})
t.Run("existing_auto-client", func(t *testing.T) {
- ip := net.IP{1, 1, 1, 1}
- ok, err := clients.AddHost(ip, "host", ClientSourceRDNS)
+ ip := netip.MustParseAddr("1.1.1.1")
+ ok, err := clients.AddHost(ip.AsSlice(), "host", ClientSourceRDNS)
require.NoError(t, err)
assert.True(t, ok)
- clients.SetWHOISInfo(ip, whois)
- v, _ := clients.ipToRC.Get(ip)
- require.NotNil(t, v)
-
- rc, ok := v.(*RuntimeClient)
- require.True(t, ok)
+ clients.setWHOISInfo(ip.AsSlice(), whois)
+ rc := clients.ipToRC[ip]
require.NotNil(t, rc)
assert.Equal(t, rc.WHOISInfo, whois)
})
t.Run("can't_set_manually-added", func(t *testing.T) {
- ip := net.IP{1, 1, 1, 2}
+ ip := netip.MustParseAddr("1.1.1.2")
ok, err := clients.Add(&Client{
IDs: []string{"1.1.1.2"},
@@ -241,9 +234,9 @@ func TestClientsWHOIS(t *testing.T) {
require.NoError(t, err)
assert.True(t, ok)
- clients.SetWHOISInfo(ip, whois)
- v, _ := clients.ipToRC.Get(ip)
- require.Nil(t, v)
+ clients.setWHOISInfo(ip.AsSlice(), whois)
+ rc := clients.ipToRC[ip]
+ require.Nil(t, rc)
assert.True(t, clients.Del("client1"))
})
@@ -287,10 +280,10 @@ func TestClientsAddExisting(t *testing.T) {
DBFilePath: "leases.db",
Conf4: dhcpd.V4ServerConf{
Enabled: true,
- GatewayIP: net.IP{1, 2, 3, 1},
- SubnetMask: net.IP{255, 255, 255, 0},
- RangeStart: net.IP{1, 2, 3, 2},
- RangeEnd: net.IP{1, 2, 3, 10},
+ GatewayIP: netip.MustParseAddr("1.2.3.1"),
+ SubnetMask: netip.MustParseAddr("255.255.255.0"),
+ RangeStart: netip.MustParseAddr("1.2.3.2"),
+ RangeEnd: netip.MustParseAddr("1.2.3.10"),
},
}
diff --git a/internal/home/clientshttp.go b/internal/home/clientshttp.go
index 59821d0a..313fd998 100644
--- a/internal/home/clientshttp.go
+++ b/internal/home/clientshttp.go
@@ -7,7 +7,6 @@ import (
"net/http"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
- "github.com/AdguardTeam/golibs/log"
)
// clientJSON is a common structure used by several handlers to deal with
@@ -70,26 +69,17 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
data.Clients = append(data.Clients, cj)
}
- clients.ipToRC.Range(func(ip net.IP, v any) (cont bool) {
- rc, ok := v.(*RuntimeClient)
- if !ok {
- log.Error("dns: bad type %T in ipToRC for %s", v, ip)
-
- return true
- }
-
+ for ip, rc := range clients.ipToRC {
cj := runtimeClientJSON{
WHOISInfo: rc.WHOISInfo,
Name: rc.Host,
Source: rc.Source,
- IP: ip,
+ IP: ip.AsSlice(),
}
data.RuntimeClients = append(data.RuntimeClients, cj)
-
- return true
- })
+ }
data.Tags = clientTags
@@ -179,6 +169,7 @@ func (clients *clientsContainer) handleDelClient(w http.ResponseWriter, r *http.
if !clients.Del(cj.Name) {
aghhttp.Error(r, w, http.StatusBadRequest, "Client not found")
+
return
}
@@ -250,7 +241,7 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http
// /etc/hosts tables, DHCP leases, or blocklists. cj is guaranteed to be
// non-nil.
func (clients *clientsContainer) findRuntime(ip net.IP, idStr string) (cj *clientJSON) {
- rc, ok := clients.FindRuntimeClient(ip)
+ rc, ok := clients.findRuntimeClient(ip)
if !ok {
// It is still possible that the IP used to be in the runtime clients
// list, but then the server was reloaded. So, check the DNS server's
diff --git a/internal/home/config.go b/internal/home/config.go
index 598baf81..c7198d93 100644
--- a/internal/home/config.go
+++ b/internal/home/config.go
@@ -3,12 +3,13 @@ package home
import (
"bytes"
"fmt"
- "net"
+ "net/netip"
"os"
"path/filepath"
"sync"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
+ "github.com/AdguardTeam/AdGuardHome/internal/aghtls"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
@@ -85,19 +86,28 @@ type configuration struct {
// It's reset after config is parsed
fileData []byte
- BindHost net.IP `yaml:"bind_host"` // BindHost is the IP address of the HTTP server to bind to
- BindPort int `yaml:"bind_port"` // BindPort is the port the HTTP server
- BetaBindPort int `yaml:"beta_bind_port"` // BetaBindPort is the port for new client
- Users []webUser `yaml:"users"` // Users that can access HTTP server
+ // BindHost is the address for the web interface server to listen on.
+ BindHost netip.Addr `yaml:"bind_host"`
+ // BindPort is the port for the web interface server to listen on.
+ BindPort int `yaml:"bind_port"`
+ // BetaBindPort is the port for the new client's web interface server to
+ // listen on.
+ BetaBindPort int `yaml:"beta_bind_port"`
+
+ // Users are the clients capable for accessing the web interface.
+ Users []webUser `yaml:"users"`
// AuthAttempts is the maximum number of failed login attempts a user
// can do before being blocked.
AuthAttempts uint `yaml:"auth_attempts"`
// AuthBlockMin is the duration, in minutes, of the block of new login
// attempts after AuthAttempts unsuccessful login attempts.
- AuthBlockMin uint `yaml:"block_auth_min"`
- ProxyURL string `yaml:"http_proxy"` // Proxy address for our HTTP client
- Language string `yaml:"language"` // two-letter ISO 639-1 language code
- DebugPProf bool `yaml:"debug_pprof"` // Enable pprof HTTP server on port 6060
+ AuthBlockMin uint `yaml:"block_auth_min"`
+ // ProxyURL is the address of proxy server for the internal HTTP client.
+ ProxyURL string `yaml:"http_proxy"`
+ // Language is a two-letter ISO 639-1 language code.
+ Language string `yaml:"language"`
+ // DebugPProf defines if the profiling HTTP handler will listen on :6060.
+ DebugPProf bool `yaml:"debug_pprof"`
// TTL for a web session (in hours)
// An active session is automatically refreshed once a day.
@@ -112,7 +122,7 @@ type configuration struct {
//
// TODO(e.burkov): Move all the filtering configuration fields into the
// only configuration subsection covering the changes with a single
- // migration.
+ // migration. Also keep the blocked services in mind.
Filters []filtering.FilterYAML `yaml:"filters"`
WhitelistFilters []filtering.FilterYAML `yaml:"whitelist_filters"`
UserRules []string `yaml:"user_rules"`
@@ -135,18 +145,26 @@ type configuration struct {
// field ordering is important -- yaml fields will mirror ordering from here
type dnsConfig struct {
- BindHosts []net.IP `yaml:"bind_hosts"`
- Port int `yaml:"port"`
+ BindHosts []netip.Addr `yaml:"bind_hosts"`
+ Port int `yaml:"port"`
- // time interval for statistics (in days)
+ // StatsInterval is the time interval for flushing statistics to the disk in
+ // days.
StatsInterval uint32 `yaml:"statistics_interval"`
- 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
+ // QueryLogEnabled defines if the query log is enabled.
+ QueryLogEnabled bool `yaml:"querylog_enabled"`
+ // QueryLogFileEnabled defines, if the query log is written to the file.
+ QueryLogFileEnabled bool `yaml:"querylog_file_enabled"`
// QueryLogInterval is the interval for query log's files rotation.
- QueryLogInterval timeutil.Duration `yaml:"querylog_interval"`
- QueryLogMemSize uint32 `yaml:"querylog_size_memory"` // number of entries kept in memory before they are flushed to disk
- AnonymizeClientIP bool `yaml:"anonymize_client_ip"` // anonymize clients' IP addresses in logs and stats
+ QueryLogInterval timeutil.Duration `yaml:"querylog_interval"`
+ // QueryLogMemSize is the number of entries kept in memory before they are
+ // flushed to disk.
+ QueryLogMemSize uint32 `yaml:"querylog_size_memory"`
+
+ // AnonymizeClientIP defines if clients' IP addresses should be anonymized
+ // in query log and statistics.
+ AnonymizeClientIP bool `yaml:"anonymize_client_ip"`
dnsforward.FilteringConfig `yaml:",inline"`
@@ -211,12 +229,12 @@ type tlsConfigSettings struct {
var config = &configuration{
BindPort: 3000,
BetaBindPort: 0,
- BindHost: net.IP{0, 0, 0, 0},
+ BindHost: netip.IPv4Unspecified(),
AuthAttempts: 5,
AuthBlockMin: 15,
WebSessionTTLHours: 30 * 24,
DNS: dnsConfig{
- BindHosts: []net.IP{{0, 0, 0, 0}},
+ BindHosts: []netip.Addr{netip.IPv4Unspecified()},
Port: defaultPortDNS,
StatsInterval: 1,
QueryLogEnabled: true,
@@ -236,6 +254,7 @@ var config = &configuration{
},
TrustedProxies: []string{"127.0.0.0/8", "::1/128"},
+ CacheSize: 4 * 1024 * 1024,
// set default maximum concurrent queries to 300
// we introduced a default limit due to this:
@@ -362,6 +381,7 @@ func parseConfig() (err error) {
// we add support for HTTP/3 for web admin interface.
addPorts(udpPorts, udpPort(config.TLS.PortDNSOverQUIC))
}
+
if err = tcpPorts.Validate(); err != nil {
return fmt.Errorf("validating tcp ports: %w", err)
} else if err = udpPorts.Validate(); err != nil {
@@ -376,6 +396,11 @@ func parseConfig() (err error) {
config.DNS.UpstreamTimeout = timeutil.Duration{Duration: dnsforward.DefaultTimeout}
}
+ err = setContextTLSCipherIDs()
+ if err != nil {
+ return err
+ }
+
return nil
}
@@ -478,3 +503,23 @@ func (c *configuration) write() (err error) {
return nil
}
+
+// setContextTLSCipherIDs sets the TLS cipher suite IDs to use.
+func setContextTLSCipherIDs() (err error) {
+ if len(config.TLS.OverrideTLSCiphers) == 0 {
+ log.Info("tls: using default ciphers")
+
+ Context.tlsCipherIDs = aghtls.SaferCipherSuites()
+
+ return nil
+ }
+
+ log.Info("tls: overriding ciphers: %s", config.TLS.OverrideTLSCiphers)
+
+ Context.tlsCipherIDs, err = aghtls.ParseCiphers(config.TLS.OverrideTLSCiphers)
+ if err != nil {
+ return fmt.Errorf("parsing override ciphers: %w", err)
+ }
+
+ return nil
+}
diff --git a/internal/home/control.go b/internal/home/control.go
index 54d2efb1..5e4e6df2 100644
--- a/internal/home/control.go
+++ b/internal/home/control.go
@@ -2,8 +2,8 @@ package home
import (
"fmt"
- "net"
"net/http"
+ "net/netip"
"net/url"
"runtime"
"strings"
@@ -20,11 +20,11 @@ import (
// appendDNSAddrs is a convenient helper for appending a formatted form of DNS
// addresses to a slice of strings.
-func appendDNSAddrs(dst []string, addrs ...net.IP) (res []string) {
+func appendDNSAddrs(dst []string, addrs ...netip.Addr) (res []string) {
for _, addr := range addrs {
var hostport string
if config.DNS.Port != defaultPortDNS {
- hostport = netutil.JoinHostPort(addr.String(), config.DNS.Port)
+ hostport = netip.AddrPortFrom(addr, uint16(config.DNS.Port)).String()
} else {
hostport = addr.String()
}
@@ -38,7 +38,7 @@ func appendDNSAddrs(dst []string, addrs ...net.IP) (res []string) {
// appendDNSAddrsWithIfaces formats and appends all DNS addresses from src to
// dst. It also adds the IP addresses of all network interfaces if src contains
// an unspecified IP address.
-func appendDNSAddrsWithIfaces(dst []string, src []net.IP) (res []string, err error) {
+func appendDNSAddrsWithIfaces(dst []string, src []netip.Addr) (res []string, err error) {
ifacesAdded := false
for _, h := range src {
if !h.IsUnspecified() {
@@ -71,7 +71,9 @@ func appendDNSAddrsWithIfaces(dst []string, src []net.IP) (res []string, err err
// on, including the addresses on all interfaces in cases of unspecified IPs.
func collectDNSAddresses() (addrs []string, err error) {
if hosts := config.DNS.BindHosts; len(hosts) == 0 {
- addrs = appendDNSAddrs(addrs, net.IP{127, 0, 0, 1})
+ addr := aghnet.IPv4Localhost()
+
+ addrs = appendDNSAddrs(addrs, addr)
} else {
addrs, err = appendDNSAddrsWithIfaces(addrs, hosts)
if err != nil {
@@ -320,6 +322,28 @@ func handleHTTPSRedirect(w http.ResponseWriter, r *http.Request) (ok bool) {
return false
}
+ var serveHTTP3 bool
+ var portHTTPS int
+ func() {
+ config.RLock()
+ defer config.RUnlock()
+
+ serveHTTP3, portHTTPS = config.DNS.ServeHTTP3, config.TLS.PortHTTPS
+ }()
+
+ respHdr := w.Header()
+
+ // Let the browser know that server supports HTTP/3.
+ //
+ // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Alt-Svc.
+ //
+ // TODO(a.garipov): Consider adding a configurable max-age. Currently, the
+ // default is 24 hours.
+ if serveHTTP3 {
+ altSvc := fmt.Sprintf(`h3=":%d"`, portHTTPS)
+ respHdr.Set(aghhttp.HdrNameAltSvc, altSvc)
+ }
+
if r.TLS == nil && web.forceHTTPS {
hostPort := host
if port := web.conf.PortHTTPS; port != defaultPortHTTPS {
@@ -346,8 +370,9 @@ func handleHTTPSRedirect(w http.ResponseWriter, r *http.Request) (ok bool) {
Scheme: aghhttp.SchemeHTTP,
Host: r.Host,
}
- w.Header().Set("Access-Control-Allow-Origin", originURL.String())
- w.Header().Set("Vary", "Origin")
+
+ respHdr.Set(aghhttp.HdrNameAccessControlAllowOrigin, originURL.String())
+ respHdr.Set(aghhttp.HdrNameVary, aghhttp.HdrNameOrigin)
return true
}
diff --git a/internal/home/controlinstall.go b/internal/home/controlinstall.go
index 7df8d320..0cbddf00 100644
--- a/internal/home/controlinstall.go
+++ b/internal/home/controlinstall.go
@@ -5,8 +5,8 @@ import (
"encoding/json"
"fmt"
"io"
- "net"
"net/http"
+ "net/netip"
"os"
"os/exec"
"path/filepath"
@@ -64,9 +64,9 @@ func (web *Web) handleInstallGetAddresses(w http.ResponseWriter, r *http.Request
}
type checkConfReqEnt struct {
- IP net.IP `json:"ip"`
- Port int `json:"port"`
- Autofix bool `json:"autofix"`
+ IP netip.Addr `json:"ip"`
+ Port int `json:"port"`
+ Autofix bool `json:"autofix"`
}
type checkConfReq struct {
@@ -117,7 +117,7 @@ func (req *checkConfReq) validateWeb(tcpPorts aghalg.UniqChecker[tcpPort]) (err
// unbound after install.
}
- return aghnet.CheckPort("tcp", req.Web.IP, portInt)
+ return aghnet.CheckPort("tcp", netip.AddrPortFrom(req.Web.IP, uint16(portInt)))
}
// validateDNS returns error if the DNS part of the initial configuration can't
@@ -142,13 +142,13 @@ func (req *checkConfReq) validateDNS(
return false, err
}
- err = aghnet.CheckPort("tcp", req.DNS.IP, port)
+ err = aghnet.CheckPort("tcp", netip.AddrPortFrom(req.DNS.IP, uint16(port)))
if err != nil {
return false, err
}
}
- err = aghnet.CheckPort("udp", req.DNS.IP, port)
+ err = aghnet.CheckPort("udp", netip.AddrPortFrom(req.DNS.IP, uint16(port)))
if !aghnet.IsAddrInUse(err) {
return false, err
}
@@ -160,7 +160,7 @@ func (req *checkConfReq) validateDNS(
log.Error("disabling DNSStubListener: %s", err)
}
- err = aghnet.CheckPort("udp", req.DNS.IP, port)
+ err = aghnet.CheckPort("udp", netip.AddrPortFrom(req.DNS.IP, uint16(port)))
canAutofix = false
}
@@ -196,7 +196,7 @@ func (web *Web) handleInstallCheckConfig(w http.ResponseWriter, r *http.Request)
// handleStaticIP - handles static IP request
// It either checks if we have a static IP
// Or if set=true, it tries to set it
-func handleStaticIP(ip net.IP, set bool) staticIPJSON {
+func handleStaticIP(ip netip.Addr, set bool) staticIPJSON {
resp := staticIPJSON{}
interfaceName := aghnet.InterfaceByIP(ip)
@@ -304,8 +304,8 @@ func disableDNSStubListener() error {
}
type applyConfigReqEnt struct {
- IP net.IP `json:"ip"`
- Port int `json:"port"`
+ IP netip.Addr `json:"ip"`
+ Port int `json:"port"`
}
type applyConfigReq struct {
@@ -397,14 +397,14 @@ func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) {
return
}
- err = aghnet.CheckPort("udp", req.DNS.IP, req.DNS.Port)
+ err = aghnet.CheckPort("udp", netip.AddrPortFrom(req.DNS.IP, uint16(req.DNS.Port)))
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return
}
- err = aghnet.CheckPort("tcp", req.DNS.IP, req.DNS.Port)
+ err = aghnet.CheckPort("tcp", netip.AddrPortFrom(req.DNS.IP, uint16(req.DNS.Port)))
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@@ -417,14 +417,14 @@ func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) {
Context.firstRun = false
config.BindHost = req.Web.IP
config.BindPort = req.Web.Port
- config.DNS.BindHosts = []net.IP{req.DNS.IP}
+ config.DNS.BindHosts = []netip.Addr{req.DNS.IP}
config.DNS.Port = req.DNS.Port
// TODO(e.burkov): StartMods() should be put in a separate goroutine at the
// moment we'll allow setting up TLS in the initial configuration or the
// configuration itself will use HTTPS protocol, because the underlying
// functions potentially restart the HTTPS server.
- err = StartMods()
+ err = startMods()
if err != nil {
Context.firstRun = true
copyInstallSettings(config, curConfig)
@@ -490,9 +490,9 @@ func decodeApplyConfigReq(r io.Reader) (req *applyConfigReq, restartHTTP bool, e
return nil, false, errors.Error("ports cannot be 0")
}
- restartHTTP = !config.BindHost.Equal(req.Web.IP) || config.BindPort != req.Web.Port
+ restartHTTP = config.BindHost != req.Web.IP || config.BindPort != req.Web.Port
if restartHTTP {
- err = aghnet.CheckPort("tcp", req.Web.IP, req.Web.Port)
+ err = aghnet.CheckPort("tcp", netip.AddrPortFrom(req.Web.IP, uint16(req.Web.Port)))
if err != nil {
return nil, false, fmt.Errorf(
"checking address %s:%d: %w",
@@ -518,9 +518,9 @@ func (web *Web) registerInstallHandlers() {
// TODO(e.burkov): This should removed with the API v1 when the appropriate
// functionality will appear in default checkConfigReqEnt.
type checkConfigReqEntBeta struct {
- IP []net.IP `json:"ip"`
- Port int `json:"port"`
- Autofix bool `json:"autofix"`
+ IP []netip.Addr `json:"ip"`
+ Port int `json:"port"`
+ Autofix bool `json:"autofix"`
}
// checkConfigReqBeta is a struct representing new client's config check request
@@ -590,8 +590,8 @@ func (web *Web) handleInstallCheckConfigBeta(w http.ResponseWriter, r *http.Requ
// TODO(e.burkov): This should removed with the API v1 when the appropriate
// functionality will appear in default applyConfigReqEnt.
type applyConfigReqEntBeta struct {
- IP []net.IP `json:"ip"`
- Port int `json:"port"`
+ IP []netip.Addr `json:"ip"`
+ Port int `json:"port"`
}
// applyConfigReqBeta is a struct representing new client's config setting
diff --git a/internal/home/dns.go b/internal/home/dns.go
index da462876..7d40ed35 100644
--- a/internal/home/dns.go
+++ b/internal/home/dns.go
@@ -3,6 +3,7 @@ package home
import (
"fmt"
"net"
+ "net/netip"
"net/url"
"os"
"path/filepath"
@@ -164,33 +165,27 @@ func onDNSRequest(pctx *proxy.DNSContext) {
}
}
-func ipsToTCPAddrs(ips []net.IP, port int) (tcpAddrs []*net.TCPAddr) {
+func ipsToTCPAddrs(ips []netip.Addr, port int) (tcpAddrs []*net.TCPAddr) {
if ips == nil {
return nil
}
- tcpAddrs = make([]*net.TCPAddr, len(ips))
- for i, ip := range ips {
- tcpAddrs[i] = &net.TCPAddr{
- IP: ip,
- Port: port,
- }
+ tcpAddrs = make([]*net.TCPAddr, 0, len(ips))
+ for _, ip := range ips {
+ tcpAddrs = append(tcpAddrs, net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port))))
}
return tcpAddrs
}
-func ipsToUDPAddrs(ips []net.IP, port int) (udpAddrs []*net.UDPAddr) {
+func ipsToUDPAddrs(ips []netip.Addr, port int) (udpAddrs []*net.UDPAddr) {
if ips == nil {
return nil
}
- udpAddrs = make([]*net.UDPAddr, len(ips))
- for i, ip := range ips {
- udpAddrs[i] = &net.UDPAddr{
- IP: ip,
- Port: port,
- }
+ udpAddrs = make([]*net.UDPAddr, 0, len(ips))
+ for _, ip := range ips {
+ udpAddrs = append(udpAddrs, net.UDPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port))))
}
return udpAddrs
@@ -200,7 +195,7 @@ func generateServerConfig() (newConf dnsforward.ServerConfig, err error) {
dnsConf := config.DNS
hosts := dnsConf.BindHosts
if len(hosts) == 0 {
- hosts = []net.IP{{127, 0, 0, 1}}
+ hosts = []netip.Addr{aghnet.IPv4Localhost()}
}
newConf = dnsforward.ServerConfig{
@@ -257,7 +252,7 @@ func generateServerConfig() (newConf dnsforward.ServerConfig, err error) {
return newConf, nil
}
-func newDNSCrypt(hosts []net.IP, tlsConf tlsConfigSettings) (dnscc dnsforward.DNSCryptConfig, err error) {
+func newDNSCrypt(hosts []netip.Addr, tlsConf tlsConfigSettings) (dnscc dnsforward.DNSCryptConfig, err error) {
if tlsConf.DNSCryptConfigFile == "" {
return dnscc, errors.Error("no dnscrypt_config_file")
}
@@ -436,17 +431,23 @@ func reconfigureDNSServer() (err error) {
return nil
}
-func stopDNSServer() error {
+func stopDNSServer() (err error) {
if !isRunning() {
return nil
}
- err := Context.dnsServer.Stop()
+ err = Context.dnsServer.Stop()
if err != nil {
- return fmt.Errorf("couldn't stop forwarding DNS server: %w", err)
+ return fmt.Errorf("stopping forwarding dns server: %w", err)
+ }
+
+ err = Context.clients.close()
+ if err != nil {
+ return fmt.Errorf("closing clients container: %w", err)
}
closeDNSServer()
+
return nil
}
diff --git a/internal/home/home.go b/internal/home/home.go
index 42c44249..6dbe1600 100644
--- a/internal/home/home.go
+++ b/internal/home/home.go
@@ -10,6 +10,7 @@ import (
"net"
"net/http"
"net/http/pprof"
+ "net/netip"
"net/url"
"os"
"os/signal"
@@ -58,7 +59,7 @@ type homeContext struct {
auth *Auth // HTTP authentication module
filters *filtering.DNSFilter // DNS filtering module
web *Web // Web (HTTP, HTTPS) module
- tls *TLSMod // TLS module
+ tls *tlsManager // TLS module
// etcHosts is an IP-hostname pairs set taken from system configuration
// (e.g. /etc/hosts) files.
etcHosts *aghnet.HostsContainer
@@ -83,6 +84,10 @@ type homeContext struct {
transport *http.Transport
client *http.Client
appSignalChannel chan os.Signal // Channel for receiving OS signals by the console app
+
+ // tlsCipherIDs are the ID of the cipher suites that AdGuard Home must use.
+ tlsCipherIDs []uint16
+
// runningAsService flag is set to true when options are passed from the service runner
runningAsService bool
}
@@ -97,9 +102,15 @@ var Context homeContext
// Main is the entry point
func Main(clientBuildFS fs.FS) {
- // config can be specified, which reads options from there, but other command line flags have to override config values
- // therefore, we must do it manually instead of using a lib
- args := loadOptions()
+ initCmdLineOpts()
+
+ // The configuration file path can be overridden, but other command-line
+ // options have to override config values. Therefore, do it manually
+ // instead of using package flag.
+ //
+ // TODO(a.garipov): The comment above is most likely false. Replace with
+ // package flag.
+ opts := loadCmdLineOpts()
Context.appSignalChannel = make(chan os.Signal)
signal.Notify(Context.appSignalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
@@ -110,8 +121,7 @@ func Main(clientBuildFS fs.FS) {
switch sig {
case syscall.SIGHUP:
Context.clients.Reload()
- Context.tls.Reload()
-
+ Context.tls.reload()
default:
cleanup(context.Background())
cleanupAlways()
@@ -120,26 +130,18 @@ func Main(clientBuildFS fs.FS) {
}
}()
- if args.serviceControlAction != "" {
- handleServiceControlAction(args, clientBuildFS)
+ if opts.serviceControlAction != "" {
+ handleServiceControlAction(opts, clientBuildFS)
return
}
// run the protection
- run(args, clientBuildFS)
+ run(opts, clientBuildFS)
}
-func setupContext(args options) {
- Context.runningAsService = args.runningAsService
- Context.disableUpdate = args.disableUpdate ||
- version.Channel() == version.ChannelDevelopment
-
- Context.firstRun = detectFirstRun()
- if Context.firstRun {
- log.Info("This is the first time AdGuard Home is launched")
- checkPermissions()
- }
+func setupContext(opts options) {
+ setupContextFlags(opts)
switch version.Channel() {
case version.ChannelEdge, version.ChannelDevelopment:
@@ -148,13 +150,13 @@ func setupContext(args options) {
// Go on.
}
- Context.tlsRoots = LoadSystemRootCAs()
+ Context.tlsRoots = aghtls.SystemRootCAs()
Context.transport = &http.Transport{
DialContext: customDialContext,
Proxy: getHTTPProxy,
TLSClientConfig: &tls.Config{
RootCAs: Context.tlsRoots,
- CipherSuites: aghtls.SaferCipherSuites(),
+ CipherSuites: Context.tlsCipherIDs,
MinVersion: tls.VersionTLS12,
},
}
@@ -174,13 +176,13 @@ func setupContext(args options) {
os.Exit(1)
}
- if args.checkConfig {
+ if opts.checkConfig {
log.Info("configuration file is ok")
os.Exit(0)
}
- if !args.noEtcHosts && config.Clients.Sources.HostsFile {
+ if !opts.noEtcHosts && config.Clients.Sources.HostsFile {
err = setupHostsContainer()
fatalOnError(err)
}
@@ -189,6 +191,24 @@ func setupContext(args options) {
Context.mux = http.NewServeMux()
}
+// setupContextFlags sets global flags and prints their status to the log.
+func setupContextFlags(opts options) {
+ Context.firstRun = detectFirstRun()
+ if Context.firstRun {
+ log.Info("This is the first time AdGuard Home is launched")
+ checkPermissions()
+ }
+
+ Context.runningAsService = opts.runningAsService
+ // Don't print the runningAsService flag, since that has already been done
+ // in [run].
+
+ Context.disableUpdate = opts.disableUpdate || version.Channel() == version.ChannelDevelopment
+ if Context.disableUpdate {
+ log.Info("AdGuard Home updates are disabled")
+ }
+}
+
// logIfUnsupported logs a formatted warning if the error is one of the
// unsupported errors and returns nil. If err is nil, logIfUnsupported returns
// nil. Otherwise, it returns err.
@@ -270,7 +290,7 @@ func setupHostsContainer() (err error) {
return nil
}
-func setupConfig(args options) (err error) {
+func setupConfig(opts options) (err error) {
config.DNS.DnsfilterConf.EtcHosts = Context.etcHosts
config.DNS.DnsfilterConf.ConfigModified = onConfigModified
config.DNS.DnsfilterConf.HTTPRegister = httpRegister
@@ -312,9 +332,9 @@ func setupConfig(args options) (err error) {
Context.clients.Init(config.Clients.Persistent, Context.dhcpServer, Context.etcHosts, arpdb)
- if args.bindPort != 0 {
+ if opts.bindPort != 0 {
tcpPorts := aghalg.UniqChecker[tcpPort]{}
- addPorts(tcpPorts, tcpPort(args.bindPort), tcpPort(config.BetaBindPort))
+ addPorts(tcpPorts, tcpPort(opts.bindPort), tcpPort(config.BetaBindPort))
udpPorts := aghalg.UniqChecker[udpPort]{}
addPorts(udpPorts, udpPort(config.DNS.Port))
@@ -336,23 +356,23 @@ func setupConfig(args options) (err error) {
return fmt.Errorf("validating udp ports: %w", err)
}
- config.BindPort = args.bindPort
+ config.BindPort = opts.bindPort
}
// override bind host/port from the console
- if args.bindHost != nil {
- config.BindHost = args.bindHost
+ if opts.bindHost.IsValid() {
+ config.BindHost = opts.bindHost
}
- if len(args.pidFile) != 0 && writePIDFile(args.pidFile) {
- Context.pidFileName = args.pidFile
+ if len(opts.pidFile) != 0 && writePIDFile(opts.pidFile) {
+ Context.pidFileName = opts.pidFile
}
return nil
}
-func initWeb(args options, clientBuildFS fs.FS) (web *Web, err error) {
+func initWeb(opts options, clientBuildFS fs.FS) (web *Web, err error) {
var clientFS, clientBetaFS fs.FS
- if args.localFrontend {
+ if opts.localFrontend {
log.Info("warning: using local frontend files")
clientFS = os.DirFS("build/static")
@@ -400,24 +420,24 @@ func fatalOnError(err error) {
}
// run configures and starts AdGuard Home.
-func run(args options, clientBuildFS fs.FS) {
+func run(opts options, clientBuildFS fs.FS) {
// configure config filename
- initConfigFilename(args)
+ initConfigFilename(opts)
// configure working dir and config path
- initWorkingDir(args)
+ initWorkingDir(opts)
// configure log level and output
- configureLogger(args)
+ configureLogger(opts)
// Print the first message after logger is configured.
log.Info(version.Full())
log.Debug("current working directory is %s", Context.workDir)
- if args.runningAsService {
+ if opts.runningAsService {
log.Info("AdGuard Home is running as a service")
}
- setupContext(args)
+ setupContext(opts)
err := configureOS(config)
fatalOnError(err)
@@ -427,7 +447,7 @@ func run(args options, clientBuildFS fs.FS) {
// but also avoid relying on automatic Go init() function
filtering.InitModule()
- err = setupConfig(args)
+ err = setupConfig(opts)
fatalOnError(err)
if !Context.firstRun {
@@ -456,7 +476,7 @@ func run(args options, clientBuildFS fs.FS) {
}
sessFilename := filepath.Join(Context.getDataDir(), "sessions.db")
- GLMode = args.glinetMode
+ GLMode = opts.glinetMode
var rateLimiter *authRateLimiter
if config.AuthAttempts > 0 && config.AuthBlockMin > 0 {
rateLimiter = newAuthRateLimiter(
@@ -478,19 +498,19 @@ func run(args options, clientBuildFS fs.FS) {
}
config.Users = nil
- Context.tls = tlsCreate(config.TLS)
- if Context.tls == nil {
- log.Fatalf("Can't initialize TLS module")
+ Context.tls, err = newTLSManager(config.TLS)
+ if err != nil {
+ log.Fatalf("initializing tls: %s", err)
}
- Context.web, err = initWeb(args, clientBuildFS)
+ Context.web, err = initWeb(opts, clientBuildFS)
fatalOnError(err)
if !Context.firstRun {
err = initDNSServer()
fatalOnError(err)
- Context.tls.Start()
+ Context.tls.start()
go func() {
serr := startDNSServer()
@@ -514,20 +534,22 @@ func run(args options, clientBuildFS fs.FS) {
select {}
}
-// StartMods initializes and starts the DNS server after installation.
-func StartMods() error {
+// startMods initializes and starts the DNS server after installation.
+func startMods() error {
err := initDNSServer()
if err != nil {
return err
}
- Context.tls.Start()
+ Context.tls.start()
err = startDNSServer()
if err != nil {
closeDNSServer()
+
return err
}
+
return nil
}
@@ -540,7 +562,7 @@ func checkPermissions() {
}
// We should check if AdGuard Home is able to bind to port 53
- err := aghnet.CheckPort("tcp", net.IP{127, 0, 0, 1}, defaultPortDNS)
+ err := aghnet.CheckPort("tcp", netip.AddrPortFrom(aghnet.IPv4Localhost(), defaultPortDNS))
if err != nil {
if errors.Is(err, os.ErrPermission) {
log.Fatal(`Permission check failed.
@@ -575,10 +597,10 @@ func writePIDFile(fn string) bool {
return true
}
-func initConfigFilename(args options) {
+func initConfigFilename(opts options) {
// config file path can be overridden by command-line arguments:
- if args.configFilename != "" {
- Context.configFilename = args.configFilename
+ if opts.confFilename != "" {
+ Context.configFilename = opts.confFilename
} else {
// Default config file name
Context.configFilename = "AdGuardHome.yaml"
@@ -587,15 +609,15 @@ func initConfigFilename(args options) {
// initWorkingDir initializes the workDir
// if no command-line arguments specified, we use the directory where our binary file is located
-func initWorkingDir(args options) {
+func initWorkingDir(opts options) {
execPath, err := os.Executable()
if err != nil {
panic(err)
}
- if args.workDir != "" {
+ if opts.workDir != "" {
// If there is a custom config file, use it's directory as our working dir
- Context.workDir = args.workDir
+ Context.workDir = opts.workDir
} else {
Context.workDir = filepath.Dir(execPath)
}
@@ -609,15 +631,15 @@ func initWorkingDir(args options) {
}
// configureLogger configures logger level and output
-func configureLogger(args options) {
+func configureLogger(opts options) {
ls := getLogSettings()
// command-line arguments can override config settings
- if args.verbose || config.Verbose {
+ if opts.verbose || config.Verbose {
ls.Verbose = true
}
- if args.logFile != "" {
- ls.File = args.logFile
+ if opts.logFile != "" {
+ ls.File = opts.logFile
} else if config.File != "" {
ls.File = config.File
}
@@ -638,7 +660,7 @@ func configureLogger(args options) {
// happen pretty quickly.
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
- if args.runningAsService && ls.File == "" && runtime.GOOS == "windows" {
+ if opts.runningAsService && ls.File == "" && runtime.GOOS == "windows" {
// When running as a Windows service, use eventlog by default if nothing
// else is configured. Otherwise, we'll simply lose the log output.
ls.File = configSyslog
@@ -711,7 +733,6 @@ func cleanup(ctx context.Context) {
}
if Context.tls != nil {
- Context.tls.Close()
Context.tls = nil
}
}
@@ -721,32 +742,37 @@ func cleanupAlways() {
if len(Context.pidFileName) != 0 {
_ = os.Remove(Context.pidFileName)
}
- log.Info("Stopped")
+
+ log.Info("stopped")
}
func exitWithError() {
os.Exit(64)
}
-// loadOptions reads command line arguments and initializes configuration
-func loadOptions() options {
- o, f, err := parse(os.Args[0], os.Args[1:])
-
+// loadCmdLineOpts reads command line arguments and initializes configuration
+// from them. If there is an error or an effect, loadCmdLineOpts processes them
+// and exits.
+func loadCmdLineOpts() (opts options) {
+ opts, eff, err := parseCmdOpts(os.Args[0], os.Args[1:])
if err != nil {
log.Error(err.Error())
- _ = printHelp(os.Args[0])
+ printHelp(os.Args[0])
+
exitWithError()
- } else if f != nil {
- err = f()
+ }
+
+ if eff != nil {
+ err = eff()
if err != nil {
log.Error(err.Error())
exitWithError()
- } else {
- os.Exit(0)
}
+
+ os.Exit(0)
}
- return o
+ return opts
}
// printWebAddrs prints addresses built from proto, addr, and an appropriate
diff --git a/internal/home/home_test.go b/internal/home/home_test.go
new file mode 100644
index 00000000..2ce1d76d
--- /dev/null
+++ b/internal/home/home_test.go
@@ -0,0 +1,12 @@
+package home
+
+import (
+ "testing"
+
+ "github.com/AdguardTeam/golibs/testutil"
+)
+
+func TestMain(m *testing.M) {
+ testutil.DiscardLogOutput(m)
+ initCmdLineOpts()
+}
diff --git a/internal/home/mobileconfig_test.go b/internal/home/mobileconfig_test.go
index 5230a2ac..3587154f 100644
--- a/internal/home/mobileconfig_test.go
+++ b/internal/home/mobileconfig_test.go
@@ -3,12 +3,11 @@ package home
import (
"bytes"
"encoding/json"
- "net"
"net/http"
"net/http/httptest"
+ "net/netip"
"testing"
- "github.com/AdguardTeam/golibs/netutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"howett.net/plist"
@@ -28,12 +27,12 @@ func setupDNSIPs(t testing.TB) {
config = &configuration{
DNS: dnsConfig{
- BindHosts: []net.IP{netutil.IPv4Zero()},
+ BindHosts: []netip.Addr{netip.IPv4Unspecified()},
Port: defaultPortDNS,
},
}
- Context.tls = &TLSMod{}
+ Context.tls = &tlsManager{}
}
func TestHandleMobileConfigDoH(t *testing.T) {
@@ -66,7 +65,7 @@ func TestHandleMobileConfigDoH(t *testing.T) {
oldTLSConf := Context.tls
t.Cleanup(func() { Context.tls = oldTLSConf })
- Context.tls = &TLSMod{conf: tlsConfigSettings{}}
+ Context.tls = &tlsManager{conf: tlsConfigSettings{}}
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/doh.mobileconfig", nil)
require.NoError(t, err)
@@ -138,7 +137,7 @@ func TestHandleMobileConfigDoT(t *testing.T) {
oldTLSConf := Context.tls
t.Cleanup(func() { Context.tls = oldTLSConf })
- Context.tls = &TLSMod{conf: tlsConfigSettings{}}
+ Context.tls = &tlsManager{conf: tlsConfigSettings{}}
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/dot.mobileconfig", nil)
require.NoError(t, err)
diff --git a/internal/home/options.go b/internal/home/options.go
index 6f5a4d8d..e14e26f6 100644
--- a/internal/home/options.go
+++ b/internal/home/options.go
@@ -2,33 +2,63 @@ package home
import (
"fmt"
- "net"
+ "net/netip"
"os"
"strconv"
+ "strings"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/stringutil"
)
-// options passed from command-line arguments
-type options struct {
- verbose bool // is verbose logging enabled
- configFilename string // path to the config file
- workDir string // path to the working directory where we will store the filters data and the querylog
- bindHost net.IP // host address to bind HTTP server on
- bindPort int // port to serve HTTP pages on
- logFile string // Path to the log file. If empty, write to stdout. If "syslog", writes to syslog
- pidFile string // File name to save PID to
- checkConfig bool // Check configuration and exit
- disableUpdate bool // If set, don't check for updates
+// TODO(a.garipov): Replace with package flag.
- // service control action (see service.ControlAction array + "status" command)
+// options represents the command-line options.
+type options struct {
+ // confFilename is the path to the configuration file.
+ confFilename string
+
+ // workDir is the path to the working directory where AdGuard Home stores
+ // filter data, the query log, and other data.
+ workDir string
+
+ // logFile is the path to the log file. If empty, AdGuard Home writes to
+ // stdout; if "syslog", to syslog.
+ logFile string
+
+ // pidFile is the file name for the file to which the PID is saved.
+ pidFile string
+
+ // serviceControlAction is the service action to perform. See
+ // [service.ControlAction] and [handleServiceControlAction].
serviceControlAction string
- // runningAsService flag is set to true when options are passed from the service runner
+ // bindHost is the address on which to serve the HTTP UI.
+ bindHost netip.Addr
+
+ // bindPort is the port on which to serve the HTTP UI.
+ bindPort int
+
+ // checkConfig is true if the current invocation is only required to check
+ // the configuration file and exit.
+ checkConfig bool
+
+ // disableUpdate, if set, makes AdGuard Home not check for updates.
+ disableUpdate bool
+
+ // verbose shows if verbose logging is enabled.
+ verbose bool
+
+ // runningAsService flag is set to true when options are passed from the
+ // service runner
+ //
+ // TODO(a.garipov): Perhaps this could be determined by a non-empty
+ // serviceControlAction?
runningAsService bool
- glinetMode bool // Activate GL-Inet compatibility mode
+ // glinetMode shows if the GL-Inet compatibility mode is enabled.
+ glinetMode bool
// noEtcHosts flag should be provided when /etc/hosts file shouldn't be
// used.
@@ -39,88 +69,86 @@ type options struct {
localFrontend bool
}
-// functions used for their side-effects
-type effect func() error
-
-type arg struct {
- description string // a short, English description of the argument
- longName string // the name of the argument used after '--'
- shortName string // the name of the argument used after '-'
-
- // only one of updateWithValue, updateNoValue, and effect should be present
-
- updateWithValue func(o options, v string) (options, error) // the mutator for arguments with parameters
- updateNoValue func(o options) (options, error) // the mutator for arguments without parameters
- effect func(o options, exec string) (f effect, err error) // the side-effect closure generator
-
- serialize func(o options) []string // the re-serialization function back to arguments (return nil for omit)
+// initCmdLineOpts completes initialization of the global command-line option
+// slice. It must only be called once.
+func initCmdLineOpts() {
+ // The --help option cannot be put directly into cmdLineOpts, because that
+ // causes initialization cycle due to printHelp referencing cmdLineOpts.
+ cmdLineOpts = append(cmdLineOpts, cmdLineOpt{
+ updateWithValue: nil,
+ updateNoValue: nil,
+ effect: func(o options, exec string) (effect, error) {
+ return func() error { printHelp(exec); exitWithError(); return nil }, nil
+ },
+ serialize: func(o options) (val string, ok bool) { return "", false },
+ description: "Print this help.",
+ longName: "help",
+ shortName: "",
+ })
}
-// {type}SliceOrNil functions check their parameter of type {type}
-// against its zero value and return nil if the parameter value is
-// zero otherwise they return a string slice of the parameter
+// effect is the type for functions used for their side-effects.
+type effect func() (err error)
-func ipSliceOrNil(ip net.IP) []string {
- if ip == nil {
- return nil
- }
+// cmdLineOpt contains the data for a single command-line option. Only one of
+// updateWithValue, updateNoValue, and effect must be present.
+type cmdLineOpt struct {
+ updateWithValue func(o options, v string) (updated options, err error)
+ updateNoValue func(o options) (updated options, err error)
+ effect func(o options, exec string) (eff effect, err error)
- return []string{ip.String()}
+ // serialize is a function that encodes the option into a slice of
+ // command-line arguments, if necessary. If ok is false, this option should
+ // be skipped.
+ serialize func(o options) (val string, ok bool)
+
+ description string
+ longName string
+ shortName string
}
-func stringSliceOrNil(s string) []string {
- if s == "" {
- return nil
- }
+// cmdLineOpts are all command-line options of AdGuard Home.
+var cmdLineOpts = []cmdLineOpt{{
+ updateWithValue: func(o options, v string) (options, error) {
+ o.confFilename = v
+ return o, nil
+ },
+ updateNoValue: nil,
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) {
+ return o.confFilename, o.confFilename != ""
+ },
+ description: "Path to the config file.",
+ longName: "config",
+ shortName: "c",
+}, {
+ updateWithValue: func(o options, v string) (options, error) { o.workDir = v; return o, nil },
+ updateNoValue: nil,
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) { return o.workDir, o.workDir != "" },
+ description: "Path to the working directory.",
+ longName: "work-dir",
+ shortName: "w",
+}, {
+ updateWithValue: func(o options, v string) (oo options, err error) {
+ o.bindHost, err = netip.ParseAddr(v)
- return []string{s}
-}
+ return o, err
+ },
+ updateNoValue: nil,
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) {
+ if !o.bindHost.IsValid() {
+ return "", false
+ }
-func intSliceOrNil(i int) []string {
- if i == 0 {
- return nil
- }
-
- return []string{strconv.Itoa(i)}
-}
-
-func boolSliceOrNil(b bool) []string {
- if b {
- return []string{}
- }
-
- return nil
-}
-
-var args []arg
-
-var configArg = arg{
- "Path to the config file.",
- "config", "c",
- func(o options, v string) (options, error) { o.configFilename = v; return o, nil },
- nil,
- nil,
- func(o options) []string { return stringSliceOrNil(o.configFilename) },
-}
-
-var workDirArg = arg{
- "Path to the working directory.",
- "work-dir", "w",
- func(o options, v string) (options, error) { o.workDir = v; return o, nil }, nil, nil,
- func(o options) []string { return stringSliceOrNil(o.workDir) },
-}
-
-var hostArg = arg{
- "Host address to bind HTTP server on.",
- "host", "h",
- func(o options, v string) (options, error) { o.bindHost = net.ParseIP(v); return o, nil }, nil, nil,
- func(o options) []string { return ipSliceOrNil(o.bindHost) },
-}
-
-var portArg = arg{
- "Port to serve HTTP pages on.",
- "port", "p",
- func(o options, v string) (options, error) {
+ return o.bindHost.String(), true
+ },
+ description: "Host address to bind HTTP server on.",
+ longName: "host",
+ shortName: "h",
+}, {
+ updateWithValue: func(o options, v string) (options, error) {
var err error
var p int
minPort, maxPort := 0, 1<<16-1
@@ -131,108 +159,81 @@ var portArg = arg{
} else {
o.bindPort = p
}
- return o, err
- }, nil, nil,
- func(o options) []string { return intSliceOrNil(o.bindPort) },
-}
-var serviceArg = arg{
- "Service control action: status, install, uninstall, start, stop, restart, reload (configuration).",
- "service", "s",
- func(o options, v string) (options, error) {
+ return o, err
+ },
+ updateNoValue: nil,
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) {
+ if o.bindPort == 0 {
+ return "", false
+ }
+
+ return strconv.Itoa(o.bindPort), true
+ },
+ description: "Port to serve HTTP pages on.",
+ longName: "port",
+ shortName: "p",
+}, {
+ updateWithValue: func(o options, v string) (options, error) {
o.serviceControlAction = v
return o, nil
- }, nil, nil,
- func(o options) []string { return stringSliceOrNil(o.serviceControlAction) },
-}
-
-var logfileArg = arg{
- "Path to log file. If empty: write to stdout; if 'syslog': write to system log.",
- "logfile", "l",
- func(o options, v string) (options, error) { o.logFile = v; return o, nil }, nil, nil,
- func(o options) []string { return stringSliceOrNil(o.logFile) },
-}
-
-var pidfileArg = arg{
- "Path to a file where PID is stored.",
- "pidfile", "",
- func(o options, v string) (options, error) { o.pidFile = v; return o, nil }, nil, nil,
- func(o options) []string { return stringSliceOrNil(o.pidFile) },
-}
-
-var checkConfigArg = arg{
- "Check configuration and exit.",
- "check-config", "",
- nil, func(o options) (options, error) { o.checkConfig = true; return o, nil }, nil,
- func(o options) []string { return boolSliceOrNil(o.checkConfig) },
-}
-
-var noCheckUpdateArg = arg{
- "Don't check for updates.",
- "no-check-update", "",
- nil, func(o options) (options, error) { o.disableUpdate = true; return o, nil }, nil,
- func(o options) []string { return boolSliceOrNil(o.disableUpdate) },
-}
-
-var disableMemoryOptimizationArg = arg{
- "Deprecated. Disable memory optimization.",
- "no-mem-optimization", "",
- nil, nil, func(_ options, _ string) (f effect, err error) {
+ },
+ updateNoValue: nil,
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) {
+ return o.serviceControlAction, o.serviceControlAction != ""
+ },
+ description: `Service control action: status, install (as a service), ` +
+ `uninstall (as a service), start, stop, restart, reload (configuration).`,
+ longName: "service",
+ shortName: "s",
+}, {
+ updateWithValue: func(o options, v string) (options, error) { o.logFile = v; return o, nil },
+ updateNoValue: nil,
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) { return o.logFile, o.logFile != "" },
+ description: `Path to log file. If empty, write to stdout; ` +
+ `if "syslog", write to system log.`,
+ longName: "logfile",
+ shortName: "l",
+}, {
+ updateWithValue: func(o options, v string) (options, error) { o.pidFile = v; return o, nil },
+ updateNoValue: nil,
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) { return o.pidFile, o.pidFile != "" },
+ description: "Path to a file where PID is stored.",
+ longName: "pidfile",
+ shortName: "",
+}, {
+ updateWithValue: nil,
+ updateNoValue: func(o options) (options, error) { o.checkConfig = true; return o, nil },
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) { return "", o.checkConfig },
+ description: "Check configuration and exit.",
+ longName: "check-config",
+ shortName: "",
+}, {
+ updateWithValue: nil,
+ updateNoValue: func(o options) (options, error) { o.disableUpdate = true; return o, nil },
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) { return "", o.disableUpdate },
+ description: "Don't check for updates.",
+ longName: "no-check-update",
+ shortName: "",
+}, {
+ updateWithValue: nil,
+ updateNoValue: nil,
+ effect: func(_ options, _ string) (f effect, err error) {
log.Info("warning: using --no-mem-optimization flag has no effect and is deprecated")
return nil, nil
},
- func(o options) []string { return nil },
-}
-
-var verboseArg = arg{
- "Enable verbose output.",
- "verbose", "v",
- nil, func(o options) (options, error) { o.verbose = true; return o, nil }, nil,
- func(o options) []string { return boolSliceOrNil(o.verbose) },
-}
-
-var glinetArg = arg{
- "Run in GL-Inet compatibility mode.",
- "glinet", "",
- nil, func(o options) (options, error) { o.glinetMode = true; return o, nil }, nil,
- func(o options) []string { return boolSliceOrNil(o.glinetMode) },
-}
-
-var versionArg = arg{
- description: "Show the version and exit. Show more detailed version description with -v.",
- longName: "version",
- shortName: "",
- updateWithValue: nil,
- updateNoValue: nil,
- effect: func(o options, exec string) (effect, error) {
- return func() error {
- if o.verbose {
- fmt.Println(version.Verbose())
- } else {
- fmt.Println(version.Full())
- }
- os.Exit(0)
-
- return nil
- }, nil
- },
- serialize: func(o options) []string { return nil },
-}
-
-var helpArg = arg{
- "Print this help.",
- "help", "",
- nil, nil, func(o options, exec string) (effect, error) {
- return func() error { _ = printHelp(exec); os.Exit(64); return nil }, nil
- },
- func(o options) []string { return nil },
-}
-
-var noEtcHostsArg = arg{
- description: "Deprecated. Do not use the OS-provided hosts.",
- longName: "no-etc-hosts",
- shortName: "",
+ serialize: func(o options) (val string, ok bool) { return "", false },
+ description: "Deprecated. Disable memory optimization.",
+ longName: "no-mem-optimization",
+ shortName: "",
+}, {
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.noEtcHosts = true; return o, nil },
effect: func(_ options, _ string) (f effect, err error) {
@@ -242,146 +243,216 @@ var noEtcHostsArg = arg{
return nil, nil
},
- serialize: func(o options) []string { return boolSliceOrNil(o.noEtcHosts) },
-}
-
-var localFrontendArg = arg{
- description: "Use local frontend directories.",
- longName: "local-frontend",
- shortName: "",
+ serialize: func(o options) (val string, ok bool) { return "", o.noEtcHosts },
+ description: "Deprecated. Do not use the OS-provided hosts.",
+ longName: "no-etc-hosts",
+ shortName: "",
+}, {
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.localFrontend = true; return o, nil },
effect: nil,
- serialize: func(o options) []string { return boolSliceOrNil(o.localFrontend) },
-}
+ serialize: func(o options) (val string, ok bool) { return "", o.localFrontend },
+ description: "Use local frontend directories.",
+ longName: "local-frontend",
+ shortName: "",
+}, {
+ updateWithValue: nil,
+ updateNoValue: func(o options) (options, error) { o.verbose = true; return o, nil },
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) { return "", o.verbose },
+ description: "Enable verbose output.",
+ longName: "verbose",
+ shortName: "v",
+}, {
+ updateWithValue: nil,
+ updateNoValue: func(o options) (options, error) { o.glinetMode = true; return o, nil },
+ effect: nil,
+ serialize: func(o options) (val string, ok bool) { return "", o.glinetMode },
+ description: "Run in GL-Inet compatibility mode.",
+ longName: "glinet",
+ shortName: "",
+}, {
+ updateWithValue: nil,
+ updateNoValue: nil,
+ effect: func(o options, exec string) (effect, error) {
+ return func() error {
+ if o.verbose {
+ fmt.Println(version.Verbose())
+ } else {
+ fmt.Println(version.Full())
+ }
-func init() {
- args = []arg{
- configArg,
- workDirArg,
- hostArg,
- portArg,
- serviceArg,
- logfileArg,
- pidfileArg,
- checkConfigArg,
- noCheckUpdateArg,
- disableMemoryOptimizationArg,
- noEtcHostsArg,
- localFrontendArg,
- verboseArg,
- glinetArg,
- versionArg,
- helpArg,
- }
-}
+ os.Exit(0)
-func getUsageLines(exec string, args []arg) []string {
- usage := []string{
- "Usage:",
- "",
- fmt.Sprintf("%s [options]", exec),
- "",
- "Options:",
- }
- for _, arg := range args {
+ return nil
+ }, nil
+ },
+ serialize: func(o options) (val string, ok bool) { return "", false },
+ description: "Show the version and exit. Show more detailed version description with -v.",
+ longName: "version",
+ shortName: "",
+}}
+
+// printHelp prints the entire help message. It exits with an error code if
+// there are any I/O errors.
+func printHelp(exec string) {
+ b := &strings.Builder{}
+
+ stringutil.WriteToBuilder(
+ b,
+ "Usage:\n\n",
+ fmt.Sprintf("%s [options]\n\n", exec),
+ "Options:\n",
+ )
+
+ var err error
+ for _, opt := range cmdLineOpts {
val := ""
- if arg.updateWithValue != nil {
+ if opt.updateWithValue != nil {
val = " VALUE"
}
- if arg.shortName != "" {
- usage = append(usage, fmt.Sprintf(" -%s, %-30s %s",
- arg.shortName,
- "--"+arg.longName+val,
- arg.description))
+
+ longDesc := opt.longName + val
+ if opt.shortName != "" {
+ _, err = fmt.Fprintf(b, " -%s, --%-28s %s\n", opt.shortName, longDesc, opt.description)
} else {
- usage = append(usage, fmt.Sprintf(" %-34s %s",
- "--"+arg.longName+val,
- arg.description))
+ _, err = fmt.Fprintf(b, " --%-32s %s\n", longDesc, opt.description)
+ }
+
+ if err != nil {
+ // The only error here can be from incorrect Fprintf usage, which is
+ // a programmer error.
+ panic(err)
}
}
- return usage
+
+ _, err = fmt.Print(b)
+ if err != nil {
+ // Exit immediately, since not being able to print out a help message
+ // essentially means that the I/O is very broken at the moment.
+ exitWithError()
+ }
}
-func printHelp(exec string) error {
- for _, line := range getUsageLines(exec, args) {
- _, err := fmt.Println(line)
+// parseCmdOpts parses the command-line arguments into options and effects.
+func parseCmdOpts(cmdName string, args []string) (o options, eff effect, err error) {
+ // Don't use range since the loop changes the loop variable.
+ argsLen := len(args)
+ for i := 0; i < len(args); i++ {
+ arg := args[i]
+ isKnown := false
+ for _, opt := range cmdLineOpts {
+ isKnown = argMatches(opt, arg)
+ if !isKnown {
+ continue
+ }
+
+ if opt.updateWithValue != nil {
+ i++
+ if i >= argsLen {
+ return o, eff, fmt.Errorf("got %s without argument", arg)
+ }
+
+ o, err = opt.updateWithValue(o, args[i])
+ } else {
+ o, eff, err = updateOptsNoValue(o, eff, opt, cmdName)
+ }
+
+ if err != nil {
+ return o, eff, fmt.Errorf("applying option %s: %w", arg, err)
+ }
+
+ break
+ }
+
+ if !isKnown {
+ return o, eff, fmt.Errorf("unknown option %s", arg)
+ }
+ }
+
+ return o, eff, err
+}
+
+// argMatches returns true if arg matches command-line option opt.
+func argMatches(opt cmdLineOpt, arg string) (ok bool) {
+ if arg == "" || arg[0] != '-' {
+ return false
+ }
+
+ arg = arg[1:]
+ if arg == "" {
+ return false
+ }
+
+ return (opt.shortName != "" && arg == opt.shortName) ||
+ (arg[0] == '-' && arg[1:] == opt.longName)
+}
+
+// updateOptsNoValue sets values or effects from opt into o or prev.
+func updateOptsNoValue(
+ o options,
+ prev effect,
+ opt cmdLineOpt,
+ cmdName string,
+) (updated options, chained effect, err error) {
+ if opt.updateNoValue != nil {
+ o, err = opt.updateNoValue(o)
+ if err != nil {
+ return o, prev, err
+ }
+
+ return o, prev, nil
+ }
+
+ next, err := opt.effect(o, cmdName)
+ if err != nil {
+ return o, prev, err
+ }
+
+ chained = chainEffect(prev, next)
+
+ return o, chained, nil
+}
+
+// chainEffect chans the next effect after the prev one. If prev is nil, eff
+// only calls next. If next is nil, eff is prev; if prev is nil, eff is next.
+func chainEffect(prev, next effect) (eff effect) {
+ if prev == nil {
+ return next
+ } else if next == nil {
+ return prev
+ }
+
+ eff = func() (err error) {
+ err = prev()
if err != nil {
return err
}
+
+ return next()
}
- return nil
+
+ return eff
}
-func argMatches(a arg, v string) bool {
- return v == "--"+a.longName || (a.shortName != "" && v == "-"+a.shortName)
-}
-
-func parse(exec string, ss []string) (o options, f effect, err error) {
- for i := 0; i < len(ss); i++ {
- v := ss[i]
- knownParam := false
- for _, arg := range args {
- if argMatches(arg, v) {
- if arg.updateWithValue != nil {
- if i+1 >= len(ss) {
- return o, f, fmt.Errorf("got %s without argument", v)
- }
- i++
- o, err = arg.updateWithValue(o, ss[i])
- if err != nil {
- return
- }
- } else if arg.updateNoValue != nil {
- o, err = arg.updateNoValue(o)
- if err != nil {
- return
- }
- } else if arg.effect != nil {
- var eff effect
- eff, err = arg.effect(o, exec)
- if err != nil {
- return
- }
- if eff != nil {
- prevf := f
- f = func() (ferr error) {
- if prevf != nil {
- ferr = prevf()
- }
- if ferr == nil {
- ferr = eff()
- }
- return ferr
- }
- }
- }
- knownParam = true
- break
- }
+// optsToArgs converts command line options into a list of arguments.
+func optsToArgs(o options) (args []string) {
+ for _, opt := range cmdLineOpts {
+ val, ok := opt.serialize(o)
+ if !ok {
+ continue
}
- if !knownParam {
- return o, f, fmt.Errorf("unknown option %v", v)
+
+ if opt.shortName != "" {
+ args = append(args, "-"+opt.shortName)
+ } else {
+ args = append(args, "--"+opt.longName)
+ }
+
+ if val != "" {
+ args = append(args, val)
}
}
- return
-}
-
-func shortestFlag(a arg) string {
- if a.shortName != "" {
- return "-" + a.shortName
- }
- return "--" + a.longName
-}
-
-func serialize(o options) []string {
- ss := []string{}
- for _, arg := range args {
- s := arg.serialize(o)
- if s != nil {
- ss = append(ss, append([]string{shortestFlag(arg)}, s...)...)
- }
- }
- return ss
+ return args
}
diff --git a/internal/home/options_test.go b/internal/home/options_test.go
index 21972b0a..32b4243a 100644
--- a/internal/home/options_test.go
+++ b/internal/home/options_test.go
@@ -2,7 +2,7 @@ package home
import (
"fmt"
- "net"
+ "net/netip"
"testing"
"github.com/stretchr/testify/assert"
@@ -12,7 +12,7 @@ import (
func testParseOK(t *testing.T, ss ...string) options {
t.Helper()
- o, _, err := parse("", ss)
+ o, _, err := parseCmdOpts("", ss)
require.NoError(t, err)
return o
@@ -21,7 +21,7 @@ func testParseOK(t *testing.T, ss ...string) options {
func testParseErr(t *testing.T, descr string, ss ...string) {
t.Helper()
- _, _, err := parse("", ss)
+ _, _, err := parseCmdOpts("", ss)
require.Error(t, err)
}
@@ -38,11 +38,11 @@ func TestParseVerbose(t *testing.T) {
}
func TestParseConfigFilename(t *testing.T) {
- assert.Equal(t, "", testParseOK(t).configFilename, "empty is no config filename")
- assert.Equal(t, "path", testParseOK(t, "-c", "path").configFilename, "-c is config filename")
+ assert.Equal(t, "", testParseOK(t).confFilename, "empty is no config filename")
+ assert.Equal(t, "path", testParseOK(t, "-c", "path").confFilename, "-c is config filename")
testParseParamMissing(t, "-c")
- assert.Equal(t, "path", testParseOK(t, "--config", "path").configFilename, "--config is config filename")
+ assert.Equal(t, "path", testParseOK(t, "--config", "path").confFilename, "--config is config filename")
testParseParamMissing(t, "--config")
}
@@ -56,11 +56,13 @@ func TestParseWorkDir(t *testing.T) {
}
func TestParseBindHost(t *testing.T) {
- assert.Nil(t, testParseOK(t).bindHost, "empty is not host")
- assert.Equal(t, net.IPv4(1, 2, 3, 4), testParseOK(t, "-h", "1.2.3.4").bindHost, "-h is host")
+ wantAddr := netip.MustParseAddr("1.2.3.4")
+
+ assert.Zero(t, testParseOK(t).bindHost, "empty is not host")
+ assert.Equal(t, wantAddr, testParseOK(t, "-h", "1.2.3.4").bindHost, "-h is host")
testParseParamMissing(t, "-h")
- assert.Equal(t, net.IPv4(1, 2, 3, 4), testParseOK(t, "--host", "1.2.3.4").bindHost, "--host is host")
+ assert.Equal(t, wantAddr, testParseOK(t, "--host", "1.2.3.4").bindHost, "--host is host")
testParseParamMissing(t, "--host")
}
@@ -103,7 +105,7 @@ func TestParseDisableUpdate(t *testing.T) {
// TODO(e.burkov): Remove after v0.108.0.
func TestParseDisableMemoryOptimization(t *testing.T) {
- o, eff, err := parse("", []string{"--no-mem-optimization"})
+ o, eff, err := parseCmdOpts("", []string{"--no-mem-optimization"})
require.NoError(t, err)
assert.Nil(t, eff)
@@ -130,73 +132,73 @@ func TestParseUnknown(t *testing.T) {
testParseErr(t, "unknown dash", "-")
}
-func TestSerialize(t *testing.T) {
+func TestOptsToArgs(t *testing.T) {
testCases := []struct {
name string
+ args []string
opts options
- ss []string
}{{
name: "empty",
+ args: []string{},
opts: options{},
- ss: []string{},
}, {
name: "config_filename",
- opts: options{configFilename: "path"},
- ss: []string{"-c", "path"},
+ args: []string{"-c", "path"},
+ opts: options{confFilename: "path"},
}, {
name: "work_dir",
+ args: []string{"-w", "path"},
opts: options{workDir: "path"},
- ss: []string{"-w", "path"},
}, {
name: "bind_host",
- opts: options{bindHost: net.IP{1, 2, 3, 4}},
- ss: []string{"-h", "1.2.3.4"},
+ opts: options{bindHost: netip.MustParseAddr("1.2.3.4")},
+ args: []string{"-h", "1.2.3.4"},
}, {
name: "bind_port",
+ args: []string{"-p", "666"},
opts: options{bindPort: 666},
- ss: []string{"-p", "666"},
}, {
name: "log_file",
+ args: []string{"-l", "path"},
opts: options{logFile: "path"},
- ss: []string{"-l", "path"},
}, {
name: "pid_file",
+ args: []string{"--pidfile", "path"},
opts: options{pidFile: "path"},
- ss: []string{"--pidfile", "path"},
}, {
name: "disable_update",
+ args: []string{"--no-check-update"},
opts: options{disableUpdate: true},
- ss: []string{"--no-check-update"},
}, {
name: "control_action",
+ args: []string{"-s", "run"},
opts: options{serviceControlAction: "run"},
- ss: []string{"-s", "run"},
}, {
name: "glinet_mode",
+ args: []string{"--glinet"},
opts: options{glinetMode: true},
- ss: []string{"--glinet"},
}, {
name: "multiple",
- opts: options{
- serviceControlAction: "run",
- configFilename: "config",
- workDir: "work",
- pidFile: "pid",
- disableUpdate: true,
- },
- ss: []string{
+ args: []string{
"-c", "config",
"-w", "work",
"-s", "run",
"--pidfile", "pid",
"--no-check-update",
},
+ opts: options{
+ serviceControlAction: "run",
+ confFilename: "config",
+ workDir: "work",
+ pidFile: "pid",
+ disableUpdate: true,
+ },
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- result := serialize(tc.opts)
- assert.ElementsMatch(t, tc.ss, result)
+ result := optsToArgs(tc.opts)
+ assert.ElementsMatch(t, tc.args, result)
})
}
}
diff --git a/internal/home/rdns.go b/internal/home/rdns.go
index 924aff37..e44000b3 100644
--- a/internal/home/rdns.go
+++ b/internal/home/rdns.go
@@ -101,7 +101,7 @@ func (r *RDNS) isCached(ip net.IP) (ok bool) {
func (r *RDNS) Begin(ip net.IP) {
r.ensurePrivateCache()
- if r.isCached(ip) || r.clients.Exists(ip, ClientSourceRDNS) {
+ if r.isCached(ip) || r.clients.exists(ip, ClientSourceRDNS) {
return
}
diff --git a/internal/home/rdns_test.go b/internal/home/rdns_test.go
index 870d0f04..9f90ce5a 100644
--- a/internal/home/rdns_test.go
+++ b/internal/home/rdns_test.go
@@ -5,6 +5,7 @@ import (
"encoding/binary"
"fmt"
"net"
+ "net/netip"
"sync"
"testing"
"time"
@@ -88,7 +89,7 @@ func TestRDNS_Begin(t *testing.T) {
clients: &clientsContainer{
list: map[string]*Client{},
idIndex: tc.cliIDIndex,
- ipToRC: netutil.NewIPMap(0),
+ ipToRC: map[netip.Addr]*RuntimeClient{},
allTags: stringutil.NewSet(),
},
}
@@ -188,13 +189,11 @@ func TestRDNS_WorkerLoop(t *testing.T) {
locUpstream := &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "local.upstream.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
- resp = aghalg.Coalesce(
- aghtest.RespondTo(t, req, dns.ClassINET, dns.TypePTR, revIPv4, "local.domain"),
- aghtest.RespondTo(t, req, dns.ClassINET, dns.TypePTR, revIPv6, "ipv6.domain"),
+ return aghalg.Coalesce(
+ aghtest.MatchedResponse(req, dns.TypePTR, revIPv4, "local.domain"),
+ aghtest.MatchedResponse(req, dns.TypePTR, revIPv6, "ipv6.domain"),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
- )
-
- return resp, nil
+ ), nil
},
}
@@ -228,7 +227,7 @@ func TestRDNS_WorkerLoop(t *testing.T) {
cc := &clientsContainer{
list: map[string]*Client{},
idIndex: map[string]*Client{},
- ipToRC: netutil.NewIPMap(0),
+ ipToRC: map[netip.Addr]*RuntimeClient{},
allTags: stringutil.NewSet(),
}
ch := make(chan net.IP)
@@ -258,7 +257,7 @@ func TestRDNS_WorkerLoop(t *testing.T) {
return
}
- assert.True(t, cc.Exists(tc.cliIP, ClientSourceRDNS))
+ assert.True(t, cc.exists(tc.cliIP, ClientSourceRDNS))
})
}
}
diff --git a/internal/home/service.go b/internal/home/service.go
index e52f9799..3aece1f2 100644
--- a/internal/home/service.go
+++ b/internal/home/service.go
@@ -197,7 +197,7 @@ func handleServiceControlAction(opts options, clientBuildFS fs.FS) {
DisplayName: serviceDisplayName,
Description: serviceDescription,
WorkingDirectory: pwd,
- Arguments: serialize(runOpts),
+ Arguments: optsToArgs(runOpts),
}
configureService(svcConfig)
diff --git a/internal/home/tls.go b/internal/home/tls.go
index a5089bd8..7fdd64d8 100644
--- a/internal/home/tls.go
+++ b/internal/home/tls.go
@@ -14,230 +14,275 @@ import (
"fmt"
"net/http"
"os"
- "path/filepath"
- "runtime"
"strings"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
+ "github.com/AdguardTeam/AdGuardHome/internal/aghtls"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/google/go-cmp/cmp"
)
-var tlsWebHandlersRegistered = false
+// tlsManager contains the current configuration and state of AdGuard Home TLS
+// encryption.
+type tlsManager struct {
+ // status is the current status of the configuration. It is never nil.
+ status *tlsConfigStatus
-// TLSMod - TLS module object
-type TLSMod struct {
- certLastMod time.Time // last modification time of the certificate file
- status tlsConfigStatus
- confLock sync.Mutex
- conf tlsConfigSettings
+ // certLastMod is the last modification time of the certificate file.
+ certLastMod time.Time
+
+ confLock sync.Mutex
+ conf tlsConfigSettings
}
-// Create TLS module
-func tlsCreate(conf tlsConfigSettings) *TLSMod {
- t := &TLSMod{}
- t.conf = conf
- if t.conf.Enabled {
- if !t.load() {
- // Something is not valid - return an empty TLS config
- return &TLSMod{conf: tlsConfigSettings{
- Enabled: conf.Enabled,
- ServerName: conf.ServerName,
- PortHTTPS: conf.PortHTTPS,
- PortDNSOverTLS: conf.PortDNSOverTLS,
- PortDNSOverQUIC: conf.PortDNSOverQUIC,
- AllowUnencryptedDoH: conf.AllowUnencryptedDoH,
- }}
+// newTLSManager initializes the TLS configuration.
+func newTLSManager(conf tlsConfigSettings) (m *tlsManager, err error) {
+ m = &tlsManager{
+ status: &tlsConfigStatus{},
+ conf: conf,
+ }
+
+ if m.conf.Enabled {
+ err = m.load()
+ if err != nil {
+ return nil, err
}
- t.setCertFileTime()
+
+ m.setCertFileTime()
}
- return t
+
+ return m, nil
}
-func (t *TLSMod) load() bool {
- if !tlsLoadConfig(&t.conf, &t.status) {
- log.Error("failed to load TLS config: %s", t.status.WarningValidation)
- return false
+// load reloads the TLS configuration from files or data from the config file.
+func (m *tlsManager) load() (err error) {
+ err = loadTLSConf(&m.conf, m.status)
+ if err != nil {
+ return fmt.Errorf("loading config: %w", err)
}
- // validate current TLS config and update warnings (it could have been loaded from file)
- data := validateCertificates(string(t.conf.CertificateChainData), string(t.conf.PrivateKeyData), t.conf.ServerName)
- if !data.ValidPair {
- log.Error("failed to validate certificate: %s", data.WarningValidation)
- return false
- }
- t.status = data
- return true
-}
-
-// Close - close module
-func (t *TLSMod) Close() {
+ return nil
}
// WriteDiskConfig - write config
-func (t *TLSMod) WriteDiskConfig(conf *tlsConfigSettings) {
- t.confLock.Lock()
- *conf = t.conf
- t.confLock.Unlock()
+func (m *tlsManager) WriteDiskConfig(conf *tlsConfigSettings) {
+ m.confLock.Lock()
+ *conf = m.conf
+ m.confLock.Unlock()
}
-func (t *TLSMod) setCertFileTime() {
- if len(t.conf.CertificatePath) == 0 {
+// setCertFileTime sets t.certLastMod from the certificate. If there are
+// errors, setCertFileTime logs them.
+func (m *tlsManager) setCertFileTime() {
+ if len(m.conf.CertificatePath) == 0 {
return
}
- fi, err := os.Stat(t.conf.CertificatePath)
+
+ fi, err := os.Stat(m.conf.CertificatePath)
if err != nil {
- log.Error("TLS: %s", err)
+ log.Error("tls: looking up certificate path: %s", err)
+
return
}
- t.certLastMod = fi.ModTime().UTC()
+
+ m.certLastMod = fi.ModTime().UTC()
}
-// Start updates the configuration of TLSMod and starts it.
-func (t *TLSMod) Start() {
- if !tlsWebHandlersRegistered {
- tlsWebHandlersRegistered = true
- t.registerWebHandlers()
- }
+// start updates the configuration of t and starts it.
+func (m *tlsManager) start() {
+ m.registerWebHandlers()
- t.confLock.Lock()
- tlsConf := t.conf
- t.confLock.Unlock()
+ m.confLock.Lock()
+ tlsConf := m.conf
+ m.confLock.Unlock()
- // The background context is used because the TLSConfigChanged wraps
- // context with timeout on its own and shuts down the server, which
- // handles current request.
+ // The background context is used because the TLSConfigChanged wraps context
+ // with timeout on its own and shuts down the server, which handles current
+ // request.
Context.web.TLSConfigChanged(context.Background(), tlsConf)
}
-// Reload updates the configuration of TLSMod and restarts it.
-func (t *TLSMod) Reload() {
- t.confLock.Lock()
- tlsConf := t.conf
- t.confLock.Unlock()
+// reload updates the configuration and restarts t.
+func (m *tlsManager) reload() {
+ m.confLock.Lock()
+ tlsConf := m.conf
+ m.confLock.Unlock()
if !tlsConf.Enabled || len(tlsConf.CertificatePath) == 0 {
return
}
+
fi, err := os.Stat(tlsConf.CertificatePath)
if err != nil {
- log.Error("TLS: %s", err)
- return
- }
- if fi.ModTime().UTC().Equal(t.certLastMod) {
- log.Debug("TLS: certificate file isn't modified")
- return
- }
- log.Debug("TLS: certificate file is modified")
+ log.Error("tls: %s", err)
- t.confLock.Lock()
- r := t.load()
- t.confLock.Unlock()
- if !r {
return
}
- t.certLastMod = fi.ModTime().UTC()
+ if fi.ModTime().UTC().Equal(m.certLastMod) {
+ log.Debug("tls: certificate file isn't modified")
+
+ return
+ }
+
+ log.Debug("tls: certificate file is modified")
+
+ m.confLock.Lock()
+ err = m.load()
+ m.confLock.Unlock()
+ if err != nil {
+ log.Error("tls: reloading: %s", err)
+
+ return
+ }
+
+ m.certLastMod = fi.ModTime().UTC()
_ = reconfigureDNSServer()
- t.confLock.Lock()
- tlsConf = t.conf
- t.confLock.Unlock()
- // The background context is used because the TLSConfigChanged wraps
- // context with timeout on its own and shuts down the server, which
- // handles current request.
+ m.confLock.Lock()
+ tlsConf = m.conf
+ m.confLock.Unlock()
+
+ // The background context is used because the TLSConfigChanged wraps context
+ // with timeout on its own and shuts down the server, which handles current
+ // request.
Context.web.TLSConfigChanged(context.Background(), tlsConf)
}
-// Set certificate and private key data
-func tlsLoadConfig(tls *tlsConfigSettings, status *tlsConfigStatus) bool {
- tls.CertificateChainData = []byte(tls.CertificateChain)
- tls.PrivateKeyData = []byte(tls.PrivateKey)
-
- var err error
- if tls.CertificatePath != "" {
- if tls.CertificateChain != "" {
- status.WarningValidation = "certificate data and file can't be set together"
- return false
- }
- tls.CertificateChainData, err = os.ReadFile(tls.CertificatePath)
+// loadTLSConf loads and validates the TLS configuration. The returned error is
+// also set in status.WarningValidation.
+func loadTLSConf(tlsConf *tlsConfigSettings, status *tlsConfigStatus) (err error) {
+ defer func() {
if err != nil {
status.WarningValidation = err.Error()
- return false
+ if status.ValidCert && status.ValidKey && status.ValidPair {
+ // Do not return warnings since those aren't critical.
+ err = nil
+ }
}
+ }()
+
+ tlsConf.CertificateChainData = []byte(tlsConf.CertificateChain)
+ tlsConf.PrivateKeyData = []byte(tlsConf.PrivateKey)
+
+ if tlsConf.CertificatePath != "" {
+ if tlsConf.CertificateChain != "" {
+ return errors.Error("certificate data and file can't be set together")
+ }
+
+ tlsConf.CertificateChainData, err = os.ReadFile(tlsConf.CertificatePath)
+ if err != nil {
+ return fmt.Errorf("reading cert file: %w", err)
+ }
+
+ // Set status.ValidCert to true to signal the frontend that the
+ // certificate opens successfully while the private key can't be opened.
status.ValidCert = true
}
- if tls.PrivateKeyPath != "" {
- if tls.PrivateKey != "" {
- status.WarningValidation = "private key data and file can't be set together"
- return false
+ if tlsConf.PrivateKeyPath != "" {
+ if tlsConf.PrivateKey != "" {
+ return errors.Error("private key data and file can't be set together")
}
- tls.PrivateKeyData, err = os.ReadFile(tls.PrivateKeyPath)
+
+ tlsConf.PrivateKeyData, err = os.ReadFile(tlsConf.PrivateKeyPath)
if err != nil {
- status.WarningValidation = err.Error()
- return false
+ return fmt.Errorf("reading key file: %w", err)
}
+
status.ValidKey = true
}
- return true
+ err = validateCertificates(
+ status,
+ tlsConf.CertificateChainData,
+ tlsConf.PrivateKeyData,
+ tlsConf.ServerName,
+ )
+ if err != nil {
+ return fmt.Errorf("validating certificate pair: %w", err)
+ }
+
+ return nil
}
+// tlsConfigStatus contains the status of a certificate chain and key pair.
type tlsConfigStatus struct {
- ValidCert bool `json:"valid_cert"` // ValidCert is true if the specified certificates chain is a valid chain of X509 certificates
- ValidChain bool `json:"valid_chain"` // ValidChain is true if the specified certificates chain is verified and issued by a known CA
- Subject string `json:"subject,omitempty"` // Subject is the subject of the first certificate in the chain
- Issuer string `json:"issuer,omitempty"` // Issuer is the issuer of the first certificate in the chain
- NotBefore time.Time `json:"not_before,omitempty"` // NotBefore is the NotBefore field of the first certificate in the chain
- NotAfter time.Time `json:"not_after,omitempty"` // NotAfter is the NotAfter field of the first certificate in the chain
- DNSNames []string `json:"dns_names"` // DNSNames is the value of SubjectAltNames field of the first certificate in the chain
+ // Subject is the subject of the first certificate in the chain.
+ Subject string `json:"subject,omitempty"`
- // key status
- ValidKey bool `json:"valid_key"` // ValidKey is true if the key is a valid private key
- KeyType string `json:"key_type,omitempty"` // KeyType is one of RSA or ECDSA
+ // Issuer is the issuer of the first certificate in the chain.
+ Issuer string `json:"issuer,omitempty"`
- // is usable? set by validator
- ValidPair bool `json:"valid_pair"` // ValidPair is true if both certificate and private key are correct
+ // KeyType is the type of the private key.
+ KeyType string `json:"key_type,omitempty"`
- // warnings
- WarningValidation string `json:"warning_validation,omitempty"` // WarningValidation is a validation warning message with the issue description
+ // NotBefore is the NotBefore field of the first certificate in the chain.
+ NotBefore time.Time `json:"not_before,omitempty"`
+
+ // NotAfter is the NotAfter field of the first certificate in the chain.
+ NotAfter time.Time `json:"not_after,omitempty"`
+
+ // WarningValidation is a validation warning message with the issue
+ // description.
+ WarningValidation string `json:"warning_validation,omitempty"`
+
+ // DNSNames is the value of SubjectAltNames field of the first certificate
+ // in the chain.
+ DNSNames []string `json:"dns_names"`
+
+ // ValidCert is true if the specified certificate chain is a valid chain of
+ // X509 certificates.
+ ValidCert bool `json:"valid_cert"`
+
+ // ValidChain is true if the specified certificate chain is verified and
+ // issued by a known CA.
+ ValidChain bool `json:"valid_chain"`
+
+ // ValidKey is true if the key is a valid private key.
+ ValidKey bool `json:"valid_key"`
+
+ // ValidPair is true if both certificate and private key are correct for
+ // each other.
+ ValidPair bool `json:"valid_pair"`
}
-// field ordering is important -- yaml fields will mirror ordering from here
+// tlsConfig is the TLS configuration and status response.
type tlsConfig struct {
- tlsConfigStatus `json:",inline"`
+ *tlsConfigStatus `json:",inline"`
tlsConfigSettingsExt `json:",inline"`
}
-// tlsConfigSettingsExt is used to (un)marshal PrivateKeySaved to ensure that
-// clients don't send and receive previously saved private keys.
+// tlsConfigSettingsExt is used to (un)marshal the PrivateKeySaved field to
+// ensure that clients don't send and receive previously saved private keys.
type tlsConfigSettingsExt struct {
tlsConfigSettings `json:",inline"`
- // If private key saved as a string, we set this flag to true
- // and omit key from answer.
+
+ // PrivateKeySaved is true if the private key is saved as a string and omit
+ // key from answer.
PrivateKeySaved bool `yaml:"-" json:"private_key_saved,inline"`
}
-func (t *TLSMod) handleTLSStatus(w http.ResponseWriter, r *http.Request) {
- t.confLock.Lock()
+func (m *tlsManager) handleTLSStatus(w http.ResponseWriter, r *http.Request) {
+ m.confLock.Lock()
data := tlsConfig{
tlsConfigSettingsExt: tlsConfigSettingsExt{
- tlsConfigSettings: t.conf,
+ tlsConfigSettings: m.conf,
},
- tlsConfigStatus: t.status,
+ tlsConfigStatus: m.status,
}
- t.confLock.Unlock()
+ m.confLock.Unlock()
+
marshalTLS(w, r, data)
}
-func (t *TLSMod) handleTLSValidate(w http.ResponseWriter, r *http.Request) {
+func (m *tlsManager) handleTLSValidate(w http.ResponseWriter, r *http.Request) {
setts, err := unmarshalTLS(r)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err)
@@ -246,7 +291,7 @@ func (t *TLSMod) handleTLSValidate(w http.ResponseWriter, r *http.Request) {
}
if setts.PrivateKeySaved {
- setts.PrivateKey = t.conf.PrivateKey
+ setts.PrivateKey = m.conf.PrivateKey
}
if setts.Enabled {
@@ -278,75 +323,74 @@ func (t *TLSMod) handleTLSValidate(w http.ResponseWriter, r *http.Request) {
return
}
- status := tlsConfigStatus{}
- if tlsLoadConfig(&setts.tlsConfigSettings, &status) {
- status = validateCertificates(string(setts.CertificateChainData), string(setts.PrivateKeyData), setts.ServerName)
- }
-
- data := tlsConfig{
+ // Skip the error check, since we are only interested in the value of
+ // status.WarningValidation.
+ status := &tlsConfigStatus{}
+ _ = loadTLSConf(&setts.tlsConfigSettings, status)
+ resp := tlsConfig{
tlsConfigSettingsExt: setts,
tlsConfigStatus: status,
}
- marshalTLS(w, r, data)
+ marshalTLS(w, r, resp)
}
-func (t *TLSMod) setConfig(newConf tlsConfigSettings, status tlsConfigStatus) (restartHTTPS bool) {
- t.confLock.Lock()
- defer t.confLock.Unlock()
+func (m *tlsManager) setConfig(newConf tlsConfigSettings, status *tlsConfigStatus) (restartHTTPS bool) {
+ m.confLock.Lock()
+ defer m.confLock.Unlock()
// Reset the DNSCrypt data before comparing, since we currently do not
// accept these from the frontend.
//
// TODO(a.garipov): Define a custom comparer for dnsforward.TLSConfig.
- newConf.DNSCryptConfigFile = t.conf.DNSCryptConfigFile
- newConf.PortDNSCrypt = t.conf.PortDNSCrypt
- if !cmp.Equal(t.conf, newConf, cmp.AllowUnexported(dnsforward.TLSConfig{})) {
+ newConf.DNSCryptConfigFile = m.conf.DNSCryptConfigFile
+ newConf.PortDNSCrypt = m.conf.PortDNSCrypt
+ if !cmp.Equal(m.conf, newConf, cmp.AllowUnexported(dnsforward.TLSConfig{})) {
log.Info("tls config has changed, restarting https server")
restartHTTPS = true
} else {
- log.Info("tls config has not changed")
+ log.Info("tls: config has not changed")
}
// Note: don't do just `t.conf = data` because we must preserve all other members of t.conf
- t.conf.Enabled = newConf.Enabled
- t.conf.ServerName = newConf.ServerName
- t.conf.ForceHTTPS = newConf.ForceHTTPS
- t.conf.PortHTTPS = newConf.PortHTTPS
- t.conf.PortDNSOverTLS = newConf.PortDNSOverTLS
- t.conf.PortDNSOverQUIC = newConf.PortDNSOverQUIC
- t.conf.CertificateChain = newConf.CertificateChain
- t.conf.CertificatePath = newConf.CertificatePath
- t.conf.CertificateChainData = newConf.CertificateChainData
- t.conf.PrivateKey = newConf.PrivateKey
- t.conf.PrivateKeyPath = newConf.PrivateKeyPath
- t.conf.PrivateKeyData = newConf.PrivateKeyData
- t.status = status
+ m.conf.Enabled = newConf.Enabled
+ m.conf.ServerName = newConf.ServerName
+ m.conf.ForceHTTPS = newConf.ForceHTTPS
+ m.conf.PortHTTPS = newConf.PortHTTPS
+ m.conf.PortDNSOverTLS = newConf.PortDNSOverTLS
+ m.conf.PortDNSOverQUIC = newConf.PortDNSOverQUIC
+ m.conf.CertificateChain = newConf.CertificateChain
+ m.conf.CertificatePath = newConf.CertificatePath
+ m.conf.CertificateChainData = newConf.CertificateChainData
+ m.conf.PrivateKey = newConf.PrivateKey
+ m.conf.PrivateKeyPath = newConf.PrivateKeyPath
+ m.conf.PrivateKeyData = newConf.PrivateKeyData
+ m.status = status
return restartHTTPS
}
-func (t *TLSMod) handleTLSConfigure(w http.ResponseWriter, r *http.Request) {
- data, err := unmarshalTLS(r)
+func (m *tlsManager) handleTLSConfigure(w http.ResponseWriter, r *http.Request) {
+ req, err := unmarshalTLS(r)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err)
return
}
- if data.PrivateKeySaved {
- data.PrivateKey = t.conf.PrivateKey
+ if req.PrivateKeySaved {
+ req.PrivateKey = m.conf.PrivateKey
}
- if data.Enabled {
+ if req.Enabled {
err = validatePorts(
tcpPort(config.BindPort),
tcpPort(config.BetaBindPort),
- tcpPort(data.PortHTTPS),
- tcpPort(data.PortDNSOverTLS),
- tcpPort(data.PortDNSCrypt),
+ tcpPort(req.PortHTTPS),
+ tcpPort(req.PortDNSOverTLS),
+ tcpPort(req.PortDNSCrypt),
udpPort(config.DNS.Port),
- udpPort(data.PortDNSOverQUIC),
+ udpPort(req.PortDNSOverQUIC),
)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@@ -356,33 +400,33 @@ func (t *TLSMod) handleTLSConfigure(w http.ResponseWriter, r *http.Request) {
}
// TODO(e.burkov): Investigate and perhaps check other ports.
- if !webCheckPortAvailable(data.PortHTTPS) {
+ if !webCheckPortAvailable(req.PortHTTPS) {
aghhttp.Error(
r,
w,
http.StatusBadRequest,
- "port %d is not available, cannot enable HTTPS on it",
- data.PortHTTPS,
+ "port %d is not available, cannot enable https on it",
+ req.PortHTTPS,
)
return
}
- status := tlsConfigStatus{}
- if !tlsLoadConfig(&data.tlsConfigSettings, &status) {
- data2 := tlsConfig{
- tlsConfigSettingsExt: data,
- tlsConfigStatus: t.status,
+ status := &tlsConfigStatus{}
+ err = loadTLSConf(&req.tlsConfigSettings, status)
+ if err != nil {
+ resp := tlsConfig{
+ tlsConfigSettingsExt: req,
+ tlsConfigStatus: status,
}
- marshalTLS(w, r, data2)
+
+ marshalTLS(w, r, resp)
return
}
- status = validateCertificates(string(data.CertificateChainData), string(data.PrivateKeyData), data.ServerName)
-
- restartHTTPS := t.setConfig(data.tlsConfigSettings, status)
- t.setCertFileTime()
+ restartHTTPS := m.setConfig(req.tlsConfigSettings, status)
+ m.setCertFileTime()
onConfigModified()
err = reconfigureDNSServer()
@@ -392,12 +436,12 @@ func (t *TLSMod) handleTLSConfigure(w http.ResponseWriter, r *http.Request) {
return
}
- data2 := tlsConfig{
- tlsConfigSettingsExt: data,
- tlsConfigStatus: t.status,
+ resp := tlsConfig{
+ tlsConfigSettingsExt: req,
+ tlsConfigStatus: m.status,
}
- marshalTLS(w, r, data2)
+ marshalTLS(w, r, resp)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
@@ -408,7 +452,7 @@ func (t *TLSMod) handleTLSConfigure(w http.ResponseWriter, r *http.Request) {
// same reason.
if restartHTTPS {
go func() {
- Context.web.TLSConfigChanged(context.Background(), data.tlsConfigSettings)
+ Context.web.TLSConfigChanged(context.Background(), req.tlsConfigSettings)
}()
}
}
@@ -445,160 +489,171 @@ func validatePorts(
return nil
}
-func verifyCertChain(data *tlsConfigStatus, certChain, serverName string) error {
- log.Tracef("TLS: got certificate: %d bytes", len(certChain))
+// validateCertChain verifies certs using the first as the main one and others
+// as intermediate. srvName stands for the expected DNS name.
+func validateCertChain(certs []*x509.Certificate, srvName string) (err error) {
+ main, others := certs[0], certs[1:]
- // now do a more extended validation
- var certs []*pem.Block // PEM-encoded certificates
-
- pemblock := []byte(certChain)
- for {
- var decoded *pem.Block
- decoded, pemblock = pem.Decode(pemblock)
- if decoded == nil {
- break
- }
- if decoded.Type == "CERTIFICATE" {
- certs = append(certs, decoded)
- }
+ pool := x509.NewCertPool()
+ for _, cert := range others {
+ log.Info("tls: got an intermediate cert")
+ pool.AddCert(cert)
}
- var parsedCerts []*x509.Certificate
-
- for _, cert := range certs {
- parsed, err := x509.ParseCertificate(cert.Bytes)
- if err != nil {
- data.WarningValidation = fmt.Sprintf("Failed to parse certificate: %s", err)
- return errors.Error(data.WarningValidation)
- }
- parsedCerts = append(parsedCerts, parsed)
- }
-
- if len(parsedCerts) == 0 {
- data.WarningValidation = "You have specified an empty certificate"
- return errors.Error(data.WarningValidation)
- }
-
- data.ValidCert = true
-
- // spew.Dump(parsedCerts)
-
opts := x509.VerifyOptions{
- DNSName: serverName,
- Roots: Context.tlsRoots,
+ DNSName: srvName,
+ Roots: Context.tlsRoots,
+ Intermediates: pool,
}
-
- log.Printf("number of certs - %d", len(parsedCerts))
- if len(parsedCerts) > 1 {
- // set up an intermediate
- pool := x509.NewCertPool()
- for _, cert := range parsedCerts[1:] {
- log.Printf("got an intermediate cert")
- pool.AddCert(cert)
- }
- opts.Intermediates = pool
- }
-
- // TODO: save it as a warning rather than error it out -- shouldn't be a big problem
- mainCert := parsedCerts[0]
- _, err := mainCert.Verify(opts)
+ _, err = main.Verify(opts)
if err != nil {
- // let self-signed certs through
- data.WarningValidation = fmt.Sprintf("Your certificate does not verify: %s", err)
- } else {
- data.ValidChain = true
- }
- // spew.Dump(chains)
-
- // update status
- if mainCert != nil {
- notAfter := mainCert.NotAfter
- data.Subject = mainCert.Subject.String()
- data.Issuer = mainCert.Issuer.String()
- data.NotAfter = notAfter
- data.NotBefore = mainCert.NotBefore
- data.DNSNames = mainCert.DNSNames
+ return fmt.Errorf("certificate does not verify: %w", err)
}
return nil
}
-func validatePkey(data *tlsConfigStatus, pkey string) error {
- // now do a more extended validation
- var key *pem.Block // PEM-encoded certificates
+// parseCertChain parses the certificate chain from raw data, and returns it.
+// If ok is true, the returned error, if any, is not critical.
+func parseCertChain(chain []byte) (parsedCerts []*x509.Certificate, ok bool, err error) {
+ log.Debug("tls: got certificate chain: %d bytes", len(chain))
- // go through all pem blocks, but take first valid pem block and drop the rest
- pemblock := []byte(pkey)
- for {
- var decoded *pem.Block
- decoded, pemblock = pem.Decode(pemblock)
- if decoded == nil {
- break
+ var certs []*pem.Block
+ for decoded, pemblock := pem.Decode(chain); decoded != nil; {
+ if decoded.Type == "CERTIFICATE" {
+ certs = append(certs, decoded)
}
+ decoded, pemblock = pem.Decode(pemblock)
+ }
+
+ parsedCerts, err = parsePEMCerts(certs)
+ if err != nil {
+ return nil, false, err
+ }
+
+ log.Info("tls: number of certs: %d", len(parsedCerts))
+
+ if !aghtls.CertificateHasIP(parsedCerts[0]) {
+ err = errors.Error(`certificate has no IP addresses` +
+ `, this may cause issues with DNS-over-TLS clients`)
+ }
+
+ return parsedCerts, true, err
+}
+
+// parsePEMCerts parses multiple PEM-encoded certificates.
+func parsePEMCerts(certs []*pem.Block) (parsedCerts []*x509.Certificate, err error) {
+ for i, cert := range certs {
+ var parsed *x509.Certificate
+ parsed, err = x509.ParseCertificate(cert.Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("parsing certificate at index %d: %w", i, err)
+ }
+
+ parsedCerts = append(parsedCerts, parsed)
+ }
+
+ if len(parsedCerts) == 0 {
+ return nil, errors.Error("empty certificate")
+ }
+
+ return parsedCerts, nil
+}
+
+// validatePKey validates the private key, returning its type. It returns an
+// empty string if error occurs.
+func validatePKey(pkey []byte) (keyType string, err error) {
+ var key *pem.Block
+
+ // Go through all pem blocks, but take first valid pem block and drop the
+ // rest.
+ for decoded, pemblock := pem.Decode([]byte(pkey)); decoded != nil; {
if decoded.Type == "PRIVATE KEY" || strings.HasSuffix(decoded.Type, " PRIVATE KEY") {
key = decoded
break
}
+
+ decoded, pemblock = pem.Decode(pemblock)
}
if key == nil {
- data.WarningValidation = "No valid keys were found"
-
- return errors.Error(data.WarningValidation)
+ return "", errors.Error("no valid keys were found")
}
- // parse the decoded key
- _, keyType, err := parsePrivateKey(key.Bytes)
+ _, keyType, err = parsePrivateKey(key.Bytes)
if err != nil {
- data.WarningValidation = fmt.Sprintf("Failed to parse private key: %s", err)
-
- return errors.Error(data.WarningValidation)
- } else if keyType == keyTypeED25519 {
- data.WarningValidation = "ED25519 keys are not supported by browsers; " +
- "did you mean to use X25519 for key exchange?"
-
- return errors.Error(data.WarningValidation)
+ return "", fmt.Errorf("parsing private key: %w", err)
}
- data.ValidKey = true
- data.KeyType = keyType
+ if keyType == keyTypeED25519 {
+ return "", errors.Error(
+ "ED25519 keys are not supported by browsers; " +
+ "did you mean to use X25519 for key exchange?",
+ )
+ }
- return nil
+ return keyType, nil
}
-// validateCertificates processes certificate data and its private key. All
-// parameters are optional. On error, validateCertificates returns a partially
-// set object with field WarningValidation containing error description.
-func validateCertificates(certChain, pkey, serverName string) tlsConfigStatus {
- var data tlsConfigStatus
+// validateCertificates processes certificate data and its private key. status
+// must not be nil, since it's used to accumulate the validation results. Other
+// parameters are optional.
+func validateCertificates(
+ status *tlsConfigStatus,
+ certChain []byte,
+ pkey []byte,
+ serverName string,
+) (err error) {
+ // Check only the public certificate separately from the key.
+ if len(certChain) > 0 {
+ var certs []*x509.Certificate
+ certs, status.ValidCert, err = parseCertChain(certChain)
+ if !status.ValidCert {
+ // Don't wrap the error, since it's informative enough as is.
+ return err
+ }
- // check only public certificate separately from the key
- if certChain != "" {
- if verifyCertChain(&data, certChain, serverName) != nil {
- return data
+ mainCert := certs[0]
+ status.Subject = mainCert.Subject.String()
+ status.Issuer = mainCert.Issuer.String()
+ status.NotAfter = mainCert.NotAfter
+ status.NotBefore = mainCert.NotBefore
+ status.DNSNames = mainCert.DNSNames
+
+ if chainErr := validateCertChain(certs, serverName); chainErr != nil {
+ // Let self-signed certs through and don't return this error to set
+ // its message into the status.WarningValidation afterwards.
+ err = chainErr
+ } else {
+ status.ValidChain = true
}
}
- // validate private key (right now the only validation possible is just parsing it)
- if pkey != "" {
- if validatePkey(&data, pkey) != nil {
- return data
+ // Validate the private key by parsing it.
+ if len(pkey) > 0 {
+ var keyErr error
+ status.KeyType, keyErr = validatePKey(pkey)
+ if keyErr != nil {
+ // Don't wrap the error, since it's informative enough as is.
+ return keyErr
}
+
+ status.ValidKey = true
}
- // if both are set, validate both in unison
- if pkey != "" && certChain != "" {
- _, err := tls.X509KeyPair([]byte(certChain), []byte(pkey))
- if err != nil {
- data.WarningValidation = fmt.Sprintf("Invalid certificate or key: %s", err)
- return data
+ // If both are set, validate together.
+ if len(certChain) > 0 && len(pkey) > 0 {
+ _, pairErr := tls.X509KeyPair(certChain, pkey)
+ if pairErr != nil {
+ return fmt.Errorf("certificate-key pair: %w", pairErr)
}
- data.ValidPair = true
+
+ status.ValidPair = true
}
- return data
+ return err
}
// Key types.
@@ -693,52 +748,9 @@ func marshalTLS(w http.ResponseWriter, r *http.Request, data tlsConfig) {
_ = aghhttp.WriteJSONResponse(w, r, data)
}
-// registerWebHandlers registers HTTP handlers for TLS configuration
-func (t *TLSMod) registerWebHandlers() {
- httpRegister(http.MethodGet, "/control/tls/status", t.handleTLSStatus)
- httpRegister(http.MethodPost, "/control/tls/configure", t.handleTLSConfigure)
- httpRegister(http.MethodPost, "/control/tls/validate", t.handleTLSValidate)
-}
-
-// LoadSystemRootCAs tries to load root certificates from the operating system.
-// It returns nil in case nothing is found so that that Go.crypto will use it's
-// default algorithm to find system root CA list.
-//
-// See https://github.com/AdguardTeam/AdGuardHome/internal/issues/1311.
-func LoadSystemRootCAs() (roots *x509.CertPool) {
- // TODO(e.burkov): Use build tags instead.
- if runtime.GOOS != "linux" {
- return nil
- }
-
- // Directories with the system root certificates, which aren't supported
- // by Go.crypto.
- dirs := []string{
- // Entware.
- "/opt/etc/ssl/certs",
- }
- roots = x509.NewCertPool()
- for _, dir := range dirs {
- dirEnts, err := os.ReadDir(dir)
- if errors.Is(err, os.ErrNotExist) {
- continue
- } else if err != nil {
- log.Error("opening directory: %q: %s", dir, err)
- }
-
- var rootsAdded bool
- for _, de := range dirEnts {
- var certData []byte
- certData, err = os.ReadFile(filepath.Join(dir, de.Name()))
- if err == nil && roots.AppendCertsFromPEM(certData) {
- rootsAdded = true
- }
- }
-
- if rootsAdded {
- return roots
- }
- }
-
- return nil
+// registerWebHandlers registers HTTP handlers for TLS configuration.
+func (m *tlsManager) registerWebHandlers() {
+ httpRegister(http.MethodGet, "/control/tls/status", m.handleTLSStatus)
+ httpRegister(http.MethodPost, "/control/tls/configure", m.handleTLSConfigure)
+ httpRegister(http.MethodPost, "/control/tls/validate", m.handleTLSValidate)
}
diff --git a/internal/home/control_test.go b/internal/home/tls_internal_test.go
similarity index 59%
rename from internal/home/control_test.go
rename to internal/home/tls_internal_test.go
index 46f14a2a..201cf551 100644
--- a/internal/home/control_test.go
+++ b/internal/home/tls_internal_test.go
@@ -4,11 +4,11 @@ import (
"testing"
"time"
+ "github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
)
-const (
- CertificateChain = `-----BEGIN CERTIFICATE-----
+var testCertChainData = []byte(`-----BEGIN CERTIFICATE-----
MIICKzCCAZSgAwIBAgIJAMT9kPVJdM7LMA0GCSqGSIb3DQEBCwUAMC0xFDASBgNV
BAoMC0FkR3VhcmQgTHRkMRUwEwYDVQQDDAxBZEd1YXJkIEhvbWUwHhcNMTkwMjI3
MDkyNDIzWhcNNDYwNzE0MDkyNDIzWjAtMRQwEgYDVQQKDAtBZEd1YXJkIEx0ZDEV
@@ -21,8 +21,9 @@ eKO029jYd2AAZEQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQB8
LwlXfbakf7qkVTlCNXgoY7RaJ8rJdPgOZPoCTVToEhT6u/cb1c2qp8QB0dNExDna
b0Z+dnODTZqQOJo6z/wIXlcUrnR4cQVvytXt8lFn+26l6Y6EMI26twC/xWr+1swq
Muj4FeWHVDerquH4yMr1jsYLD3ci+kc5sbIX6TfVxQ==
------END CERTIFICATE-----`
- PrivateKey = `-----BEGIN PRIVATE KEY-----
+-----END CERTIFICATE-----`)
+
+var testPrivateKeyData = []byte(`-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALC/BSc8mI68tw5p
aYa7pjrySwWvXeetcFywOWHGVfLw9qiFWLdfESa3Y6tWMpZAXD9t1Xh9n211YUBV
FGSB4ZshnM/tgEPU6t787lJD4NsIIRp++MkJxdAitN4oUTqL0bdpIwezQ/CrYuBX
@@ -37,36 +38,40 @@ An/jMjZSMCxNl6UyFcqt5Et1EGVhuFECQQCZLXxaT+qcyHjlHJTMzuMgkz1QFbEp
O5EX70gpeGQMPDK0QSWpaazg956njJSDbNCFM4BccrdQbJu1cW4qOsfBAkAMgZuG
O88slmgTRHX4JGFmy3rrLiHNI2BbJSuJ++Yllz8beVzh6NfvuY+HKRCmPqoBPATU
kXS9jgARhhiWXJrk
------END PRIVATE KEY-----`
-)
+-----END PRIVATE KEY-----`)
func TestValidateCertificates(t *testing.T) {
t.Run("bad_certificate", func(t *testing.T) {
- data := validateCertificates("bad cert", "", "")
- assert.NotEmpty(t, data.WarningValidation)
- assert.False(t, data.ValidCert)
- assert.False(t, data.ValidChain)
+ status := &tlsConfigStatus{}
+ err := validateCertificates(status, []byte("bad cert"), nil, "")
+ testutil.AssertErrorMsg(t, "empty certificate", err)
+ assert.False(t, status.ValidCert)
+ assert.False(t, status.ValidChain)
})
t.Run("bad_private_key", func(t *testing.T) {
- data := validateCertificates("", "bad priv key", "")
- assert.NotEmpty(t, data.WarningValidation)
- assert.False(t, data.ValidKey)
+ status := &tlsConfigStatus{}
+ err := validateCertificates(status, nil, []byte("bad priv key"), "")
+ testutil.AssertErrorMsg(t, "no valid keys were found", err)
+ assert.False(t, status.ValidKey)
})
t.Run("valid", func(t *testing.T) {
- data := validateCertificates(CertificateChain, PrivateKey, "")
- notBefore, _ := time.Parse(time.RFC3339, "2019-02-27T09:24:23Z")
- notAfter, _ := time.Parse(time.RFC3339, "2046-07-14T09:24:23Z")
- assert.NotEmpty(t, data.WarningValidation)
- assert.True(t, data.ValidCert)
- assert.False(t, data.ValidChain)
- assert.True(t, data.ValidKey)
- assert.Equal(t, "RSA", data.KeyType)
- assert.Equal(t, "CN=AdGuard Home,O=AdGuard Ltd", data.Subject)
- assert.Equal(t, "CN=AdGuard Home,O=AdGuard Ltd", data.Issuer)
- assert.Equal(t, notBefore, data.NotBefore)
- assert.Equal(t, notAfter, data.NotAfter)
- assert.True(t, data.ValidPair)
+ status := &tlsConfigStatus{}
+ err := validateCertificates(status, testCertChainData, testPrivateKeyData, "")
+ assert.Error(t, err)
+
+ notBefore := time.Date(2019, 2, 27, 9, 24, 23, 0, time.UTC)
+ notAfter := time.Date(2046, 7, 14, 9, 24, 23, 0, time.UTC)
+
+ assert.True(t, status.ValidCert)
+ assert.False(t, status.ValidChain)
+ assert.True(t, status.ValidKey)
+ assert.Equal(t, "RSA", status.KeyType)
+ assert.Equal(t, "CN=AdGuard Home,O=AdGuard Ltd", status.Subject)
+ assert.Equal(t, "CN=AdGuard Home,O=AdGuard Ltd", status.Issuer)
+ assert.Equal(t, notBefore, status.NotBefore)
+ assert.Equal(t, notAfter, status.NotAfter)
+ assert.True(t, status.ValidPair)
})
}
diff --git a/internal/home/web.go b/internal/home/web.go
index 3e248d80..7836355f 100644
--- a/internal/home/web.go
+++ b/internal/home/web.go
@@ -4,14 +4,13 @@ import (
"context"
"crypto/tls"
"io/fs"
- "net"
"net/http"
+ "net/netip"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
- "github.com/AdguardTeam/AdGuardHome/internal/aghtls"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
@@ -37,7 +36,7 @@ type webConfig struct {
clientFS fs.FS
clientBetaFS fs.FS
- BindHost net.IP
+ BindHost netip.Addr
BindPort int
BetaBindPort int
PortHTTPS int
@@ -135,8 +134,11 @@ func newWeb(conf *webConfig) (w *Web) {
//
// TODO(a.garipov): Adapt for HTTP/3.
func webCheckPortAvailable(port int) (ok bool) {
- return Context.web.httpsServer.server != nil ||
- aghnet.CheckPort("tcp", config.BindHost, port) == nil
+ if Context.web.httpsServer.server != nil {
+ return true
+ }
+
+ return aghnet.CheckPort("tcp", netip.AddrPortFrom(config.BindHost, uint16(port))) == nil
}
// TLSConfigChanged updates the TLS configuration and restarts the HTTPS server
@@ -295,7 +297,7 @@ func (web *Web) tlsServerLoop() {
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{web.httpsServer.cert},
RootCAs: Context.tlsRoots,
- CipherSuites: aghtls.SaferCipherSuites(),
+ CipherSuites: Context.tlsCipherIDs,
MinVersion: tls.VersionTLS12,
},
Handler: withMiddlewares(Context.mux, limitRequestBody),
@@ -329,7 +331,7 @@ func (web *Web) mustStartHTTP3(address string) {
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{web.httpsServer.cert},
RootCAs: Context.tlsRoots,
- CipherSuites: aghtls.SaferCipherSuites(),
+ CipherSuites: Context.tlsCipherIDs,
MinVersion: tls.VersionTLS12,
},
Handler: withMiddlewares(Context.mux, limitRequestBody),
diff --git a/internal/home/whois.go b/internal/home/whois.go
index d7543faa..c9834708 100644
--- a/internal/home/whois.go
+++ b/internal/home/whois.go
@@ -252,6 +252,6 @@ func (w *WHOIS) workerLoop() {
continue
}
- w.clients.SetWHOISInfo(ip, info)
+ w.clients.setWHOISInfo(ip, info)
}
}
diff --git a/internal/querylog/http.go b/internal/querylog/http.go
index 11f62d0d..f5c2960f 100644
--- a/internal/querylog/http.go
+++ b/internal/querylog/http.go
@@ -3,6 +3,7 @@ package querylog
import (
"encoding/json"
"fmt"
+ "math"
"net"
"net/http"
"net/url"
@@ -10,20 +11,28 @@ import (
"strings"
"time"
+ "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
- "github.com/AdguardTeam/golibs/jsonutil"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/timeutil"
"golang.org/x/net/idna"
)
-type qlogConfig struct {
- // Use float64 here to support fractional numbers and not mess the API
- // users by changing the units.
- Interval float64 `json:"interval"`
- Enabled bool `json:"enabled"`
- AnonymizeClientIP bool `json:"anonymize_client_ip"`
+// configJSON is the JSON structure for the querylog configuration.
+type configJSON struct {
+ // Interval is the querylog rotation interval. Use float64 here to support
+ // fractional numbers and not mess the API users by changing the units.
+ Interval float64 `json:"interval"`
+
+ // Enabled shows if the querylog is enabled. It is an [aghalg.NullBool]
+ // to be able to tell when it's set without using pointers.
+ Enabled aghalg.NullBool `json:"enabled"`
+
+ // AnonymizeClientIP shows if the clients' IP addresses must be anonymized.
+ // It is an [aghalg.NullBool] to be able to tell when it's set without using
+ // pointers.
+ AnonymizeClientIP aghalg.NullBool `json:"anonymize_client_ip"`
}
// Register web handlers
@@ -48,24 +57,7 @@ func (l *queryLog) handleQueryLog(w http.ResponseWriter, r *http.Request) {
// convert log entries to JSON
data := l.entriesToJSON(entries, oldest)
- jsonVal, err := json.Marshal(data)
- if err != nil {
- aghhttp.Error(
- r,
- w,
- http.StatusInternalServerError,
- "Couldn't marshal data into json: %s",
- err,
- )
-
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
- _, err = w.Write(jsonVal)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "Unable to write response json: %s", err)
- }
+ _ = aghhttp.WriteJSONResponse(w, r, data)
}
func (l *queryLog) handleQueryLogClear(_ http.ResponseWriter, _ *http.Request) {
@@ -74,23 +66,11 @@ func (l *queryLog) handleQueryLogClear(_ http.ResponseWriter, _ *http.Request) {
// Get configuration
func (l *queryLog) handleQueryLogInfo(w http.ResponseWriter, r *http.Request) {
- resp := qlogConfig{}
- resp.Enabled = l.conf.Enabled
- resp.Interval = l.conf.RotationIvl.Hours() / 24
- resp.AnonymizeClientIP = l.conf.AnonymizeClientIP
-
- jsonVal, err := json.Marshal(resp)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "json encode: %s", err)
-
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
- _, err = w.Write(jsonVal)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "http write: %s", err)
- }
+ _ = aghhttp.WriteJSONResponse(w, r, configJSON{
+ Enabled: aghalg.BoolToNullBool(l.conf.Enabled),
+ Interval: l.conf.RotationIvl.Hours() / 24,
+ AnonymizeClientIP: aghalg.BoolToNullBool(l.conf.AnonymizeClientIP),
+ })
}
// AnonymizeIP masks ip to anonymize the client if the ip is a valid one.
@@ -107,19 +87,25 @@ func AnonymizeIP(ip net.IP) {
}
}
-// Set configuration
+// handleQueryLogConfig handles the POST /control/querylog_config queries.
func (l *queryLog) handleQueryLogConfig(w http.ResponseWriter, r *http.Request) {
- d := &qlogConfig{}
- req, err := jsonutil.DecodeObject(d, r.Body)
+ // Set NaN as initial value to be able to know if it changed later by
+ // comparing it to NaN.
+ newConf := &configJSON{
+ Interval: math.NaN(),
+ }
+
+ err := json.NewDecoder(r.Body).Decode(newConf)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return
}
- ivl := time.Duration(float64(timeutil.Day) * d.Interval)
- if req.Exists("interval") && !checkInterval(ivl) {
- aghhttp.Error(r, w, http.StatusBadRequest, "Unsupported interval")
+ ivl := time.Duration(float64(timeutil.Day) * newConf.Interval)
+ hasIvl := !math.IsNaN(newConf.Interval)
+ if hasIvl && !checkInterval(ivl) {
+ aghhttp.Error(r, w, http.StatusBadRequest, "unsupported interval")
return
}
@@ -132,19 +118,23 @@ func (l *queryLog) handleQueryLogConfig(w http.ResponseWriter, r *http.Request)
// Copy data, modify it, then activate. Other threads (readers) don't need
// to use this lock.
conf := *l.conf
- if req.Exists("enabled") {
- conf.Enabled = d.Enabled
+ if newConf.Enabled != aghalg.NBNull {
+ conf.Enabled = newConf.Enabled == aghalg.NBTrue
}
- if req.Exists("interval") {
+
+ if hasIvl {
conf.RotationIvl = ivl
}
- if req.Exists("anonymize_client_ip") {
- if conf.AnonymizeClientIP = d.AnonymizeClientIP; conf.AnonymizeClientIP {
+
+ if newConf.AnonymizeClientIP != aghalg.NBNull {
+ conf.AnonymizeClientIP = newConf.AnonymizeClientIP == aghalg.NBTrue
+ if conf.AnonymizeClientIP {
l.anonymizer.Store(AnonymizeIP)
} else {
l.anonymizer.Store(nil)
}
}
+
l.conf = &conf
}
diff --git a/internal/querylog/json.go b/internal/querylog/json.go
index 24acd468..f570d40f 100644
--- a/internal/querylog/json.go
+++ b/internal/querylog/json.go
@@ -9,8 +9,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/log"
- "github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
+ "golang.org/x/exp/slices"
"golang.org/x/net/idna"
)
@@ -55,14 +55,14 @@ func (l *queryLog) entryToJSON(entry *logEntry, anonFunc aghnet.IPMutFunc) (json
question["unicode_name"] = qhost
}
- eip := netutil.CloneIP(entry.IP)
- anonFunc(eip)
+ entIP := slices.Clone(entry.IP)
+ anonFunc(entIP)
jsonEntry = jobject{
"reason": entry.Result.Reason.String(),
"elapsedMs": strconv.FormatFloat(entry.Elapsed.Seconds()*1000, 'f', -1, 64),
"time": entry.Time.Format(time.RFC3339Nano),
- "client": eip,
+ "client": entIP,
"client_proto": entry.ClientProto,
"cached": entry.Cached,
"upstream": entry.Upstream,
@@ -70,7 +70,7 @@ func (l *queryLog) entryToJSON(entry *logEntry, anonFunc aghnet.IPMutFunc) (json
"rules": resultRulesToJSONRules(entry.Result.Rules),
}
- if eip.Equal(entry.IP) {
+ if entIP.Equal(entry.IP) {
jsonEntry["client_info"] = entry.client
}
diff --git a/internal/querylog/qlog_test.go b/internal/querylog/qlog_test.go
index f33401cc..cc470438 100644
--- a/internal/querylog/qlog_test.go
+++ b/internal/querylog/qlog_test.go
@@ -8,9 +8,9 @@ import (
"testing"
"time"
- "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxyutil"
+ "github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -18,7 +18,7 @@ import (
)
func TestMain(m *testing.M) {
- aghtest.DiscardLogOutput(m)
+ testutil.DiscardLogOutput(m)
}
// TestQueryLog tests adding and loading (with filtering) entries from disk and
diff --git a/internal/stats/http.go b/internal/stats/http.go
index ae980bf3..b06a7cdc 100644
--- a/internal/stats/http.go
+++ b/internal/stats/http.go
@@ -55,12 +55,7 @@ func (s *StatsCtx) handleStats(w http.ResponseWriter, r *http.Request) {
return
}
- w.Header().Set("Content-Type", "application/json")
-
- err := json.NewEncoder(w).Encode(resp)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "json encode: %s", err)
- }
+ _ = aghhttp.WriteJSONResponse(w, r, resp)
}
// configResp is the response to the GET /control/stats_info.
@@ -71,13 +66,7 @@ type configResp struct {
// handleStatsInfo handles requests to the GET /control/stats_info endpoint.
func (s *StatsCtx) handleStatsInfo(w http.ResponseWriter, r *http.Request) {
resp := configResp{IntervalDays: atomic.LoadUint32(&s.limitHours) / 24}
-
- w.Header().Set("Content-Type", "application/json")
-
- err := json.NewEncoder(w).Encode(resp)
- if err != nil {
- aghhttp.Error(r, w, http.StatusInternalServerError, "json encode: %s", err)
- }
+ _ = aghhttp.WriteJSONResponse(w, r, resp)
}
// handleStatsConfig handles requests to the POST /control/stats_config
diff --git a/internal/stats/stats_test.go b/internal/stats/stats_test.go
index 5d86024b..bb2cc0d8 100644
--- a/internal/stats/stats_test.go
+++ b/internal/stats/stats_test.go
@@ -10,7 +10,6 @@ import (
"sync/atomic"
"testing"
- "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/stats"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
@@ -18,7 +17,7 @@ import (
)
func TestMain(m *testing.M) {
- aghtest.DiscardLogOutput(m)
+ testutil.DiscardLogOutput(m)
}
// constUnitID is the UnitIDGenFunc which always return 0.
diff --git a/internal/tools/go.mod b/internal/tools/go.mod
index 4f813b69..dcda40eb 100644
--- a/internal/tools/go.mod
+++ b/internal/tools/go.mod
@@ -4,31 +4,30 @@ go 1.18
require (
github.com/fzipp/gocyclo v0.6.0
- github.com/golangci/misspell v0.3.5
- github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8
+ github.com/golangci/misspell v0.4.0
+ github.com/gordonklaus/ineffassign v0.0.0-20220928193011-d2c82e48359b
github.com/kisielk/errcheck v1.6.2
- github.com/kyoh86/looppointer v0.1.7
- github.com/securego/gosec/v2 v2.13.1
- golang.org/x/tools v0.1.13-0.20220921142454-16b974289fe5
- golang.org/x/vuln v0.0.0-20220921153644-d9be10b6cc84
+ github.com/kyoh86/looppointer v0.1.9
+ github.com/securego/gosec/v2 v2.14.0
+ golang.org/x/tools v0.2.0
+ golang.org/x/vuln v0.0.0-20221025230227-995372c58a16
honnef.co/go/tools v0.3.3
- mvdan.cc/gofumpt v0.3.1
- mvdan.cc/unparam v0.0.0-20220831102321-2fc90a84c7ec
+ mvdan.cc/gofumpt v0.4.0
+ mvdan.cc/unparam v0.0.0-20220926085101-66de63301820
)
require (
- github.com/BurntSushi/toml v1.2.0 // indirect
- github.com/client9/misspell v0.3.4 // indirect
- github.com/google/go-cmp v0.5.8 // indirect
+ github.com/BurntSushi/toml v1.2.1 // indirect
+ github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gookit/color v1.5.2 // indirect
github.com/kyoh86/nolint v0.0.1 // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
- github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
- golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e // indirect
- golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 // indirect
- golang.org/x/mod v0.6.0-dev.0.20220907135952-02c991387e35 // indirect
- golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
- golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect
+ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
+ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect
+ golang.org/x/exp/typeparams v0.0.0-20221031165847-c99f073a8326 // indirect
+ golang.org/x/mod v0.6.0 // indirect
+ golang.org/x/sync v0.1.0 // indirect
+ golang.org/x/sys v0.1.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
diff --git a/internal/tools/go.sum b/internal/tools/go.sum
index d93d2b10..3e4f57e6 100644
--- a/internal/tools/go.sum
+++ b/internal/tools/go.sum
@@ -1,53 +1,51 @@
-github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
-github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
+github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnXyBns=
+github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
-github.com/golangci/misspell v0.3.5 h1:pLzmVdl3VxTOncgzHcvLOKirdvcx/TydsClUQXTehjo=
-github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
-github.com/google/go-cmdtest v0.4.0 h1:ToXh6W5spLp3npJV92tk6d5hIpUPYEzHLkD+rncbyhI=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0=
+github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc=
+github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
-github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 h1:PVRE9d4AQKmbelZ7emNig1+NT27DUmKZn5qXxfio54U=
-github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
+github.com/gordonklaus/ineffassign v0.0.0-20220928193011-d2c82e48359b h1:TYNAU9lu7ggdAereRq0dzCIDzHu9mNyGLj/hd5PXq8I=
+github.com/gordonklaus/ineffassign v0.0.0-20220928193011-d2c82e48359b/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
github.com/kisielk/errcheck v1.6.2 h1:uGQ9xI8/pgc9iOoCe7kWQgRE6SBTrCGmTSf0LrEtY7c=
github.com/kisielk/errcheck v1.6.2/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kyoh86/looppointer v0.1.7 h1:q5sZOhFvmvQ6ZoZxvPB/Mjj2croWX7L49BBuI4XQWCM=
-github.com/kyoh86/looppointer v0.1.7/go.mod h1:l0cRF49N6xDPx8IuBGC/imZo8Yn1BBLJY0vzI+4fepc=
-github.com/kyoh86/nolint v0.0.0-20200711045849-7a7b0d649b7a/go.mod h1:hPeUNhNOZ22wXzQKMzeYKXVFTBjJ9czxjeIDyI1ueVM=
+github.com/kyoh86/looppointer v0.1.9 h1:siTt2dqv+pW3y5gvykZXhlVcTnUVMDf11bGlB9GL5PI=
+github.com/kyoh86/looppointer v0.1.9/go.mod h1:q358WcM8cMWU+5vzqukvaZtnJi1kw/MpRHQm3xvTrjw=
github.com/kyoh86/nolint v0.0.1 h1:GjNxDEkVn2wAxKHtP7iNTrRxytRZ1wXxLV5j4XzGfRU=
github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
-github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
-github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q=
+github.com/onsi/ginkgo/v2 v2.3.1 h1:8SbseP7qM32WcvE6VaN6vfXxv698izmsJ1UQX9ve7T8=
+github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
-github.com/securego/gosec/v2 v2.13.1 h1:7mU32qn2dyC81MH9L2kefnQyRMUarfDER3iQyMHcjYM=
-github.com/securego/gosec/v2 v2.13.1/go.mod h1:EO1sImBMBWFjOTFzMWfTRrZW6M15gm60ljzrmy/wtHo=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/securego/gosec/v2 v2.14.0 h1:U1hfs0oBackChXA72plCYVA4cOlQ4gO+209dHiSNZbI=
+github.com/securego/gosec/v2 v2.14.0/go.mod h1:Ff03zEi5NwSOfXj9nFpBfhbWTtROCkg9N+9goggrYn4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
-github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
+github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@@ -55,28 +53,27 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e h1:Ctm9yurWsg7aWwIpH9Bnap/IdSVxixymIb3MhiMEQQA=
-golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
-golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 h1:Ic/qN6TEifvObMGQy72k0n1LlJr7DjWWEi+MOsDOiSk=
-golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
+golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE=
+golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/exp/typeparams v0.0.0-20221031165847-c99f073a8326 h1:fl8k2zg28yA23264d82M4dp+YlJ3ngDcpuB1bewkQi4=
+golang.org/x/exp/typeparams v0.0.0-20221031165847-c99f073a8326/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.6.0-dev.0.20220907135952-02c991387e35 h1:CZP0Rbk/s1EIiUMx5DS2MhK2ct52xpQxqddVD0FmF+o=
-golang.org/x/mod v0.6.0-dev.0.20220907135952-02c991387e35/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
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-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
-golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -86,31 +83,28 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
-golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200710042808-f1c4188a97a1/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
-golang.org/x/tools v0.1.13-0.20220921142454-16b974289fe5 h1:o1LhIiY5L+hLK9DWqfFlilCrpZnw/s7WU4iCUkb/bao=
-golang.org/x/tools v0.1.13-0.20220921142454-16b974289fe5/go.mod h1:VsjNM1dMo+Ofkp5d7y7fOdQZD8MTXSQ4w3EPk65AvKU=
-golang.org/x/vuln v0.0.0-20220921153644-d9be10b6cc84 h1:L0qUjdplndgX880fozFRGC242wAtfsViyRXWGlpZQ54=
-golang.org/x/vuln v0.0.0-20220921153644-d9be10b6cc84/go.mod h1:7tDfEDtOLlzHQRi4Yzfg5seVBSvouUIjyPzBx4q5CxQ=
+golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
+golang.org/x/vuln v0.0.0-20221025230227-995372c58a16 h1:/H6ddBUaKrFDOBFz0Y3l1/Ppbx19f/rK11jABxiqKFw=
+golang.org/x/vuln v0.0.0-20221025230227-995372c58a16/go.mod h1:F12iebNzxRMpJsm4W7ape+r/KdnXiSy3VC94WsyCG68=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -118,7 +112,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA=
honnef.co/go/tools v0.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw=
-mvdan.cc/gofumpt v0.3.1 h1:avhhrOmv0IuvQVK7fvwV91oFSGAk5/6Po8GXTzICeu8=
-mvdan.cc/gofumpt v0.3.1/go.mod h1:w3ymliuxvzVx8DAutBnVyDqYb1Niy/yCJt/lk821YCE=
-mvdan.cc/unparam v0.0.0-20220831102321-2fc90a84c7ec h1:uyA9l4gQQUHSm9zPgTzarWmsjIw7s7hAldLwVxLlu1Y=
-mvdan.cc/unparam v0.0.0-20220831102321-2fc90a84c7ec/go.mod h1:EAphbHIduKNGVweyBBwWQd24rSnLy4DsjlpxRFYE498=
+mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM=
+mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
+mvdan.cc/unparam v0.0.0-20220926085101-66de63301820 h1:fggBTMFbBz7CMny3mWZphe0B/6D8ILBunvvB1cNNHi8=
+mvdan.cc/unparam v0.0.0-20220926085101-66de63301820/go.mod h1:7fKhD/gH+APJ9Y27S2PYO7+oVWtb3XPrw9W5ayxVq2A=
diff --git a/internal/updater/updater_test.go b/internal/updater/updater_test.go
index 6eed74fd..dbf0e069 100644
--- a/internal/updater/updater_test.go
+++ b/internal/updater/updater_test.go
@@ -11,7 +11,6 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
- "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
@@ -21,7 +20,7 @@ import (
// TODO(a.garipov): Rewrite these tests.
func TestMain(m *testing.M) {
- aghtest.DiscardLogOutput(m)
+ testutil.DiscardLogOutput(m)
}
func startHTTPServer(data string) (l net.Listener, portStr string) {
diff --git a/internal/version/version.go b/internal/version/version.go
index 2091d859..ca78efff 100644
--- a/internal/version/version.go
+++ b/internal/version/version.go
@@ -63,14 +63,6 @@ func Version() (v string) {
return version
}
-// Constants defining the format of module information string.
-const (
- modInfoAtSep = "@"
- modInfoDevSep = " "
- modInfoSumLeft = " (sum: "
- modInfoSumRight = ")"
-)
-
// fmtModule returns formatted information about module. The result looks like:
//
// github.com/Username/module@v1.2.3 (sum: someHASHSUM=)
@@ -87,14 +79,16 @@ func fmtModule(m *debug.Module) (formatted string) {
stringutil.WriteToBuilder(b, m.Path)
if ver := m.Version; ver != "" {
- sep := modInfoAtSep
+ sep := "@"
if ver == "(devel)" {
- sep = modInfoDevSep
+ sep = " "
}
+
stringutil.WriteToBuilder(b, sep, ver)
}
+
if sum := m.Sum; sum != "" {
- stringutil.WriteToBuilder(b, modInfoSumLeft, sum, modInfoSumRight)
+ stringutil.WriteToBuilder(b, "(sum: ", sum, ")")
}
return b.String()
diff --git a/openapi/CHANGELOG.md b/openapi/CHANGELOG.md
index d0a450ed..a57f313c 100644
--- a/openapi/CHANGELOG.md
+++ b/openapi/CHANGELOG.md
@@ -6,6 +6,19 @@
+## v0.107.17: API Changes
+
+### `GET /control/blocked_services/services` is deprecated
+
+Use `GET /control/blocked_services/all`.
+
+### `GET /control/blocked_services/all`
+
+* The new `GET /control/blocked_services/all` HTTP API allows inspecting all
+ available services and their data, such as SVG icons and human-readable names.
+
+
+
## v0.107.15: `POST` Requests Without Bodies
As an additional CSRF protection measure, AdGuard Home now ensures that requests
diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml
index 195b0a5e..4fb68670 100644
--- a/openapi/openapi.yaml
+++ b/openapi/openapi.yaml
@@ -862,6 +862,9 @@
- 'clients'
'/blocked_services/services':
'get':
+ 'deprecated': true
+ 'description': >
+ Deprecated: Use `GET /blocked_services/all` instead.
'tags':
- 'blocked_services'
'operationId': 'blockedServicesAvailableServices'
@@ -873,6 +876,19 @@
'application/json':
'schema':
'$ref': '#/components/schemas/BlockedServicesArray'
+ '/blocked_services/all':
+ 'get':
+ 'tags':
+ - 'blocked_services'
+ 'operationId': 'blockedServicesAll'
+ 'summary': 'Get available services to use for blocking'
+ 'responses':
+ '200':
+ 'description': 'OK.'
+ 'content':
+ 'application/json':
+ 'schema':
+ '$ref': '#/components/schemas/BlockedServicesAll'
'/blocked_services/list':
'get':
'tags':
@@ -2539,6 +2555,42 @@
'type': 'array'
'items':
'type': 'string'
+ 'BlockedServicesAll':
+ 'properties':
+ 'blocked_services':
+ 'items':
+ '$ref': '#/components/schemas/BlockedService'
+ 'type': 'array'
+ 'required':
+ - 'blocked_services'
+ 'type': 'object'
+ 'BlockedService':
+ 'properties':
+ 'icon_svg':
+ 'description': >
+ The SVG icon as a Base64-encoded string to make it easier to embed
+ it into a data URL.
+ 'type': 'string'
+ 'id':
+ 'description': >
+ The ID of this service.
+ 'type': 'string'
+ 'name':
+ 'description': >
+ The human-readable name of this service.
+ 'type': 'string'
+ 'rules':
+ 'description': >
+ The array of the filtering rules.
+ 'items':
+ 'type': 'string'
+ 'type': 'array'
+ 'required':
+ - 'icon_svg'
+ - 'id'
+ - 'name'
+ - 'rules'
+ 'type': 'object'
'CheckConfigRequestBeta':
'type': 'object'
'description': 'Configuration to be checked'
diff --git a/scripts/README.md b/scripts/README.md
index 3178d7ee..5ba09aca 100644
--- a/scripts/README.md
+++ b/scripts/README.md
@@ -195,14 +195,51 @@ Optional environment:
## `companiesdb/`: Whotracks.me Database Converter
-A simple script that downloads and updates the companies DB in the `client`
+A simple script that downloads and updates the companies DB in the `client`
code from [the repo][companiesrepo].
### Usage
```sh
-cd scripts/companiesdb
-./download.sh
+( cd scripts/companiesdb && sh ./download.sh )
```
[companiesrepo]: https://github.com/AdguardTeam/companiesdb
+
+
+
+## `blocked-services/`: Blocked Services Updater
+
+A simple script that downloads and updates the blocked services index from
+AdGuard's [Hostlists Registry][reg].
+
+Optional environment:
+
+ * `URL`: the URL of the index file. By default it's
+ `https://adguardteam.github.io/HostlistsRegistry/assets/services.json`.
+
+ ### Usage
+
+```sh
+go run ./scripts/blocked-services/main.go
+```
+
+[reg]: https://github.com/AdguardTeam/HostlistsRegistry
+
+
+
+## `vetted-filters/`: Vetted Filters Updater
+
+Similar to the one above, a script that downloads and updates the vetted
+filtering list data from AdGuard's [Hostlists Registry][reg].
+
+Optional environment:
+
+ * `URL`: the URL of the index file. By default it's
+ `https://adguardteam.github.io/HostlistsRegistry/assets/filters.json`.
+
+ ### Usage
+
+```sh
+go run ./scripts/vetted-filters/main.go
+```
diff --git a/scripts/blocked-services/main.go b/scripts/blocked-services/main.go
new file mode 100644
index 00000000..ac6477b5
--- /dev/null
+++ b/scripts/blocked-services/main.go
@@ -0,0 +1,117 @@
+// blocked services fetches the most recent Hostlists Registry blocked service
+// index and transforms the filters from it to AdGuard Home's data and code
+// formats.
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/url"
+ "os"
+ "text/template"
+ "time"
+
+ "github.com/AdguardTeam/golibs/log"
+ "golang.org/x/exp/slices"
+)
+
+func main() {
+ urlStr := "https://adguardteam.github.io/HostlistsRegistry/assets/services.json"
+ if v, ok := os.LookupEnv("URL"); ok {
+ urlStr = v
+ }
+
+ // Validate the URL.
+ _, err := url.Parse(urlStr)
+ check(err)
+
+ c := &http.Client{
+ Timeout: 10 * time.Second,
+ }
+
+ resp, err := c.Get(urlStr)
+ check(err)
+ defer log.OnCloserError(resp.Body, log.ERROR)
+
+ if resp.StatusCode != http.StatusOK {
+ panic(fmt.Errorf("expected code %d, got %d", http.StatusOK, resp.StatusCode))
+ }
+
+ hlSvcs := &hlServices{}
+ err = json.NewDecoder(resp.Body).Decode(hlSvcs)
+ check(err)
+
+ // Sort all services and rules to make the output more predictable.
+ slices.SortStableFunc(hlSvcs.BlockedServices, func(a, b *hlServicesService) (less bool) {
+ return a.ID < b.ID
+ })
+ for _, s := range hlSvcs.BlockedServices {
+ slices.Sort(s.Rules)
+ }
+
+ // Use another set of delimiters to prevent them interfering with the Go
+ // code.
+ tmpl, err := template.New("main").Delims("<%", "%>").Funcs(template.FuncMap{
+ "isnotlast": func(idx, sliceLen int) (ok bool) { return idx != sliceLen-1 },
+ }).Parse(tmplStr)
+ check(err)
+
+ f, err := os.OpenFile(
+ "./internal/filtering/servicelist.go",
+ os.O_CREATE|os.O_TRUNC|os.O_WRONLY,
+ 0o644,
+ )
+ check(err)
+ defer log.OnCloserError(f, log.ERROR)
+
+ err = tmpl.Execute(f, hlSvcs)
+ check(err)
+}
+
+// tmplStr is the template for the Go source file with the services.
+const tmplStr = `// Code generated by go run ./scripts/blocked-services/main.go; DO NOT EDIT.
+
+package filtering
+
+// blockedService represents a single blocked service.
+type blockedService struct {
+ ID string ` + "`" + `json:"id"` + "`" + `
+ Name string ` + "`" + `json:"name"` + "`" + `
+ IconSVG []byte ` + "`" + `json:"icon_svg"` + "`" + `
+ Rules []string ` + "`" + `json:"rules"` + "`" + `
+}
+
+// blockedServices contains raw blocked service data.
+var blockedServices = []blockedService{<% $l := len .BlockedServices %>
+ <%- range $i, $s := .BlockedServices %>{
+ ID: <% printf "%q" $s.ID %>,
+ Name: <% printf "%q" $s.Name %>,
+ IconSVG: []byte(<% printf "%q" $s.IconSVG %>),
+ Rules: []string{<% range $s.Rules %>
+ <% printf "%q" . %>,<% end %>
+ },
+}<% if isnotlast $i $l %>, <% end %><% end %>}
+`
+
+// check is a simple error-checking helper for scripts.
+func check(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+// hlServices is the JSON structure for the Hostlists Registry blocked service
+// index.
+type hlServices struct {
+ BlockedServices []*hlServicesService `json:"blocked_services"`
+}
+
+// hlServicesService is the JSON structure for a service in the Hostlists
+// Registry.
+type hlServicesService struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ IconSVG string `json:"icon_svg"`
+ Rules []string `json:"rules"`
+}
diff --git a/scripts/companiesdb/download.sh b/scripts/companiesdb/download.sh
index dfa278f0..e56166e0 100755
--- a/scripts/companiesdb/download.sh
+++ b/scripts/companiesdb/download.sh
@@ -10,5 +10,5 @@ adguard='https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/adg
base_path='../../client/src/helpers/trackers'
readonly whotracksme adguard base_path
-curl "$whotracksme" --output "${base_path}/whotracksme.json"
-curl "$adguard" --output "${base_path}/adguard.json"
+curl -o "${base_path}/whotracksme.json" -v "$whotracksme"
+curl -o "${base_path}/adguard.json" -v "$adguard"
diff --git a/scripts/make/go-lint.sh b/scripts/make/go-lint.sh
index 17ac9457..07b1fda0 100644
--- a/scripts/make/go-lint.sh
+++ b/scripts/make/go-lint.sh
@@ -222,13 +222,13 @@ govulncheck ./...
# Apply more lax standards to the code we haven't properly refactored yet.
gocyclo --over 17 ./internal/querylog/
-gocyclo --over 15 ./internal/home/ ./internal/dhcpd
-gocyclo --over 13 ./internal/filtering/
+gocyclo --over 13 ./internal/dhcpd ./internal/filtering/ ./internal/home/
# Apply stricter standards to new or somewhat refactored code.
gocyclo --over 10 ./internal/aghio/ ./internal/aghnet/ ./internal/aghos/\
./internal/aghtest/ ./internal/dnsforward/ ./internal/stats/\
- ./internal/tools/ ./internal/updater/ ./internal/version/ ./main.go
+ ./internal/tools/ ./internal/updater/ ./internal/version/\
+ ./scripts/vetted-filters/ ./main.go
ineffassign ./...
diff --git a/scripts/translations/package.json b/scripts/translations/package.json
index c4c3948e..110db720 100644
--- a/scripts/translations/package.json
+++ b/scripts/translations/package.json
@@ -2,8 +2,8 @@
"name": "translations",
"version": "0.2.0",
"scripts": {
- "locales:download": "TWOSKY_URI=https://twosky.adtidy.org/api/v1 TWOSKY_PROJECT_ID=home node download.js ; node count.js",
- "locales:upload": "TWOSKY_URI=https://twosky.adtidy.org/api/v1 TWOSKY_PROJECT_ID=home node upload.js",
+ "locales:download": "TWOSKY_URI=https://twosky.int.agrd.dev/api/v1 TWOSKY_PROJECT_ID=home node download.js ; node count.js",
+ "locales:upload": "TWOSKY_URI=https://twosky.int.agrd.dev/api/v1 TWOSKY_PROJECT_ID=home node upload.js",
"locales:summary": "node count.js",
"locales:unused": "node unused.js"
},
diff --git a/scripts/vetted-filters/main.go b/scripts/vetted-filters/main.go
new file mode 100644
index 00000000..d5647924
--- /dev/null
+++ b/scripts/vetted-filters/main.go
@@ -0,0 +1,164 @@
+// vetted-filters fetches the most recent Hostlists Registry filtering rule list
+// index and transforms the filters from it to AdGuard Home's format.
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/url"
+ "os"
+ "time"
+
+ "github.com/AdguardTeam/golibs/log"
+ "github.com/google/renameio/maybe"
+)
+
+func main() {
+ urlStr := "https://adguardteam.github.io/HostlistsRegistry/assets/filters.json"
+ if v, ok := os.LookupEnv("URL"); ok {
+ urlStr = v
+ }
+
+ // Validate the URL.
+ _, err := url.Parse(urlStr)
+ check(err)
+
+ c := &http.Client{
+ Timeout: 10 * time.Second,
+ }
+
+ resp, err := c.Get(urlStr)
+ check(err)
+ defer log.OnCloserError(resp.Body, log.ERROR)
+
+ if resp.StatusCode != http.StatusOK {
+ panic(fmt.Errorf("expected code %d, got %d", http.StatusOK, resp.StatusCode))
+ }
+
+ hlFlt := &hlFilters{}
+ err = json.NewDecoder(resp.Body).Decode(hlFlt)
+ check(err)
+
+ aghFlt := &aghFilters{
+ Categories: map[string]*aghFiltersCategory{
+ "general": {
+ Name: "filter_category_general",
+ Description: "filter_category_general_desc",
+ },
+ "other": {
+ Name: "filter_category_other",
+ Description: "filter_category_other_desc",
+ },
+ "regional": {
+ Name: "filter_category_regional",
+ Description: "filter_category_regional_desc",
+ },
+ "security": {
+ Name: "filter_category_security",
+ Description: "filter_category_security_desc",
+ },
+ },
+ Filters: map[string]*aghFiltersFilter{},
+ }
+
+ for i, f := range hlFlt.Filters {
+ id := f.FilterID
+ cat := f.category()
+ if cat == "" {
+ log.Info("warning: filter %s at index %d does not have a fitting category", id, i)
+ }
+
+ aghFlt.Filters[id] = &aghFiltersFilter{
+ Name: f.Name,
+ CategoryID: cat,
+ Homepage: f.Homepage,
+ Source: f.SourceURL,
+ }
+ }
+
+ buf := &bytes.Buffer{}
+ _, _ = buf.WriteString(jsHeader)
+
+ enc := json.NewEncoder(buf)
+ enc.SetIndent("", " ")
+
+ err = enc.Encode(aghFlt)
+ check(err)
+
+ err = maybe.WriteFile("client/src/helpers/filters/filters.js", buf.Bytes(), 0o644)
+ check(err)
+}
+
+// jsHeader is the header for the generated JavaScript file. It informs the
+// reader that the file is generated and disables some style-related eslint
+// checks.
+const jsHeader = `// Code generated by go run ./scripts/vetted-filters/main.go; DO NOT EDIT.
+
+/* eslint quote-props: 'off', quotes: 'off', comma-dangle: 'off', semi: 'off' */
+
+export default `
+
+// check is a simple error-checking helper for scripts.
+func check(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+// hlFilters is the JSON structure for the Hostlists Registry rule list index.
+type hlFilters struct {
+ Filters []*hlFiltersFilter `json:"filters"`
+}
+
+// hlFiltersFilter is the JSON structure for a filter in the Hostlists Registry.
+type hlFiltersFilter struct {
+ FilterID string `json:"filterId"`
+ Name string `json:"name"`
+ Homepage string `json:"homepage"`
+ SourceURL string `json:"sourceUrl"`
+ Tags []string `json:"tags"`
+}
+
+// category returns the AdGuard Home category for this filter. If there is no
+// fitting category, cat is empty.
+func (f *hlFiltersFilter) category() (cat string) {
+ for _, t := range f.Tags {
+ switch t {
+ case "purpose:general":
+ return "general"
+ case "purpose:other":
+ return "other"
+ case "purpose:regional":
+ return "regional"
+ case "purpose:security":
+ return "security"
+ }
+ }
+
+ return ""
+}
+
+// aghFilters is the JSON structure for AdGuard Home's list of vetted filtering
+// rule list in file client/src/helpers/filters/filters.js.
+type aghFilters struct {
+ Categories map[string]*aghFiltersCategory `json:"categories"`
+ Filters map[string]*aghFiltersFilter `json:"filters"`
+}
+
+// aghFiltersCategory is the JSON structure for a category in the vetted
+// filtering rule list file.
+type aghFiltersCategory struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+}
+
+// aghFiltersFilter is the JSON structure for a filter in the vetted filtering
+// rule list file.
+type aghFiltersFilter struct {
+ Name string `json:"name"`
+ CategoryID string `json:"categoryId"`
+ Homepage string `json:"homepage"`
+ Source string `json:"source"`
+}