Skip to content

Commit

Permalink
fix proxy support for QUIC, fix unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tykling committed Feb 21, 2024
1 parent ffb1138 commit 1a608e3
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def proxy_server():
"127.0.0.1:1080",
],
)
time.sleep(2)
time.sleep(3)
if proc.poll():
pytest.fail("Unable to start proxy on 127.0.0.1:1080")
print("Setup finished - proxy is running on 127.0.0.1:1080!")
Expand Down
15 changes: 15 additions & 0 deletions src/dns_exporter/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import dns.flags
import dns.opcode
import dns.query
import dns.quic
import dns.rcode
import dns.rdatatype
import dns.resolver
Expand All @@ -37,6 +38,10 @@

logger = logging.getLogger(f"dns_exporter.{__name__}")

# this can be removed pending the next dnspython release
# https://github.com/rthalley/dnspython/issues/1059
_socket = socket.socket


class DNSCollector(Collector):
"""Custom collector class which does DNS lookups and returns metrics."""
Expand All @@ -60,9 +65,19 @@ def __init__(
port=self.config.proxy.port,
)
dns.query.socket_factory = socks.socksocket
# this should begin working pending the next dnspython release
# https://github.com/rthalley/dnspython/issues/1059
# dns.quic._sync.socket_factory = socks.socksocket
# patch socket.socket until then
socket.socket = socks.socksocket # type: ignore
logger.debug(f"Using proxy {self.config.proxy.geturl()}")
else:
dns.query.socket_factory = socket.socket
# this should begin working pending the next dnspython release
# https://github.com/rthalley/dnspython/issues/1059
# dns.quic._sync.socket_factory = socket.socket
# unpatch socket.socket until then
socket.socket = _socket # type: ignore
logger.debug("Not using a proxy for this request")

def describe(self) -> Iterator[Union[CounterMetricFamily, GaugeMetricFamily]]:
Expand Down
2 changes: 1 addition & 1 deletion src/dns_exporter/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ def __post_init__(self) -> None:
# validate proxy
if self.proxy:
# proxy support doesn't work for DoT and DoQ for now
if self.protocol in ["dot", "doq"]:
if self.protocol in ["dot"]:
logger.error(f"proxy not valid for protocol {self.protocol}")
raise ConfigError(
"invalid_request_config",
Expand Down
1 change: 1 addition & 0 deletions src/pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ filterwarnings =
error
ignore::pytest.PytestUnraisableExceptionWarning
ignore::ResourceWarning
ignore::DeprecationWarning:aioquic.*
40 changes: 20 additions & 20 deletions src/tests/test_exporter.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# type: ignore
"""Unit tests for dns_exporter/exporter.py."""
import logging
import sys

import cryptography
import pytest
import requests

Expand Down Expand Up @@ -279,14 +277,14 @@ def test_internal_metrics(dns_exporter_example_config, caplog):
assert f'dnsexp_build_version_info{{version="{__version__}"}} 1.0' in r.text
assert "Returning exporter metrics for request to /metrics" in caplog.text
for metric in """dnsexp_http_requests_total{path="/notfound"} 1.0
dnsexp_http_requests_total{path="/query"} 43.0
dnsexp_http_requests_total{path="/query"} 44.0
dnsexp_http_requests_total{path="/config"} 2.0
dnsexp_http_requests_total{path="/"} 1.0
dnsexp_http_requests_total{path="/metrics"} 1.0
dnsexp_http_responses_total{path="/notfound",response_code="404"} 1.0
dnsexp_http_responses_total{path="/query",response_code="200"} 43.0
dnsexp_http_responses_total{path="/query",response_code="200"} 44.0
dnsexp_http_responses_total{path="/",response_code="200"} 1.0
dnsexp_dns_queries_total 32.0
dnsexp_dns_queries_total 33.0
dnsexp_dns_responsetime_seconds_bucket{additional="0",answer="1",authority="0",family="ipv4",flags="QR RA RD",ip="8.8.4.4",le="0.005",nsid="no_nsid",opcode="QUERY",port="53",protocol="udp",proxy="none",query_name="example.com",query_type="A",rcode="NOERROR",server="udp://dns.google:53",transport="UDP"}
dnsexp_scrape_failures_total{reason="timeout"} 1.0
dnsexp_scrape_failures_total{reason="invalid_response_flags"} 6.0
Expand Down Expand Up @@ -398,21 +396,7 @@ def test_doh(dns_exporter_example_config, caplog):
assert "Protocol doh got a DNS query response over TCP" in caplog.text


def test_doq(dns_exporter_example_config, caplog):
# aioquic uses some deprecated cryptography methods and it causes some deprecation warnings
# which have to be handled different from 3.12 onwards
if sys.version_info >= (3, 12):
# py3.12 raises an extra DeprecationWarning
with pytest.deprecated_call():
with pytest.warns(cryptography.utils.CryptographyDeprecationWarning):
doq(dns_exporter_example_config, caplog)
else:
# just silence the CryptographyDeprecationWarning for py3.9-py3.11
with pytest.warns(cryptography.utils.CryptographyDeprecationWarning):
doq(dns_exporter_example_config, caplog)


def doq(dns_exporter_example_config, caplog):
def test_doq(dns_exporter_example_config, caplog, recwarn):
caplog.clear()
caplog.set_level(logging.DEBUG)
r = requests.get(
Expand Down Expand Up @@ -859,3 +843,19 @@ def test_proxy_doh(dns_exporter_example_config, proxy_server):
)
assert 'proxy="socks5://127.0.0.1:1080"' in r.text
assert 'server="https://dns.google:443/dns-query"' in r.text


def test_proxy_doq(dns_exporter_example_config, proxy_server, recwarn):
"""Test proxy functionality for doq protocol."""
r = requests.get(
"http://127.0.0.1:25353/query",
params={
"query_name": "example.com",
"server": "dns-unfiltered.adguard.com",
"family": "ipv4",
"protocol": "doq",
"proxy": "socks5://127.0.0.1:1080",
},
)
assert 'proxy="socks5://127.0.0.1:1080"' in r.text
assert 'server="doq://dns-unfiltered.adguard.com:853"' in r.text

0 comments on commit 1a608e3

Please sign in to comment.