about summary refs log tree commit diff
path: root/doc/dnsfunneld.html
blob: 7fde21f4f497785e4f818d6e527d52839af78250 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="Content-Language" content="en" />
    <title>dnsfunnel: the dnsfunneld program</title>
    <meta name="Description" content="dnsfunnel: the dnsfunneld program" />
    <meta name="Keywords" content="dnsfunnel daemon dnsfunneld /etc/resolv.conf local cache resolver 127.0.0.1" />
    <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
  </head>
<body>

<p>
<a href="index.html">dnsfunnel</a><br />
<a href="//skarnet.org/software/">Software</a><br />
<a href="//skarnet.org/">skarnet.org</a>
</p>

<h1> The <tt>dnsfunneld</tt> program </h1>

<p>
<tt>dnsfunneld</tt> is a small DNS forwarder daemon. It receives
DNS queries from clients, then forwards them to one or more DNS caches.
It collects the responses and forwards them back to the clients. Depending
on the options it is given, it may perform light processing on the
queries, the responses, or both.
</p>

<h2> Interface </h2>

<pre>
     dnsfunneld [ -v <em>verbosity</em> ] [ -1 ] [ -U | -u <em>uid</em> -g <em>gid</em> ] [ -i <em>ip</em>:<em>port</em> ] [ -R <em>root</em> ] [ -b <em>bufsize</em> ] [ -t <em>globaltimeout</em> ] [ -X | -x ] [ -N | -n ]
</pre>

<ul>
 <li> dnsfunneld creates a UDP inet domain socket and binds it
to IPv4 address <em>ip</em> (normally 127.0.0.1) and port <em>port</em>
(normally 53). </li>
 <li> Depending on the options it has been given, it may chroot and lose
privileges on its gid and uid. </li>
 <li> It reads the <tt>caches</tt> file (relative to its current
directory, which is either the directory it has been run from or, if
requested, the one it has chrooted into), expecting to find
a list of IP (v4 or v6) addresses, one per line. These addresses are the
DNS caches it will forward the queries to. </li>
 <li> dnsfunneld expects to receive packets no more than 512
bytes long, only containing DNS normal queries (QUERY) for the IN
class, on its socket. </li>
 <li> Depending on options, dnsfunneld may send additional queries
to the caches listed in <tt>caches</tt>. It handles the answers
internally: the additional queries are invisible to clients. </li>
 <li> dnsfunneld is a long-lived process. </li>
</ul>

<h2> Signals </h2>

<ul>
 <li> SIGHUP: read the <tt>caches</tt> file again, updating its
in-memory cache list. In-flight queries are still handled by the old
list; the new list will only apply for queries arriving after the SIGHUP. </li>
 <li> SIGTERM: enter lame-duck mode, do not accept any more queries. When
all in-flight queries have been answered, exit 0.
</ul>

<h2> Exit codes </h2>

<ul>
 <li> 0: SIGTERM received and all in-flight queries have been answered </li>
 <li> 100: wrong usage </li>
 <li> 111: system call failed </li>
</ul>

<h2> Options </h2>

<ul>
 <li> <tt>-v&nbsp;<em>verbosity</em></tt>&nbsp;: verbosity.
Default is 1. 0 suppresses warning messages. Higher values may give more
informational messages in the future. </li>
 <li> <tt>-1</tt>&nbsp;: readiness notification. When
dnsfunneld is ready to process queries, write a newline to stdout, then
close it. Default is no notification at all. </li>
 <li> <tt>-U</tt>&nbsp;: read an uid in the UID environment variable and a gid
in the GID environment variable, and drop privileges to that uid/gid. </li>
 <li> <tt>-u&nbsp;<em>uid</em></tt>&nbsp;: drop privileges to numerical uid
<em>uid</em>. </li>
 <li> <tt>-g&nbsp;<em>gid</em></tt>&nbsp;: drop privileges to numerical gid
<em>gid</em>. </li>
 <li> <tt>-i&nbsp;<em>ip</em>:<em>port</em></tt>&nbsp;: bind the socket to
IPv4 <em>ip</em> and port <em>port</em>. Default for <em>ip</em> is
<tt>127.0.0.1</tt>; default for <em>port</em> is 53. </li>
 <li> <tt>-R&nbsp;<em>root</em></tt>&nbsp;: chroot to <em>root</em>. Default
is <tt>/run/dnsfunnel/root</tt>. Note that chrooting only increases security
if privileges are also dropped via the <tt>-U</tt> or <tt>-u</tt> and <tt>-g</tt>
options. Chrooting is only supported on platforms that have the <tt>chroot()</tt>
primitive. You can also disable it by passing an empty string as the argument
to <tt>-R</tt>. </li>
 <li> <tt>-b&nbsp;<em>bufsize</em></tt>&nbsp;: try and reserve a kernel buffer
size of <em>bufsize</em> bytes for the socket. The default is whatever the
default is for your kernel. </li>
 <li> <tt>-t&nbsp;<em>globaltimeout</em></tt>&nbsp;: maximum resolution time.
