123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /* Copyright (c) 2014, Vsevolod Stakhov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- #include "rdns.h"
- #include "dns_private.h"
- #include "parse.h"
- #include "logger.h"
-
- static uint8_t *
- rdns_decompress_label (uint8_t *begin, uint16_t *len, uint16_t max)
- {
- uint16_t offset = (*len);
-
- if (offset > max) {
- return NULL;
- }
- *len = *(begin + offset);
- return begin + offset;
- }
-
- #define UNCOMPRESS_DNS_OFFSET(p) (((*(p)) ^ DNS_COMPRESSION_BITS) << 8) + *((p) + 1)
-
- uint8_t *
- rdns_request_reply_cmp (struct rdns_request *req, uint8_t *in, int len)
- {
- uint8_t *p, *c, *l1, *l2;
- uint16_t len1, len2;
- int decompressed = 0;
- struct rdns_resolver *resolver = req->resolver;
-
- /* QR format:
- * labels - len:octets
- * null label - 0
- * class - 2 octets
- * type - 2 octets
- */
-
- /* In p we would store current position in reply and in c - position in request */
- p = in;
- c = req->packet + req->pos;
-
- for (;;) {
- /* Get current label */
- len1 = *p;
- len2 = *c;
- if (p - in > len) {
- rdns_info ("invalid dns reply");
- return NULL;
- }
- /* This may be compressed, so we need to decompress it */
- if (len1 & DNS_COMPRESSION_BITS) {
- len1 = UNCOMPRESS_DNS_OFFSET(p);
- l1 = rdns_decompress_label (in, &len1, len);
- if (l1 == NULL) {
- return NULL;
- }
- decompressed ++;
- l1 ++;
- p += 2;
- }
- else {
- l1 = ++p;
- p += len1;
- }
- if (len2 & DNS_COMPRESSION_BITS) {
- len2 = UNCOMPRESS_DNS_OFFSET(c);
- l2 = rdns_decompress_label (c, &len2, len);
- if (l2 == NULL) {
- rdns_info ("invalid DNS pointer, cannot decompress");
- return NULL;
- }
- decompressed ++;
- l2 ++;
- c += 2;
- }
- else {
- l2 = ++c;
- c += len2;
- }
- if (len1 != len2) {
- return NULL;
- }
- if (len1 == 0) {
- break;
- }
-
- if (memcmp (l1, l2, len1) != 0) {
- return NULL;
- }
- if (decompressed == 2) {
- break;
- }
- }
-
- /* p now points to the end of QR section */
- /* Compare class and type */
- if (memcmp (p, c, sizeof (uint16_t) * 2) == 0) {
- req->pos = c - req->packet + sizeof (uint16_t) * 2;
- return p + sizeof (uint16_t) * 2;
- }
- return NULL;
- }
-
- #define MAX_RECURSION_LEVEL 10
-
- bool
- rdns_parse_labels (struct rdns_resolver *resolver,
- uint8_t *in, char **target, uint8_t **pos, struct rdns_reply *rep,
- int *remain, bool make_name)
- {
- uint16_t namelen = 0;
- uint8_t *p = *pos, *begin = *pos, *l, *t, *end = *pos + *remain, *new_pos = *pos;
- uint16_t llen;
- int length = *remain, new_remain = *remain;
- int ptrs = 0, labels = 0;
- bool got_compression = false;
-
- /* First go through labels and calculate name length */
- while (p - begin < length) {
- if (ptrs > MAX_RECURSION_LEVEL) {
- rdns_info ("dns pointers are nested too much");
- return false;
- }
- llen = *p;
- if (llen == 0) {
- if (!got_compression) {
- /* In case of compression we have already decremented the processing position */
- new_remain -= sizeof (uint8_t);
- new_pos += sizeof (uint8_t);
- }
- break;
- }
- else if ((llen & DNS_COMPRESSION_BITS)) {
- if (end - p > 1) {
- ptrs ++;
- llen = UNCOMPRESS_DNS_OFFSET(p);
- l = rdns_decompress_label (in, &llen, end - in);
- if (l == NULL) {
- rdns_info ("invalid DNS pointer");
- return false;
- }
- if (!got_compression) {
- /* Our label processing is finished actually */
- new_remain -= sizeof (uint16_t);
- new_pos += sizeof (uint16_t);
- got_compression = true;
- }
- if (l < in || l > begin + length) {
- rdns_info ("invalid pointer in DNS packet");
- return false;
- }
- begin = l;
- length = end - begin;
- p = l + *l + 1;
- namelen += *l;
- labels ++;
- }
- else {
- rdns_info ("DNS packet has incomplete compressed label, input length: %d bytes, remain: %d",
- *remain, new_remain);
- return false;
- }
- }
- else {
- namelen += llen;
- p += llen + 1;
- labels ++;
- if (!got_compression) {
- new_remain -= llen + 1;
- new_pos += llen + 1;
- }
- }
- }
-
- if (!make_name) {
- goto end;
- }
- *target = malloc (namelen + labels + 3);
- t = (uint8_t *)*target;
- p = *pos;
- begin = *pos;
- length = *remain;
- /* Now copy labels to name */
- while (p - begin < length) {
- llen = *p;
- if (llen == 0) {
- break;
- }
- else if (llen & DNS_COMPRESSION_BITS) {
- llen = UNCOMPRESS_DNS_OFFSET(p);
- l = rdns_decompress_label (in, &llen, end - in);
- begin = l;
- length = end - begin;
- p = l + *l + 1;
- memcpy (t, l + 1, *l);
- t += *l;
- *t ++ = '.';
- }
- else {
- memcpy (t, p + 1, *p);
- t += *p;
- *t ++ = '.';
- p += *p + 1;
- }
- }
- if (t > (uint8_t *)*target) {
- *(t - 1) = '\0';
- }
- else {
- /* Handle empty labels */
- **target = '\0';
- }
- end:
- *remain = new_remain;
- *pos = new_pos;
-
- return true;
- }
-
- #define GET8(x) do {(x) = ((*p)); p += sizeof (uint8_t); *remain -= sizeof (uint8_t); } while(0)
- #define GET16(x) do {(x) = ((*p) << 8) + *(p + 1); p += sizeof (uint16_t); *remain -= sizeof (uint16_t); } while(0)
- #define GET32(x) do {(x) = ((*p) << 24) + ((*(p + 1)) << 16) + ((*(p + 2)) << 8) + *(p + 3); p += sizeof (uint32_t); *remain -= sizeof (uint32_t); } while(0)
- #define SKIP(type) do { p += sizeof(type); *remain -= sizeof(type); } while (0)
-
- int
- rdns_parse_rr (struct rdns_resolver *resolver,
- uint8_t *in, struct rdns_reply_entry *elt, uint8_t **pos,
- struct rdns_reply *rep, int *remain)
- {
- uint8_t *p = *pos, parts;
- uint16_t type, datalen, txtlen, copied;
- int32_t ttl;
- bool parsed = false;
-
- /* Skip the whole name */
- if (! rdns_parse_labels (resolver, in, NULL, &p, rep, remain, false)) {
- rdns_info ("bad RR name");
- return -1;
- }
- if (*remain < (int)sizeof (uint16_t) * 6) {
- rdns_info ("stripped dns reply: %d bytes remain", *remain);
- return -1;
- }
- GET16 (type);
- /* Skip class */
- SKIP (uint16_t);
- GET32 (ttl);
- GET16 (datalen);
- elt->type = type;
- /* Now p points to RR data */
- switch (type) {
- case DNS_T_A:
- if (!(datalen & 0x3) && datalen <= *remain) {
- memcpy (&elt->content.a.addr, p, sizeof (struct in_addr));
- p += datalen;
- *remain -= datalen;
- parsed = true;
- }
- else {
- rdns_info ("corrupted A record");
- return -1;
- }
- break;
- case DNS_T_AAAA:
- if (datalen == sizeof (struct in6_addr) && datalen <= *remain) {
- memcpy (&elt->content.aaa.addr, p, sizeof (struct in6_addr));
- p += datalen;
- *remain -= datalen;
- parsed = true;
- }
- else {
- rdns_info ("corrupted AAAA record");
- return -1;
- }
- break;
- case DNS_T_PTR:
- if (! rdns_parse_labels (resolver, in, &elt->content.ptr.name, &p,
- rep, remain, true)) {
- rdns_info ("invalid labels in PTR record");
- return -1;
- }
- parsed = true;
- break;
- case DNS_T_NS:
- if (! rdns_parse_labels (resolver, in, &elt->content.ns.name, &p,
- rep, remain, true)) {
- rdns_info ("invalid labels in NS record");
- return -1;
- }
- parsed = true;
- break;
- case DNS_T_SOA:
- if (! rdns_parse_labels (resolver, in, &elt->content.soa.mname, &p,
- rep, remain, true)) {
- rdns_info ("invalid labels in NS record");
- return -1;
- }
- if (! rdns_parse_labels (resolver, in, &elt->content.soa.admin, &p,
- rep, remain, true)) {
- rdns_info ("invalid labels in NS record");
- return -1;
- }
- GET32 (elt->content.soa.serial);
- GET32 (elt->content.soa.refresh);
- GET32 (elt->content.soa.retry);
- GET32 (elt->content.soa.expire);
- GET32 (elt->content.soa.minimum);
- parsed = true;
- break;
- case DNS_T_MX:
- GET16 (elt->content.mx.priority);
- if (! rdns_parse_labels (resolver, in, &elt->content.mx.name, &p,
- rep, remain, true)) {
- rdns_info ("invalid labels in MX record");
- return -1;
- }
- parsed = true;
- break;
- case DNS_T_TXT:
- case DNS_T_SPF:
- elt->content.txt.data = malloc (datalen + 1);
- if (elt->content.txt.data == NULL) {
- rdns_err ("failed to allocate %d bytes for TXT record", (int)datalen + 1);
- return -1;
- }
- /* Now we should compose data from parts */
- copied = 0;
- parts = 0;
- while (copied + parts < datalen) {
- txtlen = *p;
- if (txtlen + copied + parts <= datalen) {
- parts ++;
- memcpy (elt->content.txt.data + copied, p + 1, txtlen);
- copied += txtlen;
- p += txtlen + 1;
- *remain -= txtlen + 1;
- }
- else {
- break;
- }
- }
- *(elt->content.txt.data + copied) = '\0';
- parsed = true;
- elt->type = RDNS_REQUEST_TXT;
- break;
- case DNS_T_SRV:
- if (p - *pos > (int)(*remain - sizeof (uint16_t) * 3)) {
- rdns_info ("stripped dns reply while reading SRV record");
- return -1;
- }
- GET16 (elt->content.srv.priority);
- GET16 (elt->content.srv.weight);
- GET16 (elt->content.srv.port);
- if (! rdns_parse_labels (resolver, in, &elt->content.srv.target,
- &p, rep, remain, true)) {
- rdns_info ("invalid labels in SRV record");
- return -1;
- }
- parsed = true;
- break;
- case DNS_T_TLSA:
- if (p - *pos > (int)(*remain - sizeof (uint8_t) * 3) || datalen <= 3) {
- rdns_info ("stripped dns reply while reading TLSA record");
- return -1;
- }
- GET8 (elt->content.tlsa.usage);
- GET8 (elt->content.tlsa.selector);
- GET8 (elt->content.tlsa.match_type);
- datalen -= 3;
- elt->content.tlsa.data = malloc (datalen);
- if (elt->content.tlsa.data == NULL) {
- rdns_err ("failed to allocate %d bytes for TLSA record", (int)datalen + 1);
- return -1;
- }
- elt->content.tlsa.datalen = datalen;
- memcpy (elt->content.tlsa.data, p, datalen);
- p += datalen;
- *remain -= datalen;
- parsed = true;
- break;
- case DNS_T_CNAME:
- /* Skip cname records */
- p += datalen;
- *remain -= datalen;
- break;
- default:
- rdns_debug ("unexpected RR type: %d", type);
- p += datalen;
- *remain -= datalen;
- break;
- }
- *pos = p;
-
- if (parsed) {
- elt->ttl = ttl;
- return 1;
- }
- return 0;
- }
|