blob: 7e8fe64c286252bbce5202736880d6a4d5810a6f [file] [log] [blame]
Simon Kelleyc49778d2016-01-06 18:52:33 +00001/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
Simon Kelley1d030162015-12-21 14:17:06 +00002
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 <http://www.gnu.org/licenses/>.
15*/
16
17#include "dnsmasq.h"
18
Simon Kelley5bb88f02015-12-21 16:23:47 +000019unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last)
Simon Kelley1d030162015-12-21 14:17:06 +000020{
21 /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
22 also return length of pseudoheader in *len and pointer to the UDP size in *p
23 Finally, check to see if a packet is signed. If it is we cannot change a single bit before
Simon Kelley5bb88f02015-12-21 16:23:47 +000024 forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
Simon Kelley1d030162015-12-21 14:17:06 +000025
26 int i, arcount = ntohs(header->arcount);
27 unsigned char *ansp = (unsigned char *)(header+1);
28 unsigned short rdlen, type, class;
29 unsigned char *ret = NULL;
30
31 if (is_sign)
32 {
33 *is_sign = 0;
34
35 if (OPCODE(header) == QUERY)
36 {
37 for (i = ntohs(header->qdcount); i != 0; i--)
38 {
39 if (!(ansp = skip_name(ansp, header, plen, 4)))
40 return NULL;
41
42 GETSHORT(type, ansp);
43 GETSHORT(class, ansp);
44
45 if (class == C_IN && type == T_TKEY)
46 *is_sign = 1;
47 }
48 }
49 }
50 else
51 {
52 if (!(ansp = skip_questions(header, plen)))
53 return NULL;
54 }
55
56 if (arcount == 0)
57 return NULL;
58
59 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
60 return NULL;
61
62 for (i = 0; i < arcount; i++)
63 {
64 unsigned char *save, *start = ansp;
65 if (!(ansp = skip_name(ansp, header, plen, 10)))
66 return NULL;
67
68 GETSHORT(type, ansp);
69 save = ansp;
70 GETSHORT(class, ansp);
71 ansp += 4; /* TTL */
72 GETSHORT(rdlen, ansp);
73 if (!ADD_RDLEN(header, ansp, plen, rdlen))
74 return NULL;
75 if (type == T_OPT)
76 {
77 if (len)
78 *len = ansp - start;
Simon Kelley5bb88f02015-12-21 16:23:47 +000079
Simon Kelley1d030162015-12-21 14:17:06 +000080 if (p)
81 *p = save;
Simon Kelley5bb88f02015-12-21 16:23:47 +000082
83 if (is_last)
84 *is_last = (i == arcount-1);
85
Simon Kelley1d030162015-12-21 14:17:06 +000086 ret = start;
87 }
88 else if (is_sign &&
89 i == arcount - 1 &&
90 class == C_ANY &&
91 type == T_TSIG)
92 *is_sign = 1;
93 }
94
95 return ret;
96}
Simon Kelley1d030162015-12-21 14:17:06 +000097
98size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
99 unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do)
100{
Simon Kelley5bb88f02015-12-21 16:23:47 +0000101 unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL;
102 int rdlen = 0, is_sign, is_last;
103 unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
Simon Kelley1d030162015-12-21 14:17:06 +0000104
Simon Kelley5bb88f02015-12-21 16:23:47 +0000105 p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
106
107 if (is_sign)
108 return plen;
109
110 if (p)
Simon Kelley1d030162015-12-21 14:17:06 +0000111 {
Simon Kelley5bb88f02015-12-21 16:23:47 +0000112 /* Existing header */
Simon Kelley1d030162015-12-21 14:17:06 +0000113 int i;
Simon Kelley5bb88f02015-12-21 16:23:47 +0000114 unsigned short code, len;
115
116 p = udp_len;
117 GETSHORT(udp_sz, p);
118 GETSHORT(rcode, p);
Simon Kelley1d030162015-12-21 14:17:06 +0000119 GETSHORT(flags, p);
Simon Kelley5bb88f02015-12-21 16:23:47 +0000120
Simon Kelley1d030162015-12-21 14:17:06 +0000121 if (set_do)
122 {
123 p -=2;
Simon Kelley5bb88f02015-12-21 16:23:47 +0000124 flags |= 0x8000;
125 PUTSHORT(flags, p);
Simon Kelley1d030162015-12-21 14:17:06 +0000126 }
127
128 lenp = p;
129 GETSHORT(rdlen, p);
130 if (!CHECK_LEN(header, p, plen, rdlen))
131 return plen; /* bad packet */
132 datap = p;
133
134 /* no option to add */
135 if (optno == 0)
136 return plen;
137
138 /* check if option already there */
139 for (i = 0; i + 4 < rdlen; i += len + 4)
140 {
141 GETSHORT(code, p);
142 GETSHORT(len, p);
143 if (code == optno)
144 return plen;
145 p += len;
146 }
Simon Kelley5bb88f02015-12-21 16:23:47 +0000147
148 /* If we're going to extend the RR, it has to be the last RR in the packet */
149 if (!is_last)
150 {
151 /* First, take a copy of the options. */
152 if (rdlen != 0 && (buff = whine_malloc(rdlen)))
153 memcpy(buff, datap, rdlen);
154
155 /* now, delete OPT RR */
156 plen = rrfilter(header, plen, 0);
157
158 /* Now, force addition of a new one */
159 p = NULL;
160 }
Simon Kelley1d030162015-12-21 14:17:06 +0000161 }
162
Simon Kelley5bb88f02015-12-21 16:23:47 +0000163 if (!p)
164 {
165 /* We are (re)adding the pseudoheader */
166 if (!(p = skip_questions(header, plen)) ||
167 !(p = skip_section(p,
168 ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
169 header, plen)))
170 return plen;
171 *p++ = 0; /* empty name */
172 PUTSHORT(T_OPT, p);
173 PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
174 PUTSHORT(rcode, p); /* extended RCODE and version */
175 PUTSHORT(flags, p); /* DO flag */
176 lenp = p;
177 PUTSHORT(rdlen, p); /* RDLEN */
178 datap = p;
179 /* Copy back any options */
180 if (buff)
181 {
182 memcpy(p, buff, rdlen);
183 free(buff);
184 p += rdlen;
185 }
186 header->arcount = htons(ntohs(header->arcount) + 1);
187 }
188
189 if (((ssize_t)optlen) > (limit - (p + 4)))
190 return plen; /* Too big */
191
192 /* Add new option */
Simon Kelley1d030162015-12-21 14:17:06 +0000193 if (optno != 0)
194 {
195 PUTSHORT(optno, p);
196 PUTSHORT(optlen, p);
197 memcpy(p, opt, optlen);
198 p += optlen;
Simon Kelley5bb88f02015-12-21 16:23:47 +0000199 PUTSHORT(p - datap, lenp);
Simon Kelley1d030162015-12-21 14:17:06 +0000200 }
Simon Kelley1d030162015-12-21 14:17:06 +0000201 return p - (unsigned char *)header;
Simon Kelley1d030162015-12-21 14:17:06 +0000202}
203
Simon Kelley33702ab2015-12-28 23:17:15 +0000204size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
Simon Kelleyefef4972015-12-21 17:30:44 +0000205{
206 return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1);
207}
208
Simon Kelley33702ab2015-12-28 23:17:15 +0000209static unsigned char char64(unsigned char c)
210{
211 return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f];
212}
213
214static void encoder(unsigned char *in, char *out)
215{
216 out[0] = char64(in[0]>>2);
217 out[1] = char64((in[0]<<4) | (in[1]>>4));
218 out[2] = char64((in[1]<<2) | (in[2]>>6));
219 out[3] = char64(in[2]);
220}
221
222static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
223{
224 int maclen;
225 unsigned char mac[DHCP_CHADDR_MAX];
226 char encode[8]; /* handle 6 byte MACs */
227
228 if ((maclen = find_mac(l3, mac, 1, now)) == 6)
229 {
230 encoder(mac, encode);
231 encoder(mac+3, encode+4);
232
233 plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, 8, 0);
234 }
235
236 if (daemon->dns_client_id)
237 plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
238 (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0);
239
240 return plen;
241}
242
243
244static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
Simon Kelley1d030162015-12-21 14:17:06 +0000245{
Simon Kelley11867dc2015-12-23 16:15:58 +0000246 int maclen;
247 unsigned char mac[DHCP_CHADDR_MAX];
Simon Kelley1d030162015-12-21 14:17:06 +0000248
Simon Kelley33702ab2015-12-28 23:17:15 +0000249 if ((maclen = find_mac(l3, mac, 1, now)) != 0)
Simon Kelley11867dc2015-12-23 16:15:58 +0000250 plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0);
Simon Kelley33702ab2015-12-28 23:17:15 +0000251
Simon Kelley11867dc2015-12-23 16:15:58 +0000252 return plen;
Simon Kelley1d030162015-12-21 14:17:06 +0000253}
254
255struct subnet_opt {
256 u16 family;
257 u8 source_netmask, scope_netmask;
258#ifdef HAVE_IPV6
259 u8 addr[IN6ADDRSZ];
260#else
261 u8 addr[INADDRSZ];
262#endif
263};
264
265static void *get_addrp(union mysockaddr *addr, const short family)
266{
267#ifdef HAVE_IPV6
268 if (family == AF_INET6)
269 return &addr->in6.sin6_addr;
270#endif
271
272 return &addr->in.sin_addr;
273}
274
275static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
276{
277 /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
278
279 int len;
280 void *addrp;
281 int sa_family = source->sa.sa_family;
282
283#ifdef HAVE_IPV6
284 if (source->sa.sa_family == AF_INET6)
285 {
286 opt->source_netmask = daemon->add_subnet6->mask;
287 if (daemon->add_subnet6->addr_used)
288 {
289 sa_family = daemon->add_subnet6->addr.sa.sa_family;
290 addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
291 }
292 else
293 addrp = &source->in6.sin6_addr;
294 }
295 else
296#endif
297 {
298 opt->source_netmask = daemon->add_subnet4->mask;
299 if (daemon->add_subnet4->addr_used)
300 {
301 sa_family = daemon->add_subnet4->addr.sa.sa_family;
302 addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
303 }
304 else
305 addrp = &source->in.sin_addr;
306 }
307
308 opt->scope_netmask = 0;
309 len = 0;
310
311 if (opt->source_netmask != 0)
312 {
313#ifdef HAVE_IPV6
314 opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
315#else
316 opt->family = htons(1);
317#endif
318 len = ((opt->source_netmask - 1) >> 3) + 1;
319 memcpy(opt->addr, addrp, len);
320 if (opt->source_netmask & 7)
321 opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
322 }
323
324 return len + 4;
325}
326
Simon Kelley33702ab2015-12-28 23:17:15 +0000327static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source)
Simon Kelley1d030162015-12-21 14:17:06 +0000328{
329 /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
330
331 int len;
332 struct subnet_opt opt;
333
334 len = calc_subnet_opt(&opt, source);
335 return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
336}
337
Simon Kelley1d030162015-12-21 14:17:06 +0000338int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
339{
340 /* Section 9.2, Check that subnet option in reply matches. */
Simon Kelley5aa5f0f2015-12-21 17:20:35 +0000341
342 int len, calc_len;
Simon Kelley1d030162015-12-21 14:17:06 +0000343 struct subnet_opt opt;
344 unsigned char *p;
345 int code, i, rdlen;
346
347 calc_len = calc_subnet_opt(&opt, peer);
348
349 if (!(p = skip_name(pseudoheader, header, plen, 10)))
350 return 1;
351
352 p += 8; /* skip UDP length and RCODE */
353
354 GETSHORT(rdlen, p);
355 if (!CHECK_LEN(header, p, plen, rdlen))
356 return 1; /* bad packet */
357
358 /* check if option there */
359 for (i = 0; i + 4 < rdlen; i += len + 4)
360 {
361 GETSHORT(code, p);
362 GETSHORT(len, p);
363 if (code == EDNS0_OPTION_CLIENT_SUBNET)
364 {
365 /* make sure this doesn't mismatch. */
366 opt.scope_netmask = p[3];
367 if (len != calc_len || memcmp(p, &opt, len) != 0)
368 return 0;
369 }
370 p += len;
371 }
372
373 return 1;
374}
Simon Kelley33702ab2015-12-28 23:17:15 +0000375
376size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
377 union mysockaddr *source, time_t now, int *check_subnet)
378{
379 *check_subnet = 0;
380
381 if (option_bool(OPT_ADD_MAC))
382 plen = add_mac(header, plen, limit, source, now);
383
384 if (option_bool(OPT_DNS_CLIENT))
385 plen = add_dns_client(header, plen, limit, source, now);
386
387 if (option_bool(OPT_CLIENT_SUBNET))
388 {
389 plen = add_source_addr(header, plen, limit, source);
390 *check_subnet = 1;
391 }
392
393 return plen;
394}