Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
0f031e4
Allow external setting of GIT_VERSION
baranyaib90 Oct 24, 2022
7a89ad5
Add hardcoded version support
baranyaib90 Nov 4, 2022
468bdbf
Run test with valgrind
baranyaib90 Oct 24, 2022
58bdca9
Merge pull request #154 from baranyaib90/fixes-12
aarond10 Jan 6, 2023
a5c4428
Cast timestamps to uint64_t to fix compatibility issue with 64-bit time.
aarond10 May 25, 2023
d03e115
Merge branch 'master' of github.com:aarond10/https_dns_proxy
aarond10 May 25, 2023
df6bbb1
clang-tidy modernize-macro-to-enum fix
baranyaib90 Oct 23, 2023
42fdaaa
clang-tidy bugprone-assignment-in-if-condition fixes
baranyaib90 Oct 23, 2023
a2d954d
Code modernization mega commit
baranyaib90 Oct 19, 2023
bd71243
Trying to have universal format specifier
baranyaib90 Oct 24, 2023
977341a
Merge pull request #163 from baranyaib90/master
aarond10 Oct 24, 2023
983e343
Rewritten hostname extraction from resolver URL
baranyaib90 Nov 17, 2023
489c57e
Merge pull request #164 from baranyaib90/master
aarond10 Nov 19, 2023
1f1cc61
Remove unnecessary math.h import
darktohka Dec 21, 2023
e8c3a5d
Allow clang-tidy to be disabled during compilation
darktohka Dec 21, 2023
5433fc1
Merge pull request #166 from darktohka/clang-tidy
aarond10 Dec 26, 2023
8afbba7
Merge pull request #165 from darktohka/master
aarond10 Dec 26, 2023
0d09533
Using DynamicUser instead of nobody/nogroup in systemd service
baranyaib90 Apr 1, 2024
6ad217c
gcc-13 and clang-tidy-18 fixes
baranyaib90 Oct 19, 2024
ae93cd1
Using Ubuntu 24.04 in github actions
baranyaib90 Oct 19, 2024
484bd15
Merge pull request #176 from baranyaib90/master
aarond10 Nov 18, 2024
409a5ec
Mitigating issue when curl needs more socket event handler
baranyaib90 Jan 4, 2025
71a56ec
Flightrecorder introduced
baranyaib90 Dec 28, 2024
8de4b0b
Count opened connections and enfore max limit
baranyaib90 Dec 29, 2024
378ca55
Reserving extra IO events for shutting down connections
baranyaib90 Dec 30, 2024
cfaa137
Fixing CURLOPT_MAXAGE_CONN
baranyaib90 Dec 30, 2024
8dc5645
Using wait for multiplexing over HTTP/1.x
baranyaib90 Jan 4, 2025
348447a
HTTPS request timeout optimalization
baranyaib90 Jan 4, 2025
d0505e4
Help printout reorganized
baranyaib90 Feb 23, 2025
fd62a31
Update HTTP3 development build script
baranyaib90 Feb 23, 2025
0e074b4
Merge pull request #182 from baranyaib90/master
aarond10 Mar 13, 2025
ef05120
Fixing ring_buffer.h define
baranyaib90 May 10, 2025
e6fd23c
Debug log HTTPS request and response data
baranyaib90 May 10, 2025
83f7e92
Printing curl library versions and used features
baranyaib90 May 10, 2025
a34e20d
Merge pull request #185 from baranyaib90/master
aarond10 May 11, 2025
57b1718
fix curl_result_code log
malash Jun 4, 2025
2963960
Merge pull request #188 from malash/fix-curl-error-log
aarond10 Jun 8, 2025
daa0d39
Large improvement pack (#192)
baranyaib90 Sep 1, 2025
7b27ecd
Fixes 16 (#193)
baranyaib90 Oct 7, 2025
7de8af2
Fix code block formatting in README.md (#195)
brlin-tw Nov 23, 2025
b499aa9
Remove redundant include from ares.
aarond10 Dec 24, 2025
67ecae0
Add -S flag for configurable source address on outbound connections (…
karl82 Dec 30, 2025
caad1eb
fix: critical bugs in DNS proxy (#201)
toxeh Feb 10, 2026
abbb5c4
Limit TCP DNS response payload to prevent overflow
aarond10 Feb 8, 2026
c97476c
Document TOCTOU race safety in TCP client lookup
aarond10 Feb 8, 2026
08d7d2e
Prevent integer overflow in TCP buffer size calculation
aarond10 Feb 8, 2026
442815e
Prevent use-after-free in TCP client removal
aarond10 Feb 8, 2026
13f5169
Fix infinite loop in TCP response sending
aarond10 Feb 10, 2026
5fd40ad
Fix memory leak in DNS response truncation
aarond10 Feb 8, 2026
87f3271
Fix NULL pointer dereference in HTTPS response callback
aarond10 Feb 10, 2026
5b95a64
Add size limit and NULL check to flight recorder
aarond10 Feb 9, 2026
95fe77c
Fix NULL pointer dereference in HTTPS response callback
aarond10 Feb 8, 2026
0ae04eb
Use ev_async for signal-safe flight recorder dump
aarond10 Feb 10, 2026
e298263
Fix critical security and stability bugs
aarond10 Feb 10, 2026
3c7fd07
Fix HIGH severity stability and resource bugs
aarond10 Feb 10, 2026
75b3200
Fix curl signal handling conflict
aarond10 Feb 10, 2026
2f1e6ed
Fix several bugs:
aarond10 Feb 10, 2026
8018812
Bind bootstrap DNS lookups to -S source address (#1) (#202)
karl82 Mar 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,32 @@ jobs:
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04

strategy:
fail-fast: false
matrix:
compiler: [gcc-10, clang-12]
compiler: [gcc-13, clang-18]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@main

- name: Update APT
run: sudo apt-get update

- name: Setup Dependencies
run: sudo apt-get install cmake libc-ares-dev libcurl4-openssl-dev libev-dev build-essential clang-tidy-12 ${{ matrix.compiler }} dnsutils python3-pip
run: sudo apt-get install cmake libc-ares-dev libcurl4-openssl-dev libev-dev libsystemd-dev build-essential clang-tidy dnsutils python3-pip python3-venv valgrind ${{ matrix.compiler }}

- name: Setup Robot Framework
run: sudo pip3 install robotframework
- name: Setup Python Virtual Environment
run: python3 -m venv ${{github.workspace}}/venv

- name: Set clang-tidy
run: sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-12 100
- name: Setup Robot Framework
run: ${{github.workspace}}/venv/bin/pip3 install robotframework

- name: Configure CMake
env:
CC: ${{ matrix.compiler }}
run: cmake -D CMAKE_BUILD_TYPE=Debug -B ${{github.workspace}}/
run: cmake -D CMAKE_BUILD_TYPE=Debug -D PYTHON3_EXE=${{github.workspace}}/venv/bin/python3 -B ${{github.workspace}}/

- name: Build
env:
Expand All @@ -43,8 +44,10 @@ jobs:
- name: Test
run: make -C ${{github.workspace}}/ test ARGS="--verbose"

- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
if: ${{ success() || failure() }}
with:
name: robot-logs-${{ matrix.compiler }}
path: ${{github.workspace}}/tests/robot/*.html
path: |
${{github.workspace}}/tests/robot/*.html
${{github.workspace}}/tests/robot/valgrind-*.log
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
build/
CMakeCache.txt
CTestTestfile.cmake
CMakeFiles/
Expand All @@ -17,3 +18,5 @@ log.html
output.xml
report.html
custom_curl/
valgrind-*.log
tests/robot/__pycache__
90 changes: 68 additions & 22 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.10)
project(HttpsDnsProxy C)
cmake_minimum_required(VERSION 3.7)

include(CheckIncludeFile)

# FUNCTIONS

Expand All @@ -25,16 +27,21 @@ if (NOT CMAKE_INSTALL_BINDIR)
set(CMAKE_INSTALL_BINDIR bin)
endif()

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra --pedantic -Wno-strict-aliasing -Wno-variadic-macros")
set(CMAKE_C_FLAGS_DEBUG "-g -DDEBUG")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -Wstrict-aliasing -Wformat=2 -Wunused -Wno-variadic-macros -Wnull-dereference -Wshadow -Wconversion -Wsign-conversion -Wfloat-conversion -Wimplicit-fallthrough")
set(CMAKE_C_FLAGS_DEBUG "-gdwarf-4 -DDEBUG")
set(CMAKE_C_FLAGS_RELEASE "-O2")

if ((CMAKE_C_COMPILER_ID MATCHES GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 9) OR
(CMAKE_C_COMPILER_ID MATCHES Clang AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10))
if (((CMAKE_C_COMPILER_ID MATCHES GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 9) AND
(CMAKE_C_COMPILER_ID MATCHES GNU AND CMAKE_C_COMPILER_VERSION VERSION_LESS 14)) OR
( CMAKE_C_COMPILER_ID MATCHES Clang AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10))
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-folding-constant")
endif()

set(SERVICE_EXTRA_OPTIONS "")
set(SERVICE_TYPE "simple")

# VERSION
# It is possible to define external default value like: cmake -DSW_VERSION=1.2-custom

find_package(Git)
if(Git_FOUND)
Expand All @@ -43,19 +50,33 @@ if(Git_FOUND)
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "Version: ${GIT_VERSION}")

if(GIT_VERSION)
set(SW_VERSION "${GIT_VERSION}")
else()
message(WARNING "Could not find out version from git command!")
endif()

# May not update version in some cases (example: git commit --amend)
set_property(GLOBAL APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS
"${CMAKE_SOURCE_DIR}/.git/index")
else()
set(GIT_VERSION "UNKNOWN")
message(WARNING "Could not find git command! Version is set to: ${GIT_VERSION}")
message(WARNING "Could not find git command!")
endif()

if(NOT SW_VERSION)
message(WARNING "Version unset, using hardcoded!")
endif()

# LIBRARY DEPENDENCIES

# Add Homebrew paths for macOS
if(APPLE)
list(APPEND CMAKE_PREFIX_PATH "/opt/homebrew")
link_directories("/opt/homebrew/lib")
endif()

find_path(LIBCARES_INCLUDE_DIR ares.h)
find_path(LIBEV_INCLUDE_DIR ev.h)

Expand All @@ -72,18 +93,33 @@ include_directories(
${LIBCARES_INCLUDE_DIR} ${LIBCURL_INCLUDE_DIR}
${LIBEV_INCLUDE_DIR} src)

check_include_file("systemd/sd-daemon.h" HAVE_SD_DAEMON_H)

if(HAVE_SD_DAEMON_H)
message(STATUS "Using libsystemd")
add_definitions(-DHAS_LIBSYSTEMD=1)
set(LIBS ${LIBS} systemd)
set(SERVICE_TYPE "notify")
endif()

# CLANG TIDY

find_program(
CLANG_TIDY_EXE
NAMES "clang-tidy"
DOC "Path to clang-tidy executable"
)
if(NOT CLANG_TIDY_EXE)
message(STATUS "clang-tidy not found.")
option(USE_CLANG_TIDY "Use clang-tidy during compilation" ON)

if(USE_CLANG_TIDY)
find_program(
CLANG_TIDY_EXE
NAMES "clang-tidy"
DOC "Path to clang-tidy executable"
)
if(NOT CLANG_TIDY_EXE)
message(STATUS "clang-tidy not found.")
else()
message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-fix" "-fix-errors" "-checks=*,-readability-identifier-length,-altera-unroll-loops,-bugprone-easily-swappable-parameters,-concurrency-mt-unsafe,-*magic-numbers,-hicpp-signed-bitwise,-readability-function-cognitive-complexity,-altera-id-dependent-backward-branch,-misc-include-cleaner,-llvmlibc-restrict-system-libc-headers,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling")
endif()
else()
message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-fix" "-checks=*,-clang-analyzer-alpha.*,-misc-unused-parameters,-cert-err34-c,-google-readability-todo,-hicpp-signed-bitwise,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-gnu-folding-constant,-gnu-zero-variadic-macro-arguments,-readability-function-cognitive-complexity,-concurrency-mt-unsafe")
message(STATUS "Not using clang-tidy.")
endif()

# BUILD
Expand All @@ -99,9 +135,12 @@ set_property(TARGET ${TARGET_NAME} PROPERTY C_STANDARD 11)

define_file_basename_for_sources("https_dns_proxy")

set_source_files_properties(
src/main.c src/options.c
PROPERTIES COMPILE_FLAGS "-DGIT_VERSION='\"${GIT_VERSION}\"'")

if(SW_VERSION)
set_source_files_properties(
src/main.c
PROPERTIES COMPILE_FLAGS "-DSW_VERSION='\"${SW_VERSION}\"'")
endif()

if(CLANG_TIDY_EXE)
set_target_properties(
Expand All @@ -114,7 +153,6 @@ endif()

install(TARGETS ${TARGET_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})

set(SERVICE_EXTRA_OPTIONS "")
if(IS_DIRECTORY "/etc/munin/plugins" AND
IS_DIRECTORY "/etc/munin/plugin-conf.d")
set(SERVICE_EXTRA_OPTIONS "-s 300")
Expand Down Expand Up @@ -144,6 +182,14 @@ else()
message(STATUS "python3 found: ${PYTHON3_EXE}")

enable_testing()

# Robot framework tests
add_test(NAME robot COMMAND ${PYTHON3_EXE} -m robot.run functional_tests.robot
WORKING_DIRECTORY tests/robot)
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/robot)
endif()

# Clean target (removes entire build directory)
add_custom_target(distclean
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}
COMMENT "Removing build directory"
)
95 changes: 64 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

`https_dns_proxy` is a light-weight DNS<-->HTTPS, non-caching translation
proxy for the [RFC 8484][rfc-8484] DNS-over-HTTPS standard. It receives
regular (UDP) DNS requests and issues them via DoH.
regular (UDP or TCP) DNS requests and issues them via DoH.

[Google's DNS-over-HTTPS][google-doh] service is default, but
[Cloudflare's service][cloudflare-doh] also works with trivial commandline flag
Expand Down Expand Up @@ -43,11 +43,12 @@ only makes sense if you trust your DoH provider.

## Build

Depends on `c-ares (>=1.11.0)`, `libcurl (>=7.64.0)`, `libev (>=4.25)`.
Depends on `c-ares (>=1.11.0)`, `libcurl (>=7.66.0)`, `libev (>=4.25)`.

On Debian-derived systems those are libc-ares-dev,
libcurl4-{openssl,nss,gnutls}-dev and libev-dev respectively.
On Redhat-derived systems those are c-ares-devel, libcurl-devel and libev-devel.
On systems with systemd it is recommended to have libsystemd development package installed.

On MacOS, you may run into issues with curl headers. Others have had success when first installing curl with brew.
```
Expand All @@ -57,7 +58,7 @@ brew link curl --force

On Ubuntu
```
apt-get install cmake libc-ares-dev libcurl4-openssl-dev libev-dev build-essential
apt-get install cmake libc-ares-dev libcurl4-openssl-dev libev-dev libsystemd-dev build-essential
```

If all pre-requisites are met, you should be able to build with:
Expand All @@ -71,14 +72,14 @@ $ make
* If system libcurl supports it by default nothing else has to be done

* If a custom build of libcurl supports HTTP/3 which is installed in a different location, that can be set when running cmake:
```
$ cmake -D CUSTOM_LIBCURL_INSTALL_PATH=/absolute/path/to/custom/libcurl/install .
```
```
$ cmake -D CUSTOM_LIBCURL_INSTALL_PATH=/absolute/path/to/custom/libcurl/install .
```

* Just to test HTTP/3 support for development purpose, simply run the following command and wait for a long time:
```
$ ./development_build_with_http3.sh
```
```
$ ./development_build_with_http3.sh
```

## INSTALL

Expand Down Expand Up @@ -158,56 +159,89 @@ docker run --name "https-dns-proxy" -p 5053:5053/udp \
Just run it as a daemon and point traffic at it. Commandline flags are:

```
Usage: ./https_dns_proxy [-a <listen_addr>] [-p <listen_port>]
[-d] [-u <user>] [-g <group>] [-b <dns_servers>]
[-i <polling_interval>] [-4] [-r <resolver_url>]
[-t <proxy_server>] [-l <logfile>] [-c <dscp_codepoint>]
[-x] [-q] [-s <statistic_interval>] [-v]+ [-V] [-h]
Usage: ./https_dns_proxy [-a <listen_addr>] [-p <listen_port>] [-T <tcp_client_limit>]
[-b <dns_servers>] [-i <polling_interval>] [-4]
[-r <resolver_url>] [-t <proxy_server>] [-S <source_addr>] [-x] [-q] [-C <ca_path>] [-c <dscp_codepoint>]
[-d] [-u <user>] [-g <group>]
[-v]+ [-l <logfile>] [-s <statistic_interval>] [-F <log_limit>] [-V] [-h]

-a listen_addr Local IPv4/v6 address to bind to. (127.0.0.1)
-p listen_port Local port to bind to. (5053)
-d Daemonize.
-u user Optional user to drop to if launched as root.
-g group Optional group to drop to if launched as root.
DNS server
-a listen_addr Local IPv4/v6 address to bind to. (Default: 127.0.0.1)
-p listen_port Local port to bind to. (Default: 5053)
-T tcp_client_limit Number of TCP clients to serve.
(Default: 20, Disabled: 0, Min: 1, Max: 200)

DNS client
-b dns_servers Comma-separated IPv4/v6 addresses and ports (addr:port)
of DNS servers to resolve resolver host (e.g. dns.google).
When specifying a port for IPv6, enclose the address in [].
(8.8.8.8,1.1.1.1,8.8.4.4,1.0.0.1,145.100.185.15,145.100.185.16,185.49.141.37)
(Default: 8.8.8.8,1.1.1.1,8.8.4.4,1.0.0.1,145.100.185.15,145.100.185.16,185.49.141.37)
-i polling_interval Optional polling interval of DNS servers.
(Default: 120, Min: 5, Max: 3600)
-4 Force IPv4 hostnames for DNS resolvers non IPv6 networks.
-r resolver_url The HTTPS path to the resolver URL. Default: https://dns.google/dns-query

HTTPS client
-r resolver_url The HTTPS path to the resolver URL. (Default: https://dns.google/dns-query)
-t proxy_server Optional HTTP proxy. e.g. socks5://127.0.0.1:1080
Remote name resolution will be used if the protocol
supports it (http, https, socks4a, socks5h), otherwise
initial DNS resolution will still be done via the
bootstrap DNS servers.
-l logfile Path to file to log to. ("-")
-c dscp_codepoint Optional DSCP codepoint[0-63] to set on upstream DNS server
connections.
-S source_addr Source IPv4/v6 address for outbound HTTPS and bootstrap DNS.
(Default: system default)
-x Use HTTP/1.1 instead of HTTP/2. Useful with broken
or limited builds of libcurl. (false)
-q Use HTTP/3 (QUIC) only. (false)
-s statistic_interval Optional statistic printout interval.
(Default: 0, Disabled: 0, Min: 1, Max: 3600)
or limited builds of libcurl.
-q Use HTTP/3 (QUIC) only.
-m max_idle_time Maximum idle time in seconds allowed for reusing a HTTPS connection.
(Default: 118, Min: 0, Max: 3600)
-L conn_loss_time Time in seconds to tolerate connection timeouts of reused connections.
This option mitigates half-open TCP connection issue (e.g. WAN IP change).
(Default: 15, Min: 5, Max: 60)
-C ca_path Optional file containing CA certificates.
-c dscp_codepoint Optional DSCP codepoint to set on upstream HTTPS server
connections. (Min: 0, Max: 63)

Process
-d Daemonize.
-u user Optional user to drop to if launched as root.
-g group Optional group to drop to if launched as root.

Logging
-v Increase logging verbosity. (Default: error)
Levels: fatal, stats, error, warning, info, debug
Request issues are logged on warning level.
-V Print version and exit.
-l logfile Path to file to log to. (Default: standard output)
-s statistic_interval Optional statistic printout interval.
(Default: 0, Disabled: 0, Min: 1, Max: 3600)
-F log_limit Flight recorder: storing desired amount of logs from all levels
in memory and dumping them on fatal error or on SIGUSR2 signal.
(Default: 0, Disabled: 0, Min: 100, Max: 100000)
-V Print versions and exit.
-h Print help and exit.
```

## Testing

Functional tests can be executed using [Robot Framework](https://robotframework.org/).

dig command is expected to be available.
dig and valgrind commands are expected to be available.

```
pip3 install robotframework
python3 -m robot.run tests/robot/functional_tests.robot
```

## Docker bootstrap DNS test

There is a repeatable Docker-based test suite for validating proxy behavior
including `-S` source address binding:

```
tests/docker/run_all_tests.sh
```

If your Docker CLI is not on `PATH`, you can set `DOCKER_BIN` to its full path.

## TODO

* Add some tests.
Expand All @@ -218,4 +252,3 @@ python3 -m robot.run tests/robot/functional_tests.robot
* Aaron Drew (aarond10@gmail.com): Original https_dns_proxy.
* Soumya ([github.com/soumya92](https://github.com/soumya92)): RFC 8484 implementation.
* baranyaib90 ([github.com/baranyaib90](https://github.com/baranyaib90)): fixes and improvements.

Loading