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