1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
24 #include "resolved-dns-domain.h"
25 #include "resolved-dns-packet.h"
27 int dns_packet_new(DnsPacket **ret, size_t mtu) {
34 a = DNS_PACKET_SIZE_START;
38 if (a < DNS_PACKET_HEADER_SIZE)
39 a = DNS_PACKET_HEADER_SIZE;
41 p = malloc0(ALIGN(sizeof(DnsPacket)) + a);
45 p->size = p->rindex = DNS_PACKET_HEADER_SIZE;
54 int dns_packet_new_query(DnsPacket **ret, size_t mtu) {
61 r = dns_packet_new(&p, mtu);
65 h = DNS_PACKET_HEADER(p);
66 h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0));
72 DnsPacket *dns_packet_ref(DnsPacket *p) {
82 static void dns_packet_free(DnsPacket *p) {
87 while ((s = hashmap_steal_first_key(p->names)))
89 hashmap_free(p->names);
95 DnsPacket *dns_packet_unref(DnsPacket *p) {
109 int dns_packet_validate(DnsPacket *p) {
112 if (p->size < DNS_PACKET_HEADER_SIZE)
118 int dns_packet_validate_reply(DnsPacket *p) {
123 r = dns_packet_validate(p);
127 if (DNS_PACKET_QR(p) == 0)
130 if (DNS_PACKET_OPCODE(p) != 0)
136 static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) {
139 if (p->size + add > p->allocated)
146 *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->size;
152 static void dns_packet_truncate(DnsPacket *p, size_t sz) {
162 HASHMAP_FOREACH_KEY(s, n, p->names, i) {
164 if (PTR_TO_SIZE(n) < sz)
167 hashmap_remove(p->names, s);
174 int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) {
180 r = dns_packet_extend(p, sizeof(uint8_t), &d, start);
184 ((uint8_t*) d)[0] = v;
189 int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) {
195 r = dns_packet_extend(p, sizeof(uint16_t), &d, start);
199 ((uint8_t*) d)[0] = (uint8_t) (v >> 8);
200 ((uint8_t*) d)[1] = (uint8_t) (v & 255);
205 int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start) {
217 r = dns_packet_extend(p, 1 + l, &d, start);
221 ((uint8_t*) d)[0] = (uint8_t) l;
222 memcpy(((uint8_t*) d) + 1, s, l);
227 int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) {
234 if (l > DNS_LABEL_MAX)
237 r = dns_packet_extend(p, 1 + l, &w, start);
241 ((uint8_t*) w)[0] = (uint8_t) l;
242 memcpy(((uint8_t*) w) + 1, d, l);
247 int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) {
254 saved_size = p->size;
257 _cleanup_free_ char *s = NULL;
258 char label[DNS_LABEL_MAX];
261 n = PTR_TO_SIZE(hashmap_get(p->names, name));
266 r = dns_packet_append_uint16(p, 0xC000 | n, NULL);
280 r = dns_label_unescape(&name, label, sizeof(label));
284 r = dns_packet_append_label(p, label, r, &n);
288 r = hashmap_ensure_allocated(&p->names, dns_name_hash_func, dns_name_compare_func);
292 r = hashmap_put(p->names, s, SIZE_TO_PTR(n));
299 r = dns_packet_append_uint8(p, 0, NULL);
310 dns_packet_truncate(p, saved_size);
314 int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) {
321 saved_size = p->size;
323 r = dns_packet_append_name(p, k->name, NULL);
327 r = dns_packet_append_uint16(p, k->type, NULL);
331 r = dns_packet_append_uint16(p, k->class, NULL);
341 dns_packet_truncate(p, saved_size);
345 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
348 if (p->rindex + sz > p->size)
352 *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->rindex;
361 static void dns_packet_rewind(DnsPacket *p, size_t idx) {
363 assert(idx <= p->size);
364 assert(idx >= DNS_PACKET_HEADER_SIZE);
369 int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) {
375 r = dns_packet_read(p, sizeof(uint8_t), &d, start);
379 *ret = ((uint8_t*) d)[0];
383 int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) {
389 r = dns_packet_read(p, sizeof(uint16_t), &d, start);
393 *ret = (((uint16_t) ((uint8_t*) d)[0]) << 8) |
394 ((uint16_t) ((uint8_t*) d)[1]);
398 int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
404 r = dns_packet_read(p, sizeof(uint32_t), &d, start);
408 *ret = (((uint32_t) ((uint8_t*) d)[0]) << 24) |
409 (((uint32_t) ((uint8_t*) d)[1]) << 16) |
410 (((uint32_t) ((uint8_t*) d)[2]) << 8) |
411 ((uint32_t) ((uint8_t*) d)[3]);
416 int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
425 saved_rindex = p->rindex;
427 r = dns_packet_read_uint8(p, &c, NULL);
431 r = dns_packet_read(p, c, &d, NULL);
435 if (memchr(d, 0, c)) {
446 if (!utf8_is_valid(t)) {
455 *start = saved_rindex;
460 dns_packet_rewind(p, saved_rindex);
464 int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) {
465 size_t saved_rindex, after_rindex = 0;
466 _cleanup_free_ char *ret = NULL;
467 size_t n = 0, allocated = 0;
474 saved_rindex = p->rindex;
479 r = dns_packet_read_uint8(p, &c, NULL);
487 _cleanup_free_ char *t = NULL;
491 r = dns_packet_read(p, c, (const void**) &label, NULL);
495 r = dns_label_escape(label, c, &t);
499 if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) {
509 memcpy(ret + n, t, c);
512 } else if ((c & 0xc0) == 0xc0) {
516 r = dns_packet_read_uint8(p, &d, NULL);
520 ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
521 if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= saved_rindex) {
526 if (after_rindex == 0)
527 after_rindex = p->rindex;
534 if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
541 if (after_rindex != 0)
542 p->rindex= after_rindex;
548 *start = saved_rindex;
553 dns_packet_rewind(p, saved_rindex);
557 int dns_packet_read_key(DnsPacket *p, DnsResourceKey *ret, size_t *start) {
558 _cleanup_(dns_resource_key_free) DnsResourceKey k = {};
565 saved_rindex = p->rindex;
567 r = dns_packet_read_name(p, &k.name, NULL);
571 r = dns_packet_read_uint16(p, &k.type, NULL);
575 r = dns_packet_read_uint16(p, &k.class, NULL);
583 *start = saved_rindex;
587 dns_packet_rewind(p, saved_rindex);
591 int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
592 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr;
593 size_t saved_rindex, offset;
601 rr = dns_resource_record_new();
605 saved_rindex = p->rindex;
607 r = dns_packet_read_key(p, &rr->key, NULL);
611 r = dns_packet_read_uint32(p, &rr->ttl, NULL);
615 r = dns_packet_read_uint16(p, &rdlength, NULL);
619 if (p->rindex + rdlength > p->size) {
626 switch (rr->key.type) {
631 r = dns_packet_read_name(p, &rr->ptr.name, NULL);
635 r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
639 r = dns_packet_read_string(p, &rr->hinfo.os, NULL);
643 r = dns_packet_read(p, sizeof(struct in_addr), &d, NULL);
647 memcpy(&rr->a.in_addr, d, sizeof(struct in_addr));
651 r = dns_packet_read(p, sizeof(struct in6_addr), &d, NULL);
655 memcpy(&rr->aaaa.in6_addr, d, sizeof(struct in6_addr));
659 r = dns_packet_read(p, rdlength, &d, NULL);
663 rr->generic.data = memdup(d, rdlength);
664 if (!rr->generic.data) {
669 rr->generic.size = rdlength;
674 if (p->rindex != offset + rdlength) {
683 *start = saved_rindex;
687 dns_packet_rewind(p, saved_rindex);
691 int dns_packet_skip_question(DnsPacket *p) {
697 n = DNS_PACKET_QDCOUNT(p);
698 for (i = 0; i < n; i++) {
699 _cleanup_(dns_resource_key_free) DnsResourceKey key = {};
701 r = dns_packet_read_key(p, &key, NULL);
709 static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
710 [DNS_RCODE_SUCCESS] = "SUCCESS",
711 [DNS_RCODE_FORMERR] = "FORMERR",
712 [DNS_RCODE_SERVFAIL] = "SERVFAIL",
713 [DNS_RCODE_NXDOMAIN] = "NXDOMAIN",
714 [DNS_RCODE_NOTIMP] = "NOTIMP",
715 [DNS_RCODE_REFUSED] = "REFUSED",
716 [DNS_RCODE_YXDOMAIN] = "YXDOMAIN",
717 [DNS_RCODE_YXRRSET] = "YRRSET",
718 [DNS_RCODE_NXRRSET] = "NXRRSET",
719 [DNS_RCODE_NOTAUTH] = "NOTAUTH",
720 [DNS_RCODE_NOTZONE] = "NOTZONE",
721 [DNS_RCODE_BADVERS] = "BADVERS",
722 [DNS_RCODE_BADKEY] = "BADKEY",
723 [DNS_RCODE_BADTIME] = "BADTIME",
724 [DNS_RCODE_BADMODE] = "BADMODE",
725 [DNS_RCODE_BADNAME] = "BADNAME",
726 [DNS_RCODE_BADALG] = "BADALG",
727 [DNS_RCODE_BADTRUNC] = "BADTRUNC",
729 DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);