1 /* $NetBSD: gethnamaddr.c,v 1.70 2006/03/22 00:03:51 christos Exp $ */ 2 3 /* 4 * ++Copyright++ 1985, 1988, 1993 5 * - 6 * Copyright (c) 1985, 1988, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * - 33 * Portions Copyright (c) 1993 by Digital Equipment Corporation. 34 * 35 * Permission to use, copy, modify, and distribute this software for any 36 * purpose with or without fee is hereby granted, provided that the above 37 * copyright notice and this permission notice appear in all copies, and that 38 * the name of Digital Equipment Corporation not be used in advertising or 39 * publicity pertaining to distribution of the document or software without 40 * specific, written prior permission. 41 * 42 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL 43 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES 44 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT 45 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 46 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 47 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 48 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 49 * SOFTWARE. 50 * - 51 * --Copyright-- 52 */ 53 54 #include <sys/cdefs.h> 55 #include <sys/types.h> 56 57 #include <sys/param.h> 58 #include <sys/socket.h> 59 #include <sys/un.h> 60 #include <netinet/in.h> 61 #include <arpa/inet.h> 62 #include <arpa/nameser.h> 63 #include "NetdClientDispatch.h" 64 #include "resolv_netid.h" 65 #include "resolv_private.h" 66 #include "resolv_cache.h" 67 #include <assert.h> 68 #include <ctype.h> 69 #include <errno.h> 70 #include <netdb.h> 71 #include <stdarg.h> 72 #include <stdio.h> 73 #include <strings.h> 74 #include <syslog.h> 75 #include <unistd.h> 76 77 #define ALIGNBYTES (sizeof(uintptr_t) - 1) 78 #define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES) 79 80 #ifndef LOG_AUTH 81 # define LOG_AUTH 0 82 #endif 83 84 #define MULTI_PTRS_ARE_ALIASES 1 /* XXX - experimental */ 85 86 #include "nsswitch.h" 87 #include <stdlib.h> 88 #include <string.h> 89 90 // This should be synchronized to ResponseCode.h 91 static const int DnsProxyQueryResult = 222; 92 93 static const char AskedForGot[] = 94 "gethostby*.getanswer: asked for \"%s\", got \"%s\""; 95 96 #define MAXPACKET (64*1024) 97 98 typedef union { 99 HEADER hdr; 100 u_char buf[MAXPACKET]; 101 } querybuf; 102 103 typedef union { 104 int32_t al; 105 char ac; 106 } align; 107 108 #ifdef DEBUG 109 static void dprintf(const char *, res_state, ...) 110 __attribute__((__format__(__printf__, 1, 3))); 111 #endif 112 static struct hostent *getanswer(const querybuf *, int, const char *, int, 113 res_state); 114 static void map_v4v6_address(const char *, char *); 115 static void map_v4v6_hostent(struct hostent *, char **, char *); 116 static void addrsort(char **, int, res_state); 117 118 static void _sethtent(int); 119 static void _endhtent(void); 120 static struct hostent *_gethtent(void); 121 void ht_sethostent(int); 122 void ht_endhostent(void); 123 struct hostent *ht_gethostbyname(char *); 124 struct hostent *ht_gethostbyaddr(const char *, int, int); 125 void dns_service(void); 126 #undef dn_skipname 127 int dn_skipname(const u_char *, const u_char *); 128 static int _gethtbyaddr(void *, void *, va_list); 129 static int _gethtbyname(void *, void *, va_list); 130 static struct hostent *_gethtbyname2(const char *, int); 131 static int _dns_gethtbyaddr(void *, void *, va_list); 132 static int _dns_gethtbyname(void *, void *, va_list); 133 134 static struct hostent *gethostbyname_internal(const char *, int, res_state, unsigned, unsigned); 135 136 static const ns_src default_dns_files[] = { 137 { NSSRC_FILES, NS_SUCCESS }, 138 { NSSRC_DNS, NS_SUCCESS }, 139 { 0, 0 } 140 }; 141 142 143 #ifdef DEBUG 144 static void 145 dprintf(const char *msg, res_state res, ...) 146 { 147 assert(msg != NULL); 148 149 if (res->options & RES_DEBUG) { 150 int save = errno; 151 va_list ap; 152 153 va_start (ap, res); 154 vprintf(msg, ap); 155 va_end (ap); 156 157 errno = save; 158 } 159 } 160 #else 161 # define dprintf(msg, res, num) ((void)0) /*nada*/ 162 #endif 163 164 #define BOUNDED_INCR(x) \ 165 do { \ 166 cp += (x); \ 167 if (cp > eom) { \ 168 h_errno = NO_RECOVERY; \ 169 return NULL; \ 170 } \ 171 } while (/*CONSTCOND*/0) 172 173 #define BOUNDS_CHECK(ptr, count) \ 174 do { \ 175 if ((ptr) + (count) > eom) { \ 176 h_errno = NO_RECOVERY; \ 177 return NULL; \ 178 } \ 179 } while (/*CONSTCOND*/0) 180 181 static struct hostent * 182 getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, 183 res_state res) 184 { 185 const HEADER *hp; 186 const u_char *cp; 187 int n; 188 const u_char *eom, *erdata; 189 char *bp, **ap, **hap, *ep; 190 int type, class, ancount, qdcount; 191 int haveanswer, had_error; 192 int toobig = 0; 193 char tbuf[MAXDNAME]; 194 const char *tname; 195 int (*name_ok)(const char *); 196 res_static rs = __res_get_static(); 197 198 assert(answer != NULL); 199 assert(qname != NULL); 200 201 tname = qname; 202 rs->host.h_name = NULL; 203 eom = answer->buf + anslen; 204 switch (qtype) { 205 case T_A: 206 case T_AAAA: 207 name_ok = res_hnok; 208 break; 209 case T_PTR: 210 name_ok = res_dnok; 211 break; 212 default: 213 return NULL; /* XXX should be abort(); */ 214 } 215 /* 216 * find first satisfactory answer 217 */ 218 hp = &answer->hdr; 219 ancount = ntohs(hp->ancount); 220 qdcount = ntohs(hp->qdcount); 221 bp = rs->hostbuf; 222 ep = rs->hostbuf + sizeof rs->hostbuf; 223 cp = answer->buf; 224 BOUNDED_INCR(HFIXEDSZ); 225 if (qdcount != 1) { 226 h_errno = NO_RECOVERY; 227 return NULL; 228 } 229 n = dn_expand(answer->buf, eom, cp, bp, ep - bp); 230 if ((n < 0) || !(*name_ok)(bp)) { 231 h_errno = NO_RECOVERY; 232 return NULL; 233 } 234 BOUNDED_INCR(n + QFIXEDSZ); 235 if (qtype == T_A || qtype == T_AAAA) { 236 /* res_send() has already verified that the query name is the 237 * same as the one we sent; this just gets the expanded name 238 * (i.e., with the succeeding search-domain tacked on). 239 */ 240 n = strlen(bp) + 1; /* for the \0 */ 241 if (n >= MAXHOSTNAMELEN) { 242 h_errno = NO_RECOVERY; 243 return NULL; 244 } 245 rs->host.h_name = bp; 246 bp += n; 247 /* The qname can be abbreviated, but h_name is now absolute. */ 248 qname = rs->host.h_name; 249 } 250 ap = rs->host_aliases; 251 *ap = NULL; 252 rs->host.h_aliases = rs->host_aliases; 253 hap = rs->h_addr_ptrs; 254 *hap = NULL; 255 rs->host.h_addr_list = rs->h_addr_ptrs; 256 haveanswer = 0; 257 had_error = 0; 258 while (ancount-- > 0 && cp < eom && !had_error) { 259 n = dn_expand(answer->buf, eom, cp, bp, ep - bp); 260 if ((n < 0) || !(*name_ok)(bp)) { 261 had_error++; 262 continue; 263 } 264 cp += n; /* name */ 265 BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); 266 type = _getshort(cp); 267 cp += INT16SZ; /* type */ 268 class = _getshort(cp); 269 cp += INT16SZ + INT32SZ; /* class, TTL */ 270 n = _getshort(cp); 271 cp += INT16SZ; /* len */ 272 BOUNDS_CHECK(cp, n); 273 erdata = cp + n; 274 if (class != C_IN) { 275 /* XXX - debug? syslog? */ 276 cp += n; 277 continue; /* XXX - had_error++ ? */ 278 } 279 if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { 280 if (ap >= &rs->host_aliases[MAXALIASES-1]) 281 continue; 282 n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); 283 if ((n < 0) || !(*name_ok)(tbuf)) { 284 had_error++; 285 continue; 286 } 287 cp += n; 288 if (cp != erdata) { 289 h_errno = NO_RECOVERY; 290 return NULL; 291 } 292 /* Store alias. */ 293 *ap++ = bp; 294 n = strlen(bp) + 1; /* for the \0 */ 295 if (n >= MAXHOSTNAMELEN) { 296 had_error++; 297 continue; 298 } 299 bp += n; 300 /* Get canonical name. */ 301 n = strlen(tbuf) + 1; /* for the \0 */ 302 if (n > ep - bp || n >= MAXHOSTNAMELEN) { 303 had_error++; 304 continue; 305 } 306 strlcpy(bp, tbuf, (size_t)(ep - bp)); 307 rs->host.h_name = bp; 308 bp += n; 309 continue; 310 } 311 if (qtype == T_PTR && type == T_CNAME) { 312 n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); 313 if (n < 0 || !res_dnok(tbuf)) { 314 had_error++; 315 continue; 316 } 317 cp += n; 318 if (cp != erdata) { 319 h_errno = NO_RECOVERY; 320 return NULL; 321 } 322 /* Get canonical name. */ 323 n = strlen(tbuf) + 1; /* for the \0 */ 324 if (n > ep - bp || n >= MAXHOSTNAMELEN) { 325 had_error++; 326 continue; 327 } 328 strlcpy(bp, tbuf, (size_t)(ep - bp)); 329 tname = bp; 330 bp += n; 331 continue; 332 } 333 if (type != qtype) { 334 if (type != T_KEY && type != T_SIG) 335 syslog(LOG_NOTICE|LOG_AUTH, 336 "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", 337 qname, p_class(C_IN), p_type(qtype), 338 p_type(type)); 339 cp += n; 340 continue; /* XXX - had_error++ ? */ 341 } 342 switch (type) { 343 case T_PTR: 344 if (strcasecmp(tname, bp) != 0) { 345 syslog(LOG_NOTICE|LOG_AUTH, 346 AskedForGot, qname, bp); 347 cp += n; 348 continue; /* XXX - had_error++ ? */ 349 } 350 n = dn_expand(answer->buf, eom, cp, bp, ep - bp); 351 if ((n < 0) || !res_hnok(bp)) { 352 had_error++; 353 break; 354 } 355 #if MULTI_PTRS_ARE_ALIASES 356 cp += n; 357 if (cp != erdata) { 358 h_errno = NO_RECOVERY; 359 return NULL; 360 } 361 if (!haveanswer) 362 rs->host.h_name = bp; 363 else if (ap < &rs->host_aliases[MAXALIASES-1]) 364 *ap++ = bp; 365 else 366 n = -1; 367 if (n != -1) { 368 n = strlen(bp) + 1; /* for the \0 */ 369 if (n >= MAXHOSTNAMELEN) { 370 had_error++; 371 break; 372 } 373 bp += n; 374 } 375 break; 376 #else 377 rs->host.h_name = bp; 378 if (res->options & RES_USE_INET6) { 379 n = strlen(bp) + 1; /* for the \0 */ 380 if (n >= MAXHOSTNAMELEN) { 381 had_error++; 382 break; 383 } 384 bp += n; 385 map_v4v6_hostent(&rs->host, &bp, ep); 386 } 387 h_errno = NETDB_SUCCESS; 388 return &rs->host; 389 #endif 390 case T_A: 391 case T_AAAA: 392 if (strcasecmp(rs->host.h_name, bp) != 0) { 393 syslog(LOG_NOTICE|LOG_AUTH, 394 AskedForGot, rs->host.h_name, bp); 395 cp += n; 396 continue; /* XXX - had_error++ ? */ 397 } 398 if (n != rs->host.h_length) { 399 cp += n; 400 continue; 401 } 402 if (type == T_AAAA) { 403 struct in6_addr in6; 404 memcpy(&in6, cp, IN6ADDRSZ); 405 if (IN6_IS_ADDR_V4MAPPED(&in6)) { 406 cp += n; 407 continue; 408 } 409 } 410 if (!haveanswer) { 411 int nn; 412 413 rs->host.h_name = bp; 414 nn = strlen(bp) + 1; /* for the \0 */ 415 bp += nn; 416 } 417 418 bp += sizeof(align) - 419 (size_t)((u_long)bp % sizeof(align)); 420 421 if (bp + n >= &rs->hostbuf[sizeof rs->hostbuf]) { 422 dprintf("size (%d) too big\n", res, n); 423 had_error++; 424 continue; 425 } 426 if (hap >= &rs->h_addr_ptrs[MAXADDRS-1]) { 427 if (!toobig++) 428 dprintf("Too many addresses (%d)\n", 429 res, MAXADDRS); 430 cp += n; 431 continue; 432 } 433 (void)memcpy(*hap++ = bp, cp, (size_t)n); 434 bp += n; 435 cp += n; 436 if (cp != erdata) { 437 h_errno = NO_RECOVERY; 438 return NULL; 439 } 440 break; 441 default: 442 abort(); 443 } 444 if (!had_error) 445 haveanswer++; 446 } 447 if (haveanswer) { 448 *ap = NULL; 449 *hap = NULL; 450 /* 451 * Note: we sort even if host can take only one address 452 * in its return structures - should give it the "best" 453 * address in that case, not some random one 454 */ 455 if (res->nsort && haveanswer > 1 && qtype == T_A) 456 addrsort(rs->h_addr_ptrs, haveanswer, res); 457 if (!rs->host.h_name) { 458 n = strlen(qname) + 1; /* for the \0 */ 459 if (n > ep - bp || n >= MAXHOSTNAMELEN) 460 goto no_recovery; 461 strlcpy(bp, qname, (size_t)(ep - bp)); 462 rs->host.h_name = bp; 463 bp += n; 464 } 465 if (res->options & RES_USE_INET6) 466 map_v4v6_hostent(&rs->host, &bp, ep); 467 h_errno = NETDB_SUCCESS; 468 return &rs->host; 469 } 470 no_recovery: 471 h_errno = NO_RECOVERY; 472 return NULL; 473 } 474 475 int 476 gethostbyname_r(const char *name, struct hostent *hp, char *buf, size_t buflen, 477 struct hostent**result, int *errorp) 478 { 479 struct hostent *res; 480 481 res = gethostbyname(name); 482 *errorp = h_errno; 483 if (res == NULL) { 484 *result = NULL; 485 return -1; 486 } 487 memcpy(hp, res, sizeof *hp); 488 *result = hp; 489 return 0; 490 } 491 492 struct hostent * 493 gethostbyname(const char *name) 494 { 495 struct hostent *hp; 496 res_state res = __res_get_state(); 497 498 if (res == NULL) 499 return NULL; 500 501 assert(name != NULL); 502 503 /* try IPv6 first - if that fails do IPv4 */ 504 if (res->options & RES_USE_INET6) { 505 hp = gethostbyname_internal(name, AF_INET6, res, NETID_UNSET, MARK_UNSET); 506 if (hp) { 507 __res_put_state(res); 508 return hp; 509 } 510 } 511 hp = gethostbyname_internal(name, AF_INET, res, NETID_UNSET, MARK_UNSET); 512 __res_put_state(res); 513 return hp; 514 } 515 516 struct hostent * 517 gethostbyname2(const char *name, int af) 518 { 519 return android_gethostbynamefornet(name, af, NETID_UNSET, MARK_UNSET); 520 } 521 522 struct hostent * 523 android_gethostbynamefornet(const char *name, int af, unsigned netid, unsigned mark) 524 { 525 struct hostent *hp; 526 res_state res = __res_get_state(); 527 528 if (res == NULL) 529 return NULL; 530 hp = gethostbyname_internal(name, af, res, netid, mark); 531 __res_put_state(res); 532 return hp; 533 } 534 535 536 static FILE* android_open_proxy() 537 { 538 int sock; 539 const int one = 1; 540 struct sockaddr_un proxy_addr; 541 542 sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 543 if (sock < 0) { 544 return NULL; 545 } 546 547 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 548 memset(&proxy_addr, 0, sizeof(proxy_addr)); 549 proxy_addr.sun_family = AF_UNIX; 550 strlcpy(proxy_addr.sun_path, "/dev/socket/dnsproxyd", sizeof(proxy_addr.sun_path)); 551 if (TEMP_FAILURE_RETRY(connect(sock, 552 (const struct sockaddr*) &proxy_addr, 553 sizeof(proxy_addr))) != 0) { 554 close(sock); 555 return NULL; 556 } 557 558 return fdopen(sock, "r+"); 559 } 560 561 static struct hostent * 562 android_read_hostent(FILE* proxy) 563 { 564 uint32_t size; 565 char buf[4]; 566 if (fread(buf, 1, sizeof(buf), proxy) != sizeof(buf)) return NULL; 567 568 /* This is reading serialized data from system/netd/server/DnsProxyListener.cpp 569 * and changes here need to be matched there */ 570 int result_code = strtol(buf, NULL, 10); 571 if (result_code != DnsProxyQueryResult) { 572 fread(&size, 1, sizeof(size), proxy); 573 return NULL; 574 } 575 576 if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL; 577 size = ntohl(size); 578 res_static rs = __res_get_static(); 579 memset(&rs->host, 0, sizeof(rs->host)); 580 char *ptr = rs->hostbuf; 581 582 if (fread(ptr, 1, size, proxy) != size) return NULL; 583 ptr += size; 584 rs->host.h_name = rs->hostbuf; 585 586 char **aliases = rs->host_aliases; 587 rs->host.h_aliases = rs->host_aliases; 588 while (1) { 589 if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL; 590 size = ntohl(size); 591 592 if (size == 0) { 593 *aliases = NULL; 594 break; 595 } 596 if (fread(ptr, 1, size, proxy) != size) return NULL; 597 *aliases++ = ptr; 598 ptr += size; 599 } 600 601 if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL; 602 rs->host.h_addrtype = ntohl(size); 603 604 if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL; 605 rs->host.h_length = ntohl(size); 606 607 char **addrs = rs->h_addr_ptrs; 608 rs->host.h_addr_list = rs->h_addr_ptrs; 609 while (1) { 610 if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL; 611 size = ntohl(size); 612 if (size == 0) { 613 *addrs = NULL; 614 break; 615 } 616 if (fread(ptr, 1, size, proxy) != size) return NULL; 617 *addrs++ = ptr; 618 ptr += size; 619 } 620 621 return &rs->host; 622 } 623 624 625 static struct hostent * 626 gethostbyname_internal_real(const char *name, int af, res_state res) 627 { 628 const char *cp; 629 char *bp, *ep; 630 int size; 631 struct hostent *hp; 632 res_static rs = __res_get_static(); 633 634 static const ns_dtab dtab[] = { 635 NS_FILES_CB(_gethtbyname, NULL) 636 { NSSRC_DNS, _dns_gethtbyname, NULL }, /* force -DHESIOD */ 637 { 0, 0, 0 } 638 }; 639 640 assert(name != NULL); 641 642 switch (af) { 643 case AF_INET: 644 size = INADDRSZ; 645 break; 646 case AF_INET6: 647 size = IN6ADDRSZ; 648 break; 649 default: 650 h_errno = NETDB_INTERNAL; 651 errno = EAFNOSUPPORT; 652 return NULL; 653 } 654 655 rs->host.h_addrtype = af; 656 rs->host.h_length = size; 657 658 /* 659 * if there aren't any dots, it could be a user-level alias. 660 * this is also done in res_nquery() since we are not the only 661 * function that looks up host names. 662 */ 663 if (!strchr(name, '.') && (cp = __hostalias(name))) 664 name = cp; 665 666 /* 667 * disallow names consisting only of digits/dots, unless 668 * they end in a dot. 669 */ 670 if (isdigit((u_char) name[0])) 671 for (cp = name;; ++cp) { 672 if (!*cp) { 673 if (*--cp == '.') 674 break; 675 /* 676 * All-numeric, no dot at the end. 677 * Fake up a hostent as if we'd actually 678 * done a lookup. 679 */ 680 if (inet_pton(af, name, 681 (char *)(void *)rs->host_addr) <= 0) { 682 h_errno = HOST_NOT_FOUND; 683 return NULL; 684 } 685 strncpy(rs->hostbuf, name, MAXDNAME); 686 rs->hostbuf[MAXDNAME] = '\0'; 687 bp = rs->hostbuf + MAXDNAME; 688 ep = rs->hostbuf + sizeof rs->hostbuf; 689 rs->host.h_name = rs->hostbuf; 690 rs->host.h_aliases = rs->host_aliases; 691 rs->host_aliases[0] = NULL; 692 rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr; 693 rs->h_addr_ptrs[1] = NULL; 694 rs->host.h_addr_list = rs->h_addr_ptrs; 695 if (res->options & RES_USE_INET6) 696 map_v4v6_hostent(&rs->host, &bp, ep); 697 h_errno = NETDB_SUCCESS; 698 return &rs->host; 699 } 700 if (!isdigit((u_char) *cp) && *cp != '.') 701 break; 702 } 703 if ((isxdigit((u_char) name[0]) && strchr(name, ':') != NULL) || 704 name[0] == ':') 705 for (cp = name;; ++cp) { 706 if (!*cp) { 707 if (*--cp == '.') 708 break; 709 /* 710 * All-IPv6-legal, no dot at the end. 711 * Fake up a hostent as if we'd actually 712 * done a lookup. 713 */ 714 if (inet_pton(af, name, 715 (char *)(void *)rs->host_addr) <= 0) { 716 h_errno = HOST_NOT_FOUND; 717 return NULL; 718 } 719 strncpy(rs->hostbuf, name, MAXDNAME); 720 rs->hostbuf[MAXDNAME] = '\0'; 721 bp = rs->hostbuf + MAXDNAME; 722 ep = rs->hostbuf + sizeof rs->hostbuf; 723 rs->host.h_name = rs->hostbuf; 724 rs->host.h_aliases = rs->host_aliases; 725 rs->host_aliases[0] = NULL; 726 rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr; 727 rs->h_addr_ptrs[1] = NULL; 728 rs->host.h_addr_list = rs->h_addr_ptrs; 729 h_errno = NETDB_SUCCESS; 730 return &rs->host; 731 } 732 if (!isxdigit((u_char) *cp) && *cp != ':' && *cp != '.') 733 break; 734 } 735 736 hp = NULL; 737 h_errno = NETDB_INTERNAL; 738 if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyname", 739 default_dns_files, name, strlen(name), af) != NS_SUCCESS) { 740 return NULL; 741 } 742 h_errno = NETDB_SUCCESS; 743 return hp; 744 } 745 746 747 // very similar in proxy-ness to android_getaddrinfo_proxy 748 static struct hostent * 749 gethostbyname_internal(const char *name, int af, res_state res, unsigned netid, unsigned mark) 750 { 751 const char *cache_mode = getenv("ANDROID_DNS_MODE"); 752 FILE* proxy = NULL; 753 struct hostent *result = NULL; 754 755 if (cache_mode != NULL && strcmp(cache_mode, "local") == 0) { 756 res_setnetid(res, netid); 757 res_setmark(res, mark); 758 return gethostbyname_internal_real(name, af, res); 759 } 760 761 proxy = android_open_proxy(); 762 if (proxy == NULL) goto exit; 763 764 netid = __netdClientDispatch.netIdForResolv(netid); 765 766 /* This is writing to system/netd/server/DnsProxyListener.cpp and changes 767 * here need to be matched there */ 768 if (fprintf(proxy, "gethostbyname %u %s %d", 769 netid, 770 name == NULL ? "^" : name, 771 af) < 0) { 772 goto exit; 773 } 774 775 if (fputc(0, proxy) == EOF || fflush(proxy) != 0) { 776 goto exit; 777 } 778 779 result = android_read_hostent(proxy); 780 781 exit: 782 if (proxy != NULL) { 783 fclose(proxy); 784 } 785 return result; 786 } 787 788 789 struct hostent * 790 android_gethostbyaddrfornet_proxy(const void *addr, 791 socklen_t len, int af, unsigned netid) 792 { 793 struct hostent *result = NULL; 794 FILE* proxy = android_open_proxy(); 795 796 if (proxy == NULL) goto exit; 797 798 char buf[INET6_ADDRSTRLEN]; //big enough for IPv4 and IPv6 799 const char * addrStr = inet_ntop(af, addr, buf, sizeof(buf)); 800 if (addrStr == NULL) goto exit; 801 802 netid = __netdClientDispatch.netIdForResolv(netid); 803 804 if (fprintf(proxy, "gethostbyaddr %s %d %d %u", 805 addrStr, len, af, netid) < 0) { 806 goto exit; 807 } 808 809 if (fputc(0, proxy) == EOF || fflush(proxy) != 0) { 810 goto exit; 811 } 812 813 result = android_read_hostent(proxy); 814 exit: 815 if (proxy != NULL) { 816 fclose(proxy); 817 } 818 return result; 819 } 820 821 struct hostent * 822 android_gethostbyaddrfornet_real(const void *addr, 823 socklen_t len, int af, unsigned netid, unsigned mark) 824 { 825 const u_char *uaddr = (const u_char *)addr; 826 socklen_t size; 827 struct hostent *hp; 828 static const ns_dtab dtab[] = { 829 NS_FILES_CB(_gethtbyaddr, NULL) 830 { NSSRC_DNS, _dns_gethtbyaddr, NULL }, /* force -DHESIOD */ 831 { 0, 0, 0 } 832 }; 833 834 assert(addr != NULL); 835 836 if (af == AF_INET6 && len == NS_IN6ADDRSZ && 837 (IN6_IS_ADDR_LINKLOCAL((const struct in6_addr *)addr) || 838 IN6_IS_ADDR_SITELOCAL((const struct in6_addr *)addr))) { 839 h_errno = HOST_NOT_FOUND; 840 return NULL; 841 } 842 if (af == AF_INET6 && len == NS_IN6ADDRSZ && 843 (IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)addr) || 844 IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)addr))) { 845 /* Unmap. */ 846 uaddr += NS_IN6ADDRSZ - NS_INADDRSZ; 847 addr = uaddr; 848 af = AF_INET; 849 len = NS_INADDRSZ; 850 } 851 switch (af) { 852 case AF_INET: 853 size = NS_INADDRSZ; 854 break; 855 case AF_INET6: 856 size = NS_IN6ADDRSZ; 857 break; 858 default: 859 errno = EAFNOSUPPORT; 860 h_errno = NETDB_INTERNAL; 861 return NULL; 862 } 863 if (size != len) { 864 errno = EINVAL; 865 h_errno = NETDB_INTERNAL; 866 return NULL; 867 } 868 hp = NULL; 869 h_errno = NETDB_INTERNAL; 870 if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyaddr", 871 default_dns_files, uaddr, len, af, netid, mark) != NS_SUCCESS) 872 return NULL; 873 h_errno = NETDB_SUCCESS; 874 return hp; 875 } 876 877 struct hostent * 878 android_gethostbyaddrfornet(const void *addr, socklen_t len, int af, unsigned netid, unsigned mark) 879 { 880 const char *cache_mode = getenv("ANDROID_DNS_MODE"); 881 882 if (cache_mode == NULL || strcmp(cache_mode, "local") != 0) { 883 return android_gethostbyaddrfornet_proxy(addr, len, af, netid); 884 } else { 885 return android_gethostbyaddrfornet_real(addr,len, af, netid, mark); 886 } 887 } 888 889 struct hostent * 890 gethostbyaddr(const void *addr, socklen_t len, int af) 891 { 892 return android_gethostbyaddrfornet(addr, len, af, NETID_UNSET, MARK_UNSET); 893 } 894 895 896 static void 897 _sethtent(int f) 898 { 899 res_static rs = __res_get_static(); 900 if (rs == NULL) return; 901 if (!rs->hostf) 902 rs->hostf = fopen(_PATH_HOSTS, "r" ); 903 else 904 rewind(rs->hostf); 905 rs->stayopen = f; 906 } 907 908 static void 909 _endhtent(void) 910 { 911 res_static rs = __res_get_static(); 912 if (rs == NULL) return; 913 914 if (rs->hostf && !rs->stayopen) { 915 (void) fclose(rs->hostf); 916 rs->hostf = NULL; 917 } 918 } 919 920 static struct hostent * 921 _gethtent(void) 922 { 923 char *p; 924 char *cp, **q; 925 int af, len; 926 res_static rs = __res_get_static(); 927 928 if (!rs->hostf && !(rs->hostf = fopen(_PATH_HOSTS, "r" ))) { 929 h_errno = NETDB_INTERNAL; 930 return NULL; 931 } 932 again: 933 if (!(p = fgets(rs->hostbuf, sizeof rs->hostbuf, rs->hostf))) { 934 h_errno = HOST_NOT_FOUND; 935 return NULL; 936 } 937 if (*p == '#') 938 goto again; 939 if (!(cp = strpbrk(p, "#\n"))) 940 goto again; 941 *cp = '\0'; 942 if (!(cp = strpbrk(p, " \t"))) 943 goto again; 944 *cp++ = '\0'; 945 if (inet_pton(AF_INET6, p, (char *)(void *)rs->host_addr) > 0) { 946 af = AF_INET6; 947 len = IN6ADDRSZ; 948 } else if (inet_pton(AF_INET, p, (char *)(void *)rs->host_addr) > 0) { 949 res_state res = __res_get_state(); 950 if (res == NULL) 951 return NULL; 952 if (res->options & RES_USE_INET6) { 953 map_v4v6_address((char *)(void *)rs->host_addr, 954 (char *)(void *)rs->host_addr); 955 af = AF_INET6; 956 len = IN6ADDRSZ; 957 } else { 958 af = AF_INET; 959 len = INADDRSZ; 960 } 961 __res_put_state(res); 962 } else { 963 goto again; 964 } 965 /* if this is not something we're looking for, skip it. */ 966 if (rs->host.h_addrtype != 0 && rs->host.h_addrtype != af) 967 goto again; 968 if (rs->host.h_length != 0 && rs->host.h_length != len) 969 goto again; 970 rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr; 971 rs->h_addr_ptrs[1] = NULL; 972 rs->host.h_addr_list = rs->h_addr_ptrs; 973 rs->host.h_length = len; 974 rs->host.h_addrtype = af; 975 while (*cp == ' ' || *cp == '\t') 976 cp++; 977 rs->host.h_name = cp; 978 q = rs->host.h_aliases = rs->host_aliases; 979 if ((cp = strpbrk(cp, " \t")) != NULL) 980 *cp++ = '\0'; 981 while (cp && *cp) { 982 if (*cp == ' ' || *cp == '\t') { 983 cp++; 984 continue; 985 } 986 if (q < &rs->host_aliases[MAXALIASES - 1]) 987 *q++ = cp; 988 if ((cp = strpbrk(cp, " \t")) != NULL) 989 *cp++ = '\0'; 990 } 991 *q = NULL; 992 h_errno = NETDB_SUCCESS; 993 return &rs->host; 994 } 995 996 /*ARGSUSED*/ 997 int 998 _gethtbyname(void *rv, void *cb_data, va_list ap) 999 { 1000 struct hostent *hp; 1001 const char *name; 1002 int af; 1003 1004 assert(rv != NULL); 1005 1006 name = va_arg(ap, char *); 1007 /* NOSTRICT skip len */(void)va_arg(ap, int); 1008 af = va_arg(ap, int); 1009 1010 hp = NULL; 1011 #if 0 1012 { 1013 res_state res = __res_get_state(); 1014 if (res == NULL) 1015 return NS_NOTFOUND; 1016 if (res->options & RES_USE_INET6) 1017 hp = _gethtbyname2(name, AF_INET6); 1018 if (hp==NULL) 1019 hp = _gethtbyname2(name, AF_INET); 1020 __res_put_state(res); 1021 } 1022 #else 1023 hp = _gethtbyname2(name, af); 1024 #endif 1025 *((struct hostent **)rv) = hp; 1026 if (hp == NULL) { 1027 h_errno = HOST_NOT_FOUND; 1028 return NS_NOTFOUND; 1029 } 1030 return NS_SUCCESS; 1031 } 1032 1033 static struct hostent * 1034 _gethtbyname2(const char *name, int af) 1035 { 1036 struct hostent *p; 1037 char *tmpbuf, *ptr, **cp; 1038 int num; 1039 size_t len; 1040 res_static rs = __res_get_static(); 1041 1042 assert(name != NULL); 1043 1044 _sethtent(rs->stayopen); 1045 ptr = tmpbuf = NULL; 1046 num = 0; 1047 while ((p = _gethtent()) != NULL && num < MAXADDRS) { 1048 if (p->h_addrtype != af) 1049 continue; 1050 if (strcasecmp(p->h_name, name) != 0) { 1051 for (cp = p->h_aliases; *cp != NULL; cp++) 1052 if (strcasecmp(*cp, name) == 0) 1053 break; 1054 if (*cp == NULL) continue; 1055 } 1056 1057 if (num == 0) { 1058 size_t bufsize; 1059 char *src; 1060 1061 bufsize = strlen(p->h_name) + 2 + 1062 MAXADDRS * p->h_length + 1063 ALIGNBYTES; 1064 for (cp = p->h_aliases; *cp != NULL; cp++) 1065 bufsize += strlen(*cp) + 1; 1066 1067 if ((tmpbuf = malloc(bufsize)) == NULL) { 1068 h_errno = NETDB_INTERNAL; 1069 return NULL; 1070 } 1071 1072 ptr = tmpbuf; 1073 src = p->h_name; 1074 while ((*ptr++ = *src++) != '\0'); 1075 for (cp = p->h_aliases; *cp != NULL; cp++) { 1076 src = *cp; 1077 while ((*ptr++ = *src++) != '\0'); 1078 } 1079 *ptr++ = '\0'; 1080 1081 ptr = (char *)(void *)ALIGN(ptr); 1082 } 1083 1084 (void)memcpy(ptr, p->h_addr_list[0], (size_t)p->h_length); 1085 ptr += p->h_length; 1086 num++; 1087 } 1088 _endhtent(); 1089 if (num == 0) return NULL; 1090 1091 len = ptr - tmpbuf; 1092 if (len > (sizeof(rs->hostbuf) - ALIGNBYTES)) { 1093 free(tmpbuf); 1094 errno = ENOSPC; 1095 h_errno = NETDB_INTERNAL; 1096 return NULL; 1097 } 1098 ptr = memcpy((void *)ALIGN(rs->hostbuf), tmpbuf, len); 1099 free(tmpbuf); 1100 1101 rs->host.h_name = ptr; 1102 while (*ptr++); 1103 1104 cp = rs->host_aliases; 1105 while (*ptr) { 1106 *cp++ = ptr; 1107 while (*ptr++); 1108 } 1109 ptr++; 1110 *cp = NULL; 1111 1112 ptr = (char *)(void *)ALIGN(ptr); 1113 cp = rs->h_addr_ptrs; 1114 while (num--) { 1115 *cp++ = ptr; 1116 ptr += rs->host.h_length; 1117 } 1118 *cp = NULL; 1119 1120 return &rs->host; 1121 } 1122 1123 /*ARGSUSED*/ 1124 static int 1125 _gethtbyaddr(void *rv, void *cb_data, va_list ap) 1126 { 1127 struct hostent *p; 1128 const unsigned char *addr; 1129 int len, af; 1130 res_static rs = __res_get_static(); 1131 1132 assert(rv != NULL); 1133 1134 addr = va_arg(ap, unsigned char *); 1135 len = va_arg(ap, int); 1136 af = va_arg(ap, int); 1137 1138 rs->host.h_length = len; 1139 rs->host.h_addrtype = af; 1140 1141 _sethtent(rs->stayopen); 1142 while ((p = _gethtent()) != NULL) 1143 if (p->h_addrtype == af && !memcmp(p->h_addr, addr, 1144 (size_t)len)) 1145 break; 1146 _endhtent(); 1147 *((struct hostent **)rv) = p; 1148 if (p==NULL) { 1149 h_errno = HOST_NOT_FOUND; 1150 return NS_NOTFOUND; 1151 } 1152 return NS_SUCCESS; 1153 } 1154 1155 static void 1156 map_v4v6_address(const char *src, char *dst) 1157 { 1158 u_char *p = (u_char *)dst; 1159 char tmp[INADDRSZ]; 1160 int i; 1161 1162 assert(src != NULL); 1163 assert(dst != NULL); 1164 1165 /* Stash a temporary copy so our caller can update in place. */ 1166 (void)memcpy(tmp, src, INADDRSZ); 1167 /* Mark this ipv6 addr as a mapped ipv4. */ 1168 for (i = 0; i < 10; i++) 1169 *p++ = 0x00; 1170 *p++ = 0xff; 1171 *p++ = 0xff; 1172 /* Retrieve the saved copy and we're done. */ 1173 (void)memcpy((void *)p, tmp, INADDRSZ); 1174 } 1175 1176 static void 1177 map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep) 1178 { 1179 char **ap; 1180 1181 assert(hp != NULL); 1182 assert(bpp != NULL); 1183 assert(ep != NULL); 1184 1185 if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ) 1186 return; 1187 hp->h_addrtype = AF_INET6; 1188 hp->h_length = IN6ADDRSZ; 1189 for (ap = hp->h_addr_list; *ap; ap++) { 1190 int i = sizeof(align) - (size_t)((u_long)*bpp % sizeof(align)); 1191 1192 if (ep - *bpp < (i + IN6ADDRSZ)) { 1193 /* Out of memory. Truncate address list here. XXX */ 1194 *ap = NULL; 1195 return; 1196 } 1197 *bpp += i; 1198 map_v4v6_address(*ap, *bpp); 1199 *ap = *bpp; 1200 *bpp += IN6ADDRSZ; 1201 } 1202 } 1203 1204 static void 1205 addrsort(char **ap, int num, res_state res) 1206 { 1207 int i, j; 1208 char **p; 1209 short aval[MAXADDRS]; 1210 int needsort = 0; 1211 1212 assert(ap != NULL); 1213 1214 p = ap; 1215 for (i = 0; i < num; i++, p++) { 1216 for (j = 0 ; (unsigned)j < res->nsort; j++) 1217 if (res->sort_list[j].addr.s_addr == 1218 (((struct in_addr *)(void *)(*p))->s_addr & 1219 res->sort_list[j].mask)) 1220 break; 1221 aval[i] = j; 1222 if (needsort == 0 && i > 0 && j < aval[i-1]) 1223 needsort = i; 1224 } 1225 if (!needsort) 1226 return; 1227 1228 while (needsort < num) { 1229 for (j = needsort - 1; j >= 0; j--) { 1230 if (aval[j] > aval[j+1]) { 1231 char *hp; 1232 1233 i = aval[j]; 1234 aval[j] = aval[j+1]; 1235 aval[j+1] = i; 1236 1237 hp = ap[j]; 1238 ap[j] = ap[j+1]; 1239 ap[j+1] = hp; 1240 } else 1241 break; 1242 } 1243 needsort++; 1244 } 1245 } 1246 1247 struct hostent * 1248 gethostent(void) 1249 { 1250 res_static rs = __res_get_static(); 1251 rs->host.h_addrtype = 0; 1252 rs->host.h_length = 0; 1253 return _gethtent(); 1254 } 1255 1256 /*ARGSUSED*/ 1257 static int 1258 _dns_gethtbyname(void *rv, void *cb_data, va_list ap) 1259 { 1260 querybuf *buf; 1261 int n, type; 1262 struct hostent *hp; 1263 const char *name; 1264 int af; 1265 res_state res; 1266 1267 assert(rv != NULL); 1268 1269 name = va_arg(ap, char *); 1270 /* NOSTRICT skip len */(void)va_arg(ap, int); 1271 af = va_arg(ap, int); 1272 1273 switch (af) { 1274 case AF_INET: 1275 type = T_A; 1276 break; 1277 case AF_INET6: 1278 type = T_AAAA; 1279 break; 1280 default: 1281 return NS_UNAVAIL; 1282 } 1283 buf = malloc(sizeof(*buf)); 1284 if (buf == NULL) { 1285 h_errno = NETDB_INTERNAL; 1286 return NS_NOTFOUND; 1287 } 1288 res = __res_get_state(); 1289 if (res == NULL) { 1290 free(buf); 1291 return NS_NOTFOUND; 1292 } 1293 n = res_nsearch(res, name, C_IN, type, buf->buf, sizeof(buf->buf)); 1294 if (n < 0) { 1295 free(buf); 1296 dprintf("res_nsearch failed (%d)\n", res, n); 1297 __res_put_state(res); 1298 return NS_NOTFOUND; 1299 } 1300 hp = getanswer(buf, n, name, type, res); 1301 free(buf); 1302 __res_put_state(res); 1303 if (hp == NULL) 1304 switch (h_errno) { 1305 case HOST_NOT_FOUND: 1306 return NS_NOTFOUND; 1307 case TRY_AGAIN: 1308 return NS_TRYAGAIN; 1309 default: 1310 return NS_UNAVAIL; 1311 } 1312 *((struct hostent **)rv) = hp; 1313 return NS_SUCCESS; 1314 } 1315 1316 /*ARGSUSED*/ 1317 static int 1318 _dns_gethtbyaddr(void *rv, void *cb_data, va_list ap) 1319 { 1320 char qbuf[MAXDNAME + 1], *qp, *ep; 1321 int n; 1322 querybuf *buf; 1323 struct hostent *hp; 1324 const unsigned char *uaddr; 1325 int len, af, advance; 1326 res_state res; 1327 unsigned netid, mark; 1328 res_static rs = __res_get_static(); 1329 1330 assert(rv != NULL); 1331 1332 uaddr = va_arg(ap, unsigned char *); 1333 len = va_arg(ap, int); 1334 af = va_arg(ap, int); 1335 netid = va_arg(ap, unsigned); 1336 mark = va_arg(ap, unsigned); 1337 1338 switch (af) { 1339 case AF_INET: 1340 (void)snprintf(qbuf, sizeof(qbuf), "%u.%u.%u.%u.in-addr.arpa", 1341 (uaddr[3] & 0xff), (uaddr[2] & 0xff), 1342 (uaddr[1] & 0xff), (uaddr[0] & 0xff)); 1343 break; 1344 1345 case AF_INET6: 1346 qp = qbuf; 1347 ep = qbuf + sizeof(qbuf) - 1; 1348 for (n = IN6ADDRSZ - 1; n >= 0; n--) { 1349 advance = snprintf(qp, (size_t)(ep - qp), "%x.%x.", 1350 uaddr[n] & 0xf, 1351 ((unsigned int)uaddr[n] >> 4) & 0xf); 1352 if (advance > 0 && qp + advance < ep) 1353 qp += advance; 1354 else { 1355 h_errno = NETDB_INTERNAL; 1356 return NS_NOTFOUND; 1357 } 1358 } 1359 if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) { 1360 h_errno = NETDB_INTERNAL; 1361 return NS_NOTFOUND; 1362 } 1363 break; 1364 default: 1365 abort(); 1366 } 1367 1368 buf = malloc(sizeof(*buf)); 1369 if (buf == NULL) { 1370 h_errno = NETDB_INTERNAL; 1371 return NS_NOTFOUND; 1372 } 1373 res = __res_get_state(); 1374 if (res == NULL) { 1375 free(buf); 1376 return NS_NOTFOUND; 1377 } 1378 res_setnetid(res, netid); 1379 res_setmark(res, mark); 1380 n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, sizeof(buf->buf)); 1381 if (n < 0) { 1382 free(buf); 1383 dprintf("res_nquery failed (%d)\n", res, n); 1384 __res_put_state(res); 1385 return NS_NOTFOUND; 1386 } 1387 hp = getanswer(buf, n, qbuf, T_PTR, res); 1388 free(buf); 1389 if (hp == NULL) { 1390 __res_put_state(res); 1391 switch (h_errno) { 1392 case HOST_NOT_FOUND: 1393 return NS_NOTFOUND; 1394 case TRY_AGAIN: 1395 return NS_TRYAGAIN; 1396 default: 1397 return NS_UNAVAIL; 1398 } 1399 } 1400 hp->h_addrtype = af; 1401 hp->h_length = len; 1402 (void)memcpy(rs->host_addr, uaddr, (size_t)len); 1403 rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr; 1404 rs->h_addr_ptrs[1] = NULL; 1405 if (af == AF_INET && (res->options & RES_USE_INET6)) { 1406 map_v4v6_address((char *)(void *)rs->host_addr, 1407 (char *)(void *)rs->host_addr); 1408 hp->h_addrtype = AF_INET6; 1409 hp->h_length = IN6ADDRSZ; 1410 } 1411 1412 __res_put_state(res); 1413 *((struct hostent **)rv) = hp; 1414 h_errno = NETDB_SUCCESS; 1415 return NS_SUCCESS; 1416 } 1417