├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── COPYING ├── Makefile ├── OWNERS ├── README.md ├── argparse ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── argparse.c ├── argparse.h ├── tap-functions ├── test.sh └── test_argparse.c ├── block.c ├── block.h ├── cert.c ├── cert.h ├── compat.h ├── config.mak.in ├── configure.ac ├── debian ├── changelog ├── compat ├── control ├── dnscrypt-wrapper.8 ├── dnscrypt-wrapper.default ├── dnscrypt-wrapper.dirs ├── dnscrypt-wrapper.docs ├── dnscrypt-wrapper.init ├── dnscrypt-wrapper.manpages ├── dnscrypt-wrapper.postinst ├── dnscrypt-wrapper.preinst ├── dnscrypt-wrapper.service └── rules ├── debug.c ├── debug.h ├── dns-protocol.h ├── dnscrypt.c ├── dnscrypt.h ├── docs └── releasing.md ├── edns.c ├── edns.h ├── example ├── 1.cert ├── 1.key ├── README.md ├── public.key ├── secret.key ├── start_proxy.sh └── start_wrapper.sh ├── fpst.c ├── fpst.h ├── gen-version.sh ├── hack ├── Dockerfile.debian ├── entrypoint.sh ├── package.sh └── setup-travis.sh ├── logger.c ├── logger.h ├── main.c ├── pidfile.c ├── pidfile.h ├── rfc1035.c ├── rfc1035.h ├── safe_rw.c ├── safe_rw.h ├── tcp_request.c ├── tcp_request.h ├── tests ├── Gemfile ├── Gemfile.lock ├── Makefile ├── README.md ├── Rakefile ├── dnscrypt-fuzzer.py ├── features │ ├── cert-distribution │ │ └── txt_records.feature │ ├── cert-generation │ │ └── generate_certs.feature │ ├── step_definitions │ │ ├── dnscrypt-wrapper-cert.rb │ │ └── dnscrypt-wrapper.rb │ └── support │ │ └── aruba.rb ├── keys1 │ ├── 1.cert │ ├── 1.key │ ├── public.key │ └── secret.key └── keys2 │ ├── 1.cert │ ├── 1.key │ ├── 1.xchacha20.cert │ ├── 2.key │ ├── public.key │ └── secret.key ├── tree.h ├── udp_request.c ├── udp_request.h └── version.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.[aos] 2 | *.out 3 | *.tar.gz 4 | *.pyc 5 | *~ 6 | /tags 7 | /dnscrypt-wrapper 8 | /version.h 9 | /test-* 10 | !test-*.c 11 | *.so 12 | *.so.dSYM/ 13 | .depend/ 14 | /tests/tmp/ 15 | /config.log 16 | /configure 17 | /configure.lineno 18 | /autom4te.cache/ 19 | /config.mak.autogen 20 | /config.mak 21 | /config.status 22 | /MAIN-CFLAGS 23 | /MAIN-LDFLAGS 24 | *.pid 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - clang 4 | - gcc 5 | before_script: 6 | - sudo apt-get install libevent-dev 7 | - curl https://download.libsodium.org/libsodium/releases/LATEST.tar.gz | tar -xvzf - 8 | - cd libsodium-stable 9 | - ./configure 10 | - make && make check 11 | - sudo make install 12 | - sudo ldconfig 13 | - cd .. 14 | script: 15 | - autoreconf -vi 16 | - ./configure 17 | - make 18 | - sudo ./hack/setup-travis.sh 19 | - cd tests && bundle install && make test 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## Table of Contents 4 | 5 | * [v0.4.2](#v042) 6 | * [v0.4.1](#v041) 7 | * [v0.4.0](#v040) 8 | * [v0.3.0](#v030) 9 | * [v0.2.2](#v022) 10 | * [v0.2.1](#v021) 11 | * [v0.2.0](#v020) 12 | 13 | ## v0.4.2 14 | 15 | - Log level of "suspicious query" changed to debug 16 | 17 | ## v0.4.1 18 | 19 | - find_cert() should search in all certs, fixes #139. 20 | - filter_signed_certs() should converts serial to uint32_t before comparison. 21 | - --cert-file-expire-days supports 'd', 'h', 'm', 's' suffixes 22 | 23 | ## v0.4.0 24 | 25 | - Use sodium_malloc() for the DNS query/response buffers 26 | - Fix stamp properties; add --nofilter 27 | - Only publish the most recent certificates 28 | - Include the signature in SignedCert 29 | - cache: do not forget to include the server PK in the hash computation 30 | - Implement a simple cache for shared keys 31 | - Add support for stamps (dnscrypt-proxy 2.x), and update the documentation 32 | - In key rotation, old certs should be provided too, see #109. 33 | - fixes #111, cert/key expires in 24 hours by default for safety see discussion: https://github.com/jedisct1/dnscrypt-proxy/issues/520 34 | - docs: suggest user to generate short-term key pairs and use key-rotation mechanism See #111. 35 | 36 | ## v0.3.0 37 | 38 | - XChaCha20 supported 39 | - a lot of tests added 40 | - and many bug fixes and improvements 41 | 42 | ## v0.2.2 43 | 44 | - remove GPLv2, release under the ISC license 45 | - update example secret key / cert, etc 46 | - fix compiler/linker flags handling 47 | 48 | ## v0.2.1 49 | 50 | - Rename --provider-publickey-fingerprint to --show-provider-publickey-fingerprint. It's more conventional to use a verb if you want to do some action, like gen-provider-keypair. 51 | - Use TCP_QUICKACK instead of TCP_NODELAY if available (Linux 2.4.4+) See https://news.ycombinator.com/item?id=10608356 52 | - Send a short packet with TC set if the query_len < response_len 53 | - Support sending server cert over tcp 54 | - Use the certificate timestamp as a serial number instead of a fixed serial. 55 | - And some other minor fixes. 56 | 57 | ## v0.2.0 58 | 59 | - Import argparse sources files directly. 60 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | dnscrypt-wrapper is modified from dnscrypt-proxy, written by Yecheng Fu 3 | and covered by the following licenses: 4 | 5 | /* 6 | * Copyright (c) 2012-2016 Yecheng Fu 7 | * 8 | * Permission to use, copy, modify, and/or distribute this software for any 9 | * purpose with or without fee is hereby granted, provided that the above 10 | * copyright notice and this permission notice appear in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 | */ 20 | 21 | ==== 22 | 23 | The externally maintained libraries used by dnscrypt-wrapper are: 24 | 25 | - libevent (http://libevent.org/). 3-clause BSD license. 26 | See libevent/LICENSE. 27 | 28 | - argparse, See argparse/LICENSE. 29 | 30 | - dns-protocol.h, rfc1035.c, rfc1035.h reuses code from dnsmasq by 31 | Simon Kelley. 32 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # The default target of this Makefile is... 2 | all:: 3 | 4 | # Section starts with '###'. 5 | # 6 | # Define V=1 to have a more verbose compile. 7 | 8 | ### Defaults 9 | 10 | BASIC_CFLAGS = -std=c99 -Wall -I./argparse 11 | BASIC_LDFLAGS = -lm -lsodium 12 | 13 | # Guard against environment variables 14 | LIB_H = 15 | LIB_OBJS = 16 | DEP_LIBS = 17 | 18 | # Having this variable in your environment would break pipelines because you 19 | # case "cd" to echo its destination to stdout. 20 | unexport CDPATH 21 | 22 | ### Configurations 23 | 24 | uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 25 | uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') 26 | uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') 27 | uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') 28 | uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') 29 | uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') 30 | 31 | # CFLAGS and LDFLAGS are for users to override 32 | CFLAGS ?= -g -O2 -Wall 33 | LDFLAGS ?= 34 | STRIP ?= strip 35 | 36 | # We use ALL_* variants 37 | ALL_CFLAGS = $(BASIC_CFLAGS) $(CFLAGS) 38 | ALL_LDFLAGS = $(BASIC_LDFLAGS) $(LDFLAGS) 39 | 40 | ifdef PREFIX 41 | prefix = $(PREFIX) 42 | else 43 | prefix = /usr/local 44 | endif 45 | bindir = $(prefix)/bin 46 | sbindir = $(prefix)/sbin 47 | sharedir = $(prefix)/share 48 | mandir = share/man 49 | infodir = share/info 50 | export prefix bindir sbindir sharedir 51 | 52 | CC = cc 53 | RM = rm -rf 54 | INSTALL = install 55 | 56 | ifeq ($(uname_S),Linux) 57 | ALL_LDFLAGS += -lrt 58 | endif 59 | 60 | ifeq ($(uname_S),OpenBSD) 61 | BASIC_LDFLAGS += -levent_core 62 | else 63 | BASIC_LDFLAGS += -levent 64 | endif 65 | 66 | ifneq ($(findstring $(MAKEFLAGS),s), s) 67 | ifndef V 68 | QUIET_CC = @echo ' ' CC $@; 69 | QUIET_AR = @echo ' ' AR $@; 70 | QUIET_LINK = @echo ' ' LINK $@; 71 | QUIET_GEN = @echo ' ' GEN $@; 72 | endif 73 | endif 74 | 75 | # configuration generated by ./configure script 76 | -include config.mak.autogen 77 | # manual configuration 78 | -include config.mak 79 | 80 | ### Dependencies 81 | 82 | LIB_H = dnscrypt.h udp_request.h edns.h logger.h argparse/argparse.h 83 | 84 | LIB_OBJS += dnscrypt.o 85 | LIB_OBJS += udp_request.o 86 | LIB_OBJS += tcp_request.o 87 | LIB_OBJS += edns.o 88 | LIB_OBJS += logger.o 89 | LIB_OBJS += rfc1035.o 90 | LIB_OBJS += safe_rw.o 91 | LIB_OBJS += cert.o 92 | LIB_OBJS += pidfile.o 93 | LIB_OBJS += debug.o 94 | LIB_OBJS += fpst.o 95 | LIB_OBJS += block.o 96 | 97 | DEP_LIBS += argparse/libargparse.a 98 | 99 | ### Automatically dependencies rules 100 | 101 | OBJECTS := $(LIB_OBJS) main.o 102 | 103 | ifndef COMPUTE_HEADER_DEPENDENCIES 104 | COMPUTE_HEADER_DEPENDENCIES = auto 105 | endif 106 | 107 | ifeq ($(COMPUTE_HEADER_DEPENDENCIES),auto) 108 | dep_check = $(shell $(CC) \ 109 | -c -MF /dev/null -MMD -MP -x c /dev/null -o /dev/null 2>&1; \ 110 | echo $$?) 111 | ifeq ($(dep_check),0) 112 | override COMPUTE_HEADER_DEPENDENCIES = yes 113 | else 114 | override COMPUTE_HEADER_DEPENDENCIES = no 115 | endif 116 | endif 117 | 118 | ifeq ($(COMPUTE_HEADER_DEPENDENCIES),yes) 119 | USE_COMPUTED_HEADER_DEPENDENCIES = YesPlease 120 | else 121 | ifneq ($(COMPUTE_HEADER_DEPENDENCIES),no) 122 | $(error please set COMPUTE_HEADER_DEPENDENCIES to yes, no, or auto \ 123 | (not "$(COMPUTE_HEADER_DEPENDENCIES)")) 124 | endif 125 | endif 126 | 127 | dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d) 128 | dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS)))) 129 | 130 | ifeq ($(COMPUTE_HEADER_DEPENDENCIES),yes) 131 | $(dep_dirs): 132 | @mkdir -p $@ 133 | missing_dep_dirs := $(filter-out $(wildcard $(dep_dirs)),$(dep_dirs)) 134 | dep_file = $(dir $@).depend/$(notdir $@).d 135 | dep_args = -MF $(dep_file) -MMD -MP 136 | endif 137 | 138 | ifdef USE_COMPUTED_HEADER_DEPENDENCIES 139 | # Take advantage of gcc's on-the-fly dependency generation 140 | # See . 141 | dep_files_present := $(wildcard $(dep_files)) 142 | ifneq ($(dep_files_present),) 143 | include $(dep_files_present) 144 | endif 145 | else 146 | $(OBJECTS): $(LIB_H) 147 | endif 148 | 149 | ### Build rules 150 | 151 | configure: configure.ac 152 | $(QUIET_GEN)autoconf -o $@ $< 153 | 154 | ifdef AUTOCONFIGURED 155 | config.status: configure 156 | $(QUIET_GEN)if test -f config.status; then \ 157 | ./config.status --recheck; \ 158 | else \ 159 | ./configure; \ 160 | fi 161 | reconfigure config.mak.autogen: config.status 162 | $(QUIET_LINK)./config.status 163 | .PHONY: reconfigure # This is a convenience target. 164 | endif 165 | 166 | argparse/libargparse.a: argparse/argparse.h 167 | @$(MAKE) -C argparse libargparse.a 168 | 169 | argparse/argparse.h: 170 | git submodule update --init argparse 171 | 172 | $(LIB_OBJS): $(LIB_H) 173 | 174 | TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)) 175 | 176 | MAIN-CFLAGS: FORCE 177 | @FLAGS='$(TRACK_CFLAGS)'; \ 178 | if test x"$$FLAGS" != x"`cat MAIN-CFLAGS 2>/dev/null`"; then \ 179 | echo "$$FLAGS" > $@; \ 180 | fi 181 | 182 | TRACK_LDFLAGS = $(subst ','\'',$(ALL_LDFLAGS)) 183 | 184 | MAIN-LDFLAGS: FORCE 185 | @FLAGS='$(TRACK_LDFLAGS)'; \ 186 | if test x"$$FLAGS" != x"`cat MAIN-LDFLAGS 2>/dev/null`"; then \ 187 | echo "$$FLAGS" > $@; \ 188 | fi 189 | 190 | $(OBJECTS): %.o: %.c $(missing_dep_dirs) MAIN-CFLAGS 191 | $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $< 192 | 193 | dnscrypt-wrapper: $(OBJECTS) $(DEP_LIBS) MAIN-LDFLAGS 194 | $(QUIET_LINK)$(CC) -o $@ $(filter %.o %.a,$^) $(ALL_LDFLAGS) 195 | 196 | main.o: version.h 197 | 198 | all:: dnscrypt-wrapper 199 | 200 | version.h: FORCE 201 | @./gen-version.sh 202 | 203 | install: all 204 | $(INSTALL) -d -m 755 '$(DESTDIR)$(sbindir)' 205 | $(INSTALL) -p dnscrypt-wrapper '$(DESTDIR)$(sbindir)' 206 | 207 | uninstall: 208 | $(RM) $(sbindir)/dnscrypt-wrapper 209 | 210 | test: 211 | make -C tests 212 | 213 | clean: 214 | $(RM) dnscrypt-wrapper 215 | $(RM) $(LIB_OBJS) 216 | make -C argparse clean 217 | 218 | .PHONY: all install uninstall clean FORCE 219 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - cofyc 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Name 2 | 3 | dnscrypt-wrapper - A server-side dnscrypt proxy. 4 | 5 | [![Build Status](https://travis-ci.org/cofyc/dnscrypt-wrapper.png?branch=master)](https://travis-ci.org/cofyc/dnscrypt-wrapper) 6 | 7 | ## Table of Contents 8 | 9 | * [Description](#description) 10 | * [Installation](#installation) 11 | * [Usage](#usage) 12 | * [Quick start](#quick-start) 13 | * [Running unauthenticated DNS and the dnscrypt service on the same port](#running-unauthenticated-dns-and-the-dnscrypt-service-on-the-same-port) 14 | * [Key rotation](#key-rotation) 15 | * [Chinese](#chinese) 16 | * [See also](#see-also) 17 | 18 | ## Description 19 | 20 | This is dnscrypt wrapper (server-side dnscrypt proxy), which helps to 21 | add dnscrypt support to any name resolver. 22 | 23 | This software is modified from 24 | [dnscrypt-proxy](https://github.com/jedisct1/dnscrypt-proxy). 25 | 26 | ## Installation 27 | 28 | Install [libsodium](https://github.com/jedisct1/libsodium) and [libevent](http://libevent.org/) 2.1.1+ first. 29 | 30 | On Linux: 31 | 32 | $ ldconfig # if you install libsodium from source 33 | $ git clone git://github.com/cofyc/dnscrypt-wrapper.git 34 | $ cd dnscrypt-wrapper 35 | $ make configure 36 | $ ./configure 37 | $ make install 38 | 39 | On FreeBSD: 40 | 41 | $ pkg install dnscrypt-wrapper 42 | 43 | On OpenBSD: 44 | 45 | $ pkg_add -r gmake autoconf 46 | $ pkg_add -r libevent 47 | $ git clone git://github.com/cofyc/dnscrypt-wrapper.git 48 | $ cd dnscrypt-wrapper 49 | $ gmake LDFLAGS='-L/usr/local/lib/' CFLAGS=-I/usr/local/include/ 50 | 51 | On MacOS: 52 | 53 | $ brew install dnscrypt-wrapper 54 | 55 | In Docker: 56 | 57 | See https://github.com/jedisct1/dnscrypt-server-docker. 58 | 59 | ## Usage 60 | 61 | ### Quick Start 62 | 63 | 1) Generate the provider key pair: 64 | 65 | ```sh 66 | $ dnscrypt-wrapper --gen-provider-keypair \ 67 | --provider-name=2.dnscrypt-cert. --ext-address= 68 | ``` 69 | 70 | If your server doesn't store logs, add `--nolog` and if it supports DNSSEC, 71 | add `--dnssec`. 72 | 73 | This will create two files in the current directory: `public.key` and 74 | `secret.key`. 75 | 76 | This is a long-term key pair that is never supposed to change unless the 77 | secret key is compromised. Make sure that `secret.key` is securely 78 | stored and backuped. 79 | 80 | It will also print the stamp for dnscrypt-proxy version 2.x. 81 | 82 | If you forgot to save your provider public key: 83 | 84 | ```sh 85 | $ dnscrypt-wrapper --show-provider-publickey --provider-publickey-file 86 | ``` 87 | 88 | This will print it out. 89 | 90 | 2) Generate a time-limited secret key, which will be used to encrypt 91 | and authenticate DNS queries. Also generate a certificate for it: 92 | 93 | ```sh 94 | $ dnscrypt-wrapper --gen-crypt-keypair --crypt-secretkey-file=1.key 95 | $ dnscrypt-wrapper --gen-cert-file --crypt-secretkey-file=1.key --provider-cert-file=1.cert \ 96 | --provider-publickey-file=public.key --provider-secretkey-file=secret.key 97 | ``` 98 | 99 | In this example, the time-limited secret key will be saved as `1.key` 100 | and its related certificate as `1.cert` in the current directory. 101 | 102 | Time-limited secret keys and certificates can be updated at any time 103 | without requiring clients to update their configuration. 104 | 105 | NOTE: By default, secret key expires in 1 day (24 hours) for safety. You can 106 | change it by adding `--cert-file-expire-days=`, 107 | but it's better to use short-term secret key and use 108 | [key-rotation](#key-rotation) mechanism. 109 | 110 | 3) Run the program with a given key, a provider name and the most recent certificate: 111 | 112 | ```sh 113 | $ dnscrypt-wrapper --resolver-address=8.8.8.8:53 --listen-address=0.0.0.0:443 \ 114 | --provider-name=2.dnscrypt-cert. \ 115 | --crypt-secretkey-file=1.key --provider-cert-file=1.cert 116 | ``` 117 | 118 | The provider name can be anything; it doesn't have to be within an existing 119 | domain name. However, it has to start with `2.dnscrypt-cert.`, e.g. 120 | `2.dnscrypt-cert.example.com`. 121 | 122 | When the service is started with the `--provider-cert-file` switch, the 123 | proxy will automatically serve the certificate as a TXT record when a 124 | query for the provider name is received. 125 | 126 | As an alternative, the TXT record can be served by a name server for 127 | an actual DNS zone you are authoritative for. In that scenario, the 128 | `--provider-cert-file` option is not required, and instructions for 129 | Unbound and TinyDNS are displayed by the program when generating a 130 | provider certificate. 131 | 132 | You can get instructions later by running: 133 | 134 | ```sh 135 | $ dnscrypt-wrapper --show-provider-publickey-dns-records 136 | --provider-cert-file 137 | ``` 138 | 139 | 4) Run dnscrypt-proxy to check if it works: 140 | 141 | ```sh 142 | $ dnscrypt-proxy --local-address=127.0.0.1:55 --resolver-address=127.0.0.1:443 \ 143 | --provider-name=2.dnscrypt-cert. \ 144 | --provider-key= 145 | $ dig -p 55 google.com @127.0.0.1 146 | ``` 147 | 148 | `` is public key generated by `dnscrypt-wrapper --gen-provider-keypair`, which looks like `4298:5F65:C295:DFAE:2BFB:20AD:5C47:F565:78EB:2404:EF83:198C:85DB:68F1:3E33:E952`. 149 | 150 | Optionally, add `-d/--daemonize` flag to run as a daemon. 151 | 152 | Run `dnscrypt-wrapper -h` to view command line options. 153 | 154 | ### Running unauthenticated DNS and the dnscrypt service on the same port 155 | 156 | By default, and with the exception of records used for the 157 | certificates, only queries using the DNSCrypt protocol will be 158 | accepted. 159 | 160 | If you want to run a service only accessible using DNSCrypt, this is 161 | what you want. 162 | 163 | If you want to run a service accessible both with and without 164 | DNSCrypt, what you usually want is to keep the standard DNS port for 165 | the unauthenticated DNS service (53), and use a different port for 166 | DNSCrypt. You don't have to change anything for this either. 167 | 168 | However, if you want to run both on the same port, maybe because only 169 | port 53 is reachable on your server, you can add the `-U` 170 | (`--unauthenticated`) switch to the command-line. This is not 171 | recommended. 172 | 173 | ### Key rotation 174 | 175 | Time-limited keys are bound to expire. 176 | 177 | `dnscrypt-proxy` can check if the current key for a given server is 178 | not going to expire soon: 179 | 180 | ```sh 181 | $ dnscrypt-proxy --resolver-address=127.0.0.1:443 \ 182 | --provider-name=2.dnscrypt-cert. \ 183 | --provider-key= \ 184 | --test=10080 185 | ``` 186 | 187 | The `--test` option is followed by a "grace margin". 188 | 189 | The command will immediately exit after verifying the certificate validity. 190 | 191 | The exit code is `0` if a valid certificate can be used, `2` if no valid 192 | certificates can be used, `3` if a timeout occurred, and `4` if a currently 193 | valid certificate is going to expire before the margin. 194 | 195 | The margin is always specified in minutes. 196 | 197 | This can be used in a cron tab to trigger an alert before a key is 198 | going to expire. 199 | 200 | In order to switch to a fresh new key: 201 | 202 | First, create a new time-limited key (do not change the provider key!) and 203 | its certificate: 204 | 205 | ```sh 206 | $ dnscrypt-wrapper --gen-crypt-keypair --crypt-secretkey-file=2.key 207 | $ dnscrypt-wrapper --gen-cert-file --crypt-secretkey-file=2.key --provider-cert-file=2.cert \ 208 | --provider-publickey-file=public.key --provider-secretkey-file=secret.key \ 209 | --cert-file-expire-days=1 210 | ``` 211 | 212 | Second, Tell new users to use the new certificate but still accept the old 213 | key until all clients have loaded the new certificate: 214 | 215 | ```sh 216 | $ dnscrypt-wrapper --resolver-address=8.8.8.8:53 --listen-address=0.0.0.0:443 \ 217 | --provider-name=2.dnscrypt-cert. \ 218 | --crypt-secretkey-file=1.key,2.key --provider-cert-file=1.cert,2.cert 219 | ``` 220 | 221 | Note that both `1.key` and `2.key` have be specified, in order to 222 | accept both the previous and the current key. 223 | 224 | Third, Clients automatically check for new certificates every hour. So, 225 | after one hour, the old certificate can be refused, by leaving only 226 | the new one in the configuration: 227 | 228 | ```sh 229 | $ dnscrypt-wrapper --resolver-address=8.8.8.8:53 --listen-address=0.0.0.0:443 \ 230 | --provider-name=2.dnscrypt-cert. \ 231 | --crypt-secretkey-file=2.key --provider-cert-file=2.cert 232 | ``` 233 | 234 | Please note that on Linux systems (kernel >= 3.9), multiples instances of 235 | `dnscrypt-wrapper` can run at the same time. Therefore, in order to 236 | switch to a new configuration, one can start a new daemon without 237 | killing the previous instance, and only kill the previous instance 238 | after the new one started. 239 | 240 | This also allows upgrades with zero downtime. 241 | 242 | ## Blocking 243 | 244 | For servers willing to block specific domain names (ads, malware), the 245 | `--blacklist-file` parameter can be added. That blacklist file accepts 246 | patterns such as: 247 | 248 | - `example.com`: blocks `example.com` as well as `www.example.com` 249 | - `*.example.com`: identical, just more explicit 250 | - `*example*`: blocks the `example` substring no matter where it appears 251 | - `ads.*`: blocks the `ads.` prefix 252 | 253 | Prefix and suffix lookups are fast and can scale to very large lists. 254 | 255 | ## Chinese 256 | 257 | - CentOS/Debian/Ubuntu 下编译 dnscrypt-wrapper: http://03k.org/centos-make-dnscrypt-wrapper.html 258 | - dnscrypt-wrapper 使用方法: http://03k.org/dnscrypt-wrapper-usage.html 259 | 260 | 注:第三方文档可能未及时与最新版本同步,以 README.md 为准。 261 | 262 | ## See also 263 | 264 | - https://dnscrypt.info/ 265 | - https://github.com/jedisct1/dnscrypt-proxy 266 | - https://github.com/cofyc/dnscrypt-wrapper 267 | -------------------------------------------------------------------------------- /argparse/.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | test_argparse 3 | *.[ao] 4 | *.dylib 5 | *.so 6 | -------------------------------------------------------------------------------- /argparse/.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | - clang 5 | script: make test 6 | -------------------------------------------------------------------------------- /argparse/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2013 Yecheng Fu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /argparse/Makefile: -------------------------------------------------------------------------------- 1 | # Defaults 2 | CFLAGS ?= -O3 -g -ggdb 3 | LDFLAGS ?= 4 | 5 | BASIC_CFLAGS = -Wall -fPIC 6 | BASIC_LDFLAGS = -lm 7 | 8 | # We use ALL_* variants 9 | ALL_CFLAGS = $(BASIC_CFLAGS) $(CFLAGS) 10 | ALL_LDFLAGS = $(BASIC_LDFLAGS) $(LDFLAGS) 11 | 12 | LIBNAME=libargparse 13 | 14 | DYLIBSUFFIX=so 15 | STLIBSUFFIX=a 16 | DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX) 17 | DYLIB_MAKE_CMD=$(CC) -shared -o $(DYLIBNAME) $(ALL_LDFLAGS) 18 | STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) 19 | STLIB_MAKE_CMD=ar rcs $(STLIBNAME) 20 | 21 | # Platform-specific overrides 22 | uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 23 | ifeq ($(uname_S),Darwin) 24 | DYLIBSUFFIX=dylib 25 | DYLIB_MAKE_CMD=$(CC) -shared -o $(DYLIBNAME) $(ALL_LDFLAGS) 26 | endif 27 | 28 | all: $(DYLIBNAME) $(STLIBNAME) 29 | 30 | OBJS += argparse.o 31 | OBJS += test_argparse.o 32 | 33 | $(OBJS): %.o: %.c 34 | $(CC) -o $*.o -c $(ALL_CFLAGS) $< 35 | 36 | $(DYLIBNAME): argparse.o 37 | $(DYLIB_MAKE_CMD) $^ 38 | 39 | $(STLIBNAME): argparse.o 40 | $(STLIB_MAKE_CMD) $^ 41 | 42 | test: test_argparse 43 | @echo "###### Unit Test #####" 44 | @./test.sh 45 | 46 | test_argparse: $(OBJS) 47 | $(CC) $(ALL_CFLAGS) -o $@ $^ $(ALL_LDFLAGS) 48 | 49 | clean: 50 | rm -rf test_argparse 51 | rm -rf *.[ao] 52 | rm -rf *.dylib 53 | -------------------------------------------------------------------------------- /argparse/README.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | argparse - A command line arguments parsing library in C (compatible with C++). 5 | 6 | [![Build Status](https://travis-ci.org/Cofyc/argparse.png)](https://travis-ci.org/Cofyc/argparse) 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | This module is inspired by parse-options.c (git) and python's argparse 12 | module. 13 | 14 | Arguments parsing is common task in cli program, but traditional `getopt` 15 | libraries are not easy to use. This library provides high-level arguments 16 | parsing solutions. 17 | 18 | The program defines what arguments it requires, and `argparse` will figure 19 | out how to parse those out of `argc` and `argv`, it also automatically 20 | generates help and usage messages and issues errors when users give the 21 | program invalid arguments. 22 | 23 | Features 24 | ======== 25 | 26 | - handles both optional and positional arguments 27 | - produces highly informative usage messages 28 | - issues errors when given invalid arguments 29 | 30 | There are basically three types of options: 31 | 32 | - boolean options 33 | - options with mandatory argument 34 | - options with optional argument 35 | 36 | There are basically two forms of options: 37 | 38 | - short option consist of one dash (`-`) and one alphanumeric character. 39 | - long option begin with two dashes (`--`) and some alphanumeric characters. 40 | 41 | Short options may be bundled, e.g. `-a -b` can be specified as `-ab`. 42 | 43 | Options are case-sensitive. 44 | 45 | Options and non-option arguments can clearly be separated using the `--` option. 46 | 47 | Examples 48 | ======== 49 | 50 | ```c 51 | #include "argparse.h" 52 | 53 | static const char *const usage[] = { 54 | "test_argparse [options] [[--] args]", 55 | "test_argparse [options]", 56 | NULL, 57 | }; 58 | 59 | #define PERM_READ (1<<0) 60 | #define PERM_WRITE (1<<1) 61 | #define PERM_EXEC (1<<2) 62 | 63 | int 64 | main(int argc, const char **argv) 65 | { 66 | int force = 0; 67 | int test = 0; 68 | int num = 0; 69 | const char *path = NULL; 70 | int perms = 0; 71 | struct argparse_option options[] = { 72 | OPT_HELP(), 73 | OPT_GROUP("Basic options"), 74 | OPT_BOOLEAN('f', "force", &force, "force to do"), 75 | OPT_BOOLEAN('t', "test", &test, "test only"), 76 | OPT_STRING('p', "path", &path, "path to read"), 77 | OPT_INTEGER('n', "num", &num, "selected num"), 78 | OPT_GROUP("Bits options"), 79 | OPT_BIT(0, "read", &perms, "read perm", NULL, PERM_READ, OPT_NONEG), 80 | OPT_BIT(0, "write", &perms, "write perm", NULL, PERM_WRITE), 81 | OPT_BIT(0, "exec", &perms, "exec perm", NULL, PERM_EXEC), 82 | OPT_END(), 83 | }; 84 | 85 | struct argparse argparse; 86 | argparse_init(&argparse, options, usage, 0); 87 | argparse_describe(&argparse, "\nA brief description of what the program does and how it works.", "\nAdditional description of the program after the description of the arguments."); 88 | argc = argparse_parse(&argparse, argc, argv); 89 | if (force != 0) 90 | printf("force: %d\n", force); 91 | if (test != 0) 92 | printf("test: %d\n", test); 93 | if (path != NULL) 94 | printf("path: %s\n", path); 95 | if (num != 0) 96 | printf("num: %d\n", num); 97 | if (argc != 0) { 98 | printf("argc: %d\n", argc); 99 | int i; 100 | for (i = 0; i < argc; i++) { 101 | printf("argv[%d]: %s\n", i, *(argv + i)); 102 | } 103 | } 104 | if (perms) { 105 | printf("perms: %d\n", perms); 106 | } 107 | return 0; 108 | } 109 | ``` 110 | -------------------------------------------------------------------------------- /argparse/argparse.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2015 Yecheng Fu 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a MIT-style license that can be found 6 | * in the LICENSE file. 7 | */ 8 | #include "argparse.h" 9 | 10 | #define OPT_UNSET 1 11 | #define OPT_LONG 1 << 1 12 | 13 | static const char * 14 | prefix_skip(const char *str, const char *prefix) 15 | { 16 | size_t len = strlen(prefix); 17 | return strncmp(str, prefix, len) ? NULL : str + len; 18 | } 19 | 20 | static int 21 | prefix_cmp(const char *str, const char *prefix) 22 | { 23 | for (;; str++, prefix++) 24 | if (!*prefix) 25 | return 0; 26 | else if (*str != *prefix) 27 | return (unsigned char)*prefix - (unsigned char)*str; 28 | } 29 | 30 | static void 31 | argparse_error(struct argparse *self, const struct argparse_option *opt, 32 | const char *reason, int flags) 33 | { 34 | if (flags & OPT_LONG) { 35 | fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason); 36 | } else { 37 | fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason); 38 | } 39 | exit(1); 40 | } 41 | 42 | static int 43 | argparse_getvalue(struct argparse *self, const struct argparse_option *opt, 44 | int flags) 45 | { 46 | const char *s = NULL; 47 | if (!opt->value) 48 | goto skipped; 49 | switch (opt->type) { 50 | case ARGPARSE_OPT_BOOLEAN: 51 | if (flags & OPT_UNSET) { 52 | *(int *)opt->value = *(int *)opt->value - 1; 53 | } else { 54 | *(int *)opt->value = *(int *)opt->value + 1; 55 | } 56 | if (*(int *)opt->value < 0) { 57 | *(int *)opt->value = 0; 58 | } 59 | break; 60 | case ARGPARSE_OPT_BIT: 61 | if (flags & OPT_UNSET) { 62 | *(int *)opt->value &= ~opt->data; 63 | } else { 64 | *(int *)opt->value |= opt->data; 65 | } 66 | break; 67 | case ARGPARSE_OPT_STRING: 68 | if (self->optvalue) { 69 | *(const char **)opt->value = self->optvalue; 70 | self->optvalue = NULL; 71 | } else if (self->argc > 1) { 72 | self->argc--; 73 | *(const char **)opt->value = *++self->argv; 74 | } else { 75 | argparse_error(self, opt, "requires a value", flags); 76 | } 77 | break; 78 | case ARGPARSE_OPT_INTEGER: 79 | if (self->optvalue) { 80 | *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0); 81 | self->optvalue = NULL; 82 | } else if (self->argc > 1) { 83 | self->argc--; 84 | *(int *)opt->value = strtol(*++self->argv, (char **)&s, 0); 85 | } else { 86 | argparse_error(self, opt, "requires a value", flags); 87 | } 88 | if (s[0] != '\0') 89 | argparse_error(self, opt, "expects a numerical value", flags); 90 | break; 91 | default: 92 | assert(0); 93 | } 94 | 95 | skipped: 96 | if (opt->callback) { 97 | return opt->callback(self, opt); 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | static void 104 | argparse_options_check(const struct argparse_option *options) 105 | { 106 | for (; options->type != ARGPARSE_OPT_END; options++) { 107 | switch (options->type) { 108 | case ARGPARSE_OPT_END: 109 | case ARGPARSE_OPT_BOOLEAN: 110 | case ARGPARSE_OPT_BIT: 111 | case ARGPARSE_OPT_INTEGER: 112 | case ARGPARSE_OPT_STRING: 113 | case ARGPARSE_OPT_GROUP: 114 | continue; 115 | default: 116 | fprintf(stderr, "wrong option type: %d", options->type); 117 | break; 118 | } 119 | } 120 | } 121 | 122 | static int 123 | argparse_short_opt(struct argparse *self, const struct argparse_option *options) 124 | { 125 | for (; options->type != ARGPARSE_OPT_END; options++) { 126 | if (options->short_name == *self->optvalue) { 127 | self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL; 128 | return argparse_getvalue(self, options, 0); 129 | } 130 | } 131 | return -2; 132 | } 133 | 134 | static int 135 | argparse_long_opt(struct argparse *self, const struct argparse_option *options) 136 | { 137 | for (; options->type != ARGPARSE_OPT_END; options++) { 138 | const char *rest; 139 | int opt_flags = 0; 140 | if (!options->long_name) 141 | continue; 142 | 143 | rest = prefix_skip(self->argv[0] + 2, options->long_name); 144 | if (!rest) { 145 | // negation disabled? 146 | if (options->flags & OPT_NONEG) { 147 | continue; 148 | } 149 | // only OPT_BOOLEAN/OPT_BIT supports negation 150 | if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != 151 | ARGPARSE_OPT_BIT) { 152 | continue; 153 | } 154 | 155 | if (prefix_cmp(self->argv[0] + 2, "no-")) { 156 | continue; 157 | } 158 | rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name); 159 | if (!rest) 160 | continue; 161 | opt_flags |= OPT_UNSET; 162 | } 163 | if (*rest) { 164 | if (*rest != '=') 165 | continue; 166 | self->optvalue = rest + 1; 167 | } 168 | return argparse_getvalue(self, options, opt_flags | OPT_LONG); 169 | } 170 | return -2; 171 | } 172 | 173 | int 174 | argparse_init(struct argparse *self, struct argparse_option *options, 175 | const char *const *usages, int flags) 176 | { 177 | memset(self, 0, sizeof(*self)); 178 | self->options = options; 179 | self->usages = usages; 180 | self->flags = flags; 181 | self->description = NULL; 182 | self->epilog = NULL; 183 | return 0; 184 | } 185 | 186 | void 187 | argparse_describe(struct argparse *self, const char *description, 188 | const char *epilog) 189 | { 190 | self->description = description; 191 | self->epilog = epilog; 192 | } 193 | 194 | int 195 | argparse_parse(struct argparse *self, int argc, const char **argv) 196 | { 197 | self->argc = argc - 1; 198 | self->argv = argv + 1; 199 | self->out = argv; 200 | 201 | argparse_options_check(self->options); 202 | 203 | for (; self->argc; self->argc--, self->argv++) { 204 | const char *arg = self->argv[0]; 205 | if (arg[0] != '-' || !arg[1]) { 206 | if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) { 207 | goto end; 208 | } 209 | // if it's not option or is a single char '-', copy verbatim 210 | self->out[self->cpidx++] = self->argv[0]; 211 | continue; 212 | } 213 | // short option 214 | if (arg[1] != '-') { 215 | self->optvalue = arg + 1; 216 | switch (argparse_short_opt(self, self->options)) { 217 | case -1: 218 | break; 219 | case -2: 220 | goto unknown; 221 | } 222 | while (self->optvalue) { 223 | switch (argparse_short_opt(self, self->options)) { 224 | case -1: 225 | break; 226 | case -2: 227 | goto unknown; 228 | } 229 | } 230 | continue; 231 | } 232 | // if '--' presents 233 | if (!arg[2]) { 234 | self->argc--; 235 | self->argv++; 236 | break; 237 | } 238 | // long option 239 | switch (argparse_long_opt(self, self->options)) { 240 | case -1: 241 | break; 242 | case -2: 243 | goto unknown; 244 | } 245 | continue; 246 | 247 | unknown: 248 | fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]); 249 | argparse_usage(self); 250 | exit(1); 251 | } 252 | 253 | end: 254 | memmove(self->out + self->cpidx, self->argv, 255 | self->argc * sizeof(*self->out)); 256 | self->out[self->cpidx + self->argc] = NULL; 257 | 258 | return self->cpidx + self->argc; 259 | } 260 | 261 | void 262 | argparse_usage(struct argparse *self) 263 | { 264 | fprintf(stdout, "Usage: %s\n", *self->usages++); 265 | while (*self->usages && **self->usages) 266 | fprintf(stdout, " or: %s\n", *self->usages++); 267 | 268 | // print description 269 | if (self->description) 270 | fprintf(stdout, "%s\n", self->description); 271 | 272 | fputc('\n', stdout); 273 | 274 | const struct argparse_option *options; 275 | 276 | // figure out best width 277 | size_t usage_opts_width = 0; 278 | size_t len; 279 | options = self->options; 280 | for (; options->type != ARGPARSE_OPT_END; options++) { 281 | len = 0; 282 | if ((options)->short_name) { 283 | len += 2; 284 | } 285 | if ((options)->short_name && (options)->long_name) { 286 | len += 2; // separator ", " 287 | } 288 | if ((options)->long_name) { 289 | len += strlen((options)->long_name) + 2; 290 | } 291 | if (options->type == ARGPARSE_OPT_INTEGER) { 292 | len += strlen("="); 293 | } else if (options->type == ARGPARSE_OPT_STRING) { 294 | len += strlen("="); 295 | } 296 | len = ceil((float)len / 4) * 4; 297 | if (usage_opts_width < len) { 298 | usage_opts_width = len; 299 | } 300 | } 301 | usage_opts_width += 4; // 4 spaces prefix 302 | 303 | options = self->options; 304 | for (; options->type != ARGPARSE_OPT_END; options++) { 305 | size_t pos = 0; 306 | int pad = 0; 307 | if (options->type == ARGPARSE_OPT_GROUP) { 308 | fputc('\n', stdout); 309 | fprintf(stdout, "%s", options->help); 310 | fputc('\n', stdout); 311 | continue; 312 | } 313 | pos = fprintf(stdout, " "); 314 | if (options->short_name) { 315 | pos += fprintf(stdout, "-%c", options->short_name); 316 | } 317 | if (options->long_name && options->short_name) { 318 | pos += fprintf(stdout, ", "); 319 | } 320 | if (options->long_name) { 321 | pos += fprintf(stdout, "--%s", options->long_name); 322 | } 323 | if (options->type == ARGPARSE_OPT_INTEGER) { 324 | pos += fprintf(stdout, "="); 325 | } else if (options->type == ARGPARSE_OPT_STRING) { 326 | pos += fprintf(stdout, "="); 327 | } 328 | if (pos <= usage_opts_width) { 329 | pad = usage_opts_width - pos; 330 | } else { 331 | fputc('\n', stdout); 332 | pad = usage_opts_width; 333 | } 334 | fprintf(stdout, "%*s%s\n", pad + 2, "", options->help); 335 | } 336 | 337 | // print epilog 338 | if (self->epilog) 339 | fprintf(stdout, "%s\n", self->epilog); 340 | } 341 | 342 | int 343 | argparse_help_cb(struct argparse *self, const struct argparse_option *option) 344 | { 345 | (void)option; 346 | argparse_usage(self); 347 | exit(0); 348 | return 0; 349 | } 350 | -------------------------------------------------------------------------------- /argparse/argparse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2015 Yecheng Fu 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a MIT-style license that can be found 6 | * in the LICENSE file. 7 | */ 8 | #ifndef ARGPARSE_H 9 | #define ARGPARSE_H 10 | 11 | /* For c++ compatibility */ 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | struct argparse; 24 | struct argparse_option; 25 | 26 | typedef int argparse_callback (struct argparse *self, 27 | const struct argparse_option *option); 28 | 29 | enum argparse_flag { 30 | ARGPARSE_STOP_AT_NON_OPTION = 1, 31 | }; 32 | 33 | enum argparse_option_type { 34 | /* special */ 35 | ARGPARSE_OPT_END, 36 | ARGPARSE_OPT_GROUP, 37 | /* options with no arguments */ 38 | ARGPARSE_OPT_BOOLEAN, 39 | ARGPARSE_OPT_BIT, 40 | /* options with arguments (optional or required) */ 41 | ARGPARSE_OPT_INTEGER, 42 | ARGPARSE_OPT_STRING, 43 | }; 44 | 45 | enum argparse_option_flags { 46 | OPT_NONEG = 1, /* disable negation */ 47 | }; 48 | 49 | /** 50 | * argparse option 51 | * 52 | * `type`: 53 | * holds the type of the option, you must have an ARGPARSE_OPT_END last in your 54 | * array. 55 | * 56 | * `short_name`: 57 | * the character to use as a short option name, '\0' if none. 58 | * 59 | * `long_name`: 60 | * the long option name, without the leading dash, NULL if none. 61 | * 62 | * `value`: 63 | * stores pointer to the value to be filled. 64 | * 65 | * `help`: 66 | * the short help message associated to what the option does. 67 | * Must never be NULL (except for ARGPARSE_OPT_END). 68 | * 69 | * `callback`: 70 | * function is called when corresponding argument is parsed. 71 | * 72 | * `data`: 73 | * associated data. Callbacks can use it like they want. 74 | * 75 | * `flags`: 76 | * option flags. 77 | */ 78 | struct argparse_option { 79 | enum argparse_option_type type; 80 | const char short_name; 81 | const char *long_name; 82 | void *value; 83 | const char *help; 84 | argparse_callback *callback; 85 | intptr_t data; 86 | int flags; 87 | }; 88 | 89 | /** 90 | * argpparse 91 | */ 92 | struct argparse { 93 | // user supplied 94 | const struct argparse_option *options; 95 | const char *const *usages; 96 | int flags; 97 | const char *description; // a description after usage 98 | const char *epilog; // a description at the end 99 | // internal context 100 | int argc; 101 | const char **argv; 102 | const char **out; 103 | int cpidx; 104 | const char *optvalue; // current option value 105 | }; 106 | 107 | // built-in callbacks 108 | int argparse_help_cb(struct argparse *self, 109 | const struct argparse_option *option); 110 | 111 | // built-in option macros 112 | #define OPT_END() { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL } 113 | #define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } 114 | #define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } 115 | #define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } 116 | #define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } 117 | #define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL } 118 | #define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, \ 119 | "show this help message and exit", \ 120 | argparse_help_cb) 121 | 122 | int argparse_init(struct argparse *self, struct argparse_option *options, 123 | const char *const *usages, int flags); 124 | void argparse_describe(struct argparse *self, const char *description, 125 | const char *epilog); 126 | int argparse_parse(struct argparse *self, int argc, const char **argv); 127 | void argparse_usage(struct argparse *self); 128 | 129 | #ifdef __cplusplus 130 | } 131 | #endif 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /argparse/tap-functions: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | _version='1.02' 5 | 6 | _plan_set=0 7 | _no_plan=0 8 | _skip_all=0 9 | _test_died=0 10 | _expected_tests=0 11 | _executed_tests=0 12 | _failed_tests=0 13 | TODO= 14 | 15 | 16 | usage(){ 17 | cat <<'USAGE' 18 | tap-functions: A TAP-producing BASH library 19 | 20 | PLAN: 21 | plan_no_plan 22 | plan_skip_all [REASON] 23 | plan_tests NB_TESTS 24 | 25 | TEST: 26 | ok RESULT [NAME] 27 | okx COMMAND 28 | is RESULT EXPECTED [NAME] 29 | isnt RESULT EXPECTED [NAME] 30 | like RESULT PATTERN [NAME] 31 | unlike RESULT PATTERN [NAME] 32 | pass [NAME] 33 | fail [NAME] 34 | 35 | SKIP: 36 | skip [CONDITION] [REASON] [NB_TESTS=1] 37 | 38 | skip $feature_not_present "feature not present" 2 || { 39 | is $a "a" 40 | is $b "b" 41 | } 42 | 43 | TODO: 44 | Specify TODO mode by setting $TODO: 45 | TODO="not implemented yet" 46 | ok $result "some not implemented test" 47 | unset TODO 48 | 49 | OTHER: 50 | diag MSG 51 | 52 | EXAMPLE: 53 | #!/bin/bash 54 | 55 | . tap-functions 56 | 57 | plan_tests 7 58 | 59 | me=$USER 60 | is $USER $me "I am myself" 61 | like $HOME $me "My home is mine" 62 | like "`id`" $me "My id matches myself" 63 | 64 | /bin/ls $HOME 1>&2 65 | ok $? "/bin/ls $HOME" 66 | # Same thing using okx shortcut 67 | okx /bin/ls $HOME 68 | 69 | [[ "`id -u`" != "0" ]] 70 | i_am_not_root=$? 71 | skip $i_am_not_root "Must be root" || { 72 | okx ls /root 73 | } 74 | 75 | TODO="figure out how to become root..." 76 | okx [ "$HOME" == "/root" ] 77 | unset TODO 78 | USAGE 79 | exit 80 | } 81 | 82 | opt= 83 | set_u= 84 | while getopts ":sx" opt ; do 85 | case $_opt in 86 | u) set_u=1 ;; 87 | *) usage ;; 88 | esac 89 | done 90 | shift $(( OPTIND - 1 )) 91 | # Don't allow uninitialized variables if requested 92 | [[ -n "$set_u" ]] && set -u 93 | unset opt set_u 94 | 95 | # Used to call _cleanup on shell exit 96 | trap _exit EXIT 97 | 98 | 99 | 100 | plan_no_plan(){ 101 | (( _plan_set != 0 )) && "You tried to plan twice!" 102 | 103 | _plan_set=1 104 | _no_plan=1 105 | 106 | return 0 107 | } 108 | 109 | 110 | plan_skip_all(){ 111 | local reason=${1:-''} 112 | 113 | (( _plan_set != 0 )) && _die "You tried to plan twice!" 114 | 115 | _print_plan 0 "Skip $reason" 116 | 117 | _skip_all=1 118 | _plan_set=1 119 | _exit 0 120 | 121 | return 0 122 | } 123 | 124 | 125 | plan_tests(){ 126 | local tests=${1:?} 127 | 128 | (( _plan_set != 0 )) && _die "You tried to plan twice!" 129 | (( tests == 0 )) && _die "You said to run 0 tests! You've got to run something." 130 | 131 | _print_plan $tests 132 | _expected_tests=$tests 133 | _plan_set=1 134 | 135 | return $tests 136 | } 137 | 138 | 139 | _print_plan(){ 140 | local tests=${1:?} 141 | local directive=${2:-''} 142 | 143 | echo -n "1..$tests" 144 | [[ -n "$directive" ]] && echo -n " # $directive" 145 | echo 146 | } 147 | 148 | 149 | pass(){ 150 | local name=$1 151 | ok 0 "$name" 152 | } 153 | 154 | 155 | fail(){ 156 | local name=$1 157 | ok 1 "$name" 158 | } 159 | 160 | 161 | # This is the workhorse method that actually 162 | # prints the tests result. 163 | ok(){ 164 | local result=${1:?} 165 | local name=${2:-''} 166 | 167 | (( _plan_set == 0 )) && _die "You tried to run a test without a plan! Gotta have a plan." 168 | 169 | _executed_tests=$(( $_executed_tests + 1 )) 170 | 171 | if [[ -n "$name" ]] ; then 172 | if _matches "$name" "^[0-9]+$" ; then 173 | diag " You named your test '$name'. You shouldn't use numbers for your test names." 174 | diag " Very confusing." 175 | fi 176 | fi 177 | 178 | if (( result != 0 )) ; then 179 | echo -n "not " 180 | _failed_tests=$(( _failed_tests + 1 )) 181 | fi 182 | echo -n "ok $_executed_tests" 183 | 184 | if [[ -n "$name" ]] ; then 185 | local ename=${name//\#/\\#} 186 | echo -n " - $ename" 187 | fi 188 | 189 | if [[ -n "$TODO" ]] ; then 190 | echo -n " # TODO $TODO" ; 191 | if (( result != 0 )) ; then 192 | _failed_tests=$(( _failed_tests - 1 )) 193 | fi 194 | fi 195 | 196 | echo 197 | if (( result != 0 )) ; then 198 | local file='tap-functions' 199 | local func= 200 | local line= 201 | 202 | local i=0 203 | local bt=$(caller $i) 204 | while _matches "$bt" "tap-functions$" ; do 205 | i=$(( $i + 1 )) 206 | bt=$(caller $i) 207 | done 208 | local backtrace= 209 | eval $(caller $i | (read line func file ; echo "backtrace=\"$file:$func() at line $line.\"")) 210 | 211 | local t= 212 | [[ -n "$TODO" ]] && t="(TODO) " 213 | 214 | if [[ -n "$name" ]] ; then 215 | diag " Failed ${t}test '$name'" 216 | diag " in $backtrace" 217 | else 218 | diag " Failed ${t}test in $backtrace" 219 | fi 220 | fi 221 | 222 | return $result 223 | } 224 | 225 | 226 | okx(){ 227 | local command="$@" 228 | 229 | local line= 230 | diag "Output of '$command':" 231 | $command | while read line ; do 232 | diag "$line" 233 | done 234 | ok ${PIPESTATUS[0]} "$command" 235 | } 236 | 237 | 238 | _equals(){ 239 | local result=${1:?} 240 | local expected=${2:?} 241 | 242 | if [[ "$result" == "$expected" ]] ; then 243 | return 0 244 | else 245 | return 1 246 | fi 247 | } 248 | 249 | 250 | # Thanks to Aaron Kangas for the patch to allow regexp matching 251 | # under bash < 3. 252 | _bash_major_version=${BASH_VERSION%%.*} 253 | _matches(){ 254 | local result=${1:?} 255 | local pattern=${2:?} 256 | 257 | if [[ -z "$result" || -z "$pattern" ]] ; then 258 | return 1 259 | else 260 | if (( _bash_major_version >= 3 )) ; then 261 | eval '[[ "$result" =~ "$pattern" ]]' 262 | else 263 | echo "$result" | egrep -q "$pattern" 264 | fi 265 | fi 266 | } 267 | 268 | 269 | _is_diag(){ 270 | local result=${1:?} 271 | local expected=${2:?} 272 | 273 | diag " got: '$result'" 274 | diag " expected: '$expected'" 275 | } 276 | 277 | 278 | is(){ 279 | local result=${1:?} 280 | local expected=${2:?} 281 | local name=${3:-''} 282 | 283 | _equals "$result" "$expected" 284 | (( $? == 0 )) 285 | ok $? "$name" 286 | local r=$? 287 | (( r != 0 )) && _is_diag "$result" "$expected" 288 | return $r 289 | } 290 | 291 | 292 | isnt(){ 293 | local result=${1:?} 294 | local expected=${2:?} 295 | local name=${3:-''} 296 | 297 | _equals "$result" "$expected" 298 | (( $? != 0 )) 299 | ok $? "$name" 300 | local r=$? 301 | (( r != 0 )) && _is_diag "$result" "$expected" 302 | return $r 303 | } 304 | 305 | 306 | like(){ 307 | local result=${1:?} 308 | local pattern=${2:?} 309 | local name=${3:-''} 310 | 311 | _matches "$result" "$pattern" 312 | (( $? == 0 )) 313 | ok $? "$name" 314 | local r=$? 315 | (( r != 0 )) && diag " '$result' doesn't match '$pattern'" 316 | return $r 317 | } 318 | 319 | 320 | unlike(){ 321 | local result=${1:?} 322 | local pattern=${2:?} 323 | local name=${3:-''} 324 | 325 | _matches "$result" "$pattern" 326 | (( $? != 0 )) 327 | ok $? "$name" 328 | local r=$? 329 | (( r != 0 )) && diag " '$result' matches '$pattern'" 330 | return $r 331 | } 332 | 333 | 334 | skip(){ 335 | local condition=${1:?} 336 | local reason=${2:-''} 337 | local n=${3:-1} 338 | 339 | if (( condition == 0 )) ; then 340 | local i= 341 | for (( i=0 ; i<$n ; i++ )) ; do 342 | _executed_tests=$(( _executed_tests + 1 )) 343 | echo "ok $_executed_tests # skip: $reason" 344 | done 345 | return 0 346 | else 347 | return 348 | fi 349 | } 350 | 351 | 352 | diag(){ 353 | local msg=${1:?} 354 | 355 | if [[ -n "$msg" ]] ; then 356 | echo "# $msg" 357 | fi 358 | 359 | return 1 360 | } 361 | 362 | 363 | _die(){ 364 | local reason=${1:-''} 365 | 366 | echo "$reason" >&2 367 | _test_died=1 368 | _exit 255 369 | } 370 | 371 | 372 | BAIL_OUT(){ 373 | local reason=${1:-''} 374 | 375 | echo "Bail out! $reason" >&2 376 | _exit 255 377 | } 378 | 379 | 380 | _cleanup(){ 381 | local rc=0 382 | 383 | if (( _plan_set == 0 )) ; then 384 | diag "Looks like your test died before it could output anything." 385 | return $rc 386 | fi 387 | 388 | if (( _test_died != 0 )) ; then 389 | diag "Looks like your test died just after $_executed_tests." 390 | return $rc 391 | fi 392 | 393 | if (( _skip_all == 0 && _no_plan != 0 )) ; then 394 | _print_plan $_executed_tests 395 | fi 396 | 397 | local s= 398 | if (( _no_plan == 0 && _expected_tests < _executed_tests )) ; then 399 | s= ; (( _expected_tests > 1 )) && s=s 400 | local extra=$(( _executed_tests - _expected_tests )) 401 | diag "Looks like you planned $_expected_tests test$s but ran $extra extra." 402 | rc=-1 ; 403 | fi 404 | 405 | if (( _no_plan == 0 && _expected_tests > _executed_tests )) ; then 406 | s= ; (( _expected_tests > 1 )) && s=s 407 | diag "Looks like you planned $_expected_tests test$s but only ran $_executed_tests." 408 | fi 409 | 410 | if (( _failed_tests > 0 )) ; then 411 | s= ; (( _failed_tests > 1 )) && s=s 412 | diag "Looks like you failed $_failed_tests test$s of $_executed_tests." 413 | fi 414 | 415 | return $rc 416 | } 417 | 418 | 419 | _exit_status(){ 420 | if (( _no_plan != 0 || _plan_set == 0 )) ; then 421 | return $_failed_tests 422 | fi 423 | 424 | if (( _expected_tests < _executed_tests )) ; then 425 | return $(( _executed_tests - _expected_tests )) 426 | fi 427 | 428 | return $(( _failed_tests + ( _expected_tests - _executed_tests ))) 429 | } 430 | 431 | 432 | _exit(){ 433 | local rc=${1:-''} 434 | if [[ -z "$rc" ]] ; then 435 | _exit_status 436 | rc=$? 437 | fi 438 | 439 | _cleanup 440 | local alt_rc=$? 441 | (( alt_rc != 0 )) && rc=$alt_rc 442 | trap - EXIT 443 | exit $rc 444 | } 445 | 446 | -------------------------------------------------------------------------------- /argparse/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . tap-functions 4 | plan_no_plan 5 | 6 | is "$(./test_argparse -f --path=/path/to/file a 2>&1)" 'force: 1 7 | path: /path/to/file 8 | argc: 1 9 | argv[0]: a' 10 | 11 | is "$(./test_argparse -f -f --force --no-force 2>&1)" 'force: 2' 12 | 13 | is "$(./test_argparse -n 2>&1)" 'error: option `-n` requires a value' 14 | 15 | is "$(./test_argparse -n 2 2>&1)" 'num: 2' 16 | 17 | is "$(./test_argparse -n2 2>&1)" 'num: 2' 18 | 19 | is "$(./test_argparse -na 2>&1)" 'error: option `-n` expects a numerical value' 20 | 21 | is "$(./test_argparse -f -- do -f -h 2>&1)" 'force: 1 22 | argc: 3 23 | argv[0]: do 24 | argv[1]: -f 25 | argv[2]: -h' 26 | 27 | is "$(./test_argparse -tf 2>&1)" 'force: 1 28 | test: 1' 29 | 30 | is "$(./test_argparse --read --write 2>&1)" 'perms: 3' 31 | 32 | is "$(./test_argparse -h)" 'Usage: test_argparse [options] [[--] args] 33 | or: test_argparse [options] 34 | 35 | A brief description of what the program does and how it works. 36 | 37 | -h, --help show this help message and exit 38 | 39 | Basic options 40 | -f, --force force to do 41 | -t, --test test only 42 | -p, --path= path to read 43 | -n, --num= selected num 44 | 45 | Bits options 46 | --read read perm 47 | --write write perm 48 | --exec exec perm 49 | 50 | Additional description of the program after the description of the arguments.' 51 | -------------------------------------------------------------------------------- /argparse/test_argparse.c: -------------------------------------------------------------------------------- 1 | #include "argparse.h" 2 | 3 | static const char *const usages[] = { 4 | "test_argparse [options] [[--] args]", 5 | "test_argparse [options]", 6 | NULL, 7 | }; 8 | 9 | #define PERM_READ (1<<0) 10 | #define PERM_WRITE (1<<1) 11 | #define PERM_EXEC (1<<2) 12 | 13 | int 14 | main(int argc, const char **argv) 15 | { 16 | int force = 0; 17 | int test = 0; 18 | int num = 0; 19 | const char *path = NULL; 20 | int perms = 0; 21 | struct argparse_option options[] = { 22 | OPT_HELP(), 23 | OPT_GROUP("Basic options"), 24 | OPT_BOOLEAN('f', "force", &force, "force to do"), 25 | OPT_BOOLEAN('t', "test", &test, "test only"), 26 | OPT_STRING('p', "path", &path, "path to read"), 27 | OPT_INTEGER('n', "num", &num, "selected num"), 28 | OPT_GROUP("Bits options"), 29 | OPT_BIT(0, "read", &perms, "read perm", NULL, PERM_READ, OPT_NONEG), 30 | OPT_BIT(0, "write", &perms, "write perm", NULL, PERM_WRITE), 31 | OPT_BIT(0, "exec", &perms, "exec perm", NULL, PERM_EXEC), 32 | OPT_END(), 33 | }; 34 | 35 | struct argparse argparse; 36 | argparse_init(&argparse, options, usages, 0); 37 | argparse_describe(&argparse, "\nA brief description of what the program does and how it works.", "\nAdditional description of the program after the description of the arguments."); 38 | argc = argparse_parse(&argparse, argc, argv); 39 | if (force != 0) 40 | printf("force: %d\n", force); 41 | if (test != 0) 42 | printf("test: %d\n", test); 43 | if (path != NULL) 44 | printf("path: %s\n", path); 45 | if (num != 0) 46 | printf("num: %d\n", num); 47 | if (argc != 0) { 48 | printf("argc: %d\n", argc); 49 | int i; 50 | for (i = 0; i < argc; i++) { 51 | printf("argv[%d]: %s\n", i, *(argv + i)); 52 | } 53 | } 54 | if (perms) { 55 | printf("perms: %d\n", perms); 56 | } 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /block.c: -------------------------------------------------------------------------------- 1 | 2 | #include "compat.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "dnscrypt.h" 14 | #include "fpst.h" 15 | 16 | #define MAX_QNAME_LENGTH 255U 17 | 18 | typedef enum BlockType { 19 | BLOCKTYPE_UNDEFINED, 20 | BLOCKTYPE_EXACT, 21 | BLOCKTYPE_PREFIX, 22 | BLOCKTYPE_SUFFIX, 23 | BLOCKTYPE_SUBSTRING 24 | } BlockType; 25 | 26 | typedef struct Blocking_ { 27 | FPST *domains; 28 | FPST *domains_rev; 29 | FPST *domains_substr; 30 | } Blocking; 31 | 32 | static char * 33 | skip_spaces(char *str) 34 | { 35 | while (*str != 0 && isspace((int) (unsigned char) *str)) { 36 | str++; 37 | } 38 | return str; 39 | } 40 | 41 | static char * 42 | skip_chars(char *str) 43 | { 44 | while (*str != 0 && !isspace((int) (unsigned char) *str)) { 45 | str++; 46 | } 47 | return str; 48 | } 49 | 50 | static void 51 | str_tolower(char *str) 52 | { 53 | while (*str != 0) { 54 | *str = (char) tolower((unsigned char) *str); 55 | str++; 56 | } 57 | } 58 | 59 | static void 60 | str_reverse(char *str) 61 | { 62 | size_t i = 0; 63 | size_t j = strlen(str); 64 | char t; 65 | 66 | while (i < j) { 67 | t = str[i]; 68 | str[i++] = str[--j]; 69 | str[j] = t; 70 | } 71 | } 72 | 73 | static char * 74 | untab(char *line) 75 | { 76 | char *ptr; 77 | 78 | while ((ptr = strchr(line, '\t')) != NULL) { 79 | *ptr = ' '; 80 | } 81 | return line; 82 | } 83 | 84 | static char * 85 | trim_comments(char *line) 86 | { 87 | char *ptr; 88 | char *s1; 89 | char *s2; 90 | 91 | while ((ptr = strchr(line, '\n')) != NULL || 92 | (ptr = strchr(line, '\r')) != NULL) { 93 | *ptr = 0; 94 | } 95 | line = skip_spaces(line); 96 | if (*line == 0 || *line == '#') { 97 | return NULL; 98 | } 99 | s1 = skip_chars(line); 100 | if (*(s2 = skip_spaces(s1)) == 0) { 101 | *s1 = 0; 102 | return line; 103 | } 104 | if (*s2 == '#') { 105 | return NULL; 106 | } 107 | *skip_chars(s2) = 0; 108 | 109 | return s2; 110 | } 111 | 112 | static void 113 | free_list(const char *key, uint32_t val) 114 | { 115 | (void) val; 116 | free((void *) key); 117 | } 118 | 119 | static int 120 | parse_domain_list(FPST ** const domain_list_p, 121 | FPST ** const domain_rev_list_p, 122 | FPST ** const domain_substr_list_p, 123 | const char * const file) 124 | { 125 | char buf[MAX_QNAME_LENGTH + 1U]; 126 | char *line; 127 | FILE *fp; 128 | FPST *domain_list; 129 | FPST *domain_list_tmp; 130 | FPST *domain_rev_list; 131 | FPST *domain_rev_list_tmp; 132 | FPST *domain_substr_list; 133 | FPST *domain_substr_list_tmp; 134 | size_t line_len; 135 | BlockType block_type = BLOCKTYPE_UNDEFINED; 136 | int ret = -1; 137 | 138 | assert(domain_list_p != NULL); 139 | assert(domain_rev_list_p != NULL); 140 | assert(domain_substr_list_p != NULL); 141 | *domain_list_p = NULL; 142 | *domain_rev_list_p = NULL; 143 | *domain_substr_list_p = NULL; 144 | domain_list = fpst_new(); 145 | domain_rev_list = fpst_new(); 146 | domain_substr_list = fpst_new(); 147 | if ((fp = fopen(file, "r")) == NULL) { 148 | return -1; 149 | } 150 | while (fgets(buf, (int) sizeof buf, fp) != NULL) { 151 | if ((line = trim_comments(untab(buf))) == NULL || *line == 0) { 152 | continue; 153 | } 154 | line_len = strlen(line); 155 | if (line[0] == '*' && line[line_len - 1] == '*') { 156 | line[line_len - 1] = 0; 157 | line++; 158 | block_type = BLOCKTYPE_SUBSTRING; 159 | } else if (line[line_len - 1] == '*') { 160 | line[line_len - 1] = 0; 161 | block_type = BLOCKTYPE_PREFIX; 162 | } else { 163 | if (line[0] == '*') { 164 | line++; 165 | } 166 | if (line[0] == '.') { 167 | line++; 168 | } 169 | str_reverse(line); 170 | block_type = BLOCKTYPE_SUFFIX; 171 | } 172 | if (*line == 0) { 173 | continue; 174 | } 175 | str_tolower(line); 176 | if ((line = strdup(line)) == NULL) { 177 | break; 178 | } 179 | if (block_type == BLOCKTYPE_SUFFIX) { 180 | if ((domain_rev_list_tmp = fpst_insert_str(domain_rev_list, line, 181 | (uint32_t) block_type)) == NULL) { 182 | free(line); 183 | break; 184 | } 185 | domain_rev_list = domain_rev_list_tmp; 186 | } else if (block_type == BLOCKTYPE_PREFIX) { 187 | if ((domain_list_tmp = fpst_insert_str(domain_list, line, 188 | (uint32_t) block_type)) == NULL) { 189 | free(line); 190 | break; 191 | } 192 | domain_list = domain_list_tmp; 193 | } else if (block_type == BLOCKTYPE_SUBSTRING) { 194 | if ((domain_substr_list_tmp = fpst_insert_str(domain_substr_list, line, 195 | (uint32_t) block_type)) == NULL) { 196 | free(line); 197 | break; 198 | } 199 | domain_substr_list = domain_substr_list_tmp; 200 | } else { 201 | free(line); 202 | } 203 | } 204 | if (!feof(fp)) { 205 | fpst_free(domain_list, free_list); 206 | fpst_free(domain_rev_list, free_list); 207 | fpst_free(domain_substr_list, free_list); 208 | } else { 209 | *domain_list_p = domain_list; 210 | *domain_rev_list_p = domain_rev_list; 211 | *domain_substr_list_p = domain_substr_list; 212 | ret = 0; 213 | } 214 | fclose(fp); 215 | logger(LOG_INFO, "Blacklist [%s] loaded", file); 216 | 217 | return ret; 218 | } 219 | 220 | static _Bool 221 | substr_match(FPST *list, const char *str, 222 | const char **found_key_p, uint32_t *found_block_type_p) 223 | { 224 | while (*str != 0) { 225 | if (fpst_str_starts_with_existing_key(list, str, found_key_p, 226 | found_block_type_p)) { 227 | return 1; 228 | } 229 | str++; 230 | } 231 | return 0; 232 | } 233 | 234 | int 235 | blocking_init(struct context *c, const char *file) 236 | { 237 | Blocking *blocking; 238 | 239 | if ((blocking = calloc((size_t) 1U, sizeof *blocking)) == NULL) { 240 | return -1; 241 | } 242 | c->blocking = blocking; 243 | blocking->domains = NULL; 244 | blocking->domains_rev = NULL; 245 | blocking->domains_substr = NULL; 246 | 247 | return parse_domain_list(&blocking->domains, &blocking->domains_rev, 248 | &blocking->domains_substr, file); 249 | } 250 | 251 | void 252 | blocking_free(struct context *c) 253 | { 254 | Blocking *blocking = c->blocking; 255 | 256 | if (blocking == NULL) { 257 | return; 258 | } 259 | fpst_free(blocking->domains, free_list); 260 | blocking->domains = NULL; 261 | fpst_free(blocking->domains_rev, free_list); 262 | blocking->domains_rev = NULL; 263 | fpst_free(blocking->domains_substr, free_list); 264 | blocking->domains_substr = NULL; 265 | free(blocking); 266 | } 267 | 268 | void 269 | str_lcpy(char *dst, const char *src, size_t dsize) 270 | { 271 | size_t nleft = dsize; 272 | 273 | if (nleft != 0) { 274 | while (--nleft != 0) { 275 | if ((*dst++ = *src++) == 0) { 276 | break; 277 | } 278 | } 279 | } 280 | if (nleft == 0 && dsize != 0) { 281 | *dst = 0; 282 | } 283 | } 284 | 285 | static int 286 | name_matches_blacklist(const Blocking * const blocking, char * const name) 287 | { 288 | char rev[MAX_QNAME_LENGTH + 1U]; 289 | const char *found_key; 290 | size_t name_len; 291 | uint32_t found_block_type; 292 | _Bool block = 0; 293 | 294 | rev[MAX_QNAME_LENGTH] = 0; 295 | name_len = strlen(name); 296 | if (name_len >= sizeof rev) { 297 | return -1; 298 | } 299 | if (name_len > (size_t) 1U && name[name_len - 1U] == '.') { 300 | name[--name_len] = 0; 301 | } 302 | if (name_len <= 0) { 303 | return 0; 304 | } 305 | str_tolower(name); 306 | do { 307 | str_lcpy(rev, name, sizeof rev); 308 | str_reverse(rev); 309 | if (fpst_starts_with_existing_key(blocking->domains_rev, 310 | rev, name_len, 311 | &found_key, &found_block_type)) { 312 | const size_t found_key_len = strlen(found_key); 313 | 314 | assert(found_block_type == BLOCKTYPE_SUFFIX); 315 | if (found_key_len <= name_len && 316 | (rev[found_key_len] == 0 || rev[found_key_len] == '.')) { 317 | block = 1; 318 | break; 319 | } 320 | if (found_key_len < name_len) { 321 | size_t owner_part_len = name_len; 322 | 323 | while (owner_part_len > 0U && rev[owner_part_len] != '.') { 324 | owner_part_len--; 325 | } 326 | rev[owner_part_len] = 0; 327 | if (owner_part_len > 0U && fpst_starts_with_existing_key 328 | (blocking->domains_rev, rev, owner_part_len, 329 | &found_key, &found_block_type)) { 330 | const size_t found_key_len = strlen(found_key); 331 | if (found_key_len <= owner_part_len && 332 | (rev[found_key_len] == 0 || rev[found_key_len] == '.')) { 333 | block = 1; 334 | break; 335 | } 336 | } 337 | } 338 | } 339 | if (fpst_starts_with_existing_key(blocking->domains, 340 | name, name_len, 341 | &found_key, &found_block_type)) { 342 | assert(found_block_type == BLOCKTYPE_PREFIX); 343 | block = 1; 344 | break; 345 | } 346 | if (blocking->domains_substr != NULL && 347 | substr_match(blocking->domains_substr, name, 348 | &found_key, &found_block_type)) { 349 | assert(found_block_type == BLOCKTYPE_SUBSTRING); 350 | block = 1; 351 | break; 352 | } 353 | } while (0); 354 | 355 | return (int) block; 356 | } 357 | 358 | int 359 | is_blocked(struct context *c, struct dns_header *header, size_t dns_query_len) 360 | { 361 | unsigned char *ansp; 362 | unsigned char *p; 363 | char *name; 364 | 365 | if (c->blocking == NULL) { 366 | return 0; 367 | } 368 | if (ntohs(header->qdcount) != 1) { 369 | return -1; 370 | } 371 | if (!(ansp = skip_questions(header, dns_query_len))) { 372 | return -1; 373 | } 374 | p = (unsigned char *)(header + 1); 375 | if (!extract_name(header, dns_query_len, &p, c->namebuff, 1, 4)) { 376 | return -1; 377 | } 378 | name = c->namebuff; 379 | 380 | return name_matches_blacklist(c->blocking, name); 381 | } 382 | -------------------------------------------------------------------------------- /block.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCK_H 2 | #define BLOCK_H 3 | 4 | int blocking_init(struct context *c, const char *file); 5 | 6 | void blocking_free(struct context *c); 7 | 8 | int is_blocked(struct context *c, struct dns_header *header, size_t dns_query_len); 9 | 10 | #endif 11 | 12 | -------------------------------------------------------------------------------- /cert.c: -------------------------------------------------------------------------------- 1 | #include "dnscrypt.h" 2 | 3 | struct SignedCert * 4 | cert_build_cert(const uint8_t *crypt_publickey, int cert_file_expire_seconds, 5 | int use_xchacha20) 6 | { 7 | struct SignedCert *signed_cert = malloc(sizeof(struct SignedCert)); 8 | if (!signed_cert) 9 | return NULL; 10 | 11 | memcpy(signed_cert->magic_cert, CERT_MAGIC_CERT, 4); 12 | signed_cert->version_major[0] = 0; 13 | if (use_xchacha20) { 14 | signed_cert->version_major[1] = 2; 15 | } else { 16 | signed_cert->version_major[1] = 1; 17 | } 18 | signed_cert->version_minor[0] = 0; 19 | signed_cert->version_minor[1] = 0; 20 | 21 | memset(signed_cert->signature, 0, sizeof signed_cert->signature); 22 | memcpy(signed_cert->server_publickey, crypt_publickey, 23 | crypto_box_PUBLICKEYBYTES); 24 | memcpy(signed_cert->magic_query, crypt_publickey, 25 | sizeof(signed_cert->magic_query)); 26 | if (use_xchacha20) { 27 | sodium_increment(signed_cert->magic_query, sizeof signed_cert->magic_query); 28 | } 29 | uint32_t ts_begin = (uint32_t)time(NULL); 30 | uint32_t ts_end = ts_begin + cert_file_expire_seconds; 31 | if (cert_file_expire_seconds <= 0) { 32 | ts_begin = ts_end; 33 | } 34 | ts_begin = htonl(ts_begin); 35 | ts_end = htonl(ts_end); 36 | memcpy(signed_cert->serial, &ts_begin, 4); 37 | memcpy(signed_cert->ts_begin, &ts_begin, 4); 38 | memcpy(signed_cert->ts_end, &ts_end, 4); 39 | 40 | return signed_cert; 41 | } 42 | 43 | int 44 | cert_sign(struct SignedCert *signed_cert, const uint8_t *provider_secretkey) 45 | { 46 | unsigned long long signed_data_len = 47 | sizeof(struct SignedCert) - offsetof(struct SignedCert, 48 | server_publickey); 49 | 50 | return crypto_sign_detached(signed_cert->signature, NULL, 51 | signed_cert->server_publickey, signed_data_len, 52 | provider_secretkey); 53 | } 54 | 55 | void 56 | cert_display_txt_record_tinydns(struct SignedCert *signed_cert) 57 | { 58 | size_t i = (size_t) 0U; 59 | int c; 60 | 61 | fputs("'2.dnscrypt-cert:", stdout); 62 | while (i < sizeof(struct SignedCert)) { 63 | c = (int)*((const uint8_t *) signed_cert + i); 64 | if (isprint(c) && c != ':' && c != '\\' && c != '&' && c != '<' 65 | && c != '>') { 66 | putchar(c); 67 | } else { 68 | printf("\\%03o", c); 69 | } 70 | i++; 71 | } 72 | puts(":86400"); 73 | } 74 | 75 | void 76 | cert_display_txt_record(struct SignedCert *signed_cert) 77 | { 78 | size_t i = (size_t) 0U; 79 | int c; 80 | 81 | fputs("2.dnscrypt-cert\t86400\tIN\tTXT\t\"", stdout); 82 | while (i < sizeof(struct SignedCert)) { 83 | c = (int)*((const uint8_t *) signed_cert + i); 84 | if (isprint(c) && c != '"' && c != '\\') { 85 | putchar(c); 86 | } else { 87 | printf("\\%03d", c); 88 | } 89 | i++; 90 | } 91 | puts("\""); 92 | } 93 | -------------------------------------------------------------------------------- /cert.h: -------------------------------------------------------------------------------- 1 | #ifndef CERT_H 2 | #define CERT_H 3 | 4 | #include 5 | #define CERT_MAGIC_CERT "DNSC" 6 | #define CERT_MAJOR_VERSION 1 7 | #define CERT_MINOR_VERSION 0 8 | #define CERT_OLD_MAGIC_HEADER "7PYqwfzt" 9 | 10 | #define CERT_FILE_EXPIRE_DAYS 1 11 | 12 | struct SignedCert { 13 | uint8_t magic_cert[4]; 14 | uint8_t version_major[2]; 15 | uint8_t version_minor[2]; 16 | 17 | uint8_t signature[crypto_sign_BYTES]; 18 | // Signed Content 19 | uint8_t server_publickey[crypto_box_PUBLICKEYBYTES]; 20 | uint8_t magic_query[8]; 21 | uint8_t serial[4]; 22 | uint8_t ts_begin[4]; 23 | uint8_t ts_end[4]; 24 | }; 25 | 26 | struct Cert { 27 | uint8_t magic_cert[4]; 28 | uint8_t version_major[2]; 29 | uint8_t version_minor[2]; 30 | 31 | // Signed Content 32 | uint8_t signed_content[64]; 33 | uint8_t server_publickey[crypto_box_PUBLICKEYBYTES]; 34 | uint8_t magic_query[8]; 35 | uint8_t serial[4]; 36 | uint8_t ts_begin[4]; 37 | uint8_t ts_end[4]; 38 | }; 39 | 40 | 41 | struct SignedCert *cert_build_cert(const uint8_t *crypt_publickey, int cert_file_expire_days, int use_xchacha20); 42 | int cert_sign(struct SignedCert *signed_cert, 43 | const uint8_t *provider_secretkey); 44 | void cert_display_txt_record_tinydns(struct SignedCert *signed_cert); 45 | void cert_display_txt_record(struct SignedCert *signed_cert); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /compat.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_H 2 | #define COMPAT_H 3 | 4 | #define _GNU_SOURCE 5 | 6 | /* ISO C */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[1])) 44 | #define COMPILER_ASSERT(X) (void)sizeof(char[(X) ? 1 : -1]) 45 | 46 | /* Test for backtrace() */ 47 | #if defined(__APPLE__) || (defined(__linux__) && defined(__GLIBC__)) 48 | #define HAVE_BACKTRACE 1 49 | #endif 50 | 51 | #if defined(__linux__) || defined(__OpenBSD__) 52 | #define _XOPEN_SOURCE 700 53 | /* 54 | * * On NetBSD, _XOPEN_SOURCE undefines _NETBSD_SOURCE and 55 | * * thus hides inet_aton etc. 56 | * */ 57 | #elif !defined(__NetBSD__) 58 | #define _XOPEN_SOURCE 59 | #endif 60 | 61 | // It converts [dhms] to seconds. 62 | // If format is invalid, or x is not a positive integer, returns -1. 63 | static inline int 64 | seconds_from_string(char *s, int *seconds) 65 | { 66 | int x = 0; 67 | char *p = s; 68 | int mul = 24 * 3600; 69 | while (*p != '\0') { 70 | if (isdigit(*p)) { 71 | p++; 72 | continue; 73 | } else { 74 | if (*p == 'd') { 75 | mul = 24 * 3600; 76 | break; 77 | } else if (*p == 'h') { 78 | mul = 3600; 79 | break; 80 | } else if (*p == 'm') { 81 | mul = 60; 82 | break; 83 | } else if (*p == 's') { 84 | mul = 1; 85 | break; 86 | } else { 87 | return -1; 88 | } 89 | } 90 | } 91 | // *p or *(p+1) should be '\0'. 92 | if (*p != '\0' && *(p + 1) != '\0') { 93 | return -2; 94 | } 95 | errno = 0; 96 | x = strtol(s, NULL, 10); 97 | if (errno != 0) { 98 | return -3; 99 | } 100 | *seconds = x * mul; 101 | return 0; 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /config.mak.in: -------------------------------------------------------------------------------- 1 | # Makefile configuration, included by Makefile. 2 | 3 | CC = @CC@ 4 | CFLAGS = @CFLAGS@ 5 | CPPFLAGS = @CPPFLAGS@ 6 | LDFLAGS = @LDFLAGS@ 7 | AR = @AR@ 8 | TAR = @TAR@ 9 | 10 | prefix = @prefix@ 11 | exec_prefix = @exec_prefix@ 12 | bindir = @bindir@ 13 | datarootdir = @datarootdir@ 14 | sysconfdir = @sysconfdir@ 15 | srcdir = @srcdir@ 16 | mandir=@mandir@ 17 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # 2 | # Process this file with autoconf to produce a configure script. 3 | # 4 | # Run ./configure with your options to generate config.mak.autogen file which 5 | # is used by Makefile. 6 | # 7 | 8 | ## Definitions of private macros. 9 | 10 | # CONF_SUBST_INIT 11 | # ------------------- 12 | # Prepare shell variables and autoconf machine required by later calls 13 | # to CONF_SUBST. 14 | AC_DEFUN([CONF_SUBST_INIT], 15 | [config_appended_defs=; newline=' 16 | ' 17 | AC_CONFIG_COMMANDS([$config_file], 18 | [echo "$config_appended_defs" >> "$config_file"], 19 | [config_file=$config_file 20 | config_appended_defs="$config_appended_defs"])] 21 | ) 22 | 23 | # CONF_SUBST(VAL, VAR) 24 | # ------------------------ 25 | # Cause the line "VAR=VAL" to be eventually appended to ${config_file}. 26 | AC_DEFUN([CONF_SUBST], [AC_REQUIRE([CONF_SUBST_INIT]) 27 | config_appended_defs="$config_appended_defs${newline}dnl 28 | $1=m4_if([$#],[1],[${$1}],[$2])"] 29 | ) 30 | 31 | ## Configure body starts here. 32 | 33 | AC_PREREQ([2.59]) 34 | AC_INIT 35 | 36 | config_file=config.mak.autogen 37 | config_in=config.mak.in 38 | 39 | # AUTOCONFIGURED 40 | CONF_SUBST([AUTOCONFIGURED], [YesPlease]) 41 | 42 | # Checks for programs 43 | AC_PROG_CC 44 | AC_CHECK_TOOLS(AR, [gar ar], :) 45 | AC_CHECK_PROGS(TAR, [gtar tar]) 46 | 47 | # libevent 48 | AC_ARG_WITH(event, 49 | [AS_HELP_STRING([--with-event=DIR], [where to find the event library])], 50 | [if test -d "$withval"; then 51 | LDFLAGS="$LDFLAGS -L$withval/lib" 52 | CFLAGS="$CFLAGS -I$withval/include" 53 | fi], 54 | withval=yes # default 55 | ) 56 | if test "$withval" != no; then 57 | AC_CHECK_LIB(event,event_base_new, 58 | [LIBS="-levent $LIBS"], 59 | [AC_MSG_ERROR([libevent2 is required - see http://libevent.org/])] 60 | ) 61 | AC_CHECK_HEADERS(event2/event.h, 62 | [LIBS="-levent $LIBS"], 63 | [AC_MSG_ERROR([libevent2 is required - see http://libevent.org/])] 64 | ) 65 | fi 66 | 67 | # libsodium 68 | AC_ARG_WITH(sodium, 69 | [AS_HELP_STRING([--with-sodium=DIR], [where to find the sodium library])], 70 | [if test -d "$withval"; then 71 | LDFLAGS="$LDFLAGS -L$withval/lib" 72 | CFLAGS="$CFLAGS -I$withval/include" 73 | fi], 74 | withval=yes # default 75 | ) 76 | if test "$withval" != no; then 77 | AC_CHECK_LIB(sodium,sodium_init, 78 | [LIBS="-lsodium $LIBS"], 79 | [AC_MSG_ERROR([sodium is required - see https://github.com/jedisct1/libsodium])] 80 | ) 81 | fi 82 | 83 | AC_CHECK_FUNCS(crypto_box_curve25519xchacha20poly1305_open_easy, 84 | [CFLAGS="$CFLAGS -DHAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_OPEN_EASY"], 85 | [AC_MSG_WARN([was libsodium compiled with --enable-minimal])] 86 | ) 87 | 88 | # Output files 89 | AC_CONFIG_FILES(["${config_file}":"${config_in}"]) 90 | AC_OUTPUT 91 | 92 | # Show summary. 93 | AC_MSG_RESULT([ 94 | Configuration summary: 95 | 96 | Support for event library: $ac_cv_lib_event_event_base_new 97 | Support for sodium library: $ac_cv_lib_sodium_sodium_init 98 | Support for xchacha20: $ac_cv_func_crypto_box_curve25519xchacha20poly1305_open_easy 99 | ]) 100 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | dnscrypt-wrapper (0.2.2) stable; urgency=medium 2 | 3 | * Initial packaging effort 4 | 5 | -- Andrey Korolyov Tue, 13 Sep 2016 20:22:12 +0300 6 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: dnscrypt-wrapper 2 | Section: net 3 | Priority: optional 4 | Maintainer: Andrey Korolyov 5 | Build-Depends: debhelper (>= 9), automake, dh-autoreconf, libsodium-dev, libevent-dev 6 | Standards-Version: 3.9.8 7 | Homepage: https://dnscrypt.info 8 | 9 | Package: dnscrypt-wrapper 10 | Architecture: any 11 | Depends: ${shlibs:Depends}, ${misc:Depends}, adduser 12 | Description: custom secured DNS query wrapper 13 | DNSCrypt is a protocol that authenticates communications between a DNS client 14 | and a DNS resolver. It prevents DNS spoofing. It uses cryptographic signatures 15 | to verify that responses originate from the chosen DNS resolver and 16 | haven't been tampered with. 17 | . 18 | This package provides server-side proxy for DNS queries to a designated 19 | upstream. 20 | 21 | -------------------------------------------------------------------------------- /debian/dnscrypt-wrapper.8: -------------------------------------------------------------------------------- 1 | '\" t 2 | .\" Title: useradd 3 | .\" Author: Aurelien Requiem 4 | .\" Generator: DocBook XSL Stylesheets v1.78.1 5 | .\" Date: 11/18/2015 6 | .\" Manual: System Management Commands 7 | .\" Source: shadow-utils 4.2 8 | .\" Language: English 9 | .\" 10 | .TH "DNSCRYPT-WRAPPER" "8" "December 26th, 2016" "Debian GNU/Linux" "Admin Manual" 11 | .\" ----------------------------------------------------------------- 12 | .\" * Define some portability stuff 13 | .\" ----------------------------------------------------------------- 14 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | .\" http://bugs.debian.org/507673 16 | .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html 17 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | .ie \n(.g .ds Aq \(aq 19 | .el .ds Aq ' 20 | .\" ----------------------------------------------------------------- 21 | .\" * set default formatting 22 | .\" ----------------------------------------------------------------- 23 | .\" disable hyphenation 24 | .nh 25 | .\" disable justification (adjust text to left margin only) 26 | .ad l 27 | .\" ----------------------------------------------------------------- 28 | .\" * MAIN CONTENT STARTS HERE * 29 | .\" ----------------------------------------------------------------- 30 | .SH "NAME" 31 | dnscrypt\-wrapper \- Creates an empty website template almost ready for deployment. 32 | .SH SYNOPSIS 33 | .B dnscrypt\-wrapper 34 | .RB [\| \-h \|] 35 | .RB [\| \-a 36 | .IR \|] 37 | .RB [\| \-r 38 | .IR \|] 39 | .RB [\| \-U \|] 40 | .RB [\| \-u 41 | .IR \|] 42 | .RB [\| \-l 43 | .IR \|] 44 | .RB [\| \-p 45 | .IR \|] 46 | .RB [\| \-d \|] 47 | .RB [\| \-V \|] 48 | .RB [\| \-v \|] 49 | .RB [\| \-\-gen-cert-file \|] 50 | .RB [\| \-\-gen-crypt-keypair \|] 51 | .RB [\| \-\-gen-provider-keypair \|] 52 | .RB [\| \-\-show-provider-publickey-fingerprint \|] 53 | .RB\fB [\| \-\-provider-cert-file=\fR\&\fI\fR \|] 54 | .RB\fB [\| \-\-provider-name=\fR\&\fI\fR \|] 55 | .RB\fB [\| \-\-provider-publickey-file=\fR\&\fI\fR \|] 56 | .RB\fB [\| \-\-provider-secretkey-file=\fR\&\fI\fR \|] 57 | .RB\fB [\| \-\-crypt-secretkey-file=\fR\&\fI\fR \|] 58 | .RB\fB [\| \-\-cert-file-expire-days=\fR\&\fI\fR \|] 59 | 60 | 61 | .SH DESCRIPTION 62 | .BR dnscrypt\-wrapper 63 | DNSCrypt is a protocol that authenticates communications between a DNS client and a DNS resolver. It prevents DNS spoofing. It uses cryptographic signatures to verify that responses originate from the chosen DNS resolver and haven't been tampered with. 64 | 65 | .SH OPTIONS 66 | .TP 67 | \fB\-h\fR, \fB\--help\fR, 68 | .RS 4 69 | Display dnscrypt\-wrapper help message and exit. 70 | .RE 71 | .TP 72 | \fB\-a\fR\ \&\fI\fR, \fB\-\-listen-address=\fR\&\fI\fR 73 | .RS 4 74 | Set local address to listen (default: 0.0.0.0:53). 75 | .RE 76 | .TP 77 | \fB\fR\ \&\fI\-r\fR, \fB\-\-resolver\-address=\fR\&\fI\fR 78 | .RS 4 79 | Set the upstream dns resolver server (). 80 | .RE 81 | .TP 82 | \fB\-U\fR, \fB\-\-unauthenticated\fR 83 | .RS 4 84 | Allow and forward unauthenticated queries (default: off). 85 | .RE 86 | .TP 87 | \fB\-u\fR\ \&\fI\fR, \fB\-\-user=\fR\&\fI\fR 88 | .RS 4 89 | Run the service as given user. 90 | .RE 91 | .TP 92 | \fB\-l\fR\ \&\fI\fR, \fB\-\-logfile=\fR\&\fI\fR 93 | .RS 4 94 | Logs file path (default: stdout). 95 | .RE 96 | .TP 97 | \fB\-p\fR\ \&\fI\fR, \fB\-\-pidfile=\fR\&\fI\fR 98 | .RS 4 99 | Pid file path (default: none). 100 | .RE 101 | .TP 102 | \fB\-d\fR, \fB\-\-daemonize\fR 103 | .RS 4 104 | Run as daemon (default: off). 105 | .RE 106 | .TP 107 | \fB\-V\fR, \fB\-\-verbose\fR 108 | .RS 4 109 | Set the service to be more verbose. Specify more \-VVV to increase verbosity. 110 | .RE 111 | .TP 112 | \fB\-v\fR, \fB\-\-version\fR 113 | .RS 4 114 | Show version info and exit. 115 | .RE 116 | .TP 117 | \fB\-\-gen\-cert\-file\fR 118 | .RS 4 119 | Generate pre\-signed certificate 120 | .RE 121 | .TP 122 | \fB\-\-gen\-crypt\-keypair\fR 123 | .RS 4 124 | Generate crypt key pair 125 | .RE 126 | .TP 127 | \fB\-\-gen\-provider\-keypair\fR 128 | .RS 4 129 | Generate provider key pair 130 | .RE 131 | .TP 132 | \fB\-\-show\-provider\-publickey\-fingerprint\fR 133 | .RS 4 134 | Display provider public key fingerprint 135 | .RE 136 | .TP 137 | \fB\-\-provider\-cert\-file=\fR\&\fI\fR 138 | .RS 4 139 | Certificate file (default: ./dnscrypt.cert) 140 | .RE 141 | .TP 142 | \fB\-\-provider\-name=\fR\&\fI\fR 143 | .RS 4 144 | Provider name. 145 | .RE 146 | .TP 147 | \fB\-\-provider\-publickey\-file=\fR\&\fI\fR 148 | .RS 4 149 | Provider public key file (default: ./public.key). 150 | .RE 151 | .TP 152 | \fB\-\-provider\-secretkey\-file=\fR\&\fI\fR 153 | .RS 4 154 | Provider secret key file (default: ./secret.key). 155 | .RE 156 | .TP 157 | \fB\-\-crypt\-secretkey\-file=\fR\&\fI\fR 158 | .RS 4 159 | Crypt secret key file (default: ./crypt_secret.key). 160 | .RE 161 | .TP 162 | \fB\-\-cert\-file\-expire\-days=\fR\&\fI\fR 163 | .RS 4 164 | cert file expire days (default: 365). 165 | .PP 166 | . 167 | .SH "SEE ALSO" 168 | \fBdnscrypt-proxy\fR(8) 169 | -------------------------------------------------------------------------------- /debian/dnscrypt-wrapper.default: -------------------------------------------------------------------------------- 1 | # This file describes option set for DNSProxy-wrapper, brief explanation is below. 2 | # If you are generating keys by an application itself, please install correct access 3 | # bits on private keys. 4 | 5 | RUNTIME_OPTS="--user=dnscrypt-wrapper --daemonize --pidfile=/var/run/dnscrypt-wrapper.pid" 6 | KEY_OPTS="--provider-publickey-file=/etc/dnscrypt-wrapper/public.key --provider-secretkey-file=/etc/dnscrypt-wrapper/private.key --crypt-secretkey-file=/etc/dnscrypt-wrapper/crypt-secret.key --provider-cert-file=/etc/dnscrypt-wrapper/provider.crt --provider-name=2.dnscrypt-cert" 7 | NETWORK_OPTS="--listen-address=127.0.0.1 --resolver-address=8.8.4.4" 8 | -------------------------------------------------------------------------------- /debian/dnscrypt-wrapper.dirs: -------------------------------------------------------------------------------- 1 | etc/dnscrypt-wrapper/ 2 | -------------------------------------------------------------------------------- /debian/dnscrypt-wrapper.docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/dnscrypt-wrapper.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start/stop the DNSCrypt wrapper. 3 | # 4 | ### BEGIN INIT INFO 5 | # Provides: dnscrypt-wrapper 6 | # Required-Start: $remote_fs $syslog $network 7 | # Required-Stop: $remote_fs $syslog $network 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 10 | # Short-Description: DNSCrypt dns proxy wrapper 11 | # Description: DNSCrypt wrapper is a secure protocol that allows 12 | # end-users to communicate with trusted DNS server when 13 | # DNSSEC or alternative query measurement tool are not 14 | # deployed or not available at all. 15 | ### END INIT INFO 16 | 17 | PATH=/bin:/usr/bin:/sbin:/usr/sbin 18 | DESC="DNSCrypt wrapper" 19 | NAME=dnscrypt-wrapper 20 | DAEMON=/usr/sbin/dnscrypt-wrapper 21 | PIDFILE=/var/run/dnscrypt-wrapper.pid 22 | SCRIPTNAME=/etc/init.d/"$NAME" 23 | 24 | test -f $DAEMON || exit 0 25 | 26 | . /lib/lsb/init-functions 27 | 28 | [ -r /etc/default/dnscrypt-wrapper ] && . /etc/default/dnscrypt-wrapper 29 | 30 | DAEMON_OPTS="$RUNTIME_OPTS $NETWORK_OPTS $KEY_OPTS" 31 | 32 | case "$1" in 33 | start) 34 | log_daemon_msg "Starting DNSCrypt wrapper" 35 | start_daemon -p $PIDFILE $DAEMON $DAEMON_OPTS 36 | log_end_msg $? 37 | ;; 38 | 39 | stop) 40 | log_daemon_msg "Stopping DNSCrypt wrapper" 41 | killproc -p $PIDFILE $DAEMON 42 | RETVAL=$? 43 | [ $RETVAL -eq 0 ] && [ -e "$PIDFILE" ] && rm -f $PIDFILE 44 | log_end_msg $RETVAL 45 | ;; 46 | 47 | restart|force-reload) 48 | log_daemon_msg "Restarting DNSCrypt wrapper" 49 | $0 stop 50 | $0 start 51 | ;; 52 | 53 | status) 54 | status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $? 55 | ;; 56 | 57 | *) log_action_msg "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" 58 | exit 2 59 | ;; 60 | esac 61 | exit 0 62 | -------------------------------------------------------------------------------- /debian/dnscrypt-wrapper.manpages: -------------------------------------------------------------------------------- 1 | debian/dnscrypt-wrapper.8 2 | -------------------------------------------------------------------------------- /debian/dnscrypt-wrapper.postinst: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -e 4 | 5 | case "$1" in 6 | upgrade|install-upgrade) 7 | ;; 8 | abort-upgrade|abort-remove|abort-deconfigure) 9 | exit 0 10 | ;; 11 | esac 12 | # 13 | # Generate the necessary crypto material for the service to run once everything is installed 14 | # 15 | . /etc/default/dnscrypt-wrapper 16 | 17 | test -f "/etc/dnscrypt-wrapper/private.key" -a -f "/etc/dnscrypt-wrapper/public.key" || dnscrypt-wrapper --gen-provider-keypair $KEY_OPTS 18 | test -f "/etc/dnscrypt-wrapper/crypt-secret.key" || dnscrypt-wrapper --gen-crypt-keypair $KEY_OPTS 19 | test -f "/etc/dnscrypt-wrapper/provider.crt" || dnscrypt-wrapper --gen-cert-file $KEY_OPTS 20 | 21 | # dh_installdeb will replace this with shell code automatically 22 | # generated by other debhelper scripts. 23 | 24 | #DEBHELPER# 25 | 26 | exit 0 27 | -------------------------------------------------------------------------------- /debian/dnscrypt-wrapper.preinst: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -e 4 | 5 | case "$1" in 6 | upgrade|install-upgrade) 7 | ;; 8 | abort-upgrade) 9 | exit 0 10 | ;; 11 | esac 12 | # 13 | # Add the "dnscrypt-wrapper" user/group to /etc/passwd if needed. 14 | # 15 | 16 | if ! grep -q "^dnscrypt-wrapper:" /etc/passwd 17 | then 18 | adduser --system --home /bin --group --shell /usr/sbin/nologin dnscrypt-wrapper 19 | fi 20 | 21 | # dh_installdeb will replace this with shell code automatically 22 | # generated by other debhelper scripts. 23 | 24 | #DEBHELPER# 25 | 26 | exit 0 27 | -------------------------------------------------------------------------------- /debian/dnscrypt-wrapper.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=DNSCrypt wrapper 3 | Documentation=man:dnscrypt-wrapper(8) 4 | After=network.target iptables.service firewalld.service 5 | 6 | [Service] 7 | Type=forking 8 | EnvironmentFile=-/etc/default/dnscrypt-wrapper 9 | ExecStart=/usr/sbin/dnscrypt-wrapper $RUNTIME_OPTS $NETWORK_OPTS $KEY_OPTS 10 | KillMode=process 11 | Restart=on-failure 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # Enable this export to get more detailed information about what debhelper does 4 | #export DH_VERBOSE=1 5 | 6 | # parallel build by default on linux 7 | ifeq ($(DEB_HOST_ARCH_OS),linux) 8 | ifeq ($(findstring parallel=,$(DEB_BUILD_OPTIONS)),) 9 | export DEB_BUILD_OPTIONS+=parallel=$(shell getconf _NPROCESSORS_ONLN) 10 | endif 11 | endif 12 | $(info DEB_BUILD_OPTIONS=$(DEB_BUILD_OPTIONS)) 13 | 14 | # These are used for cross-compiling and for saving the configure script 15 | # from having to guess our platform (since we know it already) 16 | export DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) 17 | export DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) 18 | 19 | export DEB_BUILD_MAINT_OPTIONS=hardening=+all 20 | 21 | # minimise needless linking 22 | export DEB_LDFLAGS_MAINT_APPEND= -Wl,--as-needed 23 | 24 | CONFFLAGS_COMMON = --host=$(DEB_HOST_GNU_TYPE) \ 25 | --build=$(DEB_BUILD_GNU_TYPE) \ 26 | --sysconfdir=/etc/dnscrypt-wrapper \ 27 | --prefix=/usr \ 28 | --infodir=\$${prefix}/share/info \ 29 | --mandir=\$${prefix}/share/man \ 30 | --with-event \ 31 | --with-sodium 32 | 33 | %: 34 | dh $@ --parallel --max-parallel=4 --with autoreconf 35 | 36 | -------------------------------------------------------------------------------- /debug.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include "logger.h" 3 | 4 | #ifdef __CYGWIN__ 5 | #ifndef SA_ONSTACK 6 | #define SA_ONSTACK 0x08000000 7 | #endif 8 | #endif 9 | 10 | static char *assert_err = ""; 11 | static char *assert_file = ""; 12 | static int assert_line = 0; 13 | 14 | void 15 | _debug_assert(char *err, char *file, int line) 16 | { 17 | logger(LOG_WARNING, "=== ASSERTION FAILED ==="); 18 | logger(LOG_WARNING, "%s:%d '%s' is not true", file, line, err); 19 | assert_err = err; 20 | assert_file = file; 21 | assert_line = line; 22 | // force SIGSEGV to print the bug report 23 | *((char *)-1) = 'x'; 24 | } 25 | 26 | void 27 | debug_init(void) 28 | { 29 | struct sigaction act; 30 | sigemptyset(&act.sa_mask); 31 | act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO; 32 | act.sa_sigaction = debug_segv_handler; 33 | sigaction(SIGSEGV, &act, NULL); 34 | sigaction(SIGBUS, &act, NULL); 35 | sigaction(SIGFPE, &act, NULL); 36 | sigaction(SIGILL, &act, NULL); 37 | } 38 | 39 | #ifdef HAVE_BACKTRACE 40 | #include 41 | #include 42 | static void * 43 | getMcontextEip(ucontext_t *uc) 44 | { 45 | #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6) 46 | /* OSX < 10.6 */ 47 | #if defined(__x86_64__) 48 | return (void *) uc->uc_mcontext->__ss.__rip; 49 | #elif defined(__i386__) 50 | return (void *) uc->uc_mcontext->__ss.__eip; 51 | #else 52 | return (void *) uc->uc_mcontext->__ss.__srr0; 53 | #endif 54 | #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) 55 | /* OSX >= 10.6 */ 56 | #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) 57 | return (void *) uc->uc_mcontext->__ss.__rip; 58 | #else 59 | return (void *) uc->uc_mcontext->__ss.__eip; 60 | #endif 61 | #elif defined(__linux__) 62 | /* Linux */ 63 | #if defined(__i386__) 64 | return (void *) uc->uc_mcontext.gregs[14]; /* Linux 32 */ 65 | #elif defined(__X86_64__) || defined(__x86_64__) 66 | return (void *) uc->uc_mcontext.gregs[16]; /* Linux 64 */ 67 | #elif defined(__ia64__) /* Linux IA64 */ 68 | return (void *) uc->uc_mcontext.sc_ip; 69 | #else 70 | return NULL; 71 | #endif 72 | #else 73 | return NULL; 74 | #endif 75 | } 76 | 77 | /** 78 | * Logs the stack trace using the backtrace() call. This function is designed to 79 | * be called from signal handlers safely. 80 | */ 81 | static void 82 | log_stack_trace(ucontext_t *uc) 83 | { 84 | void *trace[100]; 85 | int trace_size = 0; 86 | int fd = logger_fd >= 0 ? logger_fd : STDOUT_FILENO; 87 | /* Generate the stack trace */ 88 | trace_size = backtrace(trace, 100); 89 | /* overwrite sigaction with caller's address */ 90 | if (getMcontextEip(uc) != NULL) 91 | trace[1] = getMcontextEip(uc); 92 | 93 | backtrace_symbols_fd(trace, trace_size, fd); 94 | } 95 | 96 | #endif 97 | 98 | void 99 | debug_segv_handler(int sig, siginfo_t *info, void *secret) 100 | { 101 | logger(LOG_WARNING, "Crashed by signal: %d", sig); 102 | logger(LOG_WARNING, "--- STACK TRACE"); 103 | logger(LOG_WARNING, "Failed assertion: %s (%s:%d)", assert_err, assert_file, 104 | assert_line); 105 | #ifdef HAVE_BACKTRACE 106 | logger(LOG_WARNING, "--- STACK TRACE"); 107 | ucontext_t *uc = (ucontext_t *) secret; 108 | log_stack_trace(uc); 109 | #endif 110 | /* Make sure we exit with the right signal at the end. So for instance 111 | * the core will be dumped if enabled. */ 112 | struct sigaction act; 113 | sigemptyset(&act.sa_mask); 114 | act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND; 115 | act.sa_handler = SIG_DFL; 116 | sigaction(sig, &act, NULL); 117 | kill(getpid(), sig); 118 | } 119 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | /** 4 | * Debug facilities. 5 | */ 6 | 7 | #include "compat.h" 8 | 9 | #define debug_assert(e) ((e) ? (void)0 : (_debug_assert(#e, __FILE__, __LINE__), _exit(1))) 10 | void _debug_assert(char *err, char *file, int line); 11 | 12 | void debug_init(void); 13 | void debug_segv_handler(int sig, siginfo_t *info, void *secret); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /dns-protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef DNS_PROTOCOL_H 2 | #define DNS_PROTOCOL_H 3 | /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; version 2 dated June, 1991, or 8 | (at your option) version 3 dated 29 June, 2007. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | /** 19 | * DNS Protocol Header 20 | * 21 | * file from dnsmasq/src/dns-protocol.h with slight modification. 22 | */ 23 | 24 | #define NAMESERVER_PORT 53 25 | #define TFTP_PORT 69 26 | 27 | #define IN6ADDRSZ 16 28 | #define INADDRSZ 4 29 | 30 | #define PACKETSZ 512 /* maximum packet size */ 31 | #define MAXDNAME 1025 /* maximum presentation domain name */ 32 | #define RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ 33 | #define MAXLABEL 63 /* maximum length of domain label */ 34 | 35 | #define NOERROR 0 /* no error */ 36 | #define FORMERR 1 /* format error */ 37 | #define SERVFAIL 2 /* server failure */ 38 | #define NXDOMAIN 3 /* non existent domain */ 39 | #define NOTIMP 4 /* not implemented */ 40 | #define REFUSED 5 /* query refused */ 41 | 42 | #define QUERY 0 /* opcode */ 43 | 44 | #define C_IN 1 /* the arpa internet */ 45 | #define C_CHAOS 3 /* for chaos net (MIT) */ 46 | #define C_ANY 255 /* wildcard match */ 47 | 48 | #define T_A 1 49 | #define T_NS 2 50 | #define T_CNAME 5 51 | #define T_SOA 6 52 | #define T_PTR 12 53 | #define T_MX 15 54 | #define T_TXT 16 55 | #define T_SIG 24 56 | #define T_AAAA 28 57 | #define T_SRV 33 58 | #define T_NAPTR 35 59 | #define T_OPT 41 60 | #define T_TKEY 249 61 | #define T_TSIG 250 62 | #define T_MAILB 253 63 | #define T_ANY 255 64 | 65 | struct dns_header { 66 | uint16_t id; 67 | uint8_t hb3,hb4; 68 | uint16_t qdcount,ancount,nscount,arcount; 69 | }; 70 | 71 | #define HB3_QR 0x80 72 | #define HB3_OPCODE 0x78 73 | #define HB3_AA 0x04 74 | #define HB3_TC 0x02 75 | #define HB3_RD 0x01 76 | 77 | #define HB4_RA 0x80 78 | #define HB4_AD 0x20 79 | #define HB4_CD 0x10 80 | #define HB4_RCODE 0x0f 81 | 82 | #define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3) 83 | #define RCODE(x) ((x)->hb4 & HB4_RCODE) 84 | #define SET_RCODE(x, code) (x)->hb4 = ((x)->hb4 & ~HB4_RCODE) | code 85 | 86 | #define GETSHORT(s, cp) { \ 87 | unsigned char *t_cp = (unsigned char *)(cp); \ 88 | (s) = ((uint16_t)t_cp[0] << 8) \ 89 | | ((uint16_t)t_cp[1]) \ 90 | ; \ 91 | (cp) += 2; \ 92 | } 93 | 94 | #define GETLONG(l, cp) { \ 95 | unsigned char *t_cp = (unsigned char *)(cp); \ 96 | (l) = ((uint32_t)t_cp[0] << 24) \ 97 | | ((uint32_t)t_cp[1] << 16) \ 98 | | ((uint32_t)t_cp[2] << 8) \ 99 | | ((uint32_t)t_cp[3]) \ 100 | ; \ 101 | (cp) += 4; \ 102 | } 103 | 104 | #define PUTSHORT(s, cp) { \ 105 | uint16_t t_s = (uint16_t)(s); \ 106 | unsigned char *t_cp = (unsigned char *)(cp); \ 107 | *t_cp++ = t_s >> 8; \ 108 | *t_cp = t_s; \ 109 | (cp) += 2; \ 110 | } 111 | 112 | #define PUTLONG(l, cp) { \ 113 | uint32_t t_l = (uint32_t)(l); \ 114 | unsigned char *t_cp = (unsigned char *)(cp); \ 115 | *t_cp++ = t_l >> 24; \ 116 | *t_cp++ = t_l >> 16; \ 117 | *t_cp++ = t_l >> 8; \ 118 | *t_cp = t_l; \ 119 | (cp) += 4; \ 120 | } 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /dnscrypt.c: -------------------------------------------------------------------------------- 1 | #include "dnscrypt.h" 2 | 3 | typedef struct Cached_ { 4 | uint8_t pk[crypto_box_PUBLICKEYBYTES]; 5 | uint8_t server_pk[crypto_box_PUBLICKEYBYTES]; 6 | uint8_t shared[crypto_box_BEFORENMBYTES]; 7 | } Cached; 8 | 9 | static Cached cache[4096]; 10 | 11 | static inline size_t 12 | h12(const uint8_t pk[crypto_box_PUBLICKEYBYTES], 13 | const uint8_t server_pk[crypto_box_PUBLICKEYBYTES], bool use_xchacha20) 14 | { 15 | uint64_t a, b, c, d, e; 16 | uint32_t h; 17 | 18 | memcpy(&a, &pk[0], 8); memcpy(&b, &pk[8], 8); 19 | memcpy(&c, &pk[16], 8); memcpy(&d, &pk[24], 8); 20 | e = a ^ b ^ c ^ d; 21 | memcpy(&a, &server_pk[0], 8); memcpy(&b, &server_pk[8], 8); 22 | memcpy(&c, &server_pk[16], 8); memcpy(&d, &server_pk[24], 8); 23 | e ^= a ^ b ^ c ^ d; 24 | h = ((uint32_t) e) ^ ((uint32_t) (e >> 32)); 25 | return (size_t) (((h >> 20) ^ (h >> 8) ^ (h << 4) ^ use_xchacha20) & 0xfff); 26 | } 27 | 28 | static int 29 | cache_get(Cached ** const cached_p, 30 | const uint8_t pk[crypto_box_PUBLICKEYBYTES], 31 | const uint8_t server_pk[crypto_box_PUBLICKEYBYTES], const bool use_xchacha20) 32 | { 33 | Cached *cached = &cache[h12(pk, server_pk, use_xchacha20)]; 34 | 35 | *cached_p = cached; 36 | if (memcmp(cached->pk, pk, crypto_box_PUBLICKEYBYTES - 1) == 0 && 37 | (cached->pk[crypto_box_PUBLICKEYBYTES - 1] ^ use_xchacha20) == pk[crypto_box_PUBLICKEYBYTES - 1] && 38 | memcmp(cached->server_pk, server_pk, crypto_box_PUBLICKEYBYTES - 1) == 0) { 39 | return 1; 40 | } 41 | return 0; 42 | } 43 | 44 | static void 45 | cache_set(const uint8_t shared[crypto_box_BEFORENMBYTES], 46 | const uint8_t pk[crypto_box_PUBLICKEYBYTES], 47 | const uint8_t server_pk[crypto_box_PUBLICKEYBYTES], const bool use_xchacha20) 48 | { 49 | Cached *cached; 50 | 51 | cache_get(&cached, pk, server_pk, use_xchacha20); 52 | memcpy(cached->pk, pk, crypto_box_PUBLICKEYBYTES); 53 | cached->pk[crypto_box_PUBLICKEYBYTES - 1] ^= use_xchacha20; 54 | memcpy(cached->server_pk, server_pk, crypto_box_PUBLICKEYBYTES); 55 | memcpy(cached->shared, shared, crypto_box_BEFORENMBYTES); 56 | } 57 | 58 | const dnsccert * 59 | find_cert(const struct context *c, 60 | const unsigned char magic_query[DNSCRYPT_MAGIC_HEADER_LEN], 61 | const size_t dns_query_len) 62 | { 63 | const dnsccert *certs = c->certs; 64 | size_t i; 65 | 66 | if (dns_query_len <= DNSCRYPT_QUERY_HEADER_SIZE) { 67 | return NULL; 68 | } 69 | for (i = 0U; i < c->certs_count; i++) { 70 | if (memcmp(certs[i].magic_query, magic_query, DNSCRYPT_MAGIC_HEADER_LEN) == 0) { 71 | return &certs[i]; 72 | } 73 | } 74 | if (memcmp(magic_query, CERT_OLD_MAGIC_HEADER, DNSCRYPT_MAGIC_HEADER_LEN) == 0) { 75 | return &certs[0]; 76 | } 77 | return NULL; 78 | } 79 | 80 | int 81 | dnscrypt_cmp_client_nonce(const uint8_t 82 | client_nonce[crypto_box_HALF_NONCEBYTES], 83 | const uint8_t *const buf, const size_t len) 84 | { 85 | const size_t client_nonce_offset = sizeof(DNSCRYPT_MAGIC_RESPONSE) - 1; 86 | 87 | if (len < client_nonce_offset + crypto_box_HALF_NONCEBYTES 88 | || memcmp(client_nonce, buf + client_nonce_offset, 89 | crypto_box_HALF_NONCEBYTES) != 0) { 90 | return -1; 91 | } 92 | 93 | return 0; 94 | } 95 | 96 | uint64_t 97 | dnscrypt_hrtime(void) 98 | { 99 | struct timeval tv; 100 | uint64_t ts = (uint64_t)0U; 101 | int ret; 102 | 103 | ret = evutil_gettimeofday(&tv, NULL); 104 | assert(ret == 0); 105 | if (ret == 0) { 106 | ts = (uint64_t)tv.tv_sec * 1000000U + (uint64_t)tv.tv_usec; 107 | } 108 | return ts; 109 | } 110 | 111 | void 112 | dnscrypt_key_to_fingerprint(char fingerprint[80U], const uint8_t *const key) 113 | { 114 | const size_t fingerprint_size = 80U; 115 | size_t fingerprint_pos = (size_t) 0U; 116 | size_t key_pos = (size_t) 0U; 117 | 118 | COMPILER_ASSERT(crypto_box_PUBLICKEYBYTES == 32U); 119 | COMPILER_ASSERT(crypto_box_SECRETKEYBYTES == 32U); 120 | for (;;) { 121 | assert(fingerprint_size > fingerprint_pos); 122 | evutil_snprintf(&fingerprint[fingerprint_pos], 123 | fingerprint_size - fingerprint_pos, "%02X%02X", 124 | key[key_pos], key[key_pos + 1U]); 125 | key_pos += 2U; 126 | if (key_pos >= crypto_box_PUBLICKEYBYTES) { 127 | break; 128 | } 129 | fingerprint[fingerprint_pos + 4U] = ':'; 130 | fingerprint_pos += 5U; 131 | } 132 | } 133 | 134 | static int 135 | _dnscrypt_parse_char(uint8_t key[crypto_box_PUBLICKEYBYTES], 136 | size_t * const key_pos_p, int *const state_p, 137 | const int c, uint8_t *const val_p) 138 | { 139 | uint8_t c_val; 140 | 141 | switch (*state_p) { 142 | case 0: 143 | case 1: 144 | if (isspace(c) || (c == ':' && *state_p == 0)) { 145 | break; 146 | } 147 | if (c == '#') { 148 | *state_p = 2; 149 | break; 150 | } 151 | if (!isxdigit(c)) { 152 | return -1; 153 | } 154 | c_val = (uint8_t)((c >= '0' && c <= '9') ? c - '0' : c - 'a' + 10); 155 | assert(c_val < 16U); 156 | if (*state_p == 0) { 157 | *val_p = c_val * 16U; 158 | *state_p = 1; 159 | } else { 160 | *val_p |= c_val; 161 | key[(*key_pos_p)++] = *val_p; 162 | if (*key_pos_p >= crypto_box_PUBLICKEYBYTES) { 163 | return 0; 164 | } 165 | *state_p = 0; 166 | } 167 | break; 168 | case 2: 169 | if (c == '\n') { 170 | *state_p = 0; 171 | } 172 | } 173 | return 1; 174 | } 175 | 176 | int 177 | dnscrypt_fingerprint_to_key(const char *const fingerprint, 178 | uint8_t key[crypto_box_PUBLICKEYBYTES]) 179 | { 180 | const char *p = fingerprint; 181 | size_t key_pos = (size_t) 0U; 182 | int c; 183 | int ret; 184 | int state = 0; 185 | uint8_t val = 0U; 186 | 187 | if (fingerprint == NULL) { 188 | return -1; 189 | } 190 | while ((c = tolower((int)(unsigned char)*p)) != 0) { 191 | ret = _dnscrypt_parse_char(key, &key_pos, &state, c, &val); 192 | if (ret <= 0) { 193 | return ret; 194 | } 195 | p++; 196 | } 197 | return -1; 198 | } 199 | 200 | /** 201 | * Add random padding to a buffer, according to a client nonce. 202 | * The length has to depend on the query in order to avoid reply attacks. 203 | * 204 | * @param buf a buffer 205 | * @param len the initial size of the buffer 206 | * @param max_len the maximum size 207 | * @param nonce a nonce, made of the client nonce repeated twice 208 | * @param secretkey 209 | * @return the new size, after padding 210 | */ 211 | size_t 212 | dnscrypt_pad(uint8_t *buf, const size_t len, const size_t max_len, 213 | const uint8_t *nonce, const uint8_t *secretkey) 214 | { 215 | uint8_t *buf_padding_area = buf + len; 216 | size_t padded_len; 217 | uint32_t rnd; 218 | 219 | // no padding 220 | if (max_len < len + DNSCRYPT_MIN_PAD_LEN) 221 | return len; 222 | 223 | assert(nonce[crypto_box_HALF_NONCEBYTES] == nonce[0]); 224 | 225 | crypto_stream((unsigned char *)&rnd, (unsigned long long)sizeof(rnd), nonce, 226 | secretkey); 227 | padded_len = 228 | len + DNSCRYPT_MIN_PAD_LEN + rnd % (max_len - len - 229 | DNSCRYPT_MIN_PAD_LEN + 1); 230 | padded_len += DNSCRYPT_BLOCK_SIZE - padded_len % DNSCRYPT_BLOCK_SIZE; 231 | if (padded_len > max_len) 232 | padded_len = max_len; 233 | 234 | memset(buf_padding_area, 0, padded_len - len); 235 | *buf_padding_area = 0x80; 236 | 237 | return padded_len; 238 | } 239 | 240 | // 8 bytes: magic_query 241 | // 32 bytes: the client's DNSCurve public key (crypto_box_PUBLICKEYBYTES) 242 | // 12 bytes: a client-selected nonce (crypto_box_HALF_NONCEBYTES) 243 | // 16 bytes: Poly1305 MAC (crypto_box_MACBYTES) 244 | 245 | #define DNSCRYPT_QUERY_BOX_OFFSET \ 246 | (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + crypto_box_HALF_NONCEBYTES) 247 | 248 | int 249 | dnscrypt_server_uncurve(struct context *c, const dnsccert *cert, 250 | uint8_t client_nonce[crypto_box_HALF_NONCEBYTES], 251 | uint8_t nmkey[crypto_box_BEFORENMBYTES], 252 | uint8_t *const buf, size_t * const lenp) 253 | { 254 | size_t len = *lenp; 255 | 256 | if (len <= DNSCRYPT_QUERY_HEADER_SIZE) { 257 | return -1; 258 | } 259 | 260 | struct dnscrypt_query_header *query_header = 261 | (struct dnscrypt_query_header *)buf; 262 | Cached *cached; 263 | 264 | if (cache_get(&cached, query_header->publickey, cert->keypair->crypt_publickey, XCHACHA20_CERT(cert))) { 265 | memcpy(nmkey, cached->shared, crypto_box_BEFORENMBYTES); 266 | } else { 267 | memcpy(nmkey, query_header->publickey, crypto_box_PUBLICKEYBYTES); 268 | if (XCHACHA20_CERT(cert)) { 269 | #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_OPEN_EASY 270 | if (crypto_box_curve25519xchacha20poly1305_beforenm(nmkey, nmkey, 271 | cert->keypair->crypt_secretkey) != 0) { 272 | return -1; 273 | } 274 | #endif 275 | } else { 276 | if (crypto_box_beforenm(nmkey, nmkey, 277 | cert->keypair->crypt_secretkey) != 0) { 278 | return -1; 279 | } 280 | } 281 | cache_set(nmkey, query_header->publickey, cert->keypair->crypt_publickey, XCHACHA20_CERT(cert)); 282 | } 283 | 284 | uint8_t nonce[crypto_box_NONCEBYTES]; 285 | memcpy(nonce, query_header->nonce, crypto_box_HALF_NONCEBYTES); 286 | memset(nonce + crypto_box_HALF_NONCEBYTES, 0, crypto_box_HALF_NONCEBYTES); 287 | 288 | if (XCHACHA20_CERT(cert)) { 289 | #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_OPEN_EASY 290 | if (crypto_box_curve25519xchacha20poly1305_open_easy_afternm 291 | (buf, buf + DNSCRYPT_QUERY_BOX_OFFSET, 292 | len - DNSCRYPT_QUERY_BOX_OFFSET, nonce, nmkey) != 0) { 293 | return -1; 294 | } 295 | #endif 296 | } else { 297 | if (crypto_box_open_easy_afternm 298 | (buf, buf + DNSCRYPT_QUERY_BOX_OFFSET, 299 | len - DNSCRYPT_QUERY_BOX_OFFSET, nonce, nmkey) != 0) { 300 | return -1; 301 | } 302 | } 303 | 304 | len -= DNSCRYPT_QUERY_HEADER_SIZE; 305 | while (len > 0 && buf[--len] == 0); 306 | if (buf[len] != 0x80) { 307 | return -1; 308 | } 309 | 310 | memcpy(client_nonce, nonce, crypto_box_HALF_NONCEBYTES); 311 | *lenp = len; 312 | 313 | return 0; 314 | } 315 | 316 | void 317 | add_server_nonce(struct context *c, uint8_t *nonce) 318 | { 319 | uint64_t ts; 320 | uint64_t tsn; 321 | uint32_t suffix; 322 | ts = dnscrypt_hrtime(); 323 | if (ts <= c->nonce_ts_last) { 324 | ts = c->nonce_ts_last + 1; 325 | } 326 | c->nonce_ts_last = ts; 327 | tsn = (ts << 10) | (randombytes_random() & 0x3ff); 328 | #if (BYTE_ORDER == LITTLE_ENDIAN) 329 | tsn = 330 | (((uint64_t)htonl((uint32_t)tsn)) << 32) | htonl((uint32_t)(tsn >> 32)); 331 | #endif 332 | memcpy(nonce + crypto_box_HALF_NONCEBYTES, &tsn, 8); 333 | suffix = randombytes_random(); 334 | memcpy(nonce + crypto_box_HALF_NONCEBYTES + 8, &suffix, 4); 335 | } 336 | 337 | // 8 bytes: magic header (CERT_MAGIC_HEADER) 338 | // 12 bytes: the client's nonce 339 | // 12 bytes: server nonce extension 340 | // 16 bytes: Poly1305 MAC (crypto_box_MACBYTES) 341 | 342 | #define DNSCRYPT_REPLY_BOX_OFFSET \ 343 | (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES + crypto_box_HALF_NONCEBYTES) 344 | 345 | int 346 | dnscrypt_server_curve(struct context *c, const dnsccert *cert, 347 | uint8_t client_nonce[crypto_box_HALF_NONCEBYTES], 348 | uint8_t nmkey[crypto_box_BEFORENMBYTES], 349 | uint8_t *const buf, size_t * const lenp, 350 | const size_t max_len) 351 | { 352 | uint8_t nonce[crypto_box_NONCEBYTES]; 353 | uint8_t *boxed; 354 | size_t len = *lenp; 355 | 356 | memcpy(nonce, client_nonce, crypto_box_HALF_NONCEBYTES); 357 | memcpy(nonce + crypto_box_HALF_NONCEBYTES, client_nonce, 358 | crypto_box_HALF_NONCEBYTES); 359 | 360 | boxed = buf + DNSCRYPT_REPLY_BOX_OFFSET; 361 | memmove(boxed + crypto_box_MACBYTES, buf, len); 362 | len = 363 | dnscrypt_pad(boxed + crypto_box_MACBYTES, len, 364 | max_len - DNSCRYPT_REPLY_HEADER_SIZE, nonce, 365 | c->keypairs[0].crypt_secretkey); 366 | // add server nonce extension 367 | add_server_nonce(c, nonce); 368 | 369 | if (XCHACHA20_CERT(cert)) { 370 | #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_OPEN_EASY 371 | if (crypto_box_curve25519xchacha20poly1305_easy_afternm 372 | (boxed, boxed + crypto_box_MACBYTES, len, nonce, nmkey) != 0) { 373 | return -1; 374 | } 375 | #endif 376 | } else { 377 | if (crypto_box_easy_afternm(boxed, boxed + crypto_box_MACBYTES, 378 | len, nonce, nmkey) != 0) { 379 | return -1; 380 | } 381 | } 382 | 383 | memcpy(buf, DNSCRYPT_MAGIC_RESPONSE, DNSCRYPT_MAGIC_HEADER_LEN); 384 | memcpy(buf + DNSCRYPT_MAGIC_HEADER_LEN, nonce, crypto_box_NONCEBYTES); 385 | *lenp = len + DNSCRYPT_REPLY_HEADER_SIZE; 386 | return 0; 387 | } 388 | 389 | /** 390 | * Return 0 if served. 391 | */ 392 | int 393 | dnscrypt_self_serve_cert_file(struct context *c, struct dns_header *header, 394 | size_t *dns_query_len, size_t max_len) 395 | { 396 | unsigned char *p; 397 | unsigned char *ansp; 398 | int qtype; 399 | unsigned int nameoffset; 400 | p = (unsigned char *)(header + 1); 401 | int anscount = 0; 402 | 403 | if (ntohs(header->qdcount) != 1) { 404 | return -1; 405 | } 406 | /* determine end of questions section (we put answers there) */ 407 | if (!(ansp = skip_questions(header, *dns_query_len))) { 408 | return -2; 409 | } 410 | 411 | /* save pointer to name for copying into answers */ 412 | nameoffset = p - (unsigned char *)header; 413 | 414 | if (!extract_name(header, *dns_query_len, &p, c->namebuff, 1, 4)) { 415 | return -3; 416 | } 417 | GETSHORT(qtype, p); 418 | logger(LOG_DEBUG, "qtype: %d, c->provider_name: %s, c->namebuff: %s", qtype, c->provider_name, c->namebuff); 419 | if (qtype == T_TXT && strcasecmp(c->provider_name, c->namebuff) == 0) { 420 | // reply with signed certificate 421 | const size_t size = 1 + sizeof(struct SignedCert); 422 | static uint8_t **txt; 423 | 424 | // Allocate static buffers containing the certificates. 425 | // This is only called once the first time a TXT request is made. 426 | if(!txt) { 427 | txt = calloc(c->signed_certs_count, sizeof(uint8_t *)); 428 | if (!txt) { 429 | return -4; 430 | } 431 | for (int i=0; i < c->signed_certs_count; i++) { 432 | *(txt + i) = malloc(size); 433 | if (!*(txt + i)) 434 | return -5; 435 | **(txt + i) = sizeof(struct SignedCert); 436 | memcpy(*(txt + i) + 1, c->signed_certs + i, sizeof(struct SignedCert)); 437 | } 438 | } 439 | 440 | for (int i=0; i < c->signed_certs_count; i++) { 441 | if (add_resource_record 442 | (header, nameoffset, max_len, &ansp, 0, NULL, T_TXT, C_IN, "t", size, 443 | *(txt + i))) { 444 | anscount++; 445 | } else { 446 | return -6; 447 | } 448 | } 449 | /* done all questions, set up header and return length of result */ 450 | /* clear authoritative and truncated flags, set QR flag */ 451 | header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR; 452 | /* set RA flag */ 453 | header->hb4 |= HB4_RA; 454 | 455 | SET_RCODE(header, NOERROR); 456 | header->ancount = htons(anscount); 457 | header->nscount = htons(0); 458 | header->arcount = htons(0); 459 | *dns_query_len = ansp - (unsigned char *)header; 460 | 461 | return 0; 462 | } 463 | 464 | return -7; 465 | } 466 | 467 | -------------------------------------------------------------------------------- /dnscrypt.h: -------------------------------------------------------------------------------- 1 | #ifndef DNSCRYPT_H 2 | #define DNSCRYPT_H 3 | 4 | #include "compat.h" 5 | #include "tree.h" 6 | #include "debug.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #if SODIUM_LIBRARY_VERSION_MAJOR < 7 15 | # define sodium_allocarray(C, S) calloc(C, S) 16 | # define sodium_malloc(S) malloc(S) 17 | # define sodium_free(P) free(P) 18 | #endif 19 | 20 | #define DNS_QUERY_TIMEOUT 10 21 | 22 | #define DNS_MAX_PACKET_SIZE_UDP_RECV (65536U - 20U - 8U) 23 | #define DNS_MAX_PACKET_SIZE_UDP_SEND 512U 24 | 25 | #if DNS_MAX_PACKET_SIZE_UDP_RECV > DNS_MAX_PACKET_SIZE_UDP_SEND 26 | # define DNS_MAX_PACKET_SIZE_UDP DNS_MAX_PACKET_SIZE_UDP_RECV 27 | #else 28 | # define DNS_MAX_PACKET_SIZE_UDP DNS_MAX_PACKET_SIZE_UDP_SEND 29 | #endif 30 | 31 | #ifndef DNS_DEFAULT_STANDARD_DNS_PORT 32 | # define DNS_DEFAULT_STANDARD_DNS_PORT "53" 33 | #endif 34 | #ifndef DNS_DEFAULT_LOCAL_PORT 35 | # define DNS_DEFAULT_LOCAL_PORT DNS_DEFAULT_STANDARD_DNS_PORT 36 | #endif 37 | #ifndef DNS_DEFAULT_RESOLVER_PORT 38 | # define DNS_DEFAULT_RESOLVER_PORT "443" 39 | #endif 40 | 41 | #define DNS_HEADER_SIZE 12U 42 | #define DNS_FLAGS_TC 2U 43 | #define DNS_FLAGS_QR 128U 44 | #define DNS_FLAGS2_RA 128U 45 | 46 | #define DNS_CLASS_IN 1U 47 | #define DNS_TYPE_TXT 16U 48 | #define DNS_TYPE_OPT 41U 49 | 50 | #define DNS_OFFSET_QUESTION DNS_HEADER_SIZE 51 | #define DNS_OFFSET_FLAGS 2U 52 | #define DNS_OFFSET_FLAGS2 3U 53 | #define DNS_OFFSET_QDCOUNT 4U 54 | #define DNS_OFFSET_ANCOUNT 6U 55 | #define DNS_OFFSET_NSCOUNT 8U 56 | #define DNS_OFFSET_ARCOUNT 10U 57 | 58 | #define DNS_OFFSET_EDNS_TYPE 0U 59 | #define DNS_OFFSET_EDNS_PAYLOAD_SIZE 2U 60 | 61 | #define DNS_DEFAULT_EDNS_PAYLOAD_SIZE 1252U 62 | 63 | #define DNSCRYPT_MAGIC_HEADER_LEN 8U 64 | #define DNSCRYPT_MAGIC_RESPONSE "r6fnvWj8" 65 | 66 | #ifndef DNSCRYPT_MAX_PADDING 67 | # define DNSCRYPT_MAX_PADDING 256U 68 | #endif 69 | #ifndef DNSCRYPT_BLOCK_SIZE 70 | # define DNSCRYPT_BLOCK_SIZE 64U 71 | #endif 72 | #ifndef DNSCRYPT_MIN_PAD_LEN 73 | # define DNSCRYPT_MIN_PAD_LEN 8U 74 | #endif 75 | 76 | #define crypto_box_HALF_NONCEBYTES (crypto_box_NONCEBYTES / 2U) 77 | 78 | #include "edns.h" 79 | #include "udp_request.h" 80 | #include "tcp_request.h" 81 | #include "rfc1035.h" 82 | #include "logger.h" 83 | #include "safe_rw.h" 84 | #include "cert.h" 85 | 86 | #define DNSCRYPT_QUERY_HEADER_SIZE \ 87 | (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + crypto_box_HALF_NONCEBYTES + crypto_box_MACBYTES) 88 | #define DNSCRYPT_RESPONSE_HEADER_SIZE \ 89 | (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_NONCEBYTES + crypto_box_MACBYTES) 90 | 91 | #define DNSCRYPT_REPLY_HEADER_SIZE \ 92 | (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES * 2 + crypto_box_MACBYTES) 93 | 94 | #define XSALSA20_CERT(cert) (cert->es_version[0] == 0 && \ 95 | cert->es_version[1] == 1) 96 | #define XCHACHA20_CERT(cert) (cert->es_version[0] == 0 && \ 97 | cert->es_version[1] == 2) 98 | 99 | typedef struct KeyPair_ { 100 | uint8_t crypt_publickey[crypto_box_PUBLICKEYBYTES]; 101 | uint8_t crypt_secretkey[crypto_box_SECRETKEYBYTES]; 102 | } KeyPair; 103 | 104 | typedef struct cert_ { 105 | uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN]; 106 | uint8_t es_version[2]; 107 | KeyPair *keypair; 108 | } dnsccert; 109 | 110 | struct context { 111 | struct sockaddr_storage local_sockaddr; 112 | struct sockaddr_storage resolver_sockaddr; 113 | struct sockaddr_storage outgoing_sockaddr; 114 | ev_socklen_t local_sockaddr_len; 115 | ev_socklen_t resolver_sockaddr_len; 116 | ev_socklen_t outgoing_sockaddr_len; 117 | const char *ext_address; 118 | const char *resolver_address; 119 | const char *listen_address; 120 | const char *outgoing_address; 121 | struct evconnlistener *tcp_conn_listener; 122 | struct event *tcp_accept_timer; 123 | struct event *udp_listener_event; 124 | struct event *udp_resolver_event; 125 | evutil_socket_t udp_listener_handle; 126 | evutil_socket_t udp_resolver_handle; 127 | TCPRequestQueue tcp_request_queue; 128 | UDPRequestQueue udp_request_queue; 129 | struct event_base *event_loop; 130 | unsigned int connections; 131 | size_t edns_payload_size; 132 | 133 | /* Domain name shared buffer. */ 134 | char namebuff[MAXDNAME]; 135 | 136 | /* Process stuff. */ 137 | bool daemonize; 138 | bool allow_not_dnscrypted; 139 | char *pidfile; 140 | char *user; 141 | uid_t user_id; 142 | gid_t user_group; 143 | char *user_dir; 144 | char *logfile; 145 | char *provider_name; 146 | char *provider_publickey_file; 147 | char *provider_secretkey_file; 148 | char *provider_cert_file; 149 | struct SignedCert *signed_certs; 150 | size_t signed_certs_count; 151 | dnsccert *certs; 152 | size_t certs_count; 153 | uint8_t provider_publickey[crypto_sign_ed25519_PUBLICKEYBYTES]; 154 | uint8_t provider_secretkey[crypto_sign_ed25519_SECRETKEYBYTES]; 155 | char *crypt_secretkey_file; 156 | KeyPair *keypairs; 157 | size_t keypairs_count; 158 | uint64_t nonce_ts_last; 159 | unsigned char hash_key[crypto_shorthash_KEYBYTES]; 160 | 161 | /* blocking */ 162 | struct Blocking_ *blocking; 163 | }; 164 | 165 | const dnsccert * find_cert(const struct context *c, 166 | const unsigned char magic_query[DNSCRYPT_MAGIC_HEADER_LEN], 167 | const size_t dns_query_len); 168 | int dnscrypt_cmp_client_nonce(const uint8_t 169 | client_nonce[crypto_box_HALF_NONCEBYTES], 170 | const uint8_t *const buf, const size_t len); 171 | void dnscrypt_memzero(void *const pnt, const size_t size); 172 | uint64_t dnscrypt_hrtime(void); 173 | void dnscrypt_key_to_fingerprint(char fingerprint[80U], 174 | const uint8_t *const key); 175 | int dnscrypt_fingerprint_to_key(const char *const fingerprint, 176 | uint8_t key[crypto_box_PUBLICKEYBYTES]); 177 | 178 | // vim-like binary display 179 | static inline void 180 | print_binary_string(uint8_t *s, size_t count) 181 | { 182 | for (size_t i = 1; i <= count; i++) { 183 | uint8_t x = *((uint8_t *)s + i - 1); 184 | if (x >= (uint8_t)'0' && x <= (uint8_t)'9') { 185 | printf("%d", x); 186 | } else if (x >= (uint8_t)'a' && x <= (uint8_t)'z') { 187 | printf("%c", x); 188 | } else if (x >= (uint8_t)'A' && x <= (uint8_t)'Z') { 189 | printf("%c", x); 190 | } else { 191 | printf("\\%03d", x); 192 | } 193 | if (i % 16 == 0) { 194 | printf("\n"); 195 | } 196 | } 197 | printf("\n"); 198 | } 199 | 200 | // binary in hex 201 | static inline void 202 | print_binary_string_hex(uint8_t *s, size_t count) 203 | { 204 | for (size_t i = 1; i <= count; i++) { 205 | if ((i - 1) % 16 == 0) { 206 | printf("%04zx: ", (i - 1)); 207 | } 208 | uint8_t x = *((uint8_t *)s + i - 1); 209 | printf("%02x ", x); 210 | if (i % 16 == 0) { 211 | printf("\n"); 212 | } 213 | } 214 | printf("\n"); 215 | } 216 | 217 | struct dnscrypt_query_header { 218 | uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN]; 219 | uint8_t publickey[crypto_box_PUBLICKEYBYTES]; 220 | uint8_t nonce[crypto_box_HALF_NONCEBYTES]; 221 | uint8_t mac[crypto_box_MACBYTES]; 222 | }; 223 | 224 | int dnscrypt_server_uncurve(struct context *c, const dnsccert *cert, 225 | uint8_t client_nonce[crypto_box_HALF_NONCEBYTES], 226 | uint8_t nmkey[crypto_box_BEFORENMBYTES], 227 | uint8_t *const buf, size_t * const lenp); 228 | int dnscrypt_server_curve(struct context *c, const dnsccert *cert, 229 | uint8_t client_nonce[crypto_box_HALF_NONCEBYTES], 230 | uint8_t nmkey[crypto_box_BEFORENMBYTES], 231 | uint8_t *const buf, size_t * const lenp, 232 | const size_t max_len); 233 | /** 234 | * Given a DNS request,iterate over the question sections. 235 | * If a TXT request for provider name is made, adds the certs as TXT records 236 | * and return 0. dns_query_len is updated to reflect the size of the DNS packet. 237 | * return non-zero in case of failure. 238 | * */ 239 | int dnscrypt_self_serve_cert_file(struct context *c, 240 | struct dns_header *header, 241 | size_t *dns_query_len, size_t max_len); 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /docs/releasing.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | ## How to do a release 4 | 5 | ### Write change logs into CHANGELOG.md 6 | 7 | First, generate change logs with following command: 8 | 9 | ``` 10 | git log --oneline --no-merges v..HEAD | sed -r 's/^\w+/-/g' 11 | ``` 12 | 13 | With some manual edits, write changes logs to CHANGELOG.md. 14 | 15 | ### Update version file 16 | 17 | Update version.h file and commit with "release: bumped version to 18 | " as comment. Then create a PULL REQUEST with title "release 19 | v" as release PR and change logs as contents. 20 | 21 | ### Push a new tag and release 22 | 23 | Create a tag with `git tag -m 'v' v"` command, and 24 | push it to remote. 25 | 26 | Go to https://github.com/cofyc/dnscrypt-wrapper/releases/new to create a 27 | release on github. 28 | 29 | Starting from 0.4.0, we don't attach .zip and .tar.bz2 source files in release, 30 | because we don't have git sub-modules anymore, simply using github source 31 | tarballs is enough. 32 | 33 | ## Version 34 | 35 | Follow https://semver.org/. 36 | 37 | Starting from 0.4.0, we don't omit minor and patch versions if they're 38 | `0', see discussion in https://github.com/semver/semver/issues/237. 39 | -------------------------------------------------------------------------------- /edns.c: -------------------------------------------------------------------------------- 1 | #include "dnscrypt.h" 2 | 3 | static int 4 | _skip_name(const uint8_t *const dns_packet, const size_t dns_packet_len, 5 | size_t * const offset_p) 6 | { 7 | size_t offset = *offset_p; 8 | uint8_t name_component_len; 9 | 10 | if (dns_packet_len < (size_t) 1U || offset >= dns_packet_len - (size_t) 1U) { 11 | return -1; 12 | } 13 | do { 14 | name_component_len = dns_packet[offset]; 15 | if ((name_component_len & 0xC0) == 0xC0) { 16 | name_component_len = 1U; 17 | } 18 | if (name_component_len >= dns_packet_len - offset - 1U) { 19 | return -1; 20 | } 21 | offset += name_component_len + 1U; 22 | } while (name_component_len != 0U); 23 | if (offset >= dns_packet_len) { 24 | return -1; 25 | } 26 | *offset_p = offset; 27 | 28 | return 0; 29 | } 30 | 31 | #define DNS_QTYPE_PLUS_QCLASS_LEN 4U 32 | 33 | static ssize_t 34 | edns_get_payload_size(const uint8_t *const dns_packet, 35 | const size_t dns_packet_len) 36 | { 37 | size_t offset; 38 | size_t payload_size; 39 | unsigned int arcount; 40 | 41 | assert(dns_packet_len >= DNS_HEADER_SIZE); 42 | arcount = (dns_packet[DNS_OFFSET_ARCOUNT] << 8) | 43 | dns_packet[DNS_OFFSET_ARCOUNT + 1U]; 44 | assert(arcount > 0U); 45 | assert(DNS_OFFSET_QUESTION <= DNS_HEADER_SIZE); 46 | if (dns_packet[DNS_OFFSET_QDCOUNT] != 0U || 47 | dns_packet[DNS_OFFSET_QDCOUNT + 1U] != 1U || 48 | (dns_packet[DNS_OFFSET_ANCOUNT] | 49 | dns_packet[DNS_OFFSET_ANCOUNT + 1U]) != 0U || 50 | (dns_packet[DNS_OFFSET_NSCOUNT] | 51 | dns_packet[DNS_OFFSET_NSCOUNT + 1U]) != 0U) { 52 | return (ssize_t) - 1; 53 | } 54 | offset = DNS_OFFSET_QUESTION; 55 | if (_skip_name(dns_packet, dns_packet_len, &offset) != 0) { 56 | return (ssize_t) - 1; 57 | } 58 | assert(dns_packet_len > (size_t) DNS_QTYPE_PLUS_QCLASS_LEN); 59 | if (offset >= dns_packet_len - (size_t) DNS_QTYPE_PLUS_QCLASS_LEN) { 60 | return (ssize_t) - 1; 61 | } 62 | offset += DNS_QTYPE_PLUS_QCLASS_LEN; 63 | assert(dns_packet_len >= DNS_OFFSET_EDNS_PAYLOAD_SIZE + 2U); 64 | if (_skip_name(dns_packet, dns_packet_len, &offset) != 0 || 65 | offset >= dns_packet_len - DNS_OFFSET_EDNS_PAYLOAD_SIZE - 2U) { 66 | return (ssize_t) - 1; 67 | } 68 | assert(DNS_OFFSET_EDNS_PAYLOAD_SIZE > DNS_OFFSET_EDNS_TYPE); 69 | if (dns_packet[offset + DNS_OFFSET_EDNS_TYPE] != 0U || 70 | dns_packet[offset + DNS_OFFSET_EDNS_TYPE + 1U] != DNS_TYPE_OPT) { 71 | return (ssize_t) - 1; 72 | } 73 | payload_size = (dns_packet[offset + DNS_OFFSET_EDNS_PAYLOAD_SIZE] << 8) | 74 | dns_packet[offset + DNS_OFFSET_EDNS_PAYLOAD_SIZE + 1U]; 75 | if (payload_size < DNS_MAX_PACKET_SIZE_UDP_SEND) { 76 | payload_size = DNS_MAX_PACKET_SIZE_UDP_SEND; 77 | } 78 | return (ssize_t) payload_size; 79 | } 80 | 81 | int 82 | edns_add_section(struct context *const c, 83 | uint8_t *const dns_packet, 84 | size_t * const dns_packet_len_p, 85 | size_t dns_packet_max_size, 86 | size_t * const request_edns_payload_size) 87 | { 88 | const size_t edns_payload_size = c->edns_payload_size; 89 | 90 | assert(edns_payload_size <= (size_t) 0xFFFF); 91 | assert(DNS_OFFSET_ARCOUNT + 2U <= DNS_HEADER_SIZE); 92 | if (edns_payload_size <= DNS_MAX_PACKET_SIZE_UDP_SEND || 93 | *dns_packet_len_p <= DNS_HEADER_SIZE) { 94 | *request_edns_payload_size = (size_t) 0U; 95 | return -1; 96 | } 97 | if ((dns_packet[DNS_OFFSET_ARCOUNT] | 98 | dns_packet[DNS_OFFSET_ARCOUNT + 1U]) != 0U) { 99 | const ssize_t edns_payload_ssize = 100 | edns_get_payload_size(dns_packet, *dns_packet_len_p); 101 | if (edns_payload_ssize <= (ssize_t) 0U) { 102 | *request_edns_payload_size = (size_t) 0U; 103 | return -1; 104 | } 105 | *request_edns_payload_size = (size_t) edns_payload_ssize; 106 | return 1; 107 | } 108 | assert(dns_packet_max_size >= *dns_packet_len_p); 109 | 110 | assert(DNS_OFFSET_EDNS_TYPE == 0U); 111 | assert(DNS_OFFSET_EDNS_PAYLOAD_SIZE == 2U); 112 | uint8_t opt_rr[] = { 113 | 0U, /* name */ 114 | 0U, DNS_TYPE_OPT, /* type */ 115 | (edns_payload_size >> 8) & 0xFF, edns_payload_size & 0xFF, 116 | 0U, 0U, 0U, 0U, /* rcode */ 117 | 0U, 0U /* rdlen */ 118 | }; 119 | if (dns_packet_max_size - *dns_packet_len_p < sizeof opt_rr) { 120 | *request_edns_payload_size = (size_t) 0U; 121 | return -1; 122 | } 123 | 124 | assert(dns_packet[DNS_OFFSET_ARCOUNT + 1U] == 0U); 125 | dns_packet[DNS_OFFSET_ARCOUNT + 1U] = 1U; 126 | memcpy(dns_packet + *dns_packet_len_p, opt_rr, sizeof opt_rr); 127 | *dns_packet_len_p += sizeof opt_rr; 128 | *request_edns_payload_size = edns_payload_size; 129 | assert(*dns_packet_len_p <= dns_packet_max_size); 130 | assert(*dns_packet_len_p <= 0xFFFF); 131 | 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /edns.h: -------------------------------------------------------------------------------- 1 | #ifndef EDNS_H 2 | #define EDNS_H 3 | 4 | struct context; 5 | 6 | int edns_add_section(struct context *const c, 7 | uint8_t *const dns_packet, 8 | size_t * const dns_packet_len_p, 9 | size_t dns_packet_max_size, 10 | size_t * const request_edns_payload_size); 11 | #endif 12 | -------------------------------------------------------------------------------- /example/1.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/example/1.cert -------------------------------------------------------------------------------- /example/1.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/example/1.key -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | This is a demo. DO NOT USE ANY certificate/secret files in this directory. 4 | 5 | ## Usage 6 | 7 | 1, open a new terminal, run 8 | 9 | ``` 10 | ./start_wrapper.sh 11 | ``` 12 | 13 | 2, open a new terminal, run 14 | 15 | ``` 16 | ./start_proxy.sh 17 | ``` 18 | 19 | 3, test 20 | 21 | ``` 22 | dig www.google.com @127.0.0.1 -p 8855 23 | ``` 24 | -------------------------------------------------------------------------------- /example/public.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/example/public.key -------------------------------------------------------------------------------- /example/secret.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/example/secret.key -------------------------------------------------------------------------------- /example/start_proxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dnscrypt-proxy -a 127.0.0.1:8855 -r 127.0.0.1:8854 \ 4 | --provider-name=2.dnscrypt-cert.example.com \ 5 | --provider-key=3686:91DF:DC22:8DBB:67BF:9EF6:5471:C831:B468:E0F8:18D9:6CB1:254E:3BE7:7A88:AB24 6 | -------------------------------------------------------------------------------- /example/start_wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ../dnscrypt-wrapper --resolver-address=8.8.8.8:53 --listen-address=0.0.0.0:8854 \ 4 | --provider-name=2.dnscrypt-cert.example.com \ 5 | --provider-cert-file=1.cert \ 6 | --crypt-secretkey-file=1.key \ 7 | -VV 8 | -------------------------------------------------------------------------------- /fpst.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct FPST { 8 | struct FPST *children; 9 | const char *key; 10 | uint16_t idx; 11 | uint16_t bitmap; 12 | uint32_t val; 13 | } FPST; 14 | 15 | #define fpst_GLOBALS 16 | #include "fpst.h" 17 | 18 | #ifdef __GNUC__ 19 | # define popcount(X) ((unsigned int) __builtin_popcount(X)) 20 | # define prefetch(X) __builtin_prefetch(X) 21 | #else 22 | # define prefetch(X) (void)(X) 23 | 24 | static inline unsigned int 25 | popcount(uint32_t w) 26 | { 27 | w -= (w >> 1) & 0x55555555; 28 | w = (w & 0x33333333) + ((w >> 2) & 0x33333333); 29 | w = (w + (w >> 4)) & 0x0f0f0f0f; 30 | w = (w * 0x01010101) >> 24; 31 | return w; 32 | } 33 | #endif 34 | 35 | static inline unsigned char 36 | fpst_quadbit_at(const char *str, size_t i) 37 | { 38 | unsigned char c; 39 | 40 | c = (unsigned char) str[i / 2]; 41 | if ((i & 1U) == 0U) { 42 | c >>= 4; 43 | } 44 | return c & 0xf; 45 | } 46 | 47 | static inline int 48 | fpst_bitmap_is_set(const FPST *t, size_t bit) 49 | { 50 | return (t->bitmap & (((uint16_t) 1U) << bit)) != 0U; 51 | } 52 | 53 | static inline void 54 | fpst_bitmap_set(FPST *t, size_t bit) 55 | { 56 | t->bitmap |= (((uint16_t) 1U) << bit); 57 | } 58 | 59 | static inline size_t 60 | fpst_actual_index(const FPST *t, size_t i) 61 | { 62 | const uint16_t b = t->bitmap & ((((uint16_t) 1U) << i) - 1U); 63 | 64 | return (size_t) popcount((uint32_t) b); 65 | } 66 | 67 | static inline FPST * 68 | fpst_child_get(FPST *t, size_t i) 69 | { 70 | if (!fpst_bitmap_is_set(t, i)) { 71 | return NULL; 72 | } 73 | return &t->children[fpst_actual_index(t, i)]; 74 | } 75 | 76 | static int 77 | fpst_child_set(FPST *t, FPST *v, size_t i) 78 | { 79 | FPST *previous; 80 | FPST *tmp; 81 | size_t ri; 82 | size_t rcount; 83 | size_t count; 84 | 85 | if ((previous = fpst_child_get(t, i)) != NULL) { 86 | *previous = *v; 87 | return 0; 88 | } 89 | count = (size_t) popcount(t->bitmap) + 1U; 90 | if ((tmp = (FPST *) realloc(t->children, 91 | count * (sizeof *t->children))) == NULL) { 92 | return -1; 93 | } 94 | t->children = tmp; 95 | ri = fpst_actual_index(t, i); 96 | if ((rcount = count - ri - 1U) > 0U) { 97 | memmove(&t->children[ri + 1U], &t->children[ri], 98 | rcount * (sizeof *t->children)); 99 | } 100 | t->children[ri] = *v; 101 | fpst_bitmap_set(t, i); 102 | 103 | return 0; 104 | } 105 | 106 | FPST * 107 | fpst_new(void) 108 | { 109 | return NULL; 110 | } 111 | 112 | FPST * 113 | fpst_insert(FPST *trie, const char *key, size_t len, uint32_t val) 114 | { 115 | FPST *new_node_p; 116 | FPST *t; 117 | const char *lk; 118 | FPST new_node, saved_node; 119 | size_t i; 120 | size_t j; 121 | unsigned char c; 122 | unsigned char x; 123 | 124 | if (len >= 0x7fff) { 125 | return NULL; 126 | } 127 | if (trie == NULL) { 128 | if ((new_node_p = (FPST *) malloc(sizeof *new_node_p)) == NULL) { 129 | return NULL; 130 | } 131 | new_node_p->key = key; 132 | new_node_p->val = val; 133 | new_node_p->idx = 0U; 134 | new_node_p->bitmap = 0U; 135 | new_node_p->children = NULL; 136 | 137 | return new_node_p; 138 | } 139 | t = trie; 140 | j = 0U; 141 | for (;;) { 142 | lk = t->key; 143 | x = 0U; 144 | for (; j <= len; j++) { 145 | x = ((unsigned char) lk[j]) ^ ((unsigned char) key[j]); 146 | if (x != 0U) { 147 | break; 148 | } 149 | } 150 | if (j > len && lk[j - 1] == 0) { 151 | assert(key[j - 1] == 0); 152 | t->val = val; 153 | return trie; 154 | } 155 | i = j * 2; 156 | if ((x & 0xf0) == 0U) { 157 | i++; 158 | } 159 | if (t->bitmap == 0U) { 160 | /* keep index from the new key */ 161 | } else if (i >= t->idx) { 162 | i = t->idx; 163 | j = i / 2; 164 | } else { 165 | saved_node = *t; 166 | t->key = key; 167 | t->val = val; 168 | t->idx = (uint16_t) i; 169 | t->bitmap = 0U; 170 | t->children = NULL; 171 | c = fpst_quadbit_at(lk, i); 172 | if (fpst_child_set(t, &saved_node, (size_t) c) != 0) { 173 | *t = saved_node; 174 | return NULL; 175 | } 176 | return trie; 177 | } 178 | prefetch(t->children); 179 | c = fpst_quadbit_at(key, i); 180 | if (!fpst_bitmap_is_set(t, c)) { 181 | break; 182 | } 183 | t = fpst_child_get(t, (size_t) c); 184 | } 185 | t->idx = (uint16_t) i; 186 | assert(!fpst_bitmap_is_set(t, c)); 187 | new_node.key = key; 188 | new_node.val = val; 189 | new_node.idx = 0U; 190 | new_node.bitmap = 0U; 191 | new_node.children = NULL; 192 | if (fpst_child_set(t, &new_node, (size_t) c) != 0) { 193 | return NULL; 194 | } 195 | return trie; 196 | } 197 | 198 | FPST * 199 | fpst_insert_str(FPST *trie, const char *key, uint32_t val) 200 | { 201 | return fpst_insert(trie, key, strlen(key), val); 202 | } 203 | 204 | int 205 | fpst_starts_with_existing_key(FPST *trie, 206 | const char *str, size_t len, 207 | const char **found_key_p, 208 | uint32_t *found_val_p) 209 | { 210 | FPST *t; 211 | const char *lk; 212 | size_t i; 213 | size_t j; 214 | unsigned char c; 215 | int ret = 0; 216 | 217 | if (trie == NULL) { 218 | return 0; 219 | } 220 | t = trie; 221 | j = 0U; 222 | for (;;) { 223 | lk = t->key; 224 | for (; j <= len; j++) { 225 | if (lk[j] != str[j]) { 226 | if (lk[j] == 0) { 227 | *found_key_p = t->key; 228 | *found_val_p = t->val; 229 | ret = 1; 230 | } 231 | break; 232 | } 233 | } 234 | if (j > len) { 235 | *found_key_p = t->key; 236 | *found_val_p = t->val; 237 | ret = 1; 238 | break; 239 | } 240 | if (t->bitmap == 0U) { 241 | break; 242 | } 243 | i = t->idx; 244 | if (i > len * 2) { 245 | break; 246 | } 247 | if (j > i / 2) { 248 | j = i / 2; 249 | } 250 | prefetch(t->children); 251 | c = fpst_quadbit_at(str, i); 252 | if (!fpst_bitmap_is_set(t, c)) { 253 | if (fpst_bitmap_is_set(t, 0U)) { 254 | c = 0U; 255 | } else { 256 | break; 257 | } 258 | } 259 | t = fpst_child_get(t, (size_t) c); 260 | } 261 | return ret; 262 | } 263 | 264 | int 265 | fpst_str_starts_with_existing_key(FPST *trie, const char *str, 266 | const char **found_key_p, 267 | uint32_t *found_val_p) 268 | { 269 | return fpst_starts_with_existing_key(trie, str, strlen(str), 270 | found_key_p, found_val_p); 271 | } 272 | 273 | static void 274 | fpst_free_node(FPST *t, FPST_FreeFn free_kv_fn) 275 | { 276 | size_t count; 277 | size_t i; 278 | 279 | if (t->bitmap == 0U) { 280 | assert(t->children == NULL); 281 | } 282 | count = (size_t) popcount(t->bitmap); 283 | for (i = 0; i < count; i++) { 284 | fpst_free_node(&t->children[i], free_kv_fn); 285 | } 286 | free(t->children); 287 | t->bitmap = 0U; 288 | t->children = NULL; 289 | free_kv_fn(t->key, t->val); 290 | t->key = NULL; 291 | } 292 | 293 | int 294 | fpst_has_key(FPST *trie, const char *key, size_t len, uint32_t *found_val_p) 295 | { 296 | const char *found_key; 297 | int ret; 298 | 299 | ret = fpst_starts_with_existing_key(trie, key, len, 300 | &found_key, found_val_p); 301 | if (ret > 0 && strlen(found_key) != len) { 302 | ret = 0; 303 | } 304 | return ret; 305 | } 306 | 307 | int 308 | fpst_has_key_str(FPST *trie, const char *key, uint32_t *found_val_p) 309 | { 310 | return fpst_has_key(trie, key, strlen(key), found_val_p); 311 | } 312 | 313 | void 314 | fpst_free(FPST *trie, FPST_FreeFn free_kv_fn) 315 | { 316 | if (trie == NULL) { 317 | return; 318 | } 319 | fpst_free_node(trie, free_kv_fn); 320 | free(trie); 321 | } 322 | -------------------------------------------------------------------------------- /fpst.h: -------------------------------------------------------------------------------- 1 | #ifndef fpst_H 2 | #define fpst_H 1 3 | 4 | #include 5 | #include 6 | 7 | /** A trie */ 8 | #ifndef fpst_GLOBALS 9 | typedef struct FPST FPST; 10 | #endif 11 | 12 | /** Type of the function pointer for `fpst_free()` */ 13 | typedef void (*FPST_FreeFn)(const char *key, uint32_t val); 14 | 15 | /** Returns an empty trie */ 16 | FPST *fpst_new(void); 17 | 18 | /** 19 | * Deallocates a trie, optionally calling `free_kv_fn` for each element. 20 | * `free_kv_fn` can be `NULL` if this is not necessary. 21 | */ 22 | void fpst_free(FPST *trie, FPST_FreeFn free_kv_fn); 23 | 24 | /** 25 | * Check if the string `str` of length `len` starts with one of the keys 26 | * present in the trie. Returns `1` if this is the case, `0` otherwise. 27 | * If `found_key_p` and/or `found_val_p` are not `NULL`, these are filled 28 | * with the longest matching key and its corresponding value. 29 | */ 30 | int fpst_starts_with_existing_key(FPST *t, 31 | const char *str, size_t len, 32 | const char **found_key_p, 33 | uint32_t *found_val_p); 34 | 35 | /** 36 | * Check if the zero-terminated string `str` starts with one of the keys 37 | * present in the trie. Returns `1` if this is the case, `0` otherwise. 38 | * If `found_key_p` and/or `found_val_p` are not `NULL`, these are filled 39 | * with the longest matching key and its corresponding value. 40 | */ 41 | int fpst_str_starts_with_existing_key(FPST *trie, const char *str, 42 | const char **found_key_p, uint32_t *found_val_p); 43 | 44 | /** Check if the exact key `key` of length `len` (`\0` not included) exists */ 45 | int fpst_has_key(FPST *trie, const char *key, size_t len, uint32_t *found_val_p); 46 | 47 | /** Check if the exact zero-terminated key `key` is present in the trie */ 48 | int fpst_has_key_str(FPST *trie, const char *key, uint32_t *found_val_p); 49 | 50 | /** 51 | * Inserts a key `key` of length `len` (not including the leading `\0`) 52 | * into the trie. 53 | */ 54 | FPST * fpst_insert(FPST *trie, const char *key, size_t len, uint32_t val); 55 | 56 | /** 57 | * Inserts a zero-terminated key `key` into the trie. 58 | */ 59 | FPST * fpst_insert_str(FPST *trie, const char *key, uint32_t val); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /gen-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Generate version automatically. 3 | 4 | version_file=version.h 5 | 6 | if test -d .git; then 7 | version=$(git describe --always --match "v[0-9]*" | sed -r -e 's/-([^-]+)$/.\1/g' | sed -e 's/^v//g') 8 | if test -f "$version_file"; then 9 | VC=$(perl -ne 'print "$1" if m/.*the_version = "(.*)".*/' $version_file) 10 | else 11 | VC=unset 12 | fi 13 | 14 | if test "$VC" != "$version"; then 15 | echo "/* Automatically generated by $0 */ 16 | #ifndef VERSION_H 17 | #define VERSION_H 18 | 19 | const char *the_version = \"$version\"; 20 | 21 | #endif" > $version_file 22 | fi 23 | fi 24 | -------------------------------------------------------------------------------- /hack/Dockerfile.debian: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | RUN export DEBIAN_FRONTEND=noninteractive \ 4 | && sed -i /security.ubuntu.com/d /etc/apt/sources.list \ 5 | && apt-get update -y \ 6 | && apt-get -yy -q install --no-install-recommends --no-install-suggests --fix-missing \ 7 | dpkg-dev \ 8 | build-essential \ 9 | debhelper \ 10 | dh-systemd \ 11 | dh-autoreconf \ 12 | fakeroot \ 13 | devscripts 14 | 15 | ADD hack/entrypoint.sh /entrypoint.sh 16 | 17 | ENTRYPOINT ["/entrypoint.sh"] 18 | -------------------------------------------------------------------------------- /hack/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | apt-get install -y libsodium-dev libevent-dev 6 | 7 | cd /src/dnscrypt-wrapper 8 | debuild -us -uc 9 | -------------------------------------------------------------------------------- /hack/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # A script used to package dnscrypt-wrapper. It depends on docker. 4 | # 5 | 6 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 7 | cd $ROOT 8 | 9 | docker build -t dnscrypt-wrapper-packager -f hack/Dockerfile.debian . 10 | docker run -v $ROOT:/src/dnscrypt-wrapper --rm -it dnscrypt-wrapper-packager 11 | -------------------------------------------------------------------------------- /hack/setup-travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | # Install unbound so we can test dnscrypt-wrapper. 6 | apt-get install -y unbound 7 | 8 | # Workaround where the container does not have ::1 but unbound default to 9 | # binding to localhost (which is both 127.0.0.1 and ::1) 10 | cat < /etc/unbound/unbound.conf 11 | server: 12 | interface: 127.0.0.1 13 | remote-control: 14 | control-enable: no 15 | EOF 16 | service unbound restart 17 | 18 | # Wait unbound 19 | retries=3 20 | until [ "$retries" -le 0 ]; do 21 | nc -n -v -w 3 127.0.0.1 -z 53 22 | if [ $? -eq 0 ]; then 23 | break 24 | fi 25 | sleep 1 26 | retries=$((retries-1)) 27 | done 28 | -------------------------------------------------------------------------------- /logger.c: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | 3 | int logger_verbosity = LOG_INFO; 4 | char *logger_logfile = NULL; 5 | int logger_fd = -1; 6 | 7 | #define LOGGER_LINESIZE 1024 8 | 9 | // priority names (from ) 10 | #define INTERNAL_NOPRI 0x10 /* the "no priority" priority */ 11 | typedef struct _code { 12 | const char *c_name; 13 | int c_val; 14 | } CODE; 15 | 16 | CODE prioritynames[] = { 17 | {"emerg", LOG_EMERG}, 18 | {"alert", LOG_ALERT}, 19 | {"crit", LOG_CRIT}, 20 | {"err", LOG_ERR}, 21 | {"warning", LOG_WARNING}, 22 | {"notice", LOG_NOTICE}, 23 | {"info", LOG_INFO}, 24 | {"debug", LOG_DEBUG}, 25 | {"none", INTERNAL_NOPRI}, /* INTERNAL */ 26 | {NULL, -1} 27 | }; 28 | 29 | void 30 | _logger(int priority, const char *fmt, ...) 31 | { 32 | va_list ap; 33 | char msg[LOGGER_MAXLEN]; 34 | 35 | if (priority > logger_verbosity) 36 | return; 37 | 38 | va_start(ap, fmt); 39 | vsnprintf(msg, sizeof(msg), fmt, ap); 40 | va_end(ap); 41 | 42 | logger_lograw(priority, msg); 43 | } 44 | 45 | void 46 | _logger_with_fileline(int priority, const char *fmt, const char *file, int line, 47 | ...) 48 | { 49 | va_list ap; 50 | char msg[LOGGER_MAXLEN]; 51 | 52 | if (priority > logger_verbosity) 53 | return; 54 | 55 | size_t n = snprintf(msg, sizeof(msg), "[%s:%d] ", file, line); 56 | 57 | va_start(ap, line); 58 | vsnprintf(msg + n, sizeof(msg), fmt, ap); 59 | va_end(ap); 60 | 61 | logger_lograw(priority, msg); 62 | } 63 | 64 | /* 65 | * Low-level logging. It's only used when you want to log arbitrary length message. 66 | */ 67 | void 68 | logger_lograw(int priority, const char *msg) 69 | { 70 | FILE *fp; 71 | const char *priority_flag = NULL; 72 | 73 | if (priority > logger_verbosity) 74 | return; 75 | 76 | // invalid priority? 77 | if (priority < 0 || priority > LOG_PRIMASK) 78 | priority = INTERNAL_NOPRI; 79 | 80 | if (logger_fd == -1) { 81 | logger_reopen(); 82 | } 83 | if (logger_fd == -1) { 84 | return; 85 | } 86 | fp = (logger_logfile == NULL) ? stdout : fopen(logger_logfile, "a"); 87 | if (!fp) 88 | return; 89 | 90 | for (int i = 0; i < ARRAY_SIZE(prioritynames); i++) { 91 | CODE c = prioritynames[i]; 92 | if (c.c_val == priority) { 93 | priority_flag = c.c_name; 94 | } 95 | } 96 | assert(priority_flag); 97 | 98 | // prefix 99 | int off; 100 | struct timeval tv; 101 | gettimeofday(&tv, NULL); 102 | char buf[64]; 103 | off = strftime(buf, sizeof(buf), "%d %b %H:%M:%S.", localtime(&tv.tv_sec)); 104 | snprintf(buf + off, sizeof(buf) - off, "%03d", (int)tv.tv_usec / 1000); 105 | // format log 106 | char logbuf[LOGGER_LINESIZE]; 107 | size_t len = snprintf(logbuf, LOGGER_LINESIZE, "[%d] %s [%s] %s\n", 108 | (int)getpid(), buf, priority_flag, msg); 109 | // write 110 | write(logger_fd, logbuf, len); 111 | } 112 | 113 | void 114 | logger_reopen(void) 115 | { 116 | if (logger_logfile) { 117 | logger_fd = open(logger_logfile, O_APPEND | O_CREAT | O_WRONLY, 0644); 118 | } else { 119 | logger_fd = STDOUT_FILENO; 120 | } 121 | } 122 | 123 | void 124 | logger_close(void) 125 | { 126 | if (logger_fd >= 0) { 127 | close(logger_fd); 128 | } 129 | logger_fd = -1; 130 | } 131 | -------------------------------------------------------------------------------- /logger.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_H 2 | #define LOGGER_H 3 | /** 4 | * Logger 5 | * 6 | * @link http://en.wikipedia.org/wiki/Syslog 7 | */ 8 | 9 | #include "compat.h" 10 | 11 | #ifndef _WIN32 12 | #include 13 | #undef SYSLOG_NAMES 14 | #else 15 | #define LOG_EMERG 0 /* system is unusable */ 16 | #define LOG_ALERT 1 /* action must be taken immediately */ 17 | #define LOG_CRIT 2 /* critical conditions */ 18 | #define LOG_ERR 3 /* error conditions */ 19 | #define LOG_WARNING 4 /* warning conditions */ 20 | #define LOG_NOTICE 5 /* normal but significant condition */ 21 | #define LOG_INFO 6 /* informational */ 22 | #define LOG_DEBUG 7 /* debug-level messages */ 23 | #endif 24 | 25 | #define LOGGER_MAXLEN 8192 26 | 27 | /* Configurations. */ 28 | extern int logger_verbosity; 29 | extern char *logger_logfile; 30 | 31 | /* Global Variables. */ 32 | extern int logger_fd; 33 | 34 | // see http://stackoverflow.com/q/5588855/288089 35 | #define logger(p, fmt, ...) _logger_with_fileline((p), (fmt), __FILE__, __LINE__, ##__VA_ARGS__) 36 | void _logger(int priority, const char *fmt, ...); 37 | void _logger_with_fileline(int priority, const char *fmt, const char *file, int line, ...); 38 | void logger_lograw(int priority, const char *msg); 39 | void logger_reopen(void); 40 | void logger_close(void); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /pidfile.c: -------------------------------------------------------------------------------- 1 | #include "pidfile.h" 2 | 3 | static volatile sig_atomic_t exit_requested = 0; 4 | static const char *_pidfile = NULL; 5 | 6 | static void 7 | pidfile_remove_file(void) 8 | { 9 | if (_pidfile != NULL) { 10 | unlink(_pidfile); 11 | _pidfile = NULL; 12 | } 13 | } 14 | 15 | static void 16 | pidfile_atexit_handler(void) 17 | { 18 | pidfile_remove_file(); 19 | } 20 | 21 | static void 22 | pidfile_sig_exit_handler(int sig) 23 | { 24 | (void)sig; 25 | if (exit_requested) 26 | return; 27 | exit_requested = 1; 28 | exit(0); 29 | } 30 | 31 | static void 32 | pidfile_install_signal_handlers(void (*handler) (int)) 33 | { 34 | signal(SIGPIPE, SIG_IGN); 35 | signal(SIGALRM, handler); 36 | signal(SIGHUP, handler); 37 | signal(SIGINT, handler); 38 | signal(SIGQUIT, handler); 39 | signal(SIGTERM, handler); 40 | #ifdef SIGXCPU 41 | signal(SIGXCPU, handler); 42 | #endif 43 | } 44 | 45 | int 46 | pidfile_create(const char *const pidfile) 47 | { 48 | FILE *fp; 49 | _pidfile = pidfile; 50 | 51 | fp = fopen(pidfile, "w"); 52 | if (!fp) { 53 | return -1; 54 | } 55 | 56 | fprintf(fp, "%d\n", (int)getpid()); 57 | fclose(fp); 58 | 59 | pidfile_install_signal_handlers(pidfile_sig_exit_handler); 60 | atexit(pidfile_atexit_handler); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /pidfile.h: -------------------------------------------------------------------------------- 1 | #ifndef PIDFILE_H 2 | #define PIDFILE_H 3 | 4 | #include "dnscrypt.h" 5 | 6 | int pidfile_create(const char *const pidfile); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /rfc1035.c: -------------------------------------------------------------------------------- 1 | /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley 2 | 3 | This program is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; version 2 dated June, 1991, or 6 | (at your option) version 3 dated 29 June, 2007. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . 15 | */ 16 | /** 17 | * This file is modified from dnsmasq/src/rfc1035.c. 18 | */ 19 | 20 | #include "rfc1035.h" 21 | 22 | #define CHECK_LEN(header, pp, plen, len) \ 23 | ((size_t)((pp) - (unsigned char *) (header) + (len)) <= (plen)) 24 | 25 | #define ADD_RDLEN(header, pp, plen, len) \ 26 | (!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1)) 27 | 28 | int 29 | extract_name(struct dns_header *header, size_t plen, unsigned char **pp, 30 | char *name, int isExtract, int extrabytes) 31 | { 32 | unsigned char *cp = (unsigned char *) name, *p = *pp, *p1 = NULL; 33 | unsigned int j, l, hops = 0; 34 | int retvalue = 1; 35 | 36 | if (isExtract) { 37 | *cp = 0; 38 | } 39 | while (1) { 40 | unsigned int label_type; 41 | 42 | if (!CHECK_LEN(header, p, plen, 1)) { 43 | return 0; 44 | } 45 | if ((l = *p++) == 0) { 46 | /* end marker */ 47 | /* check that there are the correct no of bytes after the name */ 48 | if (!CHECK_LEN(header, p, plen, extrabytes)) { 49 | return 0; 50 | } 51 | if (isExtract) { 52 | if (cp != (unsigned char *) name) { 53 | cp--; 54 | } 55 | *cp = 0; /* terminate: lose final period */ 56 | } else if (*cp != 0) { 57 | retvalue = 2; 58 | } 59 | if (p1) { /* we jumped via compression */ 60 | *pp = p1; 61 | } else { 62 | *pp = p; 63 | } 64 | return retvalue; 65 | } 66 | 67 | label_type = l & 0xc0; 68 | 69 | if (label_type == 0xc0) { /* pointer */ 70 | if (!CHECK_LEN(header, p, plen, 1)) { 71 | return 0; 72 | } 73 | 74 | /* get offset */ 75 | l = (l & 0x3f) << 8; 76 | l |= *p++; 77 | 78 | if (!p1) { /* first jump, save location to go back to */ 79 | p1 = p; 80 | } 81 | hops++; /* break malicious infinite loops */ 82 | if (hops > 255) { 83 | return 0; 84 | } 85 | p = l + (unsigned char *) header; 86 | } else if (label_type == 0x80) { 87 | return 0; /* reserved */ 88 | } else if (label_type == 0x40) { /* ELT */ 89 | unsigned int count, digs; 90 | 91 | if ((l & 0x3f) != 1) { 92 | return 0; /* we only understand bitstrings */ 93 | } 94 | if (!isExtract) { 95 | return 0; /* Cannot compare bitsrings */ 96 | } 97 | count = *p++; 98 | if (count == 0) { 99 | count = 256; 100 | } 101 | digs = ((count - 1) >> 2) + 1; 102 | 103 | /* output is \[x/siz]. which is digs+9 chars */ 104 | if (cp - (unsigned char *) name + digs + 9 >= MAXDNAME) { 105 | return 0; 106 | } 107 | 108 | if (!CHECK_LEN(header, p, plen, (count - 1) >> 3)) { 109 | return 0; 110 | } 111 | 112 | *cp++ = '\\'; 113 | *cp++ = '['; 114 | *cp++ = 'x'; 115 | for (j = 0; j < digs; j++) { 116 | unsigned int dig; 117 | if (j % 2 == 0) { 118 | dig = *p >> 4; 119 | } else { 120 | dig = *p++ & 0x0f; 121 | } 122 | *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10; 123 | } 124 | cp += sprintf((char *) cp, "/%d]", count); 125 | 126 | /* do this here to overwrite the zero char from sprintf */ 127 | *cp++ = '.'; 128 | } else { /* label_type = 0 -> label. */ 129 | if (cp - (unsigned char *) name + l + 1 >= MAXDNAME) { 130 | return 0; 131 | } 132 | if (!CHECK_LEN(header, p, plen, l)) { 133 | return 0; 134 | } 135 | for (j = 0; j < l; j++, p++) { 136 | if (isExtract) { 137 | unsigned char c = *p; 138 | if (isascii(c) && !iscntrl(c) && c != '.') { 139 | *cp++ = *p; 140 | } else { 141 | return 0; 142 | } 143 | } else { 144 | unsigned char c1 = *cp, c2 = *p; 145 | 146 | if (c1 == 0) { 147 | retvalue = 2; 148 | } else { 149 | cp++; 150 | if (c1 >= 'A' && c1 <= 'Z') { 151 | c1 += 'a' - 'A'; 152 | } 153 | if (c2 >= 'A' && c2 <= 'Z') { 154 | c2 += 'a' - 'A'; 155 | } 156 | if (c1 != c2) { 157 | retvalue = 2; 158 | } 159 | } 160 | } 161 | } 162 | if (isExtract) { 163 | *cp++ = '.'; 164 | } else if (*cp != 0 && *cp++ != '.') { 165 | retvalue = 2; 166 | } 167 | } 168 | } 169 | } 170 | 171 | /* Hash the question section. This is used to safely detect query 172 | retransmission and to detect answers to questions we didn't ask, which 173 | might be poisoning attacks. Note that we decode the name rather 174 | than hash the raw bytes, since replies might be compressed differently. 175 | We ignore case in the names for the same reason. Return all-ones 176 | if there is not question section. */ 177 | int 178 | questions_hash(uint64_t *hash, struct dns_header *header, size_t plen, 179 | char *name, const unsigned char key[crypto_shorthash_KEYBYTES]) 180 | { 181 | unsigned char *p = (unsigned char *) (header + 1); 182 | size_t name_len; 183 | 184 | if (ntohs(header->qdcount) != 1 || 185 | !extract_name(header, plen, &p, name, 1, 4) || 186 | (name_len = strlen(name)) > MAXDNAME) { 187 | return -1; 188 | } 189 | crypto_shorthash((unsigned char *) hash, (const unsigned char *) name, name_len, key); 190 | 191 | return 0; 192 | } 193 | 194 | static unsigned char * 195 | skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, 196 | int extrabytes) 197 | { 198 | while (1) { 199 | unsigned int label_type; 200 | 201 | if (!CHECK_LEN(header, ansp, plen, 1)) { 202 | return NULL; 203 | } 204 | label_type = (*ansp) & 0xc0; 205 | 206 | if (label_type == 0xc0) { 207 | /* pointer for compression. */ 208 | ansp += 2; 209 | break; 210 | } else if (label_type == 0x80) { 211 | return NULL; /* reserved */ 212 | } else if (label_type == 0x40) { 213 | /* Extended label type */ 214 | unsigned int count; 215 | 216 | if (!CHECK_LEN(header, ansp, plen, 2)) { 217 | return NULL; 218 | } 219 | if (((*ansp++) & 0x3f) != 1) { 220 | return NULL; /* we only understand bitstrings */ 221 | } 222 | count = *(ansp++); /* Bits in bitstring */ 223 | 224 | if (count == 0) { /* count == 0 means 256 bits */ 225 | ansp += 32; 226 | } else { 227 | ansp += ((count - 1) >> 3) + 1; 228 | } 229 | } else { /* label type == 0 Bottom six bits is length */ 230 | unsigned int len = (*ansp++) & 0x3f; 231 | 232 | if (!ADD_RDLEN(header, ansp, plen, len)) { 233 | return NULL; 234 | } 235 | if (len == 0) { 236 | break; /* zero length label marks the end. */ 237 | } 238 | } 239 | } 240 | 241 | if (!CHECK_LEN(header, ansp, plen, extrabytes)) { 242 | return NULL; 243 | } 244 | return ansp; 245 | } 246 | 247 | unsigned char * 248 | skip_questions(struct dns_header *header, size_t plen) 249 | { 250 | int q; 251 | unsigned char *ansp = (unsigned char *) (header + 1); 252 | 253 | for (q = ntohs(header->qdcount); q != 0; q--) { 254 | if (!(ansp = skip_name(ansp, header, plen, 4))) { 255 | return NULL; 256 | } 257 | ansp += 4; /* class and type */ 258 | } 259 | return ansp; 260 | } 261 | 262 | unsigned char * 263 | do_rfc1035_name(unsigned char *p, char *sval) 264 | { 265 | int j; 266 | 267 | while (sval && *sval) { 268 | unsigned char *cp = p++; 269 | for (j = 0; *sval && (*sval != '.'); sval++, j++) { 270 | *p++ = *sval; 271 | } 272 | *cp = j; 273 | if (*sval) { 274 | sval++; 275 | } 276 | } 277 | return p; 278 | } 279 | 280 | int 281 | add_resource_record(struct dns_header *header, unsigned int nameoffset, size_t plen, 282 | unsigned char **pp, unsigned long ttl, unsigned int *offset, 283 | unsigned short type, unsigned short class, char *format, 284 | ...) 285 | { 286 | va_list ap; 287 | unsigned char *sav, *p = *pp; 288 | int j; 289 | unsigned short usval; 290 | long lval; 291 | char *sval; 292 | 293 | if (!CHECK_LEN(header, p, plen, 12)) { 294 | return 0; 295 | } 296 | PUTSHORT(nameoffset | 0xc000, p); 297 | PUTSHORT(type, p); 298 | PUTSHORT(class, p); 299 | PUTLONG(ttl, p); /* TTL */ 300 | 301 | sav = p; /* Save pointer to RDLength field */ 302 | PUTSHORT(0, p); /* Placeholder RDLength */ 303 | 304 | va_start(ap, format); /* make ap point to 1st unamed argument */ 305 | 306 | for (; *format; format++) 307 | switch (*format) { 308 | #ifdef HAVE_IPV6 309 | case '6': 310 | if (!CHECK_LEN(header, p, plen, IN6ADDRSZ)) { 311 | return 0; 312 | } 313 | sval = va_arg(ap, char *); 314 | memcpy(p, sval, IN6ADDRSZ); 315 | p += IN6ADDRSZ; 316 | break; 317 | #endif 318 | 319 | case '4': 320 | if (!CHECK_LEN(header, p, plen, INADDRSZ)) { 321 | return 0; 322 | } 323 | sval = va_arg(ap, char *); 324 | memcpy(p, sval, INADDRSZ); 325 | p += INADDRSZ; 326 | break; 327 | 328 | case 's': 329 | if (!CHECK_LEN(header, p, plen, 2)) { 330 | return 0; 331 | } 332 | usval = va_arg(ap, int); 333 | PUTSHORT(usval, p); 334 | break; 335 | 336 | case 'l': 337 | if (!CHECK_LEN(header, p, plen, 4)) { 338 | return 0; 339 | } 340 | lval = va_arg(ap, long); 341 | PUTLONG(lval, p); 342 | break; 343 | 344 | case 'd': 345 | /* get domain-name answer arg and store it in RDATA field */ 346 | if (offset) { 347 | *offset = p - (unsigned char *) header; 348 | } 349 | p = do_rfc1035_name(p, va_arg(ap, char *)); 350 | *p++ = 0; 351 | break; 352 | 353 | case 't': 354 | usval = va_arg(ap, int); 355 | sval = va_arg(ap, char *); 356 | if (!CHECK_LEN(header, p, plen, usval)) { 357 | return 0; 358 | } 359 | if (usval != 0) { 360 | memcpy(p, sval, usval); 361 | } 362 | p += usval; 363 | break; 364 | 365 | case 'z': 366 | sval = va_arg(ap, char *); 367 | usval = sval ? strlen(sval) : 0; 368 | if (usval > 255) { 369 | usval = 255; 370 | } 371 | if (!CHECK_LEN(header, p, plen, (1 + usval))) { 372 | return 0; 373 | } 374 | *p++ = (unsigned char) usval; 375 | memcpy(p, sval, usval); 376 | p += usval; 377 | break; 378 | } 379 | 380 | va_end(ap); /* clean up variable argument pointer */ 381 | 382 | j = p - sav - 2; 383 | if (!CHECK_LEN(header, sav, plen, 2)) { 384 | return 0; 385 | } 386 | PUTSHORT(j, sav); /* Now, store real RDLength */ 387 | 388 | *pp = p; 389 | 390 | return 1; 391 | } 392 | -------------------------------------------------------------------------------- /rfc1035.h: -------------------------------------------------------------------------------- 1 | #ifndef RFC1035_H 2 | #define RFC1035_H 3 | 4 | #include "compat.h" 5 | #include "dns-protocol.h" 6 | #include 7 | 8 | int questions_hash(uint64_t *hash, struct dns_header *header, size_t plen, 9 | char *buff, 10 | const unsigned char key[crypto_shorthash_KEYBYTES]); 11 | 12 | int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, 13 | char *name, int isExtract, int extrabytes); 14 | 15 | int add_resource_record(struct dns_header *header, unsigned int nameoffset, 16 | size_t plen, unsigned char **pp, unsigned long ttl, 17 | unsigned int *offset, unsigned short type, 18 | unsigned short class, char *format, ...); 19 | 20 | unsigned char *skip_questions(struct dns_header *header, size_t plen); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /safe_rw.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | #ifndef _WIN32 7 | # include 8 | #endif 9 | #include 10 | #include 11 | 12 | #include "safe_rw.h" 13 | 14 | #ifndef _WIN32 15 | ssize_t 16 | safe_write(const int fd, const void *const buf_, size_t count, 17 | const int timeout) 18 | { 19 | struct pollfd pfd; 20 | const char *buf = (const char *)buf_; 21 | ssize_t written; 22 | 23 | pfd.fd = fd; 24 | pfd.events = POLLOUT; 25 | 26 | while (count > (size_t) 0) { 27 | while ((written = write(fd, buf, count)) <= (ssize_t) 0) { 28 | if (errno == EAGAIN) { 29 | if (poll(&pfd, (nfds_t) 1, timeout) == 0) { 30 | errno = ETIMEDOUT; 31 | goto ret; 32 | } 33 | } else if (errno != EINTR) { 34 | goto ret; 35 | } 36 | } 37 | buf += written; 38 | count -= (size_t) written; 39 | } 40 | ret: 41 | return (ssize_t) (buf - (const char *)buf_); 42 | } 43 | 44 | ssize_t 45 | safe_read(const int fd, void *const buf_, size_t count) 46 | { 47 | unsigned char *buf = (unsigned char *)buf_; 48 | ssize_t readnb; 49 | 50 | do { 51 | while ((readnb = read(fd, buf, count)) < (ssize_t) 0 && errno == EINTR); 52 | if (readnb < (ssize_t) 0) { 53 | return readnb; 54 | } 55 | if (readnb == (ssize_t) 0) { 56 | break; 57 | } 58 | count -= (size_t) readnb; 59 | buf += readnb; 60 | } while (count > (ssize_t) 0); 61 | 62 | return (ssize_t) (buf - (unsigned char *)buf_); 63 | } 64 | 65 | ssize_t 66 | safe_read_partial(const int fd, void *const buf_, const size_t max_count) 67 | { 68 | unsigned char *const buf = (unsigned char *)buf_; 69 | ssize_t readnb; 70 | 71 | while ((readnb = read(fd, buf, max_count)) < (ssize_t) 0 && errno == EINTR); 72 | 73 | return readnb; 74 | } 75 | 76 | #else /* _WIN32 */ 77 | 78 | ssize_t 79 | safe_write(const int fd, const void *const buf_, size_t count, 80 | const int timeout) 81 | { 82 | assert(fd != -1); 83 | assert(buf_ != NULL); 84 | assert(count > (size_t) 0U); 85 | (void)timeout; 86 | 87 | return -1; 88 | } 89 | 90 | ssize_t 91 | safe_read(const int fd, void *const buf_, size_t count) 92 | { 93 | assert(fd != -1); 94 | assert(buf_ != NULL); 95 | assert(count > (size_t) 0U); 96 | 97 | return -1; 98 | } 99 | 100 | ssize_t 101 | safe_read_partial(const int fd, void *const buf_, const size_t max_count) 102 | { 103 | assert(fd != -1); 104 | assert(buf_ != NULL); 105 | assert(max_count > (size_t) 0U); 106 | 107 | return -1; 108 | } 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /safe_rw.h: -------------------------------------------------------------------------------- 1 | #ifndef SAFE_RW_H 2 | #define SAFE_RW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | ssize_t safe_write(const int fd, const void *const buf_, size_t count, 12 | const int timeout); 13 | 14 | ssize_t safe_read(const int fd, void *const buf_, size_t count); 15 | 16 | ssize_t safe_read_partial(const int fd, void *const buf_, 17 | const size_t max_count); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /tcp_request.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_REQUEST_H 2 | #define TCP_REQUEST_H 3 | 4 | #include "dnscrypt.h" 5 | 6 | #define DNS_MAX_PACKET_SIZE_TCP (65535U + 2U) 7 | 8 | #ifndef TCP_REQUEST_BACKLOG 9 | # define TCP_REQUEST_BACKLOG 128 10 | #endif 11 | 12 | struct context; 13 | struct cert_; 14 | 15 | typedef struct TCPRequestStatus_ { 16 | bool has_dns_query_len:1; 17 | bool has_dns_reply_len:1; 18 | bool is_in_queue:1; 19 | bool is_dying:1; 20 | } TCPRequestStatus; 21 | 22 | typedef struct TCPRequest_ { 23 | TAILQ_ENTRY(TCPRequest_) queue; 24 | struct bufferevent *client_proxy_bev; 25 | struct bufferevent *proxy_resolver_bev; 26 | struct evbuffer *proxy_resolver_query_evbuf; 27 | struct context *context; 28 | struct event *timeout_timer; 29 | uint8_t client_nonce[crypto_box_HALF_NONCEBYTES]; 30 | uint8_t nmkey[crypto_box_BEFORENMBYTES]; 31 | size_t dns_query_len; 32 | size_t dns_reply_len; 33 | TCPRequestStatus status; 34 | const struct cert_ *cert; 35 | bool is_dnscrypted; 36 | bool is_blocked; 37 | } TCPRequest; 38 | 39 | int tcp_listener_bind(struct context *c); 40 | int tcp_listener_start(struct context *c); 41 | void tcp_listener_stop(struct context *c); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /tests/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'cucumber', '~> 3.1.2' 3 | gem 'aruba', '~> 0.14.6' 4 | gem 'net-dns' -------------------------------------------------------------------------------- /tests/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | aruba (0.14.6) 5 | childprocess (>= 0.6.3, < 0.10.0) 6 | contracts (~> 0.9) 7 | cucumber (>= 1.3.19) 8 | ffi (~> 1.9.10) 9 | rspec-expectations (>= 2.99) 10 | thor (~> 0.19) 11 | backports (3.11.4) 12 | builder (3.2.3) 13 | childprocess (0.9.0) 14 | ffi (~> 1.0, >= 1.0.11) 15 | contracts (0.16.0) 16 | cucumber (3.1.2) 17 | builder (>= 2.1.2) 18 | cucumber-core (~> 3.2.0) 19 | cucumber-expressions (~> 6.0.1) 20 | cucumber-wire (~> 0.0.1) 21 | diff-lcs (~> 1.3) 22 | gherkin (~> 5.1.0) 23 | multi_json (>= 1.7.5, < 2.0) 24 | multi_test (>= 0.1.2) 25 | cucumber-core (3.2.1) 26 | backports (>= 3.8.0) 27 | cucumber-tag_expressions (~> 1.1.0) 28 | gherkin (~> 5.0) 29 | cucumber-expressions (6.0.1) 30 | cucumber-tag_expressions (1.1.1) 31 | cucumber-wire (0.0.1) 32 | diff-lcs (1.3) 33 | ffi (1.9.25) 34 | gherkin (5.1.0) 35 | multi_json (1.13.1) 36 | multi_test (0.1.2) 37 | net-dns (0.9.0) 38 | rspec-expectations (3.8.2) 39 | diff-lcs (>= 1.2.0, < 2.0) 40 | rspec-support (~> 3.8.0) 41 | rspec-support (3.8.0) 42 | thor (0.20.3) 43 | 44 | PLATFORMS 45 | ruby 46 | 47 | DEPENDENCIES 48 | aruba (~> 0.14.6) 49 | cucumber (~> 3.1.2) 50 | net-dns 51 | 52 | BUNDLED WITH 53 | 1.13.6 54 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | all: test 2 | 3 | test: 4 | rake cucumber 5 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # tests 2 | 3 | ## References 4 | 5 | - aruba: https://app.cucumber.pro/projects/aruba/ 6 | - cucumber: https://cucumber.io/docs/reference 7 | -------------------------------------------------------------------------------- /tests/Rakefile: -------------------------------------------------------------------------------- 1 | require 'cucumber' 2 | require 'cucumber/rake/task' 3 | require 'rake/clean' 4 | 5 | Cucumber::Rake::Task.new 6 | 7 | task :default => [:cucumber] 8 | -------------------------------------------------------------------------------- /tests/dnscrypt-fuzzer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | Requirements: 5 | * https://github.com/openalias/dnscrypt-python 6 | * https://github.com/warner/python-pure25519/blob/master/misc/djbec.py 7 | * a query file of the form `qname\tqtype`. Example query file https://nominum.com/measurement-tools/ 8 | 9 | 10 | Example usage: 11 | python dnscrypt-fuzzer.py \ 12 | --provider-key XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX \ 13 | --port 8443 \ 14 | -q queryfile-example-current 15 | """ 16 | import argparse 17 | import codecs 18 | import os 19 | import pdb 20 | import random 21 | import socket 22 | import time 23 | 24 | import dnscrypt 25 | 26 | qtypemap = { 27 | 'A': 1, 28 | 'NS': 2, 29 | 'MD': 3, 30 | 'MF': 4, 31 | 'CNAME': 5, 32 | 'SOA': 6, 33 | 'MB': 7, 34 | 'MG': 8, 35 | 'MR': 9, 36 | 'NULL': 10, 37 | 'WKS': 11, 38 | 'PTR': 12, 39 | 'HINFO': 13, 40 | 'MINFO': 14, 41 | 'MX': 15, 42 | 'TXT': 16, 43 | 'RP': 17, 44 | 'AFSDB': 18, 45 | 'X25': 19, 46 | 'ISDN': 20, 47 | 'RT': 21, 48 | 'NSAP': 22, 49 | 'NSAP-PTR': 23, 50 | 'SIG': 24, 51 | 'KEY': 25, 52 | 'PX': 26, 53 | 'GPOS': 27, 54 | 'AAAA': 28, 55 | 'LOC': 29, 56 | 'NXT': 30, 57 | 'EID': 31, 58 | 'NIMLOC': 32, 59 | 'SRV': 33, 60 | 'ATMA': 34, 61 | 'NAPTR': 35, 62 | 'KX': 36, 63 | 'CERT': 37, 64 | 'A6': 38, 65 | 'DNAME': 39, 66 | 'SINK': 40, 67 | 'OPT': 41, 68 | 'APL': 42, 69 | 'DS': 43, 70 | 'SSHFP': 44, 71 | 'IPSECKEY': 45, 72 | 'RRSIG': 46, 73 | 'NSEC': 47, 74 | 'DNSKEY': 48, 75 | 'DHCID': 49, 76 | 'NSEC3': 50, 77 | 'NSEC3PARAM': 51, 78 | 'TLSA': 52, 79 | 'SMIMEA': 53, 80 | 'Unassigned': 54, 81 | 'HIP': 55, 82 | 'NINFO': 56, 83 | 'RKEY': 57, 84 | 'TALINK': 58, 85 | 'CDS': 59, 86 | 'CDNSKEY': 60, 87 | 'OPENPGPKEY': 61, 88 | 'CSYNC': 62, 89 | 'SPF': 99, 90 | 'UINFO': 100, 91 | 'UID': 101, 92 | 'GID': 102, 93 | 'UNSPEC': 103, 94 | 'NID': 104, 95 | 'L32': 105, 96 | 'L64': 106, 97 | 'LP': 107, 98 | 'EUI48': 108, 99 | 'EUI64': 109, 100 | 'TKEY': 249, 101 | 'TSIG': 250, 102 | 'IXFR': 251, 103 | 'AXFR': 252, 104 | 'MAILB': 253, 105 | 'MAILA': 254, 106 | '*': 255, 107 | 'URI': 256, 108 | 'CAA': 257, 109 | 'AVC': 258, 110 | 'TA': 32768, 111 | 'DLV': 32769, 112 | } 113 | 114 | def flipbit(msg, **kwargs): 115 | idx = random.randint(0, len(msg)-1) 116 | bit_idx = random.randint(0,7) 117 | x = msg[:idx] 118 | x += chr(ord(msg[idx]) ^ 1< "\0\1", "xchacha20" => "\0\2"} 3 | 4 | def cleanup() 5 | %w{secret.key public.key 1.key 1.cert}.each do |f| 6 | begin 7 | File.delete(f) 8 | rescue 9 | end 10 | end 11 | end 12 | 13 | Before do 14 | cleanup 15 | end 16 | 17 | After do 18 | cleanup 19 | end 20 | 21 | Given /^a provider keypair$/ do 22 | str = DNSCW + " --gen-provider-keypair --provider-name=2.dnscrypt-cert.example.org --ext-address=127.0.0.1" 23 | `#{str}` 24 | end 25 | 26 | And /^a time limited secret key$/ do 27 | str = DNSCW + " --gen-crypt-keypair --crypt-secretkey-file=1.key" 28 | `#{str}` 29 | end 30 | 31 | When /^a (\w+) cert is generated$/ do |type| 32 | arg = if type == "xchacha20" then "-x" else "" end 33 | str = DNSCW + " --gen-cert-file --crypt-secretkey-file=1.key " + 34 | "--provider-cert-file=1.cert --provider-publickey-file=public.key " + 35 | "--provider-secretkey-file=secret.key --cert-file-expire-days=365 " + arg 36 | `#{str}` 37 | end 38 | 39 | Then /^it is a (\w+) cert$/ do |type| 40 | cert = open("1.cert").read() 41 | expect(cert[4..5]).to eq(ESVERSION[type]) 42 | end 43 | -------------------------------------------------------------------------------- /tests/features/step_definitions/dnscrypt-wrapper.rb: -------------------------------------------------------------------------------- 1 | require 'net/dns' 2 | require 'net/dns/resolver' 3 | 4 | WRAPPER_IP = '127.0.0.1' 5 | WRAPPER_PORT = 5443 6 | 7 | Before do 8 | @resolver = Net::DNS::Resolver.new(nameserver: WRAPPER_IP, port: WRAPPER_PORT) 9 | end 10 | 11 | After do 12 | Process.kill("KILL", @pipe.pid) if @pipe 13 | @pipe = nil 14 | end 15 | 16 | Around do |scenario, block| 17 | Timeout.timeout(3.0) do 18 | block.call 19 | end 20 | end 21 | 22 | Given /^a running dnscrypt wrapper with options "([^"]*)"$/ do |options| 23 | str = "../dnscrypt-wrapper " + 24 | "--resolver-address=127.0.0.1:53 " + 25 | "--provider-name=2.dnscrypt-cert.example.com " + 26 | "--listen-address=#{WRAPPER_IP}:#{WRAPPER_PORT} #{options}" 27 | @pipe = IO.popen(str.split, "r") 28 | begin 29 | Timeout.timeout(0.5) do 30 | Process.wait @pipe.pid 31 | @error = @pipe.read 32 | @pipe = nil 33 | end 34 | rescue Timeout::Error 35 | # The process is still running, so it did not fail yet/ 36 | end 37 | end 38 | 39 | And /^a tcp resolver$/ do 40 | @resolver.use_tcp = true 41 | end 42 | 43 | When /^a client asks dnscrypt\-wrapper for "([^"]*)" "([^"]*)" record$/ do |name, qtype| 44 | begin 45 | Timeout.timeout(0.5) do 46 | @answer_section = @resolver.query(name, Net::DNS.const_get(qtype.upcase)).answer 47 | end 48 | rescue Timeout::Error => @error 49 | rescue Errno::ECONNREFUSED => @error 50 | end 51 | end 52 | 53 | Then /^dnscrypt\-wrapper returns "([^"]*)"$/ do |certfile| 54 | cert = open(certfile).read() 55 | expect(@answer_section.collect { |a| a.txt.strip().force_encoding('UTF-8') }).to include(cert) 56 | end 57 | 58 | Then /^dnscrypt-wrapper fails with "(.*)"$/ do |error| 59 | expect(@error).to include(error) 60 | end 61 | 62 | Then /^dnscrypt\-wrapper does not return "([^"]*)"$/ do |certfile| 63 | cert = open(certfile).read() 64 | expect(@answer_section.collect { |a| a.txt.strip().force_encoding('UTF-8') }).not_to include(cert) 65 | end 66 | 67 | Then /^a "(.*)" is thrown$/ do |error| 68 | @error.class.to_s == error 69 | end 70 | -------------------------------------------------------------------------------- /tests/features/support/aruba.rb: -------------------------------------------------------------------------------- 1 | require 'aruba/cucumber' 2 | -------------------------------------------------------------------------------- /tests/keys1/1.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys1/1.cert -------------------------------------------------------------------------------- /tests/keys1/1.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys1/1.key -------------------------------------------------------------------------------- /tests/keys1/public.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys1/public.key -------------------------------------------------------------------------------- /tests/keys1/secret.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys1/secret.key -------------------------------------------------------------------------------- /tests/keys2/1.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys2/1.cert -------------------------------------------------------------------------------- /tests/keys2/1.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys2/1.key -------------------------------------------------------------------------------- /tests/keys2/1.xchacha20.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys2/1.xchacha20.cert -------------------------------------------------------------------------------- /tests/keys2/2.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys2/2.key -------------------------------------------------------------------------------- /tests/keys2/public.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys2/public.key -------------------------------------------------------------------------------- /tests/keys2/secret.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cofyc/dnscrypt-wrapper/45915218c44f05a2afe7bc9643ecb58c4ed487d5/tests/keys2/secret.key -------------------------------------------------------------------------------- /tree.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ 2 | /* 3 | * Copyright 2002 Niels Provos 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _SYS_TREE_H_ 28 | #define _SYS_TREE_H_ 29 | 30 | /* Macros that define a red-black tree */ 31 | #define RB_HEAD(name, type) \ 32 | struct name { \ 33 | struct type *rbh_root; /* root of the tree */ \ 34 | } 35 | 36 | #define RB_INITIALIZER(root) \ 37 | { NULL } 38 | 39 | #define RB_INIT(root) do { \ 40 | (root)->rbh_root = NULL; \ 41 | } while (0) 42 | 43 | #define RB_BLACK 0 44 | #define RB_RED 1 45 | #define RB_ENTRY(type) \ 46 | struct { \ 47 | struct type *rbe_left; /* left element */ \ 48 | struct type *rbe_right; /* right element */ \ 49 | struct type *rbe_parent; /* parent element */ \ 50 | int rbe_color; /* node color */ \ 51 | } 52 | 53 | #define RB_LEFT(elm, field) (elm)->field.rbe_left 54 | #define RB_RIGHT(elm, field) (elm)->field.rbe_right 55 | #define RB_PARENT(elm, field) (elm)->field.rbe_parent 56 | #define RB_COLOR(elm, field) (elm)->field.rbe_color 57 | #define RB_ROOT(head) (head)->rbh_root 58 | #define RB_EMPTY(head) (RB_ROOT(head) == NULL) 59 | 60 | #define RB_SET(elm, parent, field) do { \ 61 | RB_PARENT(elm, field) = parent; \ 62 | RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ 63 | RB_COLOR(elm, field) = RB_RED; \ 64 | } while (0) 65 | 66 | #define RB_SET_BLACKRED(black, red, field) do { \ 67 | RB_COLOR(black, field) = RB_BLACK; \ 68 | RB_COLOR(red, field) = RB_RED; \ 69 | } while (0) 70 | 71 | #ifndef RB_AUGMENT 72 | #define RB_AUGMENT(x) do {} while (0) 73 | #endif 74 | 75 | #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ 76 | (tmp) = RB_RIGHT(elm, field); \ 77 | if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ 78 | RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ 79 | } \ 80 | RB_AUGMENT(elm); \ 81 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ 82 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 83 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 84 | else \ 85 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 86 | } else \ 87 | (head)->rbh_root = (tmp); \ 88 | RB_LEFT(tmp, field) = (elm); \ 89 | RB_PARENT(elm, field) = (tmp); \ 90 | RB_AUGMENT(tmp); \ 91 | if ((RB_PARENT(tmp, field))) \ 92 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 93 | } while (0) 94 | 95 | #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ 96 | (tmp) = RB_LEFT(elm, field); \ 97 | if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ 98 | RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ 99 | } \ 100 | RB_AUGMENT(elm); \ 101 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ 102 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 103 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 104 | else \ 105 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 106 | } else \ 107 | (head)->rbh_root = (tmp); \ 108 | RB_RIGHT(tmp, field) = (elm); \ 109 | RB_PARENT(elm, field) = (tmp); \ 110 | RB_AUGMENT(tmp); \ 111 | if ((RB_PARENT(tmp, field))) \ 112 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 113 | } while (0) 114 | 115 | /* Generates prototypes and inline functions */ 116 | #define RB_PROTOTYPE(name, type, field, cmp) \ 117 | RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) 118 | #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ 119 | RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) 120 | #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ 121 | attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ 122 | attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ 123 | attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ 124 | attr struct type *name##_RB_INSERT(struct name *, struct type *); \ 125 | attr struct type *name##_RB_FIND(struct name *, struct type *); \ 126 | attr struct type *name##_RB_NFIND(struct name *, struct type *); \ 127 | attr struct type *name##_RB_NEXT(struct type *); \ 128 | attr struct type *name##_RB_PREV(struct type *); \ 129 | attr struct type *name##_RB_MINMAX(struct name *, int); \ 130 | \ 131 | 132 | /* Main rb operation. 133 | * Moves node close to the key of elm to top 134 | */ 135 | #define RB_GENERATE(name, type, field, cmp) \ 136 | RB_GENERATE_INTERNAL(name, type, field, cmp,) 137 | #define RB_GENERATE_STATIC(name, type, field, cmp) \ 138 | RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) 139 | #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ 140 | attr void \ 141 | name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ 142 | { \ 143 | struct type *parent, *gparent, *tmp; \ 144 | while ((parent = RB_PARENT(elm, field)) && \ 145 | RB_COLOR(parent, field) == RB_RED) { \ 146 | gparent = RB_PARENT(parent, field); \ 147 | if (parent == RB_LEFT(gparent, field)) { \ 148 | tmp = RB_RIGHT(gparent, field); \ 149 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 150 | RB_COLOR(tmp, field) = RB_BLACK; \ 151 | RB_SET_BLACKRED(parent, gparent, field);\ 152 | elm = gparent; \ 153 | continue; \ 154 | } \ 155 | if (RB_RIGHT(parent, field) == elm) { \ 156 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 157 | tmp = parent; \ 158 | parent = elm; \ 159 | elm = tmp; \ 160 | } \ 161 | RB_SET_BLACKRED(parent, gparent, field); \ 162 | RB_ROTATE_RIGHT(head, gparent, tmp, field); \ 163 | } else { \ 164 | tmp = RB_LEFT(gparent, field); \ 165 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 166 | RB_COLOR(tmp, field) = RB_BLACK; \ 167 | RB_SET_BLACKRED(parent, gparent, field);\ 168 | elm = gparent; \ 169 | continue; \ 170 | } \ 171 | if (RB_LEFT(parent, field) == elm) { \ 172 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 173 | tmp = parent; \ 174 | parent = elm; \ 175 | elm = tmp; \ 176 | } \ 177 | RB_SET_BLACKRED(parent, gparent, field); \ 178 | RB_ROTATE_LEFT(head, gparent, tmp, field); \ 179 | } \ 180 | } \ 181 | RB_COLOR(head->rbh_root, field) = RB_BLACK; \ 182 | } \ 183 | \ 184 | attr void \ 185 | name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ 186 | { \ 187 | struct type *tmp; \ 188 | while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ 189 | elm != RB_ROOT(head)) { \ 190 | if (RB_LEFT(parent, field) == elm) { \ 191 | tmp = RB_RIGHT(parent, field); \ 192 | if (RB_COLOR(tmp, field) == RB_RED) { \ 193 | RB_SET_BLACKRED(tmp, parent, field); \ 194 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 195 | tmp = RB_RIGHT(parent, field); \ 196 | } \ 197 | if ((RB_LEFT(tmp, field) == NULL || \ 198 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 199 | (RB_RIGHT(tmp, field) == NULL || \ 200 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 201 | RB_COLOR(tmp, field) = RB_RED; \ 202 | elm = parent; \ 203 | parent = RB_PARENT(elm, field); \ 204 | } else { \ 205 | if (RB_RIGHT(tmp, field) == NULL || \ 206 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ 207 | struct type *oleft; \ 208 | if ((oleft = RB_LEFT(tmp, field)))\ 209 | RB_COLOR(oleft, field) = RB_BLACK;\ 210 | RB_COLOR(tmp, field) = RB_RED; \ 211 | RB_ROTATE_RIGHT(head, tmp, oleft, field);\ 212 | tmp = RB_RIGHT(parent, field); \ 213 | } \ 214 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 215 | RB_COLOR(parent, field) = RB_BLACK; \ 216 | if (RB_RIGHT(tmp, field)) \ 217 | RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ 218 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 219 | elm = RB_ROOT(head); \ 220 | break; \ 221 | } \ 222 | } else { \ 223 | tmp = RB_LEFT(parent, field); \ 224 | if (RB_COLOR(tmp, field) == RB_RED) { \ 225 | RB_SET_BLACKRED(tmp, parent, field); \ 226 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 227 | tmp = RB_LEFT(parent, field); \ 228 | } \ 229 | if ((RB_LEFT(tmp, field) == NULL || \ 230 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 231 | (RB_RIGHT(tmp, field) == NULL || \ 232 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 233 | RB_COLOR(tmp, field) = RB_RED; \ 234 | elm = parent; \ 235 | parent = RB_PARENT(elm, field); \ 236 | } else { \ 237 | if (RB_LEFT(tmp, field) == NULL || \ 238 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ 239 | struct type *oright; \ 240 | if ((oright = RB_RIGHT(tmp, field)))\ 241 | RB_COLOR(oright, field) = RB_BLACK;\ 242 | RB_COLOR(tmp, field) = RB_RED; \ 243 | RB_ROTATE_LEFT(head, tmp, oright, field);\ 244 | tmp = RB_LEFT(parent, field); \ 245 | } \ 246 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 247 | RB_COLOR(parent, field) = RB_BLACK; \ 248 | if (RB_LEFT(tmp, field)) \ 249 | RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ 250 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 251 | elm = RB_ROOT(head); \ 252 | break; \ 253 | } \ 254 | } \ 255 | } \ 256 | if (elm) \ 257 | RB_COLOR(elm, field) = RB_BLACK; \ 258 | } \ 259 | \ 260 | attr struct type * \ 261 | name##_RB_REMOVE(struct name *head, struct type *elm) \ 262 | { \ 263 | struct type *child, *parent, *old = elm; \ 264 | int color; \ 265 | if (RB_LEFT(elm, field) == NULL) \ 266 | child = RB_RIGHT(elm, field); \ 267 | else if (RB_RIGHT(elm, field) == NULL) \ 268 | child = RB_LEFT(elm, field); \ 269 | else { \ 270 | struct type *left; \ 271 | elm = RB_RIGHT(elm, field); \ 272 | while ((left = RB_LEFT(elm, field))) \ 273 | elm = left; \ 274 | child = RB_RIGHT(elm, field); \ 275 | parent = RB_PARENT(elm, field); \ 276 | color = RB_COLOR(elm, field); \ 277 | if (child) \ 278 | RB_PARENT(child, field) = parent; \ 279 | if (parent) { \ 280 | if (RB_LEFT(parent, field) == elm) \ 281 | RB_LEFT(parent, field) = child; \ 282 | else \ 283 | RB_RIGHT(parent, field) = child; \ 284 | RB_AUGMENT(parent); \ 285 | } else \ 286 | RB_ROOT(head) = child; \ 287 | if (RB_PARENT(elm, field) == old) \ 288 | parent = elm; \ 289 | (elm)->field = (old)->field; \ 290 | if (RB_PARENT(old, field)) { \ 291 | if (RB_LEFT(RB_PARENT(old, field), field) == old)\ 292 | RB_LEFT(RB_PARENT(old, field), field) = elm;\ 293 | else \ 294 | RB_RIGHT(RB_PARENT(old, field), field) = elm;\ 295 | RB_AUGMENT(RB_PARENT(old, field)); \ 296 | } else \ 297 | RB_ROOT(head) = elm; \ 298 | RB_PARENT(RB_LEFT(old, field), field) = elm; \ 299 | if (RB_RIGHT(old, field)) \ 300 | RB_PARENT(RB_RIGHT(old, field), field) = elm; \ 301 | if (parent) { \ 302 | left = parent; \ 303 | do { \ 304 | RB_AUGMENT(left); \ 305 | } while ((left = RB_PARENT(left, field))); \ 306 | } \ 307 | goto color; \ 308 | } \ 309 | parent = RB_PARENT(elm, field); \ 310 | color = RB_COLOR(elm, field); \ 311 | if (child) \ 312 | RB_PARENT(child, field) = parent; \ 313 | if (parent) { \ 314 | if (RB_LEFT(parent, field) == elm) \ 315 | RB_LEFT(parent, field) = child; \ 316 | else \ 317 | RB_RIGHT(parent, field) = child; \ 318 | RB_AUGMENT(parent); \ 319 | } else \ 320 | RB_ROOT(head) = child; \ 321 | color: \ 322 | if (color == RB_BLACK) \ 323 | name##_RB_REMOVE_COLOR(head, parent, child); \ 324 | return (old); \ 325 | } \ 326 | \ 327 | /* Inserts a node into the RB tree */ \ 328 | attr struct type * \ 329 | name##_RB_INSERT(struct name *head, struct type *elm) \ 330 | { \ 331 | struct type *tmp; \ 332 | struct type *parent = NULL; \ 333 | int comp = 0; \ 334 | tmp = RB_ROOT(head); \ 335 | while (tmp) { \ 336 | parent = tmp; \ 337 | comp = (cmp)(elm, parent); \ 338 | if (comp < 0) \ 339 | tmp = RB_LEFT(tmp, field); \ 340 | else if (comp > 0) \ 341 | tmp = RB_RIGHT(tmp, field); \ 342 | else \ 343 | return (tmp); \ 344 | } \ 345 | RB_SET(elm, parent, field); \ 346 | if (parent != NULL) { \ 347 | if (comp < 0) \ 348 | RB_LEFT(parent, field) = elm; \ 349 | else \ 350 | RB_RIGHT(parent, field) = elm; \ 351 | RB_AUGMENT(parent); \ 352 | } else \ 353 | RB_ROOT(head) = elm; \ 354 | name##_RB_INSERT_COLOR(head, elm); \ 355 | return (NULL); \ 356 | } \ 357 | \ 358 | /* Finds the node with the same key as elm */ \ 359 | attr struct type * \ 360 | name##_RB_FIND(struct name *head, struct type *elm) \ 361 | { \ 362 | struct type *tmp = RB_ROOT(head); \ 363 | int comp; \ 364 | while (tmp) { \ 365 | comp = cmp(elm, tmp); \ 366 | if (comp < 0) \ 367 | tmp = RB_LEFT(tmp, field); \ 368 | else if (comp > 0) \ 369 | tmp = RB_RIGHT(tmp, field); \ 370 | else \ 371 | return (tmp); \ 372 | } \ 373 | return (NULL); \ 374 | } \ 375 | \ 376 | /* Finds the first node greater than or equal to the search key */ \ 377 | attr struct type * \ 378 | name##_RB_NFIND(struct name *head, struct type *elm) \ 379 | { \ 380 | struct type *tmp = RB_ROOT(head); \ 381 | struct type *res = NULL; \ 382 | int comp; \ 383 | while (tmp) { \ 384 | comp = cmp(elm, tmp); \ 385 | if (comp < 0) { \ 386 | res = tmp; \ 387 | tmp = RB_LEFT(tmp, field); \ 388 | } \ 389 | else if (comp > 0) \ 390 | tmp = RB_RIGHT(tmp, field); \ 391 | else \ 392 | return (tmp); \ 393 | } \ 394 | return (res); \ 395 | } \ 396 | \ 397 | /* ARGSUSED */ \ 398 | attr struct type * \ 399 | name##_RB_NEXT(struct type *elm) \ 400 | { \ 401 | if (RB_RIGHT(elm, field)) { \ 402 | elm = RB_RIGHT(elm, field); \ 403 | while (RB_LEFT(elm, field)) \ 404 | elm = RB_LEFT(elm, field); \ 405 | } else { \ 406 | if (RB_PARENT(elm, field) && \ 407 | (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ 408 | elm = RB_PARENT(elm, field); \ 409 | else { \ 410 | while (RB_PARENT(elm, field) && \ 411 | (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ 412 | elm = RB_PARENT(elm, field); \ 413 | elm = RB_PARENT(elm, field); \ 414 | } \ 415 | } \ 416 | return (elm); \ 417 | } \ 418 | \ 419 | /* ARGSUSED */ \ 420 | attr struct type * \ 421 | name##_RB_PREV(struct type *elm) \ 422 | { \ 423 | if (RB_LEFT(elm, field)) { \ 424 | elm = RB_LEFT(elm, field); \ 425 | while (RB_RIGHT(elm, field)) \ 426 | elm = RB_RIGHT(elm, field); \ 427 | } else { \ 428 | if (RB_PARENT(elm, field) && \ 429 | (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ 430 | elm = RB_PARENT(elm, field); \ 431 | else { \ 432 | while (RB_PARENT(elm, field) && \ 433 | (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ 434 | elm = RB_PARENT(elm, field); \ 435 | elm = RB_PARENT(elm, field); \ 436 | } \ 437 | } \ 438 | return (elm); \ 439 | } \ 440 | \ 441 | attr struct type * \ 442 | name##_RB_MINMAX(struct name *head, int val) \ 443 | { \ 444 | struct type *tmp = RB_ROOT(head); \ 445 | struct type *parent = NULL; \ 446 | while (tmp) { \ 447 | parent = tmp; \ 448 | if (val < 0) \ 449 | tmp = RB_LEFT(tmp, field); \ 450 | else \ 451 | tmp = RB_RIGHT(tmp, field); \ 452 | } \ 453 | return (parent); \ 454 | } 455 | 456 | #define RB_NEGINF -1 457 | #define RB_INF 1 458 | 459 | #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) 460 | #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) 461 | #define RB_FIND(name, x, y) name##_RB_FIND(x, y) 462 | #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) 463 | #define RB_NEXT(name, x, y) name##_RB_NEXT(y) 464 | #define RB_PREV(name, x, y) name##_RB_PREV(y) 465 | #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) 466 | #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) 467 | 468 | #define RB_FOREACH(x, name, head) \ 469 | for ((x) = RB_MIN(name, head); \ 470 | (x) != NULL; \ 471 | (x) = name##_RB_NEXT(x)) 472 | 473 | #define RB_FOREACH_SAFE(x, name, head, y) \ 474 | for ((x) = RB_MIN(name, head); \ 475 | ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ 476 | (x) = (y)) 477 | 478 | #define RB_FOREACH_REVERSE(x, name, head) \ 479 | for ((x) = RB_MAX(name, head); \ 480 | (x) != NULL; \ 481 | (x) = name##_RB_PREV(x)) 482 | 483 | #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ 484 | for ((x) = RB_MAX(name, head); \ 485 | ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ 486 | (x) = (y)) 487 | 488 | #endif /* _SYS_TREE_H_ */ 489 | -------------------------------------------------------------------------------- /udp_request.h: -------------------------------------------------------------------------------- 1 | #ifndef UDP_REQUEST_H 2 | #define UDP_REQUEST_H 3 | 4 | #include "dnscrypt.h" 5 | 6 | struct context; 7 | struct cert_; 8 | 9 | typedef struct UDPRequestStatus_ { 10 | bool is_dying:1; 11 | bool is_in_queue:1; 12 | } UDPRequestStatus; 13 | 14 | typedef struct UDPRequest_ { 15 | RB_ENTRY(UDPRequest_) queue; 16 | struct context *context; 17 | struct event *sendto_retry_timer; 18 | struct event *timeout_timer; 19 | uint64_t hash; 20 | uint16_t id; 21 | uint16_t gen; 22 | uint16_t len; 23 | uint8_t client_nonce[crypto_box_HALF_NONCEBYTES]; 24 | uint8_t nmkey[crypto_box_BEFORENMBYTES]; 25 | struct sockaddr_storage client_sockaddr; 26 | evutil_socket_t client_proxy_handle; 27 | ev_socklen_t client_sockaddr_len; 28 | UDPRequestStatus status; 29 | unsigned char retries; 30 | const struct cert_ *cert; 31 | bool is_dnscrypted; 32 | bool is_blocked; 33 | } UDPRequest; 34 | 35 | typedef TAILQ_HEAD(TCPRequestQueue_, TCPRequest_) TCPRequestQueue; 36 | typedef RB_HEAD(UDPRequestQueue_, UDPRequest_) UDPRequestQueue; 37 | 38 | int udp_listener_bind(struct context *c); 39 | int udp_listener_start(struct context *c); 40 | void udp_listener_stop(struct context *c); 41 | int udp_listener_kill_oldest_request(struct context *c); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | /* Automatically generated by ./gen-version.sh */ 2 | #ifndef VERSION_H 3 | #define VERSION_H 4 | 5 | const char *the_version = "0.4.2"; 6 | 7 | #endif 8 | --------------------------------------------------------------------------------