1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-02-12 21:57:27 +03:00

resolved: add cleanup function to rewind packets

This makes the behaviour more consistent. Before we would not rewind
after some errors, but this seems to have been an unintentional
omission.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2016-01-30 16:42:16 -05:00
parent 4ac2ca1bdb
commit e18a3c7314

View File

@ -30,6 +30,19 @@
#define EDNS0_OPT_DO (1<<15) #define EDNS0_OPT_DO (1<<15)
typedef struct DnsPacketRewinder {
DnsPacket *packet;
size_t saved_rindex;
} DnsPacketRewinder;
static void rewind_dns_packet(DnsPacketRewinder *rewinder) {
if (rewinder->packet)
dns_packet_rewind(rewinder->packet, rewinder->saved_rindex);
}
#define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while(0)
#define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while(0)
int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
DnsPacket *p; DnsPacket *p;
size_t a; size_t a;
@ -1231,80 +1244,67 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
} }
int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) { int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
size_t saved_rindex; _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
const void *d; const void *d;
char *t; char *t;
uint8_t c; uint8_t c;
int r; int r;
assert(p); assert(p);
INIT_REWINDER(rewinder, p);
saved_rindex = p->rindex;
r = dns_packet_read_uint8(p, &c, NULL); r = dns_packet_read_uint8(p, &c, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read(p, c, &d, NULL); r = dns_packet_read(p, c, &d, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (memchr(d, 0, c)) { if (memchr(d, 0, c))
r = -EBADMSG; return -EBADMSG;
goto fail;
}
t = strndup(d, c); t = strndup(d, c);
if (!t) { if (!t)
r = -ENOMEM; return -ENOMEM;
goto fail;
}
if (!utf8_is_valid(t)) { if (!utf8_is_valid(t)) {
free(t); free(t);
r = -EBADMSG; return -EBADMSG;
goto fail;
} }
*ret = t; *ret = t;
if (start) if (start)
*start = saved_rindex; *start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0; return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
} }
int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) { int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) {
size_t saved_rindex; _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
uint8_t c; uint8_t c;
int r; int r;
assert(p); assert(p);
INIT_REWINDER(rewinder, p);
saved_rindex = p->rindex;
r = dns_packet_read_uint8(p, &c, NULL); r = dns_packet_read_uint8(p, &c, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read(p, c, ret, NULL); r = dns_packet_read(p, c, ret, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (size) if (size)
*size = c; *size = c;
if (start) if (start)
*start = saved_rindex; *start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0; return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
} }
int dns_packet_read_name( int dns_packet_read_name(
@ -1313,7 +1313,8 @@ int dns_packet_read_name(
bool allow_compression, bool allow_compression,
size_t *start) { size_t *start) {
size_t saved_rindex, after_rindex = 0, jump_barrier; _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
size_t after_rindex = 0, jump_barrier;
_cleanup_free_ char *ret = NULL; _cleanup_free_ char *ret = NULL;
size_t n = 0, allocated = 0; size_t n = 0, allocated = 0;
bool first = true; bool first = true;
@ -1321,19 +1322,18 @@ int dns_packet_read_name(
assert(p); assert(p);
assert(_ret); assert(_ret);
INIT_REWINDER(rewinder, p);
jump_barrier = p->rindex;
if (p->refuse_compression) if (p->refuse_compression)
allow_compression = false; allow_compression = false;
saved_rindex = p->rindex;
jump_barrier = p->rindex;
for (;;) { for (;;) {
uint8_t c, d; uint8_t c, d;
r = dns_packet_read_uint8(p, &c, NULL); r = dns_packet_read_uint8(p, &c, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (c == 0) if (c == 0)
/* End of name */ /* End of name */
@ -1344,12 +1344,10 @@ int dns_packet_read_name(
/* Literal label */ /* Literal label */
r = dns_packet_read(p, c, (const void**) &label, NULL); r = dns_packet_read(p, c, (const void**) &label, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
r = -ENOMEM; return -ENOMEM;
goto fail;
}
if (first) if (first)
first = false; first = false;
@ -1358,7 +1356,7 @@ int dns_packet_read_name(
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
if (r < 0) if (r < 0)
goto fail; return r;
n += r; n += r;
continue; continue;
@ -1368,13 +1366,11 @@ int dns_packet_read_name(
/* Pointer */ /* Pointer */
r = dns_packet_read_uint8(p, &d, NULL); r = dns_packet_read_uint8(p, &d, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) { if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier)
r = -EBADMSG; return -EBADMSG;
goto fail;
}
if (after_rindex == 0) if (after_rindex == 0)
after_rindex = p->rindex; after_rindex = p->rindex;
@ -1382,16 +1378,12 @@ int dns_packet_read_name(
/* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */ /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
jump_barrier = ptr; jump_barrier = ptr;
p->rindex = ptr; p->rindex = ptr;
} else { } else
r = -EBADMSG; return -EBADMSG;
goto fail;
}
} }
if (!GREEDY_REALLOC(ret, allocated, n + 1)) { if (!GREEDY_REALLOC(ret, allocated, n + 1))
r = -ENOMEM; return -ENOMEM;
goto fail;
}
ret[n] = 0; ret[n] = 0;
@ -1402,13 +1394,10 @@ int dns_packet_read_name(
ret = NULL; ret = NULL;
if (start) if (start)
*start = saved_rindex; *start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0; return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
} }
static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) { static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) {
@ -1418,32 +1407,31 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
uint8_t bit = 0; uint8_t bit = 0;
unsigned i; unsigned i;
bool found = false; bool found = false;
size_t saved_rindex; _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
int r; int r;
assert(p); assert(p);
assert(types); assert(types);
INIT_REWINDER(rewinder, p);
saved_rindex = p->rindex;
r = bitmap_ensure_allocated(types); r = bitmap_ensure_allocated(types);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &window, NULL); r = dns_packet_read_uint8(p, &window, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &length, NULL); r = dns_packet_read_uint8(p, &length, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (length == 0 || length > 32) if (length == 0 || length > 32)
return -EBADMSG; return -EBADMSG;
r = dns_packet_read(p, length, (const void **)&bitmap, NULL); r = dns_packet_read(p, length, (const void **)&bitmap, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
uint8_t bitmask = 1 << 7; uint8_t bitmask = 1 << 7;
@ -1468,7 +1456,7 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
r = bitmap_set(*types, n); r = bitmap_set(*types, n);
if (r < 0) if (r < 0)
goto fail; return r;
} }
bit ++; bit ++;
@ -1480,70 +1468,61 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
return -EBADMSG; return -EBADMSG;
if (start) if (start)
*start = saved_rindex; *start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0; return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
} }
static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) { static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) {
size_t saved_rindex; _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
int r; int r;
saved_rindex = p->rindex; INIT_REWINDER(rewinder, p);
while (p->rindex < saved_rindex + size) { while (p->rindex < rewinder.saved_rindex + size) {
r = dns_packet_read_type_window(p, types, NULL); r = dns_packet_read_type_window(p, types, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
/* don't read past end of current RR */ /* don't read past end of current RR */
if (p->rindex > saved_rindex + size) { if (p->rindex > rewinder.saved_rindex + size)
r = -EBADMSG; return -EBADMSG;
goto fail;
}
} }
if (p->rindex != saved_rindex + size) { if (p->rindex != rewinder.saved_rindex + size)
r = -EBADMSG; return -EBADMSG;
goto fail;
}
if (start) if (start)
*start = saved_rindex; *start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0; return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
} }
int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) { int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
_cleanup_free_ char *name = NULL; _cleanup_free_ char *name = NULL;
bool cache_flush = false; bool cache_flush = false;
uint16_t class, type; uint16_t class, type;
DnsResourceKey *key; DnsResourceKey *key;
size_t saved_rindex;
int r; int r;
assert(p); assert(p);
assert(ret); assert(ret);
INIT_REWINDER(rewinder, p);
saved_rindex = p->rindex;
r = dns_packet_read_name(p, &name, true, NULL); r = dns_packet_read_name(p, &name, true, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint16(p, &type, NULL); r = dns_packet_read_uint16(p, &type, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint16(p, &class, NULL); r = dns_packet_read_uint16(p, &class, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (p->protocol == DNS_PROTOCOL_MDNS) { if (p->protocol == DNS_PROTOCOL_MDNS) {
/* See RFC6762, Section 10.2 */ /* See RFC6762, Section 10.2 */
@ -1555,10 +1534,8 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus
} }
key = dns_resource_key_new_consume(class, type, name); key = dns_resource_key_new_consume(class, type, name);
if (!key) { if (!key)
r = -ENOMEM; return -ENOMEM;
goto fail;
}
name = NULL; name = NULL;
*ret = key; *ret = key;
@ -1566,12 +1543,10 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus
if (ret_cache_flush) if (ret_cache_flush)
*ret_cache_flush = cache_flush; *ret_cache_flush = cache_flush;
if (start) if (start)
*start = saved_rindex; *start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0; return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
} }
static bool loc_size_ok(uint8_t size) { static bool loc_size_ok(uint8_t size) {
@ -1583,7 +1558,8 @@ static bool loc_size_ok(uint8_t size) {
int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) { int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
size_t saved_rindex, offset; _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
size_t offset;
uint16_t rdlength; uint16_t rdlength;
bool cache_flush; bool cache_flush;
int r; int r;
@ -1591,27 +1567,22 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
assert(p); assert(p);
assert(ret); assert(ret);
saved_rindex = p->rindex; INIT_REWINDER(rewinder, p);
r = dns_packet_read_key(p, &key, &cache_flush, NULL); r = dns_packet_read_key(p, &key, &cache_flush, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (!dns_class_is_valid_rr(key->class)|| if (!dns_class_is_valid_rr(key->class) || !dns_type_is_valid_rr(key->type))
!dns_type_is_valid_rr(key->type)) { return -EBADMSG;
r = -EBADMSG;
goto fail;
}
rr = dns_resource_record_new(key); rr = dns_resource_record_new(key);
if (!rr) { if (!rr)
r = -ENOMEM; return -ENOMEM;
goto fail;
}
r = dns_packet_read_uint32(p, &rr->ttl, NULL); r = dns_packet_read_uint32(p, &rr->ttl, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
/* RFC 2181, Section 8, suggests to /* RFC 2181, Section 8, suggests to
* treat a TTL with the MSB set as a zero TTL. */ * treat a TTL with the MSB set as a zero TTL. */
@ -1620,12 +1591,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_uint16(p, &rdlength, NULL); r = dns_packet_read_uint16(p, &rdlength, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (p->rindex + rdlength > p->size) { if (p->rindex + rdlength > p->size)
r = -EBADMSG; return -EBADMSG;
goto fail;
}
offset = p->rindex; offset = p->rindex;
@ -1634,13 +1603,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_SRV: case DNS_TYPE_SRV:
r = dns_packet_read_uint16(p, &rr->srv.priority, NULL); r = dns_packet_read_uint16(p, &rr->srv.priority, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint16(p, &rr->srv.weight, NULL); r = dns_packet_read_uint16(p, &rr->srv.weight, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint16(p, &rr->srv.port, NULL); r = dns_packet_read_uint16(p, &rr->srv.port, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_name(p, &rr->srv.name, true, NULL); r = dns_packet_read_name(p, &rr->srv.name, true, NULL);
break; break;
@ -1654,7 +1623,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_HINFO: case DNS_TYPE_HINFO:
r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL); r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_string(p, &rr->hinfo.os, NULL); r = dns_packet_read_string(p, &rr->hinfo.os, NULL);
break; break;
@ -1710,27 +1679,27 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_SOA: case DNS_TYPE_SOA:
r = dns_packet_read_name(p, &rr->soa.mname, true, NULL); r = dns_packet_read_name(p, &rr->soa.mname, true, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_name(p, &rr->soa.rname, true, NULL); r = dns_packet_read_name(p, &rr->soa.rname, true, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->soa.serial, NULL); r = dns_packet_read_uint32(p, &rr->soa.serial, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL); r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->soa.retry, NULL); r = dns_packet_read_uint32(p, &rr->soa.retry, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->soa.expire, NULL); r = dns_packet_read_uint32(p, &rr->soa.expire, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL); r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL);
break; break;
@ -1738,7 +1707,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_MX: case DNS_TYPE_MX:
r = dns_packet_read_uint16(p, &rr->mx.priority, NULL); r = dns_packet_read_uint16(p, &rr->mx.priority, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL); r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL);
break; break;
@ -1749,49 +1718,43 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_uint8(p, &t, &pos); r = dns_packet_read_uint8(p, &t, &pos);
if (r < 0) if (r < 0)
goto fail; return r;
if (t == 0) { if (t == 0) {
rr->loc.version = t; rr->loc.version = t;
r = dns_packet_read_uint8(p, &rr->loc.size, NULL); r = dns_packet_read_uint8(p, &rr->loc.size, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (!loc_size_ok(rr->loc.size)) { if (!loc_size_ok(rr->loc.size))
r = -EBADMSG; return -EBADMSG;
goto fail;
}
r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL); r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (!loc_size_ok(rr->loc.horiz_pre)) { if (!loc_size_ok(rr->loc.horiz_pre))
r = -EBADMSG; return -EBADMSG;
goto fail;
}
r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL); r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (!loc_size_ok(rr->loc.vert_pre)) { if (!loc_size_ok(rr->loc.vert_pre))
r = -EBADMSG; return -EBADMSG;
goto fail;
}
r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL); r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL); r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL); r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
break; break;
} else { } else {
@ -1804,122 +1767,114 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_DS: case DNS_TYPE_DS:
r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL); r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL); r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL); r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_memdup(p, rdlength - 4, r = dns_packet_read_memdup(p, rdlength - 4,
&rr->ds.digest, &rr->ds.digest_size, &rr->ds.digest, &rr->ds.digest_size,
NULL); NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (rr->ds.digest_size <= 0) { if (rr->ds.digest_size <= 0)
/* the accepted size depends on the algorithm, but for now /* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */ just ensure that the value is greater than zero */
r = -EBADMSG; return -EBADMSG;
goto fail;
}
break; break;
case DNS_TYPE_SSHFP: case DNS_TYPE_SSHFP:
r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL); r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL); r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_memdup(p, rdlength - 2, r = dns_packet_read_memdup(p, rdlength - 2,
&rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size, &rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size,
NULL); NULL);
if (rr->sshfp.fingerprint_size <= 0) { if (rr->sshfp.fingerprint_size <= 0)
/* the accepted size depends on the algorithm, but for now /* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */ just ensure that the value is greater than zero */
r = -EBADMSG; return -EBADMSG;
goto fail;
}
break; break;
case DNS_TYPE_DNSKEY: case DNS_TYPE_DNSKEY:
r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL); r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL); r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL); r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_memdup(p, rdlength - 4, r = dns_packet_read_memdup(p, rdlength - 4,
&rr->dnskey.key, &rr->dnskey.key_size, &rr->dnskey.key, &rr->dnskey.key_size,
NULL); NULL);
if (rr->dnskey.key_size <= 0) { if (rr->dnskey.key_size <= 0)
/* the accepted size depends on the algorithm, but for now /* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */ just ensure that the value is greater than zero */
r = -EBADMSG; return -EBADMSG;
goto fail;
}
break; break;
case DNS_TYPE_RRSIG: case DNS_TYPE_RRSIG:
r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL); r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL); r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL); r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL); r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL); r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL); r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL); r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL); r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_memdup(p, offset + rdlength - p->rindex, r = dns_packet_read_memdup(p, offset + rdlength - p->rindex,
&rr->rrsig.signature, &rr->rrsig.signature_size, &rr->rrsig.signature, &rr->rrsig.signature_size,
NULL); NULL);
if (rr->rrsig.signature_size <= 0) { if (rr->rrsig.signature_size <= 0)
/* the accepted size depends on the algorithm, but for now /* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */ just ensure that the value is greater than zero */
r = -EBADMSG; return -EBADMSG;
goto fail;
}
break; break;
@ -1934,11 +1889,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL); r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL); r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL);
if (r < 0)
goto fail;
/* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself /* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself
* is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records * is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records
@ -1951,41 +1904,39 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL); r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL); r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL); r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
/* this may be zero */ /* this may be zero */
r = dns_packet_read_uint8(p, &size, NULL); r = dns_packet_read_uint8(p, &size, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL); r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &size, NULL); r = dns_packet_read_uint8(p, &size, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
if (size <= 0) { if (size <= 0)
r = -EBADMSG; return -EBADMSG;
goto fail;
}
r = dns_packet_read_memdup(p, size, &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, NULL); r = dns_packet_read_memdup(p, size,
&rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size,
NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL); r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL);
if (r < 0)
goto fail;
/* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */ /* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */
@ -1995,25 +1946,24 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_TLSA: case DNS_TYPE_TLSA:
r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL); r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL); r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL); r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL);
if (r < 0) if (r < 0)
goto fail; return r;
r = dns_packet_read_memdup(p, rdlength - 3, r = dns_packet_read_memdup(p, rdlength - 3,
&rr->tlsa.data, &rr->tlsa.data_size, &rr->tlsa.data, &rr->tlsa.data_size,
NULL); NULL);
if (rr->tlsa.data_size <= 0) {
if (rr->tlsa.data_size <= 0)
/* the accepted size depends on the algorithm, but for now /* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */ just ensure that the value is greater than zero */
r = -EBADMSG; return -EBADMSG;
goto fail;
}
break; break;
@ -2022,16 +1972,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
default: default:
unparseable: unparseable:
r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL); r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL);
if (r < 0)
goto fail;
break; break;
} }
if (r < 0) if (r < 0)
goto fail; return r;
if (p->rindex != offset + rdlength) { if (p->rindex != offset + rdlength)
r = -EBADMSG; return -EBADMSG;
goto fail;
}
*ret = rr; *ret = rr;
rr = NULL; rr = NULL;
@ -2039,12 +1986,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (ret_cache_flush) if (ret_cache_flush)
*ret_cache_flush = cache_flush; *ret_cache_flush = cache_flush;
if (start) if (start)
*start = saved_rindex; *start = rewinder.saved_rindex;
CANCEL_REWINDER(rewinder);
return 0; return 0;
fail:
dns_packet_rewind(p, saved_rindex);
return r;
} }
static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) { static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
@ -2092,23 +2037,21 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
int dns_packet_extract(DnsPacket *p) { int dns_packet_extract(DnsPacket *p) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
size_t saved_rindex; _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {};
unsigned n, i; unsigned n, i;
int r; int r;
if (p->extracted) if (p->extracted)
return 0; return 0;
saved_rindex = p->rindex; INIT_REWINDER(rewinder, p);
dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE); dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
n = DNS_PACKET_QDCOUNT(p); n = DNS_PACKET_QDCOUNT(p);
if (n > 0) { if (n > 0) {
question = dns_question_new(n); question = dns_question_new(n);
if (!question) { if (!question)
r = -ENOMEM; return -ENOMEM;
goto finish;
}
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
@ -2116,21 +2059,17 @@ int dns_packet_extract(DnsPacket *p) {
r = dns_packet_read_key(p, &key, &cache_flush, NULL); r = dns_packet_read_key(p, &key, &cache_flush, NULL);
if (r < 0) if (r < 0)
goto finish; return r;
if (cache_flush) { if (cache_flush)
r = -EBADMSG; return -EBADMSG;
goto finish;
}
if (!dns_type_is_valid_query(key->type)) { if (!dns_type_is_valid_query(key->type))
r = -EBADMSG; return -EBADMSG;
goto finish;
}
r = dns_question_add(question, key); r = dns_question_add(question, key);
if (r < 0) if (r < 0)
goto finish; return r;
} }
} }
@ -2140,10 +2079,8 @@ int dns_packet_extract(DnsPacket *p) {
bool bad_opt = false; bool bad_opt = false;
answer = dns_answer_new(n); answer = dns_answer_new(n);
if (!answer) { if (!answer)
r = -ENOMEM; return -ENOMEM;
goto finish;
}
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
@ -2151,7 +2088,7 @@ int dns_packet_extract(DnsPacket *p) {
r = dns_packet_read_rr(p, &rr, &cache_flush, NULL); r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
if (r < 0) if (r < 0)
goto finish; return r;
/* Try to reduce memory usage a bit */ /* Try to reduce memory usage a bit */
if (previous) if (previous)
@ -2214,7 +2151,7 @@ int dns_packet_extract(DnsPacket *p) {
(i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) | (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
(p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0)); (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
if (r < 0) if (r < 0)
goto finish; return r;
} }
/* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note
@ -2235,11 +2172,8 @@ int dns_packet_extract(DnsPacket *p) {
p->extracted = true; p->extracted = true;
r = 0; /* no CANCEL, always rewind */
return 0;
finish:
p->rindex = saved_rindex;
return r;
} }
int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) { int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) {