fix typo in pt.po headers
[debian/iodine.git] / src / dns.c
1 /*
2  * Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
3  * 2006-2009 Bjorn Andersson <flex@kryo.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 #include <time.h>
19 #include <stdio.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <strings.h>
25 #include <ctype.h>
26
27 #ifdef WINDOWS32
28 #include "windows.h"
29 #else
30 #ifdef ANDROID
31 #include "android_dns.h"
32 #endif
33 #include <arpa/nameser.h>
34 #ifdef DARWIN
35 #define BIND_8_COMPAT
36 #include <arpa/nameser_compat.h>
37 #endif
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <err.h>
41 #endif
42
43
44 #include "dns.h"
45 #include "encoding.h"
46 #include "read.h"
47
48 int dnsc_use_edns0 = 1;
49
50 #define CHECKLEN(x) if (buflen < (x) + (unsigned)(p-buf))  return 0
51
52 int
53 dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_t datalen)
54 {
55         HEADER *header;
56         short name;
57         char *p;
58         int len;
59         int ancnt;
60
61         if (buflen < sizeof(HEADER))
62                 return 0;
63
64         memset(buf, 0, buflen);
65
66         header = (HEADER*)buf;
67
68         header->id = htons(q->id);
69         header->qr = (qr == QR_ANSWER);
70         header->opcode = 0;
71         header->aa = (qr == QR_ANSWER);
72         header->tc = 0;
73         header->rd = (qr == QR_QUERY);
74         header->ra = 0;
75
76         p = buf + sizeof(HEADER);
77
78         switch (qr) {
79         case QR_ANSWER:
80                 header->qdcount = htons(1);
81
82                 name = 0xc000 | ((p - buf) & 0x3fff);
83
84                 /* Question section */
85                 putname(&p, buflen - (p - buf), q->name);
86
87                 CHECKLEN(4);
88                 putshort(&p, q->type);
89                 putshort(&p, C_IN);
90
91                 /* Answer section */
92
93                 if (q->type == T_CNAME || q->type == T_A) {
94                         /* data is expected to be like "Hblabla.host.name.com\0" */
95
96                         char *startp;
97                         int namelen;
98
99                         CHECKLEN(10);
100                         putshort(&p, name);
101                         if (q->type == T_A)
102                                 /* answer CNAME to A question */
103                                 putshort(&p, T_CNAME);
104                         else
105                                 putshort(&p, q->type);
106                         putshort(&p, C_IN);
107                         putlong(&p, 0);         /* TTL */
108
109                         startp = p;
110                         p += 2;                 /* skip 2 bytes length */
111                         putname(&p, buflen - (p - buf), data);
112                         CHECKLEN(0);
113                         namelen = p - startp;
114                         namelen -= 2;
115                         putshort(&startp, namelen);
116                         ancnt = 1;
117                 } else if (q->type == T_MX || q->type == T_SRV) {
118                         /* Data is expected to be like
119                            "Hblabla.host.name.com\0Hanother.com\0\0"
120                            For SRV, see RFC2782.
121                          */
122
123                         char *mxdata = data;
124                         char *startp;
125                         int namelen;
126
127                         ancnt = 1;
128                         while (1) {
129                                 CHECKLEN(10);
130                                 putshort(&p, name);
131                                 putshort(&p, q->type);
132                                 putshort(&p, C_IN);
133                                 putlong(&p, 0);         /* TTL */
134
135                                 startp = p;
136                                 p += 2;                 /* skip 2 bytes length */
137                                 CHECKLEN(2);
138                                 putshort(&p, 10 * ancnt);       /* preference */
139
140                                 if (q->type == T_SRV) {
141                                         /* weight, port (5060 = SIP) */
142                                         CHECKLEN(4);
143                                         putshort(&p, 10);
144                                         putshort(&p, 5060);
145                                 }
146
147                                 putname(&p, buflen - (p - buf), mxdata);
148                                 CHECKLEN(0);
149                                 namelen = p - startp;
150                                 namelen -= 2;
151                                 putshort(&startp, namelen);
152
153                                 mxdata = mxdata + strlen(mxdata) + 1;
154                                 if (*mxdata == '\0')
155                                         break;
156
157                                 ancnt++;
158                         }
159                 } else if (q->type == T_TXT) {
160                         /* TXT has binary or base-X data */
161                         char *startp;
162                         int txtlen;
163
164                         CHECKLEN(10);
165                         putshort(&p, name);
166                         putshort(&p, q->type);
167                         putshort(&p, C_IN);
168                         putlong(&p, 0);         /* TTL */
169
170                         startp = p;
171                         p += 2;                 /* skip 2 bytes length */
172                         puttxtbin(&p, buflen - (p - buf), data, datalen);
173                         CHECKLEN(0);
174                         txtlen = p - startp;
175                         txtlen -= 2;
176                         putshort(&startp, txtlen);
177                         ancnt = 1;
178                 } else {
179                         /* NULL has raw binary data */
180
181                         CHECKLEN(10);
182                         putshort(&p, name);
183                         putshort(&p, q->type);
184                         putshort(&p, C_IN);
185                         putlong(&p, 0);         /* TTL */
186
187                         datalen = MIN(datalen, buflen - (p - buf));
188                         CHECKLEN(2);
189                         putshort(&p, datalen);
190                         CHECKLEN(datalen);
191                         putdata(&p, data, datalen);
192                         CHECKLEN(0);
193                         ancnt = 1;
194                 }
195                 header->ancount = htons(ancnt);
196                 break;
197         case QR_QUERY:
198                 /* Note that iodined also uses this for forward queries */
199
200                 header->qdcount = htons(1);
201
202                 datalen = MIN(datalen, buflen - (p - buf));
203                 putname(&p, datalen, data);
204
205                 CHECKLEN(4);
206                 putshort(&p, q->type);
207                 putshort(&p, C_IN);
208
209                 /* EDNS0 to advertise maximum response length
210                    (even CNAME/A/MX, 255+255+header would be >512) */
211                 if (dnsc_use_edns0) {
212                         header->arcount = htons(1);
213                         CHECKLEN(11);
214                         putbyte(&p, 0x00);    /* Root */
215                         putshort(&p, 0x0029); /* OPT */
216                         putshort(&p, 0x1000); /* Payload size: 4096 */
217                         putshort(&p, 0x0000); /* Higher bits/edns version */
218                         putshort(&p, 0x8000); /* Z */
219                         putshort(&p, 0x0000); /* Data length */
220                 }
221
222                 break;
223         }
224
225         len = p - buf;
226
227         return len;
228 }
229
230 int
231 dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain)
232 /* Only used when iodined gets an NS type query */
233 /* Mostly same as dns_encode_a_response() below */
234 {
235         HEADER *header;
236         int len;
237         short name;
238         short topname;
239         short nsname;
240         char *ipp;
241         int domain_len;
242         char *p;
243
244         if (buflen < sizeof(HEADER))
245                 return 0;
246
247         memset(buf, 0, buflen);
248
249         header = (HEADER*)buf;
250
251         header->id = htons(q->id);
252         header->qr = 1;
253         header->opcode = 0;
254         header->aa = 1;
255         header->tc = 0;
256         header->rd = 0;
257         header->ra = 0;
258
259         p = buf + sizeof(HEADER);
260
261         header->qdcount = htons(1);
262         header->ancount = htons(1);
263         header->arcount = htons(1);
264
265         /* pointer to start of name */
266         name = 0xc000 | ((p - buf) & 0x3fff);
267
268         domain_len = strlen(q->name) - strlen(topdomain);
269         if (domain_len < 0 || domain_len == 1)
270                 return -1;
271         if (strcasecmp(q->name + domain_len, topdomain))
272                 return -1;
273         if (domain_len >= 1 && q->name[domain_len - 1] != '.')
274                 return -1;
275
276         /* pointer to start of topdomain; instead of dots at the end
277            we have length-bytes in front, so total length is the same */
278         topname = 0xc000 | ((p - buf + domain_len) & 0x3fff);
279
280         /* Query section */
281         putname(&p, buflen - (p - buf), q->name);       /* Name */
282         CHECKLEN(4);
283         putshort(&p, q->type);                  /* Type */
284         putshort(&p, C_IN);                     /* Class */
285
286         /* Answer section */
287         CHECKLEN(12);
288         putshort(&p, name);                     /* Name */
289         putshort(&p, q->type);                  /* Type */
290         putshort(&p, C_IN);                     /* Class */
291         putlong(&p, 3600);                      /* TTL */
292         putshort(&p, 5);                        /* Data length */
293
294         /* pointer to ns.topdomain */
295         nsname = 0xc000 | ((p - buf) & 0x3fff);
296         CHECKLEN(5);
297         putbyte(&p, 2);
298         putbyte(&p, 'n');
299         putbyte(&p, 's');
300         putshort(&p, topname);                  /* Name Server */
301
302         /* Additional data (A-record of NS server) */
303         CHECKLEN(12);
304         putshort(&p, nsname);                   /* Name Server */
305         putshort(&p, T_A);                      /* Type */
306         putshort(&p, C_IN);                     /* Class */
307         putlong(&p, 3600);                      /* TTL */
308         putshort(&p, 4);                        /* Data length */
309
310         /* ugly hack to output IP address */
311         ipp = (char *) &q->destination;
312         CHECKLEN(4);
313         putbyte(&p, *(ipp++));
314         putbyte(&p, *(ipp++));
315         putbyte(&p, *(ipp++));
316         putbyte(&p, *ipp);
317
318         len = p - buf;
319         return len;
320 }
321
322 int
323 dns_encode_a_response(char *buf, size_t buflen, struct query *q)
324 /* Only used when iodined gets an A type query for ns.topdomain or www.topdomain */
325 /* Mostly same as dns_encode_ns_response() above */
326 {
327         HEADER *header;
328         int len;
329         short name;
330         char *ipp;
331         char *p;
332
333         if (buflen < sizeof(HEADER))
334                 return 0;
335
336         memset(buf, 0, buflen);
337
338         header = (HEADER*)buf;
339
340         header->id = htons(q->id);
341         header->qr = 1;
342         header->opcode = 0;
343         header->aa = 1;
344         header->tc = 0;
345         header->rd = 0;
346         header->ra = 0;
347
348         p = buf + sizeof(HEADER);
349
350         header->qdcount = htons(1);
351         header->ancount = htons(1);
352
353         /* pointer to start of name */
354         name = 0xc000 | ((p - buf) & 0x3fff);
355
356         /* Query section */
357         putname(&p, buflen - (p - buf), q->name);       /* Name */
358         CHECKLEN(4);
359         putshort(&p, q->type);                  /* Type */
360         putshort(&p, C_IN);                     /* Class */
361
362         /* Answer section */
363         CHECKLEN(12);
364         putshort(&p, name);                     /* Name */
365         putshort(&p, q->type);                  /* Type */
366         putshort(&p, C_IN);                     /* Class */
367         putlong(&p, 3600);                      /* TTL */
368         putshort(&p, 4);                        /* Data length */
369
370         /* ugly hack to output IP address */
371         ipp = (char *) &q->destination;
372         CHECKLEN(4);
373         putbyte(&p, *(ipp++));
374         putbyte(&p, *(ipp++));
375         putbyte(&p, *(ipp++));
376         putbyte(&p, *ipp);
377
378         len = p - buf;
379         return len;
380 }
381
382 #undef CHECKLEN
383
384 unsigned short
385 dns_get_id(char *packet, size_t packetlen)
386 {
387         HEADER *header;
388         header = (HEADER*)packet;
389
390         if (packetlen < sizeof(HEADER))
391                 return 0;
392
393         return ntohs(header->id);
394 }
395
396 #define CHECKLEN(x) if (packetlen < (x) + (unsigned)(data-packet))  return 0
397
398 int
399 dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, size_t packetlen)
400 {
401         char name[QUERY_NAME_SIZE];
402         char rdata[4*1024];
403         HEADER *header;
404         short qdcount;
405         short ancount;
406         uint32_t ttl;
407         unsigned short class;
408         unsigned short type;
409         char *data;
410         unsigned short rlen;
411         int id;
412         int rv;
413
414         q->id2 = 0;
415         rv = 0;
416         header = (HEADER*)packet;
417
418         /* Reject short packets */
419         if (packetlen < sizeof(HEADER))
420                 return 0;
421
422         if (header->qr != qr) {
423                 warnx("header->qr does not match the requested qr");
424                 return -1;
425         }
426
427         data = packet + sizeof(HEADER);
428         qdcount = ntohs(header->qdcount);
429         ancount = ntohs(header->ancount);
430
431         id = ntohs(header->id);
432         id = id & 0xFFFF; /* Kill any sign extension */
433
434         rlen = 0;
435
436         if (q != NULL)
437                 q->rcode = header->rcode;
438
439         switch (qr) {
440         case QR_ANSWER:
441                 if(qdcount < 1) {
442                         /* We need a question */
443                         return -1;
444                 }
445
446                 if (q != NULL)
447                         q->id = id;
448
449                 /* Read name even if no answer, to give better error message */
450                 readname(packet, packetlen, &data, name, sizeof(name));
451                 CHECKLEN(4);
452                 readshort(packet, &data, &type);
453                 readshort(packet, &data, &class);
454
455                 /* if CHECKLEN okay, then we're sure to have a proper name */
456                 if (q != NULL) {
457                         /* We only need the first char to check it */
458                         q->name[0] = name[0];
459                         q->name[1] = '\0';
460                 }
461
462                 if (ancount < 1) {
463                         /* DNS errors like NXDOMAIN have ancount=0 and
464                            stop here. CNAME may also have A; MX/SRV may have
465                            multiple results. */
466                         return -1;
467                 }
468
469                 /* Here type is still the question type */
470                 if (type == T_NULL || type == T_PRIVATE) {
471                         /* Assume that first answer is what we wanted */
472                         readname(packet, packetlen, &data, name, sizeof(name));
473                         CHECKLEN(10);
474                         readshort(packet, &data, &type);
475                         readshort(packet, &data, &class);
476                         readlong(packet, &data, &ttl);
477                         readshort(packet, &data, &rlen);
478
479                         rv = MIN(rlen, sizeof(rdata));
480                         rv = readdata(packet, &data, rdata, rv);
481                         if (rv >= 2 && buf) {
482                                 rv = MIN(rv, buflen);
483                                 memcpy(buf, rdata, rv);
484                         } else {
485                                 rv = 0;
486                         }
487                 }
488                 else if ((type == T_A || type == T_CNAME) && buf) {
489                         /* Assume that first answer is what we wanted */
490                         readname(packet, packetlen, &data, name, sizeof(name));
491                         CHECKLEN(10);
492                         readshort(packet, &data, &type);
493                         readshort(packet, &data, &class);
494                         readlong(packet, &data, &ttl);
495                         readshort(packet, &data, &rlen);
496
497                         memset(name, 0, sizeof(name));
498                         readname(packet, packetlen, &data, name, sizeof(name) - 1);
499                         name[sizeof(name)-1] = '\0';
500                         strncpy(buf, name, buflen);
501                         buf[buflen - 1] = '\0';
502                         rv = strlen(buf);
503                 }
504                 else if ((type == T_MX || type == T_SRV) && buf) {
505                         /* We support 250 records, 250*(255+header) ~= 64kB.
506                            Only exact 10-multiples are accepted, and gaps in
507                            numbering are not jumped over (->truncated).
508                            Hopefully DNS servers won't mess around too much.
509                          */
510                         char names[250][QUERY_NAME_SIZE];
511                         char *rdatastart;
512                         unsigned short pref;
513                         int i;
514                         int offset;
515
516                         memset(names, 0, sizeof(names));
517
518                         for (i=0; i < ancount; i++) {
519                                 readname(packet, packetlen, &data, name, sizeof(name));
520                                 CHECKLEN(12);
521                                 readshort(packet, &data, &type);
522                                 readshort(packet, &data, &class);
523                                 readlong(packet, &data, &ttl);
524                                 readshort(packet, &data, &rlen);
525                                 rdatastart = data;
526                                 readshort(packet, &data, &pref);
527
528                                 if (type == T_SRV) {
529                                         /* skip weight, port */
530                                         data += 4;
531                                         CHECKLEN(0);
532                                 }
533
534                                 if (pref % 10 == 0 && pref >= 10 &&
535                                     pref < 2500) {
536                                         readname(packet, packetlen, &data,
537                                                  names[pref / 10 - 1],
538                                                  QUERY_NAME_SIZE - 1);
539                                         names[pref / 10 - 1][QUERY_NAME_SIZE-1] = '\0';
540                                 }
541
542                                 /* always trust rlen, not name encoding */
543                                 data = rdatastart + rlen;
544                                 CHECKLEN(0);
545                         }
546
547                         /* output is like Hname10.com\0Hname20.com\0\0 */
548                         offset = 0;
549                         i = 0;
550                         while (names[i][0] != '\0') {
551                                 int l = MIN(strlen(names[i]), buflen-offset-2);
552                                 if (l <= 0)
553                                         break;
554                                 memcpy(buf + offset, names[i], l);
555                                 offset += l;
556                                 *(buf + offset) = '\0';
557                                 offset++;
558                                 i++;
559                         }
560                         *(buf + offset) = '\0';
561                         rv = offset;
562                 }
563                 else if (type == T_TXT && buf) {
564                         /* Assume that first answer is what we wanted */
565                         readname(packet, packetlen, &data, name, sizeof(name));
566                         CHECKLEN(10);
567                         readshort(packet, &data, &type);
568                         readshort(packet, &data, &class);
569                         readlong(packet, &data, &ttl);
570                         readshort(packet, &data, &rlen);
571
572                         rv = readtxtbin(packet, &data, rlen, rdata, sizeof(rdata));
573                         if (rv >= 1) {
574                                 rv = MIN(rv, buflen);
575                                 memcpy(buf, rdata, rv);
576                         } else {
577                                 rv = 0;
578                         }
579                 }
580
581                 /* Here type is the answer type (note A->CNAME) */
582                 if (q != NULL)
583                         q->type = type;
584                 break;
585         case QR_QUERY:
586                 if (qdcount < 1) {
587                         warnx("no question section in name query");
588                         return -1;
589                 }
590
591                 memset(name, 0, sizeof(name));
592                 readname(packet, packetlen, &data, name, sizeof(name) - 1);
593                 name[sizeof(name)-1] = '\0';
594                 CHECKLEN(4);
595                 readshort(packet, &data, &type);
596                 readshort(packet, &data, &class);
597
598                 if (q == NULL) {
599                         rv = 0;
600                         break;
601                 }
602
603                 strncpy(q->name, name, sizeof(q->name));
604                 q->name[sizeof(q->name) - 1] = '\0';
605                 q->type = type;
606                 q->id = id;
607
608                 rv = strlen(q->name);
609                 break;
610         }
611
612         return rv;
613 }
614