1 /* $OpenBSD: asr_utils.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */ 2 /* 3 * Copyright (c) 2009-2012 Eric Faurot <eric@faurot.net> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 21 #include <netinet/in.h> 22 #include <arpa/inet.h> 23 #include <arpa/nameser.h> 24 25 #include <ctype.h> 26 #include <errno.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "asr.h" 32 #include "asr_private.h" 33 34 static int dname_check_label(const char*, size_t); 35 static ssize_t dname_expand(const unsigned char*, size_t, size_t, size_t*, 36 char *, size_t); 37 38 static int unpack_data(struct packed*, void*, size_t); 39 static int unpack_u16(struct packed*, uint16_t*); 40 static int unpack_u32(struct packed*, uint32_t*); 41 static int unpack_inaddr(struct packed*, struct in_addr*); 42 static int unpack_in6addr(struct packed*, struct in6_addr*); 43 static int unpack_dname(struct packed*, char*, size_t); 44 45 static int pack_data(struct packed*, const void*, size_t); 46 static int pack_u16(struct packed*, uint16_t); 47 static int pack_dname(struct packed*, const char*); 48 49 static int 50 dname_check_label(const char *s, size_t l) 51 { 52 if (l == 0 || l > 63) 53 return (-1); 54 55 for(l--; l; l--, s++) 56 if (!(isalnum(*s) || *s == '_' || *s == '-')) 57 return (-1); 58 59 return (0); 60 } 61 62 ssize_t 63 dname_from_fqdn(const char *str, char *dst, size_t max) 64 { 65 ssize_t res; 66 size_t l, n; 67 char *d; 68 69 res = 0; 70 71 /* special case: the root domain */ 72 if (str[0] == '.') { 73 if (str[1] != '\0') 74 return (-1); 75 if (dst && max >= 1) 76 *dst = '\0'; 77 return (1); 78 } 79 80 for(; *str; str = d + 1) { 81 82 d = strchr(str, '.'); 83 if (d == NULL || d == str) 84 return (-1); 85 86 l = (d - str); 87 88 if (dname_check_label(str, l) == -1) 89 return (-1); 90 91 res += l + 1; 92 93 if (dst) { 94 *dst++ = l; 95 max -= 1; 96 n = (l > max) ? max : l; 97 memmove(dst, str, n); 98 max -= n; 99 if (max == 0) 100 dst = NULL; 101 else 102 dst += n; 103 } 104 } 105 106 if (dst) 107 *dst++ = '\0'; 108 109 return (res + 1); 110 } 111 112 static ssize_t 113 dname_expand(const unsigned char *data, size_t len, size_t offset, 114 size_t *newoffset, char *dst, size_t max) 115 { 116 size_t n, count, end, ptr, start; 117 ssize_t res; 118 119 if (offset >= len) 120 return (-1); 121 122 res = 0; 123 end = start = offset; 124 125 for(; (n = data[offset]); ) { 126 if ((n & 0xc0) == 0xc0) { 127 if (offset + 2 > len) 128 return (-1); 129 ptr = 256 * (n & ~0xc0) + data[offset + 1]; 130 if (ptr >= start) 131 return (-1); 132 if (end < offset + 2) 133 end = offset + 2; 134 offset = ptr; 135 continue; 136 } 137 if (offset + n + 1 > len) 138 return (-1); 139 140 if (dname_check_label(data + offset + 1, n) == -1) 141 return (-1); 142 143 /* copy n + at offset+1 */ 144 if (dst != NULL && max != 0) { 145 count = (max < n + 1) ? (max) : (n + 1); 146 memmove(dst, data + offset, count); 147 dst += count; 148 max -= count; 149 } 150 res += n + 1; 151 offset += n + 1; 152 if (end < offset) 153 end = offset; 154 } 155 if (end < offset + 1) 156 end = offset + 1; 157 158 if (dst != NULL && max != 0) 159 dst[0] = 0; 160 if (newoffset) 161 *newoffset = end; 162 return (res + 1); 163 } 164 165 void 166 packed_init(struct packed *pack, char *data, size_t len) 167 { 168 pack->data = data; 169 pack->len = len; 170 pack->offset = 0; 171 pack->err = NULL; 172 } 173 174 static int 175 unpack_data(struct packed *p, void *data, size_t len) 176 { 177 if (p->err) 178 return (-1); 179 180 if (p->len - p->offset < len) { 181 p->err = "too short"; 182 return (-1); 183 } 184 185 memmove(data, p->data + p->offset, len); 186 p->offset += len; 187 188 return (0); 189 } 190 191 static int 192 unpack_u16(struct packed *p, uint16_t *u16) 193 { 194 if (unpack_data(p, u16, 2) == -1) 195 return (-1); 196 197 *u16 = ntohs(*u16); 198 199 return (0); 200 } 201 202 static int 203 unpack_u32(struct packed *p, uint32_t *u32) 204 { 205 if (unpack_data(p, u32, 4) == -1) 206 return (-1); 207 208 *u32 = ntohl(*u32); 209 210 return (0); 211 } 212 213 static int 214 unpack_inaddr(struct packed *p, struct in_addr *a) 215 { 216 return (unpack_data(p, a, 4)); 217 } 218 219 static int 220 unpack_in6addr(struct packed *p, struct in6_addr *a6) 221 { 222 return (unpack_data(p, a6, 16)); 223 } 224 225 static int 226 unpack_dname(struct packed *p, char *dst, size_t max) 227 { 228 ssize_t e; 229 230 if (p->err) 231 return (-1); 232 233 e = dname_expand(p->data, p->len, p->offset, &p->offset, dst, max); 234 if (e == -1) { 235 p->err = "bad domain name"; 236 return (-1); 237 } 238 if (e < 0 || e > MAXDNAME) { 239 p->err = "domain name too long"; 240 return (-1); 241 } 242 243 return (0); 244 } 245 246 int 247 unpack_header(struct packed *p, struct header *h) 248 { 249 if (unpack_data(p, h, HFIXEDSZ) == -1) 250 return (-1); 251 252 h->flags = ntohs(h->flags); 253 h->qdcount = ntohs(h->qdcount); 254 h->ancount = ntohs(h->ancount); 255 h->nscount = ntohs(h->nscount); 256 h->arcount = ntohs(h->arcount); 257 258 return (0); 259 } 260 261 int 262 unpack_query(struct packed *p, struct query *q) 263 { 264 unpack_dname(p, q->q_dname, sizeof(q->q_dname)); 265 unpack_u16(p, &q->q_type); 266 unpack_u16(p, &q->q_class); 267 268 return (p->err) ? (-1) : (0); 269 } 270 271 int 272 unpack_rr(struct packed *p, struct rr *rr) 273 { 274 uint16_t rdlen; 275 size_t save_offset; 276 277 unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname)); 278 unpack_u16(p, &rr->rr_type); 279 unpack_u16(p, &rr->rr_class); 280 unpack_u32(p, &rr->rr_ttl); 281 unpack_u16(p, &rdlen); 282 283 if (p->err) 284 return (-1); 285 286 if (p->len - p->offset < rdlen) { 287 p->err = "too short"; 288 return (-1); 289 } 290 291 save_offset = p->offset; 292 293 switch(rr->rr_type) { 294 295 case T_CNAME: 296 unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname)); 297 break; 298 299 case T_MX: 300 unpack_u16(p, &rr->rr.mx.preference); 301 unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange)); 302 break; 303 304 case T_NS: 305 unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname)); 306 break; 307 308 case T_PTR: 309 unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname)); 310 break; 311 312 case T_SOA: 313 unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname)); 314 unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname)); 315 unpack_u32(p, &rr->rr.soa.serial); 316 unpack_u32(p, &rr->rr.soa.refresh); 317 unpack_u32(p, &rr->rr.soa.retry); 318 unpack_u32(p, &rr->rr.soa.expire); 319 unpack_u32(p, &rr->rr.soa.minimum); 320 break; 321 322 case T_A: 323 if (rr->rr_class != C_IN) 324 goto other; 325 unpack_inaddr(p, &rr->rr.in_a.addr); 326 break; 327 328 case T_AAAA: 329 if (rr->rr_class != C_IN) 330 goto other; 331 unpack_in6addr(p, &rr->rr.in_aaaa.addr6); 332 break; 333 default: 334 other: 335 rr->rr.other.rdata = p->data + p->offset; 336 rr->rr.other.rdlen = rdlen; 337 p->offset += rdlen; 338 } 339 340 if (p->err) 341 return (-1); 342 343 /* make sure that the advertised rdlen is really ok */ 344 if (p->offset - save_offset != rdlen) 345 p->err = "bad dlen"; 346 347 return (p->err) ? (-1) : (0); 348 } 349 350 static int 351 pack_data(struct packed *p, const void *data, size_t len) 352 { 353 if (p->err) 354 return (-1); 355 356 if (p->len < p->offset + len) { 357 p->err = "no space"; 358 return (-1); 359 } 360 361 memmove(p->data + p->offset, data, len); 362 p->offset += len; 363 364 return (0); 365 } 366 367 static int 368 pack_u16(struct packed *p, uint16_t v) 369 { 370 v = htons(v); 371 372 return (pack_data(p, &v, 2)); 373 } 374 375 static int 376 pack_dname(struct packed *p, const char *dname) 377 { 378 /* dname compression would be nice to have here. 379 * need additionnal context. 380 */ 381 return (pack_data(p, dname, strlen(dname) + 1)); 382 } 383 384 int 385 pack_header(struct packed *p, const struct header *h) 386 { 387 struct header c; 388 389 c.id = h->id; 390 c.flags = htons(h->flags); 391 c.qdcount = htons(h->qdcount); 392 c.ancount = htons(h->ancount); 393 c.nscount = htons(h->nscount); 394 c.arcount = htons(h->arcount); 395 396 return (pack_data(p, &c, HFIXEDSZ)); 397 } 398 399 int 400 pack_query(struct packed *p, uint16_t type, uint16_t class, const char *dname) 401 { 402 pack_dname(p, dname); 403 pack_u16(p, type); 404 pack_u16(p, class); 405 406 return (p->err) ? (-1) : (0); 407 } 408 409 int 410 sockaddr_from_str(struct sockaddr *sa, int family, const char *str) 411 { 412 struct in_addr ina; 413 struct in6_addr in6a; 414 struct sockaddr_in *sin; 415 struct sockaddr_in6 *sin6; 416 417 switch (family) { 418 case PF_UNSPEC: 419 if (sockaddr_from_str(sa, PF_INET, str) == 0) 420 return (0); 421 return sockaddr_from_str(sa, PF_INET6, str); 422 423 case PF_INET: 424 if (inet_pton(PF_INET, str, &ina) != 1) 425 return (-1); 426 427 sin = (struct sockaddr_in *)sa; 428 memset(sin, 0, sizeof *sin); 429 sin->sin_len = sizeof(struct sockaddr_in); 430 sin->sin_family = PF_INET; 431 sin->sin_addr.s_addr = ina.s_addr; 432 return (0); 433 434 case PF_INET6: 435 if (inet_pton(PF_INET6, str, &in6a) != 1) 436 return (-1); 437 438 sin6 = (struct sockaddr_in6 *)sa; 439 memset(sin6, 0, sizeof *sin6); 440 sin6->sin6_len = sizeof(struct sockaddr_in6); 441 sin6->sin6_family = PF_INET6; 442 sin6->sin6_addr = in6a; 443 return (0); 444 445 default: 446 break; 447 } 448 449 return (-1); 450 } 451