X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-packet.c;h=ba056f1f444441000c65a77d3c9bc217bfbcdb52;hb=7263f72499e962b3fd54cdb7c79d49ca72121ede;hp=1ff56875e64fd10f9c9e185fedb126f77bac38b8;hpb=623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbf;p=elogind.git diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 1ff56875e..ba056f1f4 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -21,6 +21,7 @@ #include "utf8.h" #include "util.h" +#include "strv.h" #include "resolved-dns-domain.h" #include "resolved-dns-packet.h" @@ -72,10 +73,26 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { h = DNS_PACKET_HEADER(p); - if (protocol == DNS_PROTOCOL_DNS) - h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0)); /* ask for recursion */ + if (protocol == DNS_PROTOCOL_LLMNR) + h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, + 0 /* opcode */, + 0 /* c */, + 0 /* tc */, + 0 /* t */, + 0 /* ra */, + 0 /* ad */, + 0 /* cd */, + 0 /* rcode */)); else - h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0)); + h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, + 0 /* opcode */, + 0 /* aa */, + 0 /* tc */, + 1 /* rd (ask for recursion) */, + 0 /* ra */, + 0 /* ad */, + 0 /* cd */, + 0 /* rcode */)); *ret = p; return 0; @@ -148,6 +165,11 @@ int dns_packet_validate_reply(DnsPacket *p) { if (DNS_PACKET_OPCODE(p) != 0) return -EBADMSG; + /* RFC 4795, Section 2.1.1. says to discard all replies with QDCOUNT != 1 */ + if (p->protocol == DNS_PROTOCOL_LLMNR && + DNS_PACKET_QDCOUNT(p) != 1) + return -EBADMSG; + return 1; } @@ -169,13 +191,16 @@ int dns_packet_validate_query(DnsPacket *p) { if (DNS_PACKET_TC(p)) return -EBADMSG; + /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */ if (p->protocol == DNS_PROTOCOL_LLMNR && DNS_PACKET_QDCOUNT(p) != 1) return -EBADMSG; + /* RFC 4795, Section 2.1.1. says to discard all queries with ANCOUNT != 0 */ if (DNS_PACKET_ANCOUNT(p) > 0) return -EBADMSG; + /* RFC 4795, Section 2.1.1. says to discard all queries with NSCOUNT != 0 */ if (DNS_PACKET_NSCOUNT(p) > 0) return -EBADMSG; @@ -365,6 +390,7 @@ int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) { _cleanup_free_ char *s = NULL; char label[DNS_LABEL_MAX]; size_t n; + int k; n = PTR_TO_SIZE(hashmap_get(p->names, name)); if (n > 0) { @@ -389,6 +415,17 @@ int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) { if (r < 0) goto fail; + if (p->protocol == DNS_PROTOCOL_DNS) + k = dns_label_apply_idna(label, r, label, sizeof(label)); + else + k = dns_label_undo_idna(label, r, label, sizeof(label)); + if (k < 0) { + r = k; + goto fail; + } + if (k > 0) + r = k; + r = dns_packet_append_label(p, label, r, &n); if (r < 0) goto fail; @@ -472,11 +509,28 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star if (r < 0) goto fail; - switch (rr->key->type) { + switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + + case DNS_TYPE_SRV: + r = dns_packet_append_uint16(p, rr->srv.priority, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, rr->srv.weight, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, rr->srv.port, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_name(p, rr->srv.name, NULL); + break; case DNS_TYPE_PTR: case DNS_TYPE_NS: case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: r = dns_packet_append_name(p, rr->ptr.name, NULL); break; @@ -488,6 +542,20 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star r = dns_packet_append_string(p, rr->hinfo.os, NULL); break; + case DNS_TYPE_SPF: /* exactly the same as TXT */ + case DNS_TYPE_TXT: { + char **s; + + STRV_FOREACH(s, rr->txt.strings) { + r = dns_packet_append_string(p, *s, NULL); + if (r < 0) + goto fail; + } + + r = 0; + break; + } + case DNS_TYPE_A: r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); break; @@ -525,11 +593,55 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star break; case DNS_TYPE_MX: - case DNS_TYPE_TXT: - case DNS_TYPE_SRV: - case DNS_TYPE_DNAME: + r = dns_packet_append_uint16(p, rr->mx.priority, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_name(p, rr->mx.exchange, NULL); + break; + + case DNS_TYPE_LOC: + r = dns_packet_append_uint8(p, rr->loc.version, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->loc.size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->loc.horiz_pre, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->loc.vert_pre, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->loc.latitude, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->loc.longitude, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->loc.altitude, NULL); + break; + case DNS_TYPE_SSHFP: + r = dns_packet_append_uint8(p, rr->sshfp.algorithm, NULL); + if (r < 0) + goto fail; + r = dns_packet_append_uint8(p, rr->sshfp.fptype, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->sshfp.key, rr->sshfp.key_size, NULL); + break; + + case _DNS_TYPE_INVALID: /* unparseable */ default: + r = dns_packet_append_blob(p, rr->generic.data, rr->generic.size, NULL); break; } @@ -550,6 +662,9 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star goto fail; p->size = end; + if (start) + *start = saved_size; + return 0; fail: @@ -737,7 +852,7 @@ int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) { else first = false; - memcpy(ret + n, t, c); + memcpy(ret + n, t, r); n += r; continue; } else if ((c & 0xc0) == 0xc0) { @@ -827,6 +942,12 @@ fail: return r; } +static bool loc_size_ok(uint8_t size) { + uint8_t m = size >> 4, e = size & 0xF; + + return m <= 9 && e <= 9 && (m > 0 || e == 0); +} + int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; @@ -844,6 +965,12 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; + if (key->class == DNS_CLASS_ANY || + key->type == DNS_TYPE_ANY) { + r = -EBADMSG; + goto fail; + } + rr = dns_resource_record_new(key); if (!rr) { r = -ENOMEM; @@ -867,9 +994,23 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { switch (rr->key->type) { + case DNS_TYPE_SRV: + r = dns_packet_read_uint16(p, &rr->srv.priority, NULL); + if (r < 0) + goto fail; + r = dns_packet_read_uint16(p, &rr->srv.weight, NULL); + if (r < 0) + goto fail; + r = dns_packet_read_uint16(p, &rr->srv.port, NULL); + if (r < 0) + goto fail; + r = dns_packet_read_name(p, &rr->srv.name, NULL); + break; + case DNS_TYPE_PTR: case DNS_TYPE_NS: case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: r = dns_packet_read_name(p, &rr->ptr.name, NULL); break; @@ -881,6 +1022,24 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { r = dns_packet_read_string(p, &rr->hinfo.os, NULL); break; + case DNS_TYPE_SPF: /* exactly the same as TXT */ + case DNS_TYPE_TXT: { + char *s; + + while (p->rindex < offset + rdlength) { + r = dns_packet_read_string(p, &s, NULL); + if (r < 0) + goto fail; + + r = strv_consume(&rr->txt.strings, s); + if (r < 0) + goto fail; + } + + r = 0; + break; + } + case DNS_TYPE_A: r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); break; @@ -918,11 +1077,96 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { break; case DNS_TYPE_MX: - case DNS_TYPE_TXT: - case DNS_TYPE_SRV: - case DNS_TYPE_DNAME: + r = dns_packet_read_uint16(p, &rr->mx.priority, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_name(p, &rr->mx.exchange, NULL); + break; + + case DNS_TYPE_LOC: { + uint8_t t; + size_t pos; + + r = dns_packet_read_uint8(p, &t, &pos); + if (r < 0) + goto fail; + + if (t == 0) { + rr->loc.version = t; + + r = dns_packet_read_uint8(p, &rr->loc.size, NULL); + if (r < 0) + goto fail; + + if (!loc_size_ok(rr->loc.size)) { + r = -EBADMSG; + goto fail; + } + + r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL); + if (r < 0) + goto fail; + + if (!loc_size_ok(rr->loc.horiz_pre)) { + r = -EBADMSG; + goto fail; + } + + r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL); + if (r < 0) + goto fail; + + if (!loc_size_ok(rr->loc.vert_pre)) { + r = -EBADMSG; + goto fail; + } + + r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL); + if (r < 0) + goto fail; + + break; + } else { + dns_packet_rewind(p, pos); + rr->unparseable = true; + goto unparseable; + } + } + case DNS_TYPE_SSHFP: + + r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read(p, rdlength - 2, &d, NULL); + if (r < 0) + goto fail; + + rr->sshfp.key = memdup(d, rdlength - 2); + if (!rr->sshfp.key) { + r = -ENOMEM; + goto fail; + } + + rr->sshfp.key_size = rdlength - 2; + break; + default: + unparseable: r = dns_packet_read(p, rdlength, &d, NULL); if (r < 0) goto fail;