1 /* $OpenBSD: asr_utils.c,v 1.11 2014/03/14 11:07:33 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 #include <net/if.h> 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 <stdint.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "asr.h" 34 #include "asr_private.h" 35 36 static int dname_check_label(const char *, size_t); 37 static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *, 38 char *, size_t); 39 40 static int unpack_data(struct asr_unpack *, void *, size_t); 41 static int unpack_u16(struct asr_unpack *, uint16_t *); 42 static int unpack_u32(struct asr_unpack *, uint32_t *); 43 static int unpack_inaddr(struct asr_unpack *, struct in_addr *); 44 static int unpack_in6addr(struct asr_unpack *, struct in6_addr *); 45 static int unpack_dname(struct asr_unpack *, char *, size_t); 46 47 static int pack_data(struct asr_pack *, const void *, size_t); 48 static int pack_u16(struct asr_pack *, uint16_t); 49 static int pack_dname(struct asr_pack *, const char *); 50 51 static int 52 dname_check_label(const char *s, size_t l) 53 { 54 if (l == 0 || l > 63) 55 return (-1); 56 57 return (0); 58 } 59 60 ssize_t 61 asr_dname_from_fqdn(const char *str, char *dst, size_t max) 62 { 63 ssize_t res; 64 size_t l, n; 65 char *d; 66 67 res = 0; 68 69 /* special case: the root domain */ 70 if (str[0] == '.') { 71 if (str[1] != '\0') 72 return (-1); 73 if (dst && max >= 1) 74 *dst = '\0'; 75 return (1); 76 } 77 78 for (; *str; str = d + 1) { 79 80 d = strchr(str, '.'); 81 if (d == NULL || d == str) 82 return (-1); 83 84 l = (d - str); 85 86 if (dname_check_label(str, l) == -1) 87 return (-1); 88 89 res += l + 1; 90 91 if (dst) { 92 *dst++ = l; 93 max -= 1; 94 n = (l > max) ? max : l; 95 memmove(dst, str, n); 96 max -= n; 97 if (max == 0) 98 dst = NULL; 99 else 100 dst += n; 101 } 102 } 103 104 if (dst) 105 *dst++ = '\0'; 106 107 return (res + 1); 108 } 109 110 static ssize_t 111 dname_expand(const unsigned char *data, size_t len, size_t offset, 112 size_t *newoffset, char *dst, size_t max) 113 { 114 size_t n, count, end, ptr, start; 115 ssize_t res; 116 117 if (offset >= len) 118 return (-1); 119 120 res = 0; 121 end = start = offset; 122 123 for (; (n = data[offset]); ) { 124 if ((n & 0xc0) == 0xc0) { 125 if (offset + 2 > len) 126 return (-1); 127 ptr = 256 * (n & ~0xc0) + data[offset + 1]; 128 if (ptr >= start) 129 return (-1); 130 if (end < offset + 2) 131 end = offset + 2; 132 offset = start = ptr; 133 continue; 134 } 135 if (offset + n + 1 > len) 136 return (-1); 137 138 if (dname_check_label(data + offset + 1, n) == -1) 139 return (-1); 140 141 /* copy n + at offset+1 */ 142 if (dst != NULL && max != 0) { 143 count = (max < n + 1) ? (max) : (n + 1); 144 memmove(dst, data + offset, count); 145 dst += count; 146 max -= count; 147 } 148 res += n + 1; 149 offset += n + 1; 150 if (end < offset) 151 end = offset; 152 } 153 if (end < offset + 1) 154 end = offset + 1; 155 156 if (dst != NULL && max != 0) 157 dst[0] = 0; 158 if (newoffset) 159 *newoffset = end; 160 return (res + 1); 161 } 162 163 void 164 asr_pack_init(struct asr_pack *pack, char *buf, size_t len) 165 { 166 pack->buf = buf; 167 pack->len = len; 168 pack->offset = 0; 169 pack->err = NULL; 170 } 171 172 void 173 asr_unpack_init(struct asr_unpack *unpack, const char *buf, size_t len) 174 { 175 unpack->buf = buf; 176 unpack->len = len; 177 unpack->offset = 0; 178 unpack->err = NULL; 179 } 180 181 static int 182 unpack_data(struct asr_unpack *p, void *data, size_t len) 183 { 184 if (p->err) 185 return (-1); 186 187 if (p->len - p->offset < len) { 188 p->err = "too short"; 189 return (-1); 190 } 191 192 memmove(data, p->buf + p->offset, len); 193 p->offset += len; 194 195 return (0); 196 } 197 198 static int 199 unpack_u16(struct asr_unpack *p, uint16_t *u16) 200 { 201 if (unpack_data(p, u16, 2) == -1) 202 return (-1); 203 204 *u16 = ntohs(*u16); 205 206 return (0); 207 } 208 209 static int 210 unpack_u32(struct asr_unpack *p, uint32_t *u32) 211 { 212 if (unpack_data(p, u32, 4) == -1) 213 return (-1); 214 215 *u32 = ntohl(*u32); 216 217 return (0); 218 } 219 220 static int 221 unpack_inaddr(struct asr_unpack *p, struct in_addr *a) 222 { 223 return (unpack_data(p, a, 4)); 224 } 225 226 static int 227 unpack_in6addr(struct asr_unpack *p, struct in6_addr *a6) 228 { 229 return (unpack_data(p, a6, 16)); 230 } 231 232 static int 233 unpack_dname(struct asr_unpack *p, char *dst, size_t max) 234 { 235 ssize_t e; 236 237 if (p->err) 238 return (-1); 239 240 e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max); 241 if (e == -1) { 242 p->err = "bad domain name"; 243 return (-1); 244 } 245 if (e < 0 || e > MAXDNAME) { 246 p->err = "domain name too long"; 247 return (-1); 248 } 249 250 return (0); 251 } 252 253 int 254 asr_unpack_header(struct asr_unpack *p, struct asr_dns_header *h) 255 { 256 if (unpack_data(p, h, HFIXEDSZ) == -1) 257 return (-1); 258 259 h->flags = ntohs(h->flags); 260 h->qdcount = ntohs(h->qdcount); 261 h->ancount = ntohs(h->ancount); 262 h->nscount = ntohs(h->nscount); 263 h->arcount = ntohs(h->arcount); 264 265 return (0); 266 } 267 268 int 269 asr_unpack_query(struct asr_unpack *p, struct asr_dns_query *q) 270 { 271 unpack_dname(p, q->q_dname, sizeof(q->q_dname)); 272 unpack_u16(p, &q->q_type); 273 unpack_u16(p, &q->q_class); 274 275 return (p->err) ? (-1) : (0); 276 } 277 278 int 279 asr_unpack_rr(struct asr_unpack *p, struct asr_dns_rr *rr) 280 { 281 uint16_t rdlen; 282 size_t save_offset; 283 284 unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname)); 285 unpack_u16(p, &rr->rr_type); 286 unpack_u16(p, &rr->rr_class); 287 unpack_u32(p, &rr->rr_ttl); 288 unpack_u16(p, &rdlen); 289 290 if (p->err) 291 return (-1); 292 293 if (p->len - p->offset < rdlen) { 294 p->err = "too short"; 295 return (-1); 296 } 297 298 save_offset = p->offset; 299 300 switch (rr->rr_type) { 301 302 case T_CNAME: 303 unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname)); 304 break; 305 306 case T_MX: 307 unpack_u16(p, &rr->rr.mx.preference); 308 unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange)); 309 break; 310 311 case T_NS: 312 unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname)); 313 break; 314 315 case T_PTR: 316 unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname)); 317 break; 318 319 case T_SOA: 320 unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname)); 321 unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname)); 322 unpack_u32(p, &rr->rr.soa.serial); 323 unpack_u32(p, &rr->rr.soa.refresh); 324 unpack_u32(p, &rr->rr.soa.retry); 325 unpack_u32(p, &rr->rr.soa.expire); 326 unpack_u32(p, &rr->rr.soa.minimum); 327 break; 328 329 case T_A: 330 if (rr->rr_class != C_IN) 331 goto other; 332 unpack_inaddr(p, &rr->rr.in_a.addr); 333 break; 334 335 case T_AAAA: 336 if (rr->rr_class != C_IN) 337 goto other; 338 unpack_in6addr(p, &rr->rr.in_aaaa.addr6); 339 break; 340 default: 341 other: 342 rr->rr.other.rdata = p->buf + p->offset; 343 rr->rr.other.rdlen = rdlen; 344 p->offset += rdlen; 345 } 346 347 if (p->err) 348 return (-1); 349 350 /* make sure that the advertised rdlen is really ok */ 351 if (p->offset - save_offset != rdlen) 352 p->err = "bad dlen"; 353 354 return (p->err) ? (-1) : (0); 355 } 356 357 static int 358 pack_data(struct asr_pack *p, const void *data, size_t len) 359 { 360 if (p->err) 361 return (-1); 362 363 if (p->len < p->offset + len) { 364 p->err = "no space"; 365 return (-1); 366 } 367 368 memmove(p->buf + p->offset, data, len); 369 p->offset += len; 370 371 return (0); 372 } 373 374 static int 375 pack_u16(struct asr_pack *p, uint16_t v) 376 { 377 v = htons(v); 378 379 return (pack_data(p, &v, 2)); 380 } 381 382 static int 383 pack_dname(struct asr_pack *p, const char *dname) 384 { 385 /* dname compression would be nice to have here. 386 * need additionnal context. 387 */ 388 return (pack_data(p, dname, strlen(dname) + 1)); 389 } 390 391 int 392 asr_pack_header(struct asr_pack *p, const struct asr_dns_header *h) 393 { 394 struct asr_dns_header c; 395 396 c.id = h->id; 397 c.flags = htons(h->flags); 398 c.qdcount = htons(h->qdcount); 399 c.ancount = htons(h->ancount); 400 c.nscount = htons(h->nscount); 401 c.arcount = htons(h->arcount); 402 403 return (pack_data(p, &c, HFIXEDSZ)); 404 } 405 406 int 407 asr_pack_query(struct asr_pack *p, uint16_t type, uint16_t class, const char *dname) 408 { 409 pack_dname(p, dname); 410 pack_u16(p, type); 411 pack_u16(p, class); 412 413 return (p->err) ? (-1) : (0); 414 } 415 416 int 417 asr_sockaddr_from_str(struct sockaddr *sa, int family, const char *str) 418 { 419 struct in_addr ina; 420 struct in6_addr in6a; 421 struct sockaddr_in *sin; 422 struct sockaddr_in6 *sin6; 423 char *cp, *str2; 424 const char *errstr; 425 426 switch (family) { 427 case PF_UNSPEC: 428 if (asr_sockaddr_from_str(sa, PF_INET, str) == 0) 429 return (0); 430 return asr_sockaddr_from_str(sa, PF_INET6, str); 431 432 case PF_INET: 433 if (inet_pton(PF_INET, str, &ina) != 1) 434 return (-1); 435 436 sin = (struct sockaddr_in *)sa; 437 memset(sin, 0, sizeof *sin); 438 sin->sin_len = sizeof(struct sockaddr_in); 439 sin->sin_family = PF_INET; 440 sin->sin_addr.s_addr = ina.s_addr; 441 return (0); 442 443 case PF_INET6: 444 cp = strchr(str, SCOPE_DELIMITER); 445 if (cp) { 446 str2 = strdup(str); 447 if (str2 == NULL) 448 return (-1); 449 str2[cp - str] = '\0'; 450 if (inet_pton(PF_INET6, str2, &in6a) != 1) { 451 free(str2); 452 return (-1); 453 } 454 cp++; 455 free(str2); 456 } else if (inet_pton(PF_INET6, str, &in6a) != 1) 457 return (-1); 458 459 sin6 = (struct sockaddr_in6 *)sa; 460 memset(sin6, 0, sizeof *sin6); 461 sin6->sin6_len = sizeof(struct sockaddr_in6); 462 sin6->sin6_family = PF_INET6; 463 sin6->sin6_addr = in6a; 464 465 if (cp == NULL) 466 return (0); 467 468 if (IN6_IS_ADDR_LINKLOCAL(&in6a) || 469 IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || 470 IN6_IS_ADDR_MC_INTFACELOCAL(&in6a)) 471 if ((sin6->sin6_scope_id = if_nametoindex(cp))) 472 return (0); 473 474 sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr); 475 if (errstr) 476 return (-1); 477 return (0); 478 479 default: 480 break; 481 } 482 483 return (-1); 484 } 485 486 ssize_t 487 asr_addr_as_fqdn(const char *addr, int family, char *dst, size_t max) 488 { 489 const struct in6_addr *in6_addr; 490 in_addr_t in_addr; 491 492 switch (family) { 493 case AF_INET: 494 in_addr = ntohl(*((const in_addr_t *)addr)); 495 snprintf(dst, max, 496 "%d.%d.%d.%d.in-addr.arpa.", 497 in_addr & 0xff, 498 (in_addr >> 8) & 0xff, 499 (in_addr >> 16) & 0xff, 500 (in_addr >> 24) & 0xff); 501 break; 502 case AF_INET6: 503 in6_addr = (const struct in6_addr *)addr; 504 snprintf(dst, max, 505 "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." 506 "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." 507 "ip6.arpa.", 508 in6_addr->s6_addr[15] & 0xf, 509 (in6_addr->s6_addr[15] >> 4) & 0xf, 510 in6_addr->s6_addr[14] & 0xf, 511 (in6_addr->s6_addr[14] >> 4) & 0xf, 512 in6_addr->s6_addr[13] & 0xf, 513 (in6_addr->s6_addr[13] >> 4) & 0xf, 514 in6_addr->s6_addr[12] & 0xf, 515 (in6_addr->s6_addr[12] >> 4) & 0xf, 516 in6_addr->s6_addr[11] & 0xf, 517 (in6_addr->s6_addr[11] >> 4) & 0xf, 518 in6_addr->s6_addr[10] & 0xf, 519 (in6_addr->s6_addr[10] >> 4) & 0xf, 520 in6_addr->s6_addr[9] & 0xf, 521 (in6_addr->s6_addr[9] >> 4) & 0xf, 522 in6_addr->s6_addr[8] & 0xf, 523 (in6_addr->s6_addr[8] >> 4) & 0xf, 524 in6_addr->s6_addr[7] & 0xf, 525 (in6_addr->s6_addr[7] >> 4) & 0xf, 526 in6_addr->s6_addr[6] & 0xf, 527 (in6_addr->s6_addr[6] >> 4) & 0xf, 528 in6_addr->s6_addr[5] & 0xf, 529 (in6_addr->s6_addr[5] >> 4) & 0xf, 530 in6_addr->s6_addr[4] & 0xf, 531 (in6_addr->s6_addr[4] >> 4) & 0xf, 532 in6_addr->s6_addr[3] & 0xf, 533 (in6_addr->s6_addr[3] >> 4) & 0xf, 534 in6_addr->s6_addr[2] & 0xf, 535 (in6_addr->s6_addr[2] >> 4) & 0xf, 536 in6_addr->s6_addr[1] & 0xf, 537 (in6_addr->s6_addr[1] >> 4) & 0xf, 538 in6_addr->s6_addr[0] & 0xf, 539 (in6_addr->s6_addr[0] >> 4) & 0xf); 540 break; 541 default: 542 return (-1); 543 } 544 return (0); 545 } 546