| 1 | /* |
| 2 | * addrfam.c |
| 3 | * - address-family specific code |
| 4 | */ |
| 5 | /* |
| 6 | * This file is part of adns, which is |
| 7 | * Copyright (C) 1997-2000,2003,2006,2014-2016 Ian Jackson |
| 8 | * Copyright (C) 2014 Mark Wooding |
| 9 | * Copyright (C) 1999-2000,2003,2006 Tony Finch |
| 10 | * Copyright (C) 1991 Massachusetts Institute of Technology |
| 11 | * (See the file INSTALL for full details.) |
| 12 | * |
| 13 | * This program is free software; you can redistribute it and/or modify |
| 14 | * it under the terms of the GNU General Public License as published by |
| 15 | * the Free Software Foundation; either version 3, or (at your option) |
| 16 | * any later version. |
| 17 | * |
| 18 | * This program is distributed in the hope that it will be useful, |
| 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 21 | * GNU General Public License for more details. |
| 22 | * |
| 23 | * You should have received a copy of the GNU General Public License |
| 24 | * along with this program; if not, write to the Free Software Foundation. |
| 25 | */ |
| 26 | |
| 27 | #include <stdlib.h> |
| 28 | #include <errno.h> |
| 29 | #include <limits.h> |
| 30 | #include <unistd.h> |
| 31 | #include <inttypes.h> |
| 32 | #include <stddef.h> |
| 33 | #include <stdbool.h> |
| 34 | |
| 35 | #include <sys/types.h> |
| 36 | #include <sys/socket.h> |
| 37 | #include <netinet/in.h> |
| 38 | #include <arpa/inet.h> |
| 39 | #include <net/if.h> |
| 40 | |
| 41 | #include "internal.h" |
| 42 | |
| 43 | /* |
| 44 | * General address-family operations. |
| 45 | */ |
| 46 | |
| 47 | #define SIN(cnst, sa) ((void)(sa)->sa_family, (cnst struct sockaddr_in *)(sa)) |
| 48 | #define SIN6(cnst, sa) ((void)(sa)->sa_family, (cnst struct sockaddr_in6 *)(sa)) |
| 49 | |
| 50 | static void unknown_af(int af) NONRETURNING; |
| 51 | static void unknown_af(int af) { |
| 52 | fprintf(stderr, "ADNS INTERNAL: unknown address family %d\n", af); |
| 53 | abort(); |
| 54 | } |
| 55 | |
| 56 | /* |
| 57 | * SOCKADDR_IN_IN6(CNST, struct sockaddr *sa, SIN, { |
| 58 | * // struct sockaddr_in *const SIN; // implicitly |
| 59 | * code for inet; |
| 60 | * }, { |
| 61 | * // struct sockaddr_in6 *const SIN6; // implicitly |
| 62 | * code for inet6; |
| 63 | * }) |
| 64 | * |
| 65 | * SOCKADDR_IN_IN6_PAIR(CNST, struct sockaddr *sa, SINA, |
| 66 | * struct sockaddr *sb, SINB, { |
| 67 | * // struct sockaddr_in *const SINA; // implicitly |
| 68 | * // struct sockaddr_in *const SINB; // implicitly |
| 69 | * code for inet; |
| 70 | * },{ |
| 71 | * // struct sockaddr_in6 *const SINA6; // implicitly |
| 72 | * // struct sockaddr_in6 *const SINB6; // implicitly |
| 73 | * code for inet6; |
| 74 | * }); |
| 75 | * |
| 76 | * SOCKADDR_IN_IN6_OTHER(CNST, struct sockaddr *sa, SIN, { in }, { in6 }, { |
| 77 | * code for other address family |
| 78 | * }) |
| 79 | * |
| 80 | * AF_IN_IN6_OTHER(af, { in }, { in6 }, { other }) |
| 81 | * |
| 82 | * Executes the first or second block according to the AF in sa. CNST |
| 83 | * may be `const' or empty. For _PAIR, sa and sb must be same AF. |
| 84 | * |
| 85 | * All except _OTHER handle unknown AFs with unknown_af. |
| 86 | * |
| 87 | * Code blocks may not contain , outside parens. |
| 88 | */ |
| 89 | #define AF_IN_IN6_OTHER(af, for_inet, for_inet6, other) \ |
| 90 | if ((af) == AF_INET) { \ |
| 91 | for_inet \ |
| 92 | } else if ((af) == AF_INET6) { \ |
| 93 | for_inet6 \ |
| 94 | } else { \ |
| 95 | other \ |
| 96 | } |
| 97 | #define SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, for_inet6, other) \ |
| 98 | AF_IN_IN6_OTHER((sa)->sa_family, { \ |
| 99 | cnst struct sockaddr_in *const sin = SIN(cnst,(sa)); \ |
| 100 | for_inet \ |
| 101 | }, { \ |
| 102 | cnst struct sockaddr_in6 *const sin##6 = SIN6(cnst,(sa)); \ |
| 103 | for_inet6 \ |
| 104 | }, \ |
| 105 | other \ |
| 106 | ) |
| 107 | #define SOCKADDR_IN_IN6(cnst, sa, sin, for_inet, for_inet6) \ |
| 108 | SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, for_inet6, { \ |
| 109 | unknown_af((sa)->sa_family); \ |
| 110 | }) |
| 111 | #define SOCKADDR_IN_IN6_PAIR(cnst, sa, sina, sb, sinb, for_inet, for_inet6) \ |
| 112 | do{ \ |
| 113 | assert((sa)->sa_family == (sb)->sa_family); \ |
| 114 | SOCKADDR_IN_IN6(cnst, sa, sina, { \ |
| 115 | cnst struct sockaddr_in *const sinb = SIN(cnst,(sb)); \ |
| 116 | for_inet \ |
| 117 | }, { \ |
| 118 | cnst struct sockaddr_in6 *const sinb##6 = SIN6(cnst,(sb)); \ |
| 119 | for_inet6 \ |
| 120 | }); \ |
| 121 | }while(0) |
| 122 | |
| 123 | int adns__addrs_equal_raw(const struct sockaddr *a, |
| 124 | int bf, const void *b) { |
| 125 | if (a->sa_family != bf) return 0; |
| 126 | |
| 127 | SOCKADDR_IN_IN6(const, a, sin, { |
| 128 | return sin->sin_addr.s_addr == ((const struct in_addr*)b)->s_addr; |
| 129 | }, { |
| 130 | return !memcmp(&sin6->sin6_addr, b, sizeof(struct in6_addr)); |
| 131 | }); |
| 132 | } |
| 133 | |
| 134 | int adns__addrs_equal(const adns_sockaddr *a, const adns_sockaddr *b) { |
| 135 | return adns__addrs_equal_raw(&a->sa, b->sa.sa_family, |
| 136 | adns__sockaddr_addr(&b->sa)); |
| 137 | } |
| 138 | |
| 139 | int adns__sockaddrs_equal(const struct sockaddr *sa, |
| 140 | const struct sockaddr *sb) { |
| 141 | if (!adns__addrs_equal_raw(sa, sb->sa_family, adns__sockaddr_addr(sb))) |
| 142 | return 0; |
| 143 | SOCKADDR_IN_IN6_PAIR(const, sa, sina, sb, sinb, { |
| 144 | return sina->sin_port == sinb->sin_port; |
| 145 | }, { |
| 146 | return sina6->sin6_port == sinb6->sin6_port && |
| 147 | sina6->sin6_scope_id == sinb6->sin6_scope_id; |
| 148 | }); |
| 149 | } |
| 150 | |
| 151 | int adns__addr_width(int af) { |
| 152 | AF_IN_IN6_OTHER(af, { |
| 153 | return 32; |
| 154 | }, { |
| 155 | return 128; |
| 156 | }, { |
| 157 | unknown_af(af); |
| 158 | }); |
| 159 | } |
| 160 | |
| 161 | void adns__prefix_mask(adns_sockaddr *sa, int len) { |
| 162 | SOCKADDR_IN_IN6(, &sa->sa, sin, { |
| 163 | assert(len <= 32); |
| 164 | sin->sin_addr.s_addr= htonl(!len ? 0 : 0xffffffff << (32-len)); |
| 165 | }, { |
| 166 | int i= len/8; |
| 167 | int j= len%8; |
| 168 | unsigned char *m= sin6->sin6_addr.s6_addr; |
| 169 | assert(len <= 128); |
| 170 | memset(m, 0xff, i); |
| 171 | if (j) m[i++]= (0xff << (8-j)) & 0xff; |
| 172 | memset(m+i, 0, 16-i); |
| 173 | }); |
| 174 | } |
| 175 | |
| 176 | int adns__guess_prefix_length(const adns_sockaddr *sa) { |
| 177 | SOCKADDR_IN_IN6(const, &sa->sa, sin, { |
| 178 | unsigned a= (ntohl(sin->sin_addr.s_addr) >> 24) & 0xff; |
| 179 | if (a < 128) return 8; |
| 180 | else if (a < 192) return 16; |
| 181 | else if (a < 224) return 24; |
| 182 | else return -1; |
| 183 | }, { |
| 184 | (void)sin6; |
| 185 | return 64; |
| 186 | }); |
| 187 | } |
| 188 | |
| 189 | int adns__addr_matches(int af, const void *addr, |
| 190 | const adns_sockaddr *base, const adns_sockaddr *mask) |
| 191 | { |
| 192 | if (af != base->sa.sa_family) return 0; |
| 193 | SOCKADDR_IN_IN6_PAIR(const, &base->sa, sbase, &mask->sa, smask, { |
| 194 | const struct in_addr *v4 = addr; |
| 195 | return (v4->s_addr & smask->sin_addr.s_addr) |
| 196 | == sbase->sin_addr.s_addr; |
| 197 | }, { |
| 198 | int i; |
| 199 | const char *a= addr; |
| 200 | const char *b= sbase6->sin6_addr.s6_addr; |
| 201 | const char *m= smask6->sin6_addr.s6_addr; |
| 202 | for (i = 0; i < 16; i++) |
| 203 | if ((a[i] & m[i]) != b[i]) return 0; |
| 204 | return 1; |
| 205 | }); |
| 206 | } |
| 207 | |
| 208 | const void *adns__sockaddr_addr(const struct sockaddr *sa) { |
| 209 | SOCKADDR_IN_IN6(const, sa, sin, { |
| 210 | return &sin->sin_addr; |
| 211 | }, { |
| 212 | return &sin6->sin6_addr; |
| 213 | }); |
| 214 | } |
| 215 | |
| 216 | void adns__addr_inject(const void *a, adns_sockaddr *sa) { |
| 217 | SOCKADDR_IN_IN6( , &sa->sa, sin, { |
| 218 | memcpy(&sin->sin_addr, a, sizeof(sin->sin_addr)); |
| 219 | }, { |
| 220 | memcpy(&sin6->sin6_addr, a, sizeof(sin6->sin6_addr)); |
| 221 | }); |
| 222 | } |
| 223 | |
| 224 | /* |
| 225 | * addr2text and text2addr |
| 226 | */ |
| 227 | |
| 228 | #define ADDRFAM_DEBUG |
| 229 | #ifdef ADDRFAM_DEBUG |
| 230 | static void af_debug_func(const char *fmt, ...) { |
| 231 | int esave= errno; |
| 232 | va_list al; |
| 233 | va_start(al,fmt); |
| 234 | vfprintf(stderr,fmt,al); |
| 235 | va_end(al); |
| 236 | errno= esave; |
| 237 | } |
| 238 | # define af_debug(fmt,...) \ |
| 239 | (af_debug_func("%s: " fmt "\n", __func__, __VA_ARGS__)) |
| 240 | #else |
| 241 | # define af_debug(fmt,...) ((void)("" fmt "", __VA_ARGS__)) |
| 242 | #endif |
| 243 | |
| 244 | static bool addrtext_our_errno(int e) { |
| 245 | return |
| 246 | e==EAFNOSUPPORT || |
| 247 | e==EINVAL || |
| 248 | e==ENOSPC || |
| 249 | e==ENOSYS; |
| 250 | } |
| 251 | |
| 252 | static bool addrtext_scope_use_ifname(const struct sockaddr *sa) { |
| 253 | const struct in6_addr *in6= &SIN6(const,sa)->sin6_addr; |
| 254 | return |
| 255 | IN6_IS_ADDR_LINKLOCAL(in6) || |
| 256 | IN6_IS_ADDR_MC_LINKLOCAL(in6); |
| 257 | } |
| 258 | |
| 259 | static int textaddr_check_qf(adns_queryflags flags) { |
| 260 | if (flags & ~(adns_queryflags)(adns_qf_addrlit_scope_forbid| |
| 261 | adns_qf_addrlit_scope_numeric| |
| 262 | adns_qf_addrlit_ipv4_quadonly| |
| 263 | 0x40000000)) |
| 264 | return ENOSYS; |
| 265 | return 0; |
| 266 | } |
| 267 | |
| 268 | int adns_text2addr(const char *text, uint16_t port, adns_queryflags flags, |
| 269 | struct sockaddr *sa, socklen_t *salen_io) { |
| 270 | int r, af; |
| 271 | char copybuf[INET6_ADDRSTRLEN]; |
| 272 | const char *parse=text; |
| 273 | const char *scopestr=0; |
| 274 | socklen_t needlen; |
| 275 | void *dst; |
| 276 | uint16_t *portp; |
| 277 | |
| 278 | r= textaddr_check_qf(flags); if (r) return r; |
| 279 | |
| 280 | #define INVAL(how) do{ \ |
| 281 | af_debug("invalid: %s: `%s'", how, text); \ |
| 282 | return EINVAL; \ |
| 283 | }while(0) |
| 284 | |
| 285 | #define AFCORE(INETx,SINx,sinx) \ |
| 286 | af= AF_##INETx; \ |
| 287 | dst = &SINx(,sa)->sinx##_addr; \ |
| 288 | portp = &SINx(,sa)->sinx##_port; \ |
| 289 | needlen= sizeof(*SINx(,sa)); |
| 290 | |
| 291 | if (!strchr(text, ':')) { /* INET */ |
| 292 | |
| 293 | AFCORE(INET,SIN,sin); |
| 294 | |
| 295 | } else { /* INET6 */ |
| 296 | |
| 297 | AFCORE(INET6,SIN6,sin6); |
| 298 | |
| 299 | const char *percent= strchr(text, '%'); |
| 300 | if (percent) { |
| 301 | ptrdiff_t lhslen = percent - text; |
| 302 | if (lhslen >= INET6_ADDRSTRLEN) INVAL("scoped addr lhs too long"); |
| 303 | memcpy(copybuf, text, lhslen); |
| 304 | copybuf[lhslen]= 0; |
| 305 | |
| 306 | parse= copybuf; |
| 307 | scopestr= percent+1; |
| 308 | |
| 309 | af_debug("will parse scoped addr `%s' %% `%s'", parse, scopestr); |
| 310 | } |
| 311 | |
| 312 | } |
| 313 | |
| 314 | #undef AFCORE |
| 315 | |
| 316 | if (scopestr && (flags & adns_qf_addrlit_scope_forbid)) |
| 317 | INVAL("scoped addr but _scope_forbid"); |
| 318 | |
| 319 | if (*salen_io < needlen) { |
| 320 | *salen_io = needlen; |
| 321 | return ENOSPC; |
| 322 | } |
| 323 | |
| 324 | memset(sa, 0, needlen); |
| 325 | |
| 326 | sa->sa_family= af; |
| 327 | *portp = htons(port); |
| 328 | |
| 329 | if (af == AF_INET && !(flags & adns_qf_addrlit_ipv4_quadonly)) { |
| 330 | /* we have to use inet_aton to deal with non-dotted-quad literals */ |
| 331 | int r= inet_aton(parse,&SIN(,sa)->sin_addr); |
| 332 | if (!r) INVAL("inet_aton rejected"); |
| 333 | } else { |
| 334 | int r= inet_pton(af,parse,dst); |
| 335 | if (!r) INVAL("inet_pton rejected"); |
| 336 | assert(r>0); |
| 337 | } |
| 338 | |
| 339 | if (scopestr) { |
| 340 | errno=0; |
| 341 | char *ep; |
| 342 | unsigned long scope= strtoul(scopestr,&ep,10); |
| 343 | if (errno==ERANGE) INVAL("numeric scope id too large for unsigned long"); |
| 344 | assert(!errno); |
| 345 | if (!*ep) { |
| 346 | if (scope > ~(uint32_t)0) |
| 347 | INVAL("numeric scope id too large for uint32_t"); |
| 348 | } else { /* !!*ep */ |
| 349 | if (flags & adns_qf_addrlit_scope_numeric) |
| 350 | INVAL("non-numeric scope but _scope_numeric"); |
| 351 | if (!addrtext_scope_use_ifname(sa)) { |
| 352 | af_debug("cannot convert non-numeric scope" |
| 353 | " in non-link-local addr `%s'", text); |
| 354 | return ENOSYS; |
| 355 | } |
| 356 | errno= 0; |
| 357 | scope= if_nametoindex(scopestr); |
| 358 | if (!scope) { |
| 359 | /* RFC3493 says "No errors are defined". It's not clear |
| 360 | * whether that is supposed to mean if_nametoindex "can't |
| 361 | * fail" (other than by the supplied name not being that of an |
| 362 | * interface) which seems unrealistic, or that it conflates |
| 363 | * all its errors together by failing to set errno, or simply |
| 364 | * that they didn't bother to document the errors. |
| 365 | * |
| 366 | * glibc, FreeBSD and OpenBSD all set errno (to ENXIO when |
| 367 | * appropriate). See Debian bug #749349. |
| 368 | * |
| 369 | * We attempt to deal with this by clearing errno to start |
| 370 | * with, and then perhaps mapping the results. */ |
| 371 | af_debug("if_nametoindex rejected scope name (errno=%s)", |
| 372 | strerror(errno)); |
| 373 | if (errno==0) { |
| 374 | return ENXIO; |
| 375 | } else if (addrtext_our_errno(errno)) { |
| 376 | /* we use these for other purposes, urgh. */ |
| 377 | perror("adns: adns_text2addr: if_nametoindex" |
| 378 | " failed with unexpected error"); |
| 379 | return EIO; |
| 380 | } else { |
| 381 | return errno; |
| 382 | } |
| 383 | } else { /* ix>0 */ |
| 384 | if (scope > ~(uint32_t)0) { |
| 385 | fprintf(stderr,"adns: adns_text2addr: if_nametoindex" |
| 386 | " returned an interface index >=2^32 which will not fit" |
| 387 | " in sockaddr_in6.sin6_scope_id"); |
| 388 | return EIO; |
| 389 | } |
| 390 | } |
| 391 | } /* else; !!*ep */ |
| 392 | |
| 393 | SIN6(,sa)->sin6_scope_id= scope; |
| 394 | } /* if (scopestr) */ |
| 395 | |
| 396 | *salen_io = needlen; |
| 397 | return 0; |
| 398 | } |
| 399 | |
| 400 | int adns_addr2text(const struct sockaddr *sa, adns_queryflags flags, |
| 401 | char *buffer, int *buflen_io, int *port_r) { |
| 402 | const void *src; |
| 403 | int r, port; |
| 404 | |
| 405 | r= textaddr_check_qf(flags); if (r) return r; |
| 406 | |
| 407 | if (*buflen_io < ADNS_ADDR2TEXT_BUFLEN) { |
| 408 | *buflen_io = ADNS_ADDR2TEXT_BUFLEN; |
| 409 | return ENOSPC; |
| 410 | } |
| 411 | |
| 412 | SOCKADDR_IN_IN6_OTHER(const, sa, sin, { |
| 413 | src= &sin->sin_addr; port= sin->sin_port; |
| 414 | }, { |
| 415 | src= &sin6->sin6_addr; port= sin6->sin6_port; |
| 416 | }, { |
| 417 | return EAFNOSUPPORT; |
| 418 | }); |
| 419 | |
| 420 | const char *ok= inet_ntop(sa->sa_family, src, buffer, *buflen_io); |
| 421 | assert(ok); |
| 422 | |
| 423 | if (sa->sa_family == AF_INET6) { |
| 424 | uint32_t scope = SIN6(const,sa)->sin6_scope_id; |
| 425 | if (scope) { |
| 426 | if (flags & adns_qf_addrlit_scope_forbid) |
| 427 | return EINVAL; |
| 428 | int scopeoffset = strlen(buffer); |
| 429 | int remain = *buflen_io - scopeoffset; |
| 430 | char *scopeptr = buffer + scopeoffset; |
| 431 | assert(remain >= IF_NAMESIZE+1/*%*/); |
| 432 | *scopeptr++= '%'; remain--; |
| 433 | bool parsedname = 0; |
| 434 | af_debug("will print scoped addr `%.*s' %% %"PRIu32"", |
| 435 | scopeoffset,buffer, scope); |
| 436 | if (scope <= UINT_MAX /* so we can pass it to if_indextoname */ |
| 437 | && !(flags & adns_qf_addrlit_scope_numeric) |
| 438 | && addrtext_scope_use_ifname(sa)) { |
| 439 | parsedname = if_indextoname(scope, scopeptr); |
| 440 | if (!parsedname) { |
| 441 | af_debug("if_indextoname rejected scope (errno=%s)", |
| 442 | strerror(errno)); |
| 443 | if (errno==ENXIO) { |
| 444 | /* fair enough, show it as a number then */ |
| 445 | } else if (addrtext_our_errno(errno)) { |
| 446 | /* we use these for other purposes, urgh. */ |
| 447 | perror("adns: adns_addr2text: if_indextoname" |
| 448 | " failed with unexpected error"); |
| 449 | return EIO; |
| 450 | } else { |
| 451 | return errno; |
| 452 | } |
| 453 | } |
| 454 | } |
| 455 | if (!parsedname) { |
| 456 | int r = snprintf(scopeptr, remain, |
| 457 | "%"PRIu32"", scope); |
| 458 | assert(r < *buflen_io - scopeoffset); |
| 459 | } |
| 460 | af_debug("printed scoped addr `%s'", buffer); |
| 461 | } |
| 462 | } |
| 463 | |
| 464 | if (port_r) *port_r= ntohs(port); |
| 465 | return 0; |
| 466 | } |
| 467 | |
| 468 | char *adns__sockaddr_ntoa(const struct sockaddr *sa, char *buf) { |
| 469 | int err; |
| 470 | int len= ADNS_ADDR2TEXT_BUFLEN; |
| 471 | |
| 472 | err= adns_addr2text(sa, 0, buf, &len, 0); |
| 473 | if (err == EIO) |
| 474 | err= adns_addr2text(sa, adns_qf_addrlit_scope_numeric, buf, &len, 0); |
| 475 | assert(!err); |
| 476 | return buf; |
| 477 | } |
| 478 | |
| 479 | /* |
| 480 | * Reverse-domain parsing and construction. |
| 481 | */ |
| 482 | |
| 483 | int adns__make_reverse_domain(const struct sockaddr *sa, const char *zone, |
| 484 | char **buf_io, size_t bufsz, |
| 485 | char **buf_free_r) { |
| 486 | size_t req; |
| 487 | char *p; |
| 488 | unsigned c, y; |
| 489 | unsigned long aa; |
| 490 | const unsigned char *ap; |
| 491 | int i, j; |
| 492 | |
| 493 | AF_IN_IN6_OTHER(sa->sa_family, { |
| 494 | req= 4 * 4; |
| 495 | if (!zone) zone= "in-addr.arpa"; |
| 496 | }, { |
| 497 | req = 2 * 32; |
| 498 | if (!zone) zone= "ip6.arpa"; |
| 499 | }, { |
| 500 | return ENOSYS; |
| 501 | }); |
| 502 | |
| 503 | req += strlen(zone) + 1; |
| 504 | if (req <= bufsz) |
| 505 | p= *buf_io; |
| 506 | else { |
| 507 | p= malloc(req); if (!p) return errno; |
| 508 | *buf_free_r = p; |
| 509 | } |
| 510 | |
| 511 | *buf_io= p; |
| 512 | SOCKADDR_IN_IN6(const, sa, sin, { |
| 513 | aa= ntohl(sin->sin_addr.s_addr); |
| 514 | for (i=0; i<4; i++) { |
| 515 | p += sprintf(p, "%d", (int)(aa & 0xff)); |
| 516 | *p++= '.'; |
| 517 | aa >>= 8; |
| 518 | } |
| 519 | }, { |
| 520 | ap= sin6->sin6_addr.s6_addr + 16; |
| 521 | for (i=0; i<16; i++) { |
| 522 | c= *--ap; |
| 523 | for (j=0; j<2; j++) { |
| 524 | y= c & 0xf; |
| 525 | *p++= (y < 10) ? y + '0' : y - 10 + 'a'; |
| 526 | c >>= 4; |
| 527 | *p++= '.'; |
| 528 | } |
| 529 | } |
| 530 | }); |
| 531 | |
| 532 | strcpy(p, zone); |
| 533 | return 0; |
| 534 | } |
| 535 | |
| 536 | |
| 537 | #define REVPARSE_P_L(labnum) \ |
| 538 | const char *p= dgram + rps->labstart[labnum]; \ |
| 539 | int l= rps->lablen[labnum] |
| 540 | /* |
| 541 | * REVPARSE_P_L(int labnum); |
| 542 | * expects: |
| 543 | * const char *dgram; |
| 544 | * const struct revparse_state *rps; |
| 545 | * produces: |
| 546 | * const char *p; // start of label labnum in dgram |
| 547 | * int l; // length of label in dgram |
| 548 | */ |
| 549 | |
| 550 | static bool revparse_check_tail(struct revparse_state *rps, |
| 551 | const char *dgram, int nlabels, |
| 552 | int bodylen, const char *inarpa) { |
| 553 | int i; |
| 554 | |
| 555 | if (nlabels != bodylen+2) return 0; |
| 556 | for (i=0; i<2; i++) { |
| 557 | REVPARSE_P_L(bodylen+i); |
| 558 | const char *want= !i ? inarpa : "arpa"; |
| 559 | if (!adns__labels_equal(p,l, want,strlen(want))) return 0; |
| 560 | } |
| 561 | return 1; |
| 562 | } |
| 563 | |
| 564 | static bool revparse_atoi(const char *p, int l, int base, |
| 565 | unsigned max, unsigned *v_r) { |
| 566 | if (l>3) return 0; |
| 567 | if (l>1 && p[0]=='0') return 0; |
| 568 | unsigned v=0; |
| 569 | while (l-- > 0) { |
| 570 | int tv; |
| 571 | int c= ctype_toupper(*p++); |
| 572 | if ('0'<=c && c<='9') tv = c-'0'; |
| 573 | else if ('A'<=c && c<='Z') tv = c-'A'+10; |
| 574 | else return 0; |
| 575 | if (tv >= base) return 0; |
| 576 | v *= base; |
| 577 | v += tv; |
| 578 | } |
| 579 | if (v>max) return 0; |
| 580 | *v_r= v; |
| 581 | return 1; |
| 582 | } |
| 583 | |
| 584 | static bool revparse_inet(struct revparse_state *rps, |
| 585 | const char *dgram, int nlabels, |
| 586 | adns_rrtype *rrtype_r, adns_sockaddr *addr_r) { |
| 587 | if (!revparse_check_tail(rps,dgram,nlabels,4,"in-addr")) return 0; |
| 588 | |
| 589 | uint32_t a=0; |
| 590 | int i; |
| 591 | for (i=3; i>=0; i--) { |
| 592 | REVPARSE_P_L(i); |
| 593 | unsigned v; |
| 594 | if (!revparse_atoi(p,l,10,255,&v)) return 0; |
| 595 | a <<= 8; |
| 596 | a |= v; |
| 597 | } |
| 598 | *rrtype_r= adns_r_a; |
| 599 | addr_r->inet.sin_family= AF_INET; |
| 600 | addr_r->inet.sin_addr.s_addr= htonl(a); |
| 601 | return 1; |
| 602 | } |
| 603 | |
| 604 | static bool revparse_inet6(struct revparse_state *rps, |
| 605 | const char *dgram, int nlabels, |
| 606 | adns_rrtype *rrtype_r, adns_sockaddr *addr_r) { |
| 607 | if (!revparse_check_tail(rps,dgram,nlabels,32,"ip6")) return 0; |
| 608 | |
| 609 | int i, j; |
| 610 | memset(addr_r,0,sizeof(*addr_r)); |
| 611 | unsigned char *a= addr_r->inet6.sin6_addr.s6_addr+16; |
| 612 | for (i=0; i<32; ) { /* i incremented in inner loop */ |
| 613 | unsigned b=0; |
| 614 | for (j=0; j<2; j++, i++) { |
| 615 | REVPARSE_P_L(i); |
| 616 | unsigned v; |
| 617 | if (!revparse_atoi(p,l,16,15,&v)) return 0; |
| 618 | b >>= 4; |
| 619 | b |= v << 4; |
| 620 | } |
| 621 | *--a= b; |
| 622 | } |
| 623 | *rrtype_r= adns_r_aaaa; |
| 624 | addr_r->inet.sin_family= AF_INET6; |
| 625 | return 1; |
| 626 | } |
| 627 | |
| 628 | bool adns__revparse_label(struct revparse_state *rps, int labnum, |
| 629 | const char *dgram, int labstart, int lablen) { |
| 630 | if (labnum >= MAXREVLABELS) |
| 631 | return 0; |
| 632 | |
| 633 | assert(labstart <= 65535); |
| 634 | assert(lablen <= 255); |
| 635 | rps->labstart[labnum] = labstart; |
| 636 | rps->lablen[labnum] = lablen; |
| 637 | return 1; |
| 638 | } |
| 639 | |
| 640 | bool adns__revparse_done(struct revparse_state *rps, |
| 641 | const char *dgram, int nlabels, |
| 642 | adns_rrtype *rrtype_r, adns_sockaddr *addr_r) { |
| 643 | return |
| 644 | revparse_inet(rps,dgram,nlabels,rrtype_r,addr_r) || |
| 645 | revparse_inet6(rps,dgram,nlabels,rrtype_r,addr_r); |
| 646 | } |