chiark / gitweb /
resolved: add DNS cache
[elogind.git] / src / resolve / resolved-dns-packet.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20  ***/
21
22 #include "utf8.h"
23 #include "util.h"
24 #include "resolved-dns-domain.h"
25 #include "resolved-dns-packet.h"
26
27 int dns_packet_new(DnsPacket **ret, size_t mtu) {
28         DnsPacket *p;
29         size_t a;
30
31         assert(ret);
32
33         if (mtu <= 0)
34                 a = DNS_PACKET_SIZE_START;
35         else
36                 a = mtu;
37
38         if (a < DNS_PACKET_HEADER_SIZE)
39                 a = DNS_PACKET_HEADER_SIZE;
40
41         /* round up to next page size */
42         a = PAGE_ALIGN(ALIGN(sizeof(DnsPacket)) + a) - ALIGN(sizeof(DnsPacket));
43
44         /* make sure we never allocate more than useful */
45         if (a > DNS_PACKET_SIZE_MAX)
46                 a = DNS_PACKET_SIZE_MAX;
47
48         p = malloc0(ALIGN(sizeof(DnsPacket)) + a);
49         if (!p)
50                 return -ENOMEM;
51
52         p->size = p->rindex = DNS_PACKET_HEADER_SIZE;
53         p->allocated = a;
54         p->n_ref = 1;
55
56         *ret = p;
57
58         return 0;
59 }
60
61 int dns_packet_new_query(DnsPacket **ret, size_t mtu) {
62         DnsPacket *p;
63         DnsPacketHeader *h;
64         int r;
65
66         assert(ret);
67
68         r = dns_packet_new(&p, mtu);
69         if (r < 0)
70                 return r;
71
72         h = DNS_PACKET_HEADER(p);
73         h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0));
74
75         *ret = p;
76         return 0;
77 }
78
79 DnsPacket *dns_packet_ref(DnsPacket *p) {
80
81         if (!p)
82                 return NULL;
83
84         assert(p->n_ref > 0);
85         p->n_ref++;
86         return p;
87 }
88
89 static void dns_packet_free(DnsPacket *p) {
90         char *s;
91
92         assert(p);
93
94         if (p->rrs)
95                 dns_resource_record_freev(p->rrs, DNS_PACKET_RRCOUNT(p));
96
97         while ((s = hashmap_steal_first_key(p->names)))
98                 free(s);
99         hashmap_free(p->names);
100
101         free(p->data);
102         free(p);
103 }
104
105 DnsPacket *dns_packet_unref(DnsPacket *p) {
106         if (!p)
107                 return NULL;
108
109         assert(p->n_ref > 0);
110
111         if (p->n_ref == 1)
112                 dns_packet_free(p);
113         else
114                 p->n_ref--;
115
116         return NULL;
117 }
118
119 int dns_packet_validate(DnsPacket *p) {
120         assert(p);
121
122         if (p->size < DNS_PACKET_HEADER_SIZE)
123                 return -EBADMSG;
124
125         if (p->size > DNS_PACKET_SIZE_MAX)
126                 return -EBADMSG;
127
128         return 0;
129 }
130
131 int dns_packet_validate_reply(DnsPacket *p) {
132         int r;
133
134         assert(p);
135
136         r = dns_packet_validate(p);
137         if (r < 0)
138                 return r;
139
140         if (DNS_PACKET_QR(p) == 0)
141                 return -EBADMSG;
142
143         if (DNS_PACKET_OPCODE(p) != 0)
144                 return -EBADMSG;
145
146         return 0;
147 }
148
149 static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) {
150         assert(p);
151
152         if (p->size + add > p->allocated) {
153                 size_t a;
154
155                 a = PAGE_ALIGN((p->size + add) * 2);
156                 if (a > DNS_PACKET_SIZE_MAX)
157                         a = DNS_PACKET_SIZE_MAX;
158
159                 if (p->size + add > a)
160                         return -EMSGSIZE;
161
162                 if (p->data) {
163                         void *d;
164
165                         d = realloc(p->data, a);
166                         if (!d)
167                                 return -ENOMEM;
168
169                         p->data = d;
170                 } else {
171                         p->data = malloc(a);
172                         if (!p->data)
173                                 return -ENOMEM;
174
175                         memcpy(p->data, (uint8_t*) p + ALIGN(sizeof(DnsPacket)), p->size);
176                         memzero((uint8_t*) p->data + p->size, a - p->size);
177                 }
178
179                 p->allocated = a;
180         }
181
182         if (start)
183                 *start = p->size;
184
185         if (ret)
186                 *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->size;
187
188         p->size += add;
189         return 0;
190 }
191
192 static void dns_packet_truncate(DnsPacket *p, size_t sz) {
193         Iterator i;
194         char *s;
195         void *n;
196
197         assert(p);
198
199         if (p->size <= sz)
200                 return;
201
202         HASHMAP_FOREACH_KEY(s, n, p->names, i) {
203
204                 if (PTR_TO_SIZE(n) < sz)
205                         continue;
206
207                 hashmap_remove(p->names, s);
208                 free(s);
209         }
210
211         p->size = sz;
212 }
213
214 int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) {
215         void *d;
216         int r;
217
218         assert(p);
219
220         r = dns_packet_extend(p, sizeof(uint8_t), &d, start);
221         if (r < 0)
222                 return r;
223
224         ((uint8_t*) d)[0] = v;
225
226         return 0;
227 }
228
229 int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) {
230         void *d;
231         int r;
232
233         assert(p);
234
235         r = dns_packet_extend(p, sizeof(uint16_t), &d, start);
236         if (r < 0)
237                 return r;
238
239         ((uint8_t*) d)[0] = (uint8_t) (v >> 8);
240         ((uint8_t*) d)[1] = (uint8_t) (v & 255);
241
242         return 0;
243 }
244
245 int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start) {
246         void *d;
247         size_t l;
248         int r;
249
250         assert(p);
251         assert(s);
252
253         l = strlen(s);
254         if (l > 255)
255                 return -E2BIG;
256
257         r = dns_packet_extend(p, 1 + l, &d, start);
258         if (r < 0)
259                 return r;
260
261         ((uint8_t*) d)[0] = (uint8_t) l;
262         memcpy(((uint8_t*) d) + 1, s, l);
263
264         return 0;
265 }
266
267 int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) {
268         void *w;
269         int r;
270
271         assert(p);
272         assert(d);
273
274         if (l > DNS_LABEL_MAX)
275                 return -E2BIG;
276
277         r = dns_packet_extend(p, 1 + l, &w, start);
278         if (r < 0)
279                 return r;
280
281         ((uint8_t*) w)[0] = (uint8_t) l;
282         memcpy(((uint8_t*) w) + 1, d, l);
283
284         return 0;
285 }
286
287 int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) {
288         size_t saved_size;
289         int r;
290
291         assert(p);
292         assert(name);
293
294         saved_size = p->size;
295
296         while (*name) {
297                 _cleanup_free_ char *s = NULL;
298                 char label[DNS_LABEL_MAX];
299                 size_t n;
300
301                 n = PTR_TO_SIZE(hashmap_get(p->names, name));
302                 if (n > 0) {
303                         assert(n < p->size);
304
305                         if (n < 0x4000) {
306                                 r = dns_packet_append_uint16(p, 0xC000 | n, NULL);
307                                 if (r < 0)
308                                         goto fail;
309
310                                 goto done;
311                         }
312                 }
313
314                 s = strdup(name);
315                 if (!s) {
316                         r = -ENOMEM;
317                         goto fail;
318                 }
319
320                 r = dns_label_unescape(&name, label, sizeof(label));
321                 if (r < 0)
322                         goto fail;
323
324                 r = dns_packet_append_label(p, label, r, &n);
325                 if (r < 0)
326                         goto fail;
327
328                 r = hashmap_ensure_allocated(&p->names, dns_name_hash_func, dns_name_compare_func);
329                 if (r < 0)
330                         goto fail;
331
332                 r = hashmap_put(p->names, s, SIZE_TO_PTR(n));
333                 if (r < 0)
334                         goto fail;
335
336                 s = NULL;
337         }
338
339         r = dns_packet_append_uint8(p, 0, NULL);
340         if (r < 0)
341                 return r;
342
343 done:
344         if (start)
345                 *start = saved_size;
346
347         return 0;
348
349 fail:
350         dns_packet_truncate(p, saved_size);
351         return r;
352 }
353
354 int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) {
355         size_t saved_size;
356         int r;
357
358         assert(p);
359         assert(k);
360
361         saved_size = p->size;
362
363         r = dns_packet_append_name(p, k->name, NULL);
364         if (r < 0)
365                 goto fail;
366
367         r = dns_packet_append_uint16(p, k->type, NULL);
368         if (r < 0)
369                 goto fail;
370
371         r = dns_packet_append_uint16(p, k->class, NULL);
372         if (r < 0)
373                 goto fail;
374
375         if (start)
376                 *start = saved_size;
377
378         return 0;
379
380 fail:
381         dns_packet_truncate(p, saved_size);
382         return r;
383 }
384
385 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
386         assert(p);
387
388         if (p->rindex + sz > p->size)
389                 return -EMSGSIZE;
390
391         if (ret)
392                 *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->rindex;
393
394         if (start)
395                 *start = p->rindex;
396
397         p->rindex += sz;
398         return 0;
399 }
400
401 void dns_packet_rewind(DnsPacket *p, size_t idx) {
402         assert(p);
403         assert(idx <= p->size);
404         assert(idx >= DNS_PACKET_HEADER_SIZE);
405
406         p->rindex = idx;
407 }
408
409 int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) {
410         const void *d;
411         int r;
412
413         assert(p);
414
415         r = dns_packet_read(p, sizeof(uint8_t), &d, start);
416         if (r < 0)
417                 return r;
418
419         *ret = ((uint8_t*) d)[0];
420         return 0;
421 }
422
423 int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) {
424         const void *d;
425         int r;
426
427         assert(p);
428
429         r = dns_packet_read(p, sizeof(uint16_t), &d, start);
430         if (r < 0)
431                 return r;
432
433         *ret = (((uint16_t) ((uint8_t*) d)[0]) << 8) |
434                 ((uint16_t) ((uint8_t*) d)[1]);
435         return 0;
436 }
437
438 int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
439         const void *d;
440         int r;
441
442         assert(p);
443
444         r = dns_packet_read(p, sizeof(uint32_t), &d, start);
445         if (r < 0)
446                 return r;
447
448         *ret = (((uint32_t) ((uint8_t*) d)[0]) << 24) |
449                (((uint32_t) ((uint8_t*) d)[1]) << 16) |
450                (((uint32_t) ((uint8_t*) d)[2]) << 8) |
451                 ((uint32_t) ((uint8_t*) d)[3]);
452
453         return 0;
454 }
455
456 int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
457         size_t saved_rindex;
458         const void *d;
459         char *t;
460         uint8_t c;
461         int r;
462
463         assert(p);
464
465         saved_rindex = p->rindex;
466
467         r = dns_packet_read_uint8(p, &c, NULL);
468         if (r < 0)
469                 goto fail;
470
471         r = dns_packet_read(p, c, &d, NULL);
472         if (r < 0)
473                 goto fail;
474
475         if (memchr(d, 0, c)) {
476                 r = -EBADMSG;
477                 goto fail;
478         }
479
480         t = strndup(d, c);
481         if (!t) {
482                 r = -ENOMEM;
483                 goto fail;
484         }
485
486         if (!utf8_is_valid(t)) {
487                 free(t);
488                 r = -EBADMSG;
489                 goto fail;
490         }
491
492         *ret = t;
493
494         if (start)
495                 *start = saved_rindex;
496
497         return 0;
498
499 fail:
500         dns_packet_rewind(p, saved_rindex);
501         return r;
502 }
503
504 int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) {
505         size_t saved_rindex, after_rindex = 0;
506         _cleanup_free_ char *ret = NULL;
507         size_t n = 0, allocated = 0;
508         bool first = true;
509         int r;
510
511         assert(p);
512         assert(_ret);
513
514         saved_rindex = p->rindex;
515
516         for (;;) {
517                 uint8_t c, d;
518
519                 r = dns_packet_read_uint8(p, &c, NULL);
520                 if (r < 0)
521                         goto fail;
522
523                 if (c == 0)
524                         /* End of name */
525                         break;
526                 else if (c <= 63) {
527                         _cleanup_free_ char *t = NULL;
528                         const char *label;
529
530                         /* Literal label */
531                         r = dns_packet_read(p, c, (const void**) &label, NULL);
532                         if (r < 0)
533                                 goto fail;
534
535                         r = dns_label_escape(label, c, &t);
536                         if (r < 0)
537                                 goto fail;
538
539                         if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) {
540                                 r = -ENOMEM;
541                                 goto fail;
542                         }
543
544                         if (!first)
545                                 ret[n++] = '.';
546                         else
547                                 first = false;
548
549                         memcpy(ret + n, t, c);
550                         n += r;
551                         continue;
552                 } else if ((c & 0xc0) == 0xc0) {
553                         uint16_t ptr;
554
555                         /* Pointer */
556                         r = dns_packet_read_uint8(p, &d, NULL);
557                         if (r < 0)
558                                 goto fail;
559
560                         ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
561                         if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= saved_rindex) {
562                                 r = -EBADMSG;
563                                 goto fail;
564                         }
565
566                         if (after_rindex == 0)
567                                 after_rindex = p->rindex;
568
569                         p->rindex = ptr;
570                 } else
571                         goto fail;
572         }
573
574         if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
575                 r = -ENOMEM;
576                 goto fail;
577         }
578
579         ret[n] = 0;
580
581         if (after_rindex != 0)
582                 p->rindex= after_rindex;
583
584         *_ret = ret;
585         ret = NULL;
586
587         if (start)
588                 *start = saved_rindex;
589
590         return 0;
591
592 fail:
593         dns_packet_rewind(p, saved_rindex);
594         return r;
595 }
596
597 int dns_packet_read_key(DnsPacket *p, DnsResourceKey *ret, size_t *start) {
598         _cleanup_(dns_resource_key_free) DnsResourceKey k = {};
599         size_t saved_rindex;
600         int r;
601
602         assert(p);
603         assert(ret);
604
605         saved_rindex = p->rindex;
606
607         r = dns_packet_read_name(p, &k.name, NULL);
608         if (r < 0)
609                 goto fail;
610
611         r = dns_packet_read_uint16(p, &k.type, NULL);
612         if (r < 0)
613                 goto fail;
614
615         r = dns_packet_read_uint16(p, &k.class, NULL);
616         if (r < 0)
617                 goto fail;
618
619         *ret = k;
620         zero(k);
621
622         if (start)
623                 *start = saved_rindex;
624
625         return 0;
626 fail:
627         dns_packet_rewind(p, saved_rindex);
628         return r;
629 }
630
631 int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
632         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr;
633         size_t saved_rindex, offset;
634         uint16_t rdlength;
635         const void *d;
636         int r;
637
638         assert(p);
639         assert(ret);
640
641         rr = dns_resource_record_new();
642         if (!rr)
643                 return -ENOMEM;
644
645         saved_rindex = p->rindex;
646
647         r = dns_packet_read_key(p, &rr->key, NULL);
648         if (r < 0)
649                 goto fail;
650
651         r = dns_packet_read_uint32(p, &rr->ttl, NULL);
652         if (r < 0)
653                 goto fail;
654
655         r = dns_packet_read_uint16(p, &rdlength, NULL);
656         if (r < 0)
657                 goto fail;
658
659         if (p->rindex + rdlength > p->size) {
660                 r = -EBADMSG;
661                 goto fail;
662         }
663
664         offset = p->rindex;
665
666         switch (rr->key.type) {
667
668         case DNS_TYPE_PTR:
669         case DNS_TYPE_NS:
670         case DNS_TYPE_CNAME:
671                 r = dns_packet_read_name(p, &rr->ptr.name, NULL);
672                 break;
673
674         case DNS_TYPE_HINFO:
675                 r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
676                 if (r < 0)
677                         goto fail;
678
679                 r = dns_packet_read_string(p, &rr->hinfo.os, NULL);
680                 break;
681
682         case DNS_TYPE_A:
683                 r = dns_packet_read(p, sizeof(struct in_addr), &d, NULL);
684                 if (r < 0)
685                         goto fail;
686
687                 memcpy(&rr->a.in_addr, d, sizeof(struct in_addr));
688                 break;
689
690         case DNS_TYPE_AAAA:
691                 r = dns_packet_read(p, sizeof(struct in6_addr), &d, NULL);
692                 if (r < 0)
693                         goto fail;
694
695                 memcpy(&rr->aaaa.in6_addr, d, sizeof(struct in6_addr));
696                 break;
697
698         default:
699                 r = dns_packet_read(p, rdlength, &d, NULL);
700                 if (r < 0)
701                         goto fail;
702
703                 rr->generic.data = memdup(d, rdlength);
704                 if (!rr->generic.data) {
705                         r = -ENOMEM;
706                         goto fail;
707                 }
708
709                 rr->generic.size = rdlength;
710                 break;
711         }
712         if (r < 0)
713                 goto fail;
714         if (p->rindex != offset + rdlength) {
715                 r = -EBADMSG;
716                 goto fail;
717         }
718
719         *ret = rr;
720         rr = NULL;
721
722         if (start)
723                 *start = saved_rindex;
724
725         return 0;
726 fail:
727         dns_packet_rewind(p, saved_rindex);
728         return r;
729 }
730
731 int dns_packet_skip_question(DnsPacket *p) {
732         unsigned i, n;
733         int r;
734
735         assert(p);
736
737         dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
738
739         n = DNS_PACKET_QDCOUNT(p);
740         for (i = 0; i < n; i++) {
741                 _cleanup_(dns_resource_key_free) DnsResourceKey key = {};
742
743                 r = dns_packet_read_key(p, &key, NULL);
744                 if (r < 0)
745                         return r;
746         }
747
748         return 0;
749 }
750
751 int dns_packet_extract_rrs(DnsPacket *p) {
752         DnsResourceRecord **rrs = NULL;
753         size_t saved_rindex;
754         unsigned n, added = 0;
755         int r;
756
757         if (p->rrs)
758                 return (int) DNS_PACKET_RRCOUNT(p);
759
760         saved_rindex = p->rindex;
761
762         r = dns_packet_skip_question(p);
763         if (r < 0)
764                 goto finish;
765
766         n = DNS_PACKET_RRCOUNT(p);
767         if (n <= 0) {
768                 r = 0;
769                 goto finish;
770         }
771
772         rrs = new0(DnsResourceRecord*, n);
773         if (!rrs) {
774                 r = -ENOMEM;
775                 goto finish;
776         }
777
778         for (added = 0; added < n; added++) {
779                 r = dns_packet_read_rr(p, &rrs[added], NULL);
780                 if (r < 0) {
781                         dns_resource_record_freev(rrs, added);
782                         goto finish;
783                 }
784         }
785
786         p->rrs = rrs;
787         r = (int) n;
788
789 finish:
790         p->rindex = saved_rindex;
791         return r;
792 }
793
794 static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
795         [DNS_RCODE_SUCCESS] = "SUCCESS",
796         [DNS_RCODE_FORMERR] = "FORMERR",
797         [DNS_RCODE_SERVFAIL] = "SERVFAIL",
798         [DNS_RCODE_NXDOMAIN] = "NXDOMAIN",
799         [DNS_RCODE_NOTIMP] = "NOTIMP",
800         [DNS_RCODE_REFUSED] = "REFUSED",
801         [DNS_RCODE_YXDOMAIN] = "YXDOMAIN",
802         [DNS_RCODE_YXRRSET] = "YRRSET",
803         [DNS_RCODE_NXRRSET] = "NXRRSET",
804         [DNS_RCODE_NOTAUTH] = "NOTAUTH",
805         [DNS_RCODE_NOTZONE] = "NOTZONE",
806         [DNS_RCODE_BADVERS] = "BADVERS",
807         [DNS_RCODE_BADKEY] = "BADKEY",
808         [DNS_RCODE_BADTIME] = "BADTIME",
809         [DNS_RCODE_BADMODE] = "BADMODE",
810         [DNS_RCODE_BADNAME] = "BADNAME",
811         [DNS_RCODE_BADALG] = "BADALG",
812         [DNS_RCODE_BADTRUNC] = "BADTRUNC",
813 };
814 DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);