1/* $OpenBSD: unpack_dns.c,v 1.3 2022/01/20 14:18:10 naddy Exp $ */ 2 3/* 4 * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <string.h> 20 21#include "unpack_dns.h" 22 23static int unpack_data(struct unpack *, void *, size_t); 24static int unpack_u16(struct unpack *, uint16_t *); 25static int unpack_u32(struct unpack *, uint32_t *); 26static int unpack_inaddr(struct unpack *, struct in_addr *); 27static int unpack_in6addr(struct unpack *, struct in6_addr *); 28static int unpack_dname(struct unpack *, char *, size_t); 29 30void 31unpack_init(struct unpack *unpack, const char *buf, size_t len) 32{ 33 unpack->buf = buf; 34 unpack->len = len; 35 unpack->offset = 0; 36 unpack->err = NULL; 37} 38 39int 40unpack_header(struct unpack *p, struct dns_header *h) 41{ 42 if (unpack_data(p, h, HFIXEDSZ) == -1) 43 return (-1); 44 45 h->flags = ntohs(h->flags); 46 h->qdcount = ntohs(h->qdcount); 47 h->ancount = ntohs(h->ancount); 48 h->nscount = ntohs(h->nscount); 49 h->arcount = ntohs(h->arcount); 50 51 return (0); 52} 53 54int 55unpack_query(struct unpack *p, struct dns_query *q) 56{ 57 unpack_dname(p, q->q_dname, sizeof(q->q_dname)); 58 unpack_u16(p, &q->q_type); 59 unpack_u16(p, &q->q_class); 60 61 return (p->err) ? (-1) : (0); 62} 63 64int 65unpack_rr(struct unpack *p, struct dns_rr *rr) 66{ 67 uint16_t rdlen; 68 size_t save_offset; 69 70 unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname)); 71 unpack_u16(p, &rr->rr_type); 72 unpack_u16(p, &rr->rr_class); 73 unpack_u32(p, &rr->rr_ttl); 74 unpack_u16(p, &rdlen); 75 76 if (p->err) 77 return (-1); 78 79 if (p->len - p->offset < rdlen) { 80 p->err = "too short"; 81 return (-1); 82 } 83 84 save_offset = p->offset; 85 86 switch (rr->rr_type) { 87 88 case T_CNAME: 89 unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname)); 90 break; 91 92 case T_MX: 93 unpack_u16(p, &rr->rr.mx.preference); 94 unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange)); 95 break; 96 97 case T_NS: 98 unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname)); 99 break; 100 101 case T_PTR: 102 unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname)); 103 break; 104 105 case T_SOA: 106 unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname)); 107 unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname)); 108 unpack_u32(p, &rr->rr.soa.serial); 109 unpack_u32(p, &rr->rr.soa.refresh); 110 unpack_u32(p, &rr->rr.soa.retry); 111 unpack_u32(p, &rr->rr.soa.expire); 112 unpack_u32(p, &rr->rr.soa.minimum); 113 break; 114 115 case T_A: 116 if (rr->rr_class != C_IN) 117 goto other; 118 unpack_inaddr(p, &rr->rr.in_a.addr); 119 break; 120 121 case T_AAAA: 122 if (rr->rr_class != C_IN) 123 goto other; 124 unpack_in6addr(p, &rr->rr.in_aaaa.addr6); 125 break; 126 default: 127 other: 128 rr->rr.other.rdata = p->buf + p->offset; 129 rr->rr.other.rdlen = rdlen; 130 p->offset += rdlen; 131 } 132 133 if (p->err) 134 return (-1); 135 136 /* make sure that the advertised rdlen is really ok */ 137 if (p->offset - save_offset != rdlen) 138 p->err = "bad dlen"; 139 140 return (p->err) ? (-1) : (0); 141} 142 143ssize_t 144dname_expand(const unsigned char *data, size_t len, size_t offset, 145 size_t *newoffset, char *dst, size_t max) 146{ 147 size_t n, count, end, ptr, start; 148 ssize_t res; 149 150 if (offset >= len) 151 return (-1); 152 153 res = 0; 154 end = start = offset; 155 156 for (; (n = data[offset]); ) { 157 if ((n & 0xc0) == 0xc0) { 158 if (offset + 2 > len) 159 return (-1); 160 ptr = 256 * (n & ~0xc0) + data[offset + 1]; 161 if (ptr >= start) 162 return (-1); 163 if (end < offset + 2) 164 end = offset + 2; 165 offset = start = ptr; 166 continue; 167 } 168 if (offset + n + 1 > len) 169 return (-1); 170 171 /* copy n + at offset+1 */ 172 if (dst != NULL && max != 0) { 173 count = (max < n + 1) ? (max) : (n + 1); 174 memmove(dst, data + offset, count); 175 dst += count; 176 max -= count; 177 } 178 res += n + 1; 179 offset += n + 1; 180 if (end < offset) 181 end = offset; 182 } 183 if (end < offset + 1) 184 end = offset + 1; 185 186 if (dst != NULL && max != 0) 187 dst[0] = 0; 188 if (newoffset) 189 *newoffset = end; 190 return (res + 1); 191} 192 193char * 194print_dname(const char *_dname, char *buf, size_t max) 195{ 196 const unsigned char *dname = _dname; 197 char *res; 198 size_t left, count; 199 200 if (_dname[0] == 0) { 201 (void)strlcpy(buf, ".", max); 202 return buf; 203 } 204 205 res = buf; 206 left = max - 1; 207 while (dname[0] && left) { 208 count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); 209 memmove(buf, dname + 1, count); 210 dname += dname[0] + 1; 211 left -= count; 212 buf += count; 213 if (left) { 214 left -= 1; 215 *buf++ = '.'; 216 } 217 } 218 buf[0] = 0; 219 220 return (res); 221} 222 223static int 224unpack_data(struct unpack *p, void *data, size_t len) 225{ 226 if (p->err) 227 return (-1); 228 229 if (p->len - p->offset < len) { 230 p->err = "too short"; 231 return (-1); 232 } 233 234 memmove(data, p->buf + p->offset, len); 235 p->offset += len; 236 237 return (0); 238} 239 240static int 241unpack_u16(struct unpack *p, uint16_t *u16) 242{ 243 if (unpack_data(p, u16, 2) == -1) 244 return (-1); 245 246 *u16 = ntohs(*u16); 247 248 return (0); 249} 250 251static int 252unpack_u32(struct unpack *p, uint32_t *u32) 253{ 254 if (unpack_data(p, u32, 4) == -1) 255 return (-1); 256 257 *u32 = ntohl(*u32); 258 259 return (0); 260} 261 262static int 263unpack_inaddr(struct unpack *p, struct in_addr *a) 264{ 265 return (unpack_data(p, a, 4)); 266} 267 268static int 269unpack_in6addr(struct unpack *p, struct in6_addr *a6) 270{ 271 return (unpack_data(p, a6, 16)); 272} 273 274static int 275unpack_dname(struct unpack *p, char *dst, size_t max) 276{ 277 ssize_t e; 278 279 if (p->err) 280 return (-1); 281 282 e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max); 283 if (e == -1) { 284 p->err = "bad domain name"; 285 return (-1); 286 } 287 if (e < 0 || e > MAXDNAME) { 288 p->err = "domain name too long"; 289 return (-1); 290 } 291 292 return (0); 293} 294