If a query takes more than <em>globaltimeout</em> milliseconds to resolve,
abandon it and return a SERVFAIL to the client. Default is 0, meaning infinite:
no global timeout is set. </li>
</ul>

<p>
 The other options control the activation or deactivation of various
features. See below for the detail of operations.
</p>

<ul>
 <li> <tt>-X</tt>&nbsp;: Do not activate truncation of responses. This is
the default. </li>
 <li> <tt>-x</tt>&nbsp;: If a DNS response is bigger than 510 bytes,
truncate its last resource records until it fits into 510 bytes and can
be sent in a UDP packet. </li>
 <li> <tt>-N</tt>&nbsp;: Do not activate NXDOMAIN workaround. This is the
default. </li>
 <li> <tt>-n</tt>&nbsp;: Activate NXDOMAIN workaround. </li>
 <li> Other options may be added in the future. </li>
</ul>

<h2> DNS forwarding behaviour </h2>

<ul>
 <li> When it receives a query, dnsfunneld forwards it to the first DNS cache
in the list it has read from the <tt>caches</tt> file. </li>
 <li> If it receives a response with the TC bit, it resends the query over TCP. </li>
 <li> If it receives a suitable response within a given time frame, it forwards
it to the client. </li>
 <li> On SERVFAIL, or after a timeout of 1 second, it gives up and sends the
query to the next DNS cache in its list. (If the first cache answers after the time
frame, the answer is dropped.)
 <li> If dnsfunneld reaches the end of its cache list, it retries the whole
procedure starting at the beginning of the list, but with a timeout of 3 seconds.
Caches that returned a SERVFAIL are crossed off the list for that query. </li>
 <li> If the second pass fails again, dnsfunneld tries again with a timeout of
11 seconds, then with a timeout of 45 seconds. If all of this fails, it returns
a SERVFAIL to the client. </li>
 <li> A machine should not use a DNS cache that is too far away. In normal operation,
a timeout of 1 second should be more than enough for a cache to answer, if it already
has the answer. If the answer is absent from all caches and it takes them more than
1 second to resolve the query, the answer will be obtained by dnsfunneld in the second
pass. Realistically, the only cases when caches that are not at the top of the list
are used are:
 <ul>
  <li> obscure DNS queries, not likely to be in the caches, and that will take
time to resolve; </li>
  <li> or the first cache has really gone to lunch. </li>
 </ul>
</ul>

<h2> dnsfunneld operations </h2>

<p>
 Depending on the options it has been given, dnsfunneld may perform the
following operations on the queries or responses it receives:
</p>

<h3> Truncation </h3>

<p>
 If a DNS response is more than 510 bytes
long, dnsfunneld will truncate the <em>last</em> resource records in the response,
until it fits into 510 bytes and can be given to the client in a UDP packet.
The structure of a DNS packet makes it so the RRs are listed in order of
decreasing importance, so keeping as many RRs as will fit in 510 bytes
without reordering them is the natural way of truncating a response.
</p>

<h3> NXDOMAIN workaround </h3>

<p>
 Some DNS servers incorrectly answer NXDOMAIN when
they should just answer NODATA, and querying for another, existing, record
type for the same domain allows dnsfunneld to tell the difference between a
real NXDOMAIN.
 When that operation is requested, for every A or AAAA query dnsfunneld
receives and forwards, it also sends
an additional AAAA or A query for the same domain. If the main query returns
NXDOMAIN, dnsfunneld waits for the response to the auxiliary query: if this
response is not NXDOMAIN, then dnsfunneld answers NODATA to the client instead
of NXDOMAIN. Be aware that activating this workaround can practically double
the number of queries sent to the DNS caches, and may cause additional delays
before the clients get their answers.
</p>

<h2> Notes </h2>

<ul>
 <li> The point of dnsfunneld is to work around ill-designed or unreliable
client setups with several motley <tt>nameserver</tt> entries in
<tt>/etc/resolv.conf</tt>. By converting those entries to a cache list
instead (via the <a href="dnsfunnel-translate.html">dnsfunnel-translate</a>
program), running dnsfunneld on 127.0.0.1, and enforcing a policy of one
single <tt>nameserver 127.0.0.1</tt> entry in <tt>/etc/resolv.conf</tt>,
the setup can be made more reliable and more consistent. </li>
 <li> Such a policy can be automated, for instance, by listening to
changes on the <tt>/etc/resolv.conf</tt> file (via inotify or kqueue,
depending on your system) and immediately calling
<a href="dnsfunnel-translate.html">dnsfunnel-translate</a>, sending
a SIGHUP to dnsfunneld, and forcefully overwriting <tt>/etc/resolv.conf</tt>. </li>
 <li> It is easy to send a SIGHUP to dnsfunneld even without knowing its
pid, if it is run under a process supervision system such as
<a href="//skarnet.org/software/s6/">s6</a>. </li>
</ul>

</body>
</html>