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