1 /* 2 * Copyright (c) 1983, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. 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 * $FreeBSD$ 34 */ 35 36 #if defined(LIBC_SCCS) && !defined(lint) 37 static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; 38 #endif /* LIBC_SCCS and not lint */ 39 40 #include <sys/param.h> 41 #include <sys/socket.h> 42 #include <sys/stat.h> 43 44 #include <netinet/in.h> 45 #include <arpa/inet.h> 46 47 #include <signal.h> 48 #include <fcntl.h> 49 #include <netdb.h> 50 #include <unistd.h> 51 #include <pwd.h> 52 #include <errno.h> 53 #include <stdio.h> 54 #include <ctype.h> 55 #include <string.h> 56 #ifdef YP 57 #include <rpc/rpc.h> 58 #include <rpcsvc/yp_prot.h> 59 #include <rpcsvc/ypclnt.h> 60 #endif 61 #include <arpa/nameser.h> 62 63 /* wrapper for KAME-special getnameinfo() */ 64 #ifndef NI_WITHSCOPEID 65 #define NI_WITHSCOPEID 0 66 #endif 67 68 extern int innetgr __P(( const char *, const char *, const char *, const char * )); 69 70 #define max(a, b) ((a > b) ? a : b) 71 72 int __ivaliduser __P((FILE *, u_int32_t, const char *, const char *)); 73 int __ivaliduser_af __P((FILE *,const void *, const char *, const char *, 74 int, int)); 75 int __ivaliduser_sa __P((FILE *, const struct sockaddr *, socklen_t, 76 const char *,const char *)); 77 static int __icheckhost __P((const struct sockaddr *, socklen_t, 78 const char *)); 79 80 char paddr[NI_MAXHOST]; 81 82 int 83 rcmd(ahost, rport, locuser, remuser, cmd, fd2p) 84 char **ahost; 85 u_short rport; 86 const char *locuser, *remuser, *cmd; 87 int *fd2p; 88 { 89 return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); 90 } 91 92 int 93 rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af) 94 char **ahost; 95 u_short rport; 96 const char *locuser, *remuser, *cmd; 97 int *fd2p; 98 int af; 99 { 100 struct addrinfo hints, *res, *ai; 101 struct sockaddr_storage from; 102 fd_set reads; 103 long oldmask; 104 pid_t pid; 105 int s, aport, lport, timo, error; 106 char c; 107 int refused, nres; 108 char num[8]; 109 static char canonnamebuf[MAXDNAME]; /* is it proper here? */ 110 111 pid = getpid(); 112 113 memset(&hints, 0, sizeof(hints)); 114 hints.ai_flags = AI_CANONNAME; 115 hints.ai_family = af; 116 hints.ai_socktype = SOCK_STREAM; 117 hints.ai_protocol = 0; 118 (void)snprintf(num, sizeof(num), "%d", ntohs(rport)); 119 error = getaddrinfo(*ahost, num, &hints, &res); 120 if (error) { 121 fprintf(stderr, "rcmd: getaddrinfo: %s\n", 122 gai_strerror(error)); 123 if (error == EAI_SYSTEM) 124 fprintf(stderr, "rcmd: getaddrinfo: %s\n", 125 strerror(errno)); 126 return (-1); 127 } 128 129 if (res->ai_canonname 130 && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) { 131 strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf)); 132 *ahost = canonnamebuf; 133 } 134 nres = 0; 135 for (ai = res; ai; ai = ai->ai_next) 136 nres++; 137 ai = res; 138 refused = 0; 139 oldmask = sigblock(sigmask(SIGURG)); 140 for (timo = 1, lport = IPPORT_RESERVED - 1;;) { 141 s = rresvport_af(&lport, ai->ai_family); 142 if (s < 0) { 143 if (errno != EAGAIN && ai->ai_next) { 144 ai = ai->ai_next; 145 continue; 146 } 147 if (errno == EAGAIN) 148 (void)fprintf(stderr, 149 "rcmd: socket: All ports in use\n"); 150 else 151 (void)fprintf(stderr, "rcmd: socket: %s\n", 152 strerror(errno)); 153 freeaddrinfo(res); 154 sigsetmask(oldmask); 155 return (-1); 156 } 157 _fcntl(s, F_SETOWN, pid); 158 if (connect(s, ai->ai_addr, ai->ai_addrlen) >= 0) 159 break; 160 (void)_close(s); 161 if (errno == EADDRINUSE) { 162 lport--; 163 continue; 164 } 165 if (errno == ECONNREFUSED) 166 refused = 1; 167 if (ai->ai_next == NULL && (!refused || timo > 16)) { 168 (void)fprintf(stderr, "%s: %s\n", 169 *ahost, strerror(errno)); 170 freeaddrinfo(res); 171 sigsetmask(oldmask); 172 return (-1); 173 } 174 if (nres > 1) { 175 int oerrno = errno; 176 177 getnameinfo(ai->ai_addr, ai->ai_addrlen, 178 paddr, sizeof(paddr), 179 NULL, 0, 180 NI_NUMERICHOST|NI_WITHSCOPEID); 181 (void)fprintf(stderr, "connect to address %s: ", 182 paddr); 183 errno = oerrno; 184 perror(0); 185 } 186 if ((ai = ai->ai_next) == NULL) { 187 /* refused && timo <= 16 */ 188 struct timespec time_to_sleep, time_remaining; 189 190 time_to_sleep.tv_sec = timo; 191 time_to_sleep.tv_nsec = 0; 192 (void)_nanosleep(&time_to_sleep, &time_remaining); 193 timo *= 2; 194 ai = res; 195 refused = 0; 196 } 197 if (nres > 1) { 198 getnameinfo(ai->ai_addr, ai->ai_addrlen, 199 paddr, sizeof(paddr), 200 NULL, 0, 201 NI_NUMERICHOST|NI_WITHSCOPEID); 202 fprintf(stderr, "Trying %s...\n", paddr); 203 } 204 } 205 lport--; 206 if (fd2p == 0) { 207 _write(s, "", 1); 208 lport = 0; 209 } else { 210 char num[8]; 211 int s2 = rresvport_af(&lport, ai->ai_family), s3; 212 int len = ai->ai_addrlen; 213 int nfds; 214 215 if (s2 < 0) 216 goto bad; 217 listen(s2, 1); 218 (void)snprintf(num, sizeof(num), "%d", lport); 219 if (_write(s, num, strlen(num)+1) != strlen(num)+1) { 220 (void)fprintf(stderr, 221 "rcmd: write (setting up stderr): %s\n", 222 strerror(errno)); 223 (void)_close(s2); 224 goto bad; 225 } 226 nfds = max(s, s2)+1; 227 if(nfds > FD_SETSIZE) { 228 fprintf(stderr, "rcmd: too many files\n"); 229 (void)_close(s2); 230 goto bad; 231 } 232 again: 233 FD_ZERO(&reads); 234 FD_SET(s, &reads); 235 FD_SET(s2, &reads); 236 errno = 0; 237 if (select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){ 238 if (errno != 0) 239 (void)fprintf(stderr, 240 "rcmd: select (setting up stderr): %s\n", 241 strerror(errno)); 242 else 243 (void)fprintf(stderr, 244 "select: protocol failure in circuit setup\n"); 245 (void)_close(s2); 246 goto bad; 247 } 248 s3 = accept(s2, (struct sockaddr *)&from, &len); 249 switch (from.ss_family) { 250 case AF_INET: 251 aport = ntohs(((struct sockaddr_in *)&from)->sin_port); 252 break; 253 #ifdef INET6 254 case AF_INET6: 255 aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port); 256 break; 257 #endif 258 default: 259 aport = 0; /* error */ 260 break; 261 } 262 /* 263 * XXX careful for ftp bounce attacks. If discovered, shut them 264 * down and check for the real auxiliary channel to connect. 265 */ 266 if (aport == 20) { 267 _close(s3); 268 goto again; 269 } 270 (void)_close(s2); 271 if (s3 < 0) { 272 (void)fprintf(stderr, 273 "rcmd: accept: %s\n", strerror(errno)); 274 lport = 0; 275 goto bad; 276 } 277 *fd2p = s3; 278 if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) { 279 (void)fprintf(stderr, 280 "socket: protocol failure in circuit setup.\n"); 281 goto bad2; 282 } 283 } 284 (void)_write(s, locuser, strlen(locuser)+1); 285 (void)_write(s, remuser, strlen(remuser)+1); 286 (void)_write(s, cmd, strlen(cmd)+1); 287 if (_read(s, &c, 1) != 1) { 288 (void)fprintf(stderr, 289 "rcmd: %s: %s\n", *ahost, strerror(errno)); 290 goto bad2; 291 } 292 if (c != 0) { 293 while (_read(s, &c, 1) == 1) { 294 (void)_write(STDERR_FILENO, &c, 1); 295 if (c == '\n') 296 break; 297 } 298 goto bad2; 299 } 300 sigsetmask(oldmask); 301 freeaddrinfo(res); 302 return (s); 303 bad2: 304 if (lport) 305 (void)_close(*fd2p); 306 bad: 307 (void)_close(s); 308 sigsetmask(oldmask); 309 freeaddrinfo(res); 310 return (-1); 311 } 312 313 int 314 rresvport(port) 315 int *port; 316 { 317 return rresvport_af(port, AF_INET); 318 } 319 320 int 321 rresvport_af(alport, family) 322 int *alport, family; 323 { 324 int i, s, len, err; 325 struct sockaddr_storage ss; 326 u_short *sport; 327 328 memset(&ss, 0, sizeof(ss)); 329 ss.ss_family = family; 330 switch (family) { 331 case AF_INET: 332 ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in); 333 sport = &((struct sockaddr_in *)&ss)->sin_port; 334 ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY; 335 break; 336 #ifdef INET6 337 case AF_INET6: 338 ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6); 339 sport = &((struct sockaddr_in6 *)&ss)->sin6_port; 340 ((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any; 341 break; 342 #endif 343 default: 344 errno = EAFNOSUPPORT; 345 return -1; 346 } 347 348 s = socket(ss.ss_family, SOCK_STREAM, 0); 349 if (s < 0) 350 return (-1); 351 #if 0 /* compat_exact_traditional_rresvport_semantics */ 352 sin.sin_port = htons((u_short)*alport); 353 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 354 return (s); 355 if (errno != EADDRINUSE) { 356 (void)_close(s); 357 return (-1); 358 } 359 #endif 360 *sport = 0; 361 if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) { 362 (void)_close(s); 363 return (-1); 364 } 365 *alport = (int)ntohs(*sport); 366 return (s); 367 } 368 369 int __check_rhosts_file = 1; 370 char *__rcmd_errstr; 371 372 int 373 ruserok(rhost, superuser, ruser, luser) 374 const char *rhost, *ruser, *luser; 375 int superuser; 376 { 377 struct addrinfo hints, *res, *r; 378 int error; 379 380 memset(&hints, 0, sizeof(hints)); 381 hints.ai_family = PF_UNSPEC; 382 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 383 error = getaddrinfo(rhost, "0", &hints, &res); 384 if (error) 385 return (-1); 386 387 for (r = res; r; r = r->ai_next) { 388 if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, 389 luser) == 0) { 390 freeaddrinfo(res); 391 return (0); 392 } 393 } 394 freeaddrinfo(res); 395 return (-1); 396 } 397 398 /* 399 * New .rhosts strategy: We are passed an ip address. We spin through 400 * hosts.equiv and .rhosts looking for a match. When the .rhosts only 401 * has ip addresses, we don't have to trust a nameserver. When it 402 * contains hostnames, we spin through the list of addresses the nameserver 403 * gives us and look for a match. 404 * 405 * Returns 0 if ok, -1 if not ok. 406 */ 407 int 408 iruserok(raddr, superuser, ruser, luser) 409 unsigned long raddr; 410 int superuser; 411 const char *ruser, *luser; 412 { 413 struct sockaddr_in sin; 414 415 memset(&sin, 0, sizeof(sin)); 416 sin.sin_family = AF_INET; 417 sin.sin_len = sizeof(struct sockaddr_in); 418 memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 419 return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser, 420 ruser, luser); 421 } 422 423 /* 424 * AF independent extension of iruserok. 425 * 426 * Returns 0 if ok, -1 if not ok. 427 */ 428 int 429 iruserok_sa(ra, rlen, superuser, ruser, luser) 430 const void *ra; 431 int rlen; 432 int superuser; 433 const char *ruser, *luser; 434 { 435 register char *cp; 436 struct stat sbuf; 437 struct passwd *pwd; 438 FILE *hostf; 439 uid_t uid; 440 int first; 441 char pbuf[MAXPATHLEN]; 442 const struct sockaddr *raddr; 443 struct sockaddr_storage ss; 444 445 /* avoid alignment issue */ 446 if (rlen > sizeof(ss)) 447 return(-1); 448 memcpy(&ss, ra, rlen); 449 raddr = (struct sockaddr *)&ss; 450 451 first = 1; 452 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); 453 again: 454 if (hostf) { 455 if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) { 456 (void)fclose(hostf); 457 return (0); 458 } 459 (void)fclose(hostf); 460 } 461 if (first == 1 && (__check_rhosts_file || superuser)) { 462 first = 0; 463 if ((pwd = getpwnam(luser)) == NULL) 464 return (-1); 465 (void)strcpy(pbuf, pwd->pw_dir); 466 (void)strcat(pbuf, "/.rhosts"); 467 468 /* 469 * Change effective uid while opening .rhosts. If root and 470 * reading an NFS mounted file system, can't read files that 471 * are protected read/write owner only. 472 */ 473 uid = geteuid(); 474 (void)seteuid(pwd->pw_uid); 475 hostf = fopen(pbuf, "r"); 476 (void)seteuid(uid); 477 478 if (hostf == NULL) 479 return (-1); 480 /* 481 * If not a regular file, or is owned by someone other than 482 * user or root or if writeable by anyone but the owner, quit. 483 */ 484 cp = NULL; 485 if (lstat(pbuf, &sbuf) < 0) 486 cp = ".rhosts lstat failed"; 487 else if (!S_ISREG(sbuf.st_mode)) 488 cp = ".rhosts not regular file"; 489 else if (fstat(fileno(hostf), &sbuf) < 0) 490 cp = ".rhosts fstat failed"; 491 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) 492 cp = "bad .rhosts owner"; 493 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) 494 cp = ".rhosts writeable by other than owner"; 495 /* If there were any problems, quit. */ 496 if (cp) { 497 __rcmd_errstr = cp; 498 (void)fclose(hostf); 499 return (-1); 500 } 501 goto again; 502 } 503 return (-1); 504 } 505 506 /* 507 * XXX 508 * Don't make static, used by lpd(8). 509 * 510 * Returns 0 if ok, -1 if not ok. 511 */ 512 int 513 __ivaliduser(hostf, raddr, luser, ruser) 514 FILE *hostf; 515 u_int32_t raddr; 516 const char *luser, *ruser; 517 { 518 struct sockaddr_in sin; 519 520 memset(&sin, 0, sizeof(sin)); 521 sin.sin_family = AF_INET; 522 sin.sin_len = sizeof(struct sockaddr_in); 523 memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 524 return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len, 525 luser, ruser); 526 } 527 528 /* 529 * Returns 0 if ok, -1 if not ok. 530 * 531 * XXX obsolete API. 532 */ 533 int 534 __ivaliduser_af(hostf, raddr, luser, ruser, af, len) 535 FILE *hostf; 536 const void *raddr; 537 const char *luser, *ruser; 538 int af, len; 539 { 540 struct sockaddr *sa = NULL; 541 struct sockaddr_in *sin = NULL; 542 #ifdef INET6 543 struct sockaddr_in6 *sin6 = NULL; 544 #endif 545 struct sockaddr_storage ss; 546 547 memset(&ss, 0, sizeof(ss)); 548 switch (af) { 549 case AF_INET: 550 if (len != sizeof(sin->sin_addr)) 551 return -1; 552 sin = (struct sockaddr_in *)&ss; 553 sin->sin_family = AF_INET; 554 sin->sin_len = sizeof(struct sockaddr_in); 555 memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr)); 556 break; 557 #ifdef INET6 558 case AF_INET6: 559 if (len != sizeof(sin6->sin6_addr)) 560 return -1; 561 /* you will lose scope info */ 562 sin6 = (struct sockaddr_in6 *)&ss; 563 sin6->sin6_family = AF_INET6; 564 sin6->sin6_len = sizeof(struct sockaddr_in6); 565 memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr)); 566 break; 567 #endif 568 default: 569 return -1; 570 } 571 572 sa = (struct sockaddr *)&ss; 573 return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser); 574 } 575 576 /* 577 * Returns 0 if ok, -1 if not ok. 578 */ 579 int 580 __ivaliduser_sa(hostf, raddr, salen, luser, ruser) 581 FILE *hostf; 582 const struct sockaddr *raddr; 583 socklen_t salen; 584 const char *luser, *ruser; 585 { 586 register char *user, *p; 587 int ch; 588 char buf[MAXHOSTNAMELEN + 128]; /* host + login */ 589 char hname[MAXHOSTNAMELEN]; 590 /* Presumed guilty until proven innocent. */ 591 int userok = 0, hostok = 0; 592 int h_error; 593 #ifdef YP 594 char *ypdomain; 595 596 if (yp_get_default_domain(&ypdomain)) 597 ypdomain = NULL; 598 #else 599 #define ypdomain NULL 600 #endif 601 /* We need to get the damn hostname back for netgroup matching. */ 602 if (getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0, 603 NI_NAMEREQD) != 0) 604 return (-1); 605 606 while (fgets(buf, sizeof(buf), hostf)) { 607 p = buf; 608 /* Skip lines that are too long. */ 609 if (strchr(p, '\n') == NULL) { 610 while ((ch = getc(hostf)) != '\n' && ch != EOF); 611 continue; 612 } 613 if (*p == '\n' || *p == '#') { 614 /* comment... */ 615 continue; 616 } 617 while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { 618 *p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p; 619 p++; 620 } 621 if (*p == ' ' || *p == '\t') { 622 *p++ = '\0'; 623 while (*p == ' ' || *p == '\t') 624 p++; 625 user = p; 626 while (*p != '\n' && *p != ' ' && 627 *p != '\t' && *p != '\0') 628 p++; 629 } else 630 user = p; 631 *p = '\0'; 632 /* 633 * Do +/- and +@/-@ checking. This looks really nasty, 634 * but it matches SunOS's behavior so far as I can tell. 635 */ 636 switch(buf[0]) { 637 case '+': 638 if (!buf[1]) { /* '+' matches all hosts */ 639 hostok = 1; 640 break; 641 } 642 if (buf[1] == '@') /* match a host by netgroup */ 643 hostok = innetgr((char *)&buf[2], 644 (char *)&hname, NULL, ypdomain); 645 else /* match a host by addr */ 646 hostok = __icheckhost(raddr, salen, 647 (char *)&buf[1]); 648 break; 649 case '-': /* reject '-' hosts and all their users */ 650 if (buf[1] == '@') { 651 if (innetgr((char *)&buf[2], 652 (char *)&hname, NULL, ypdomain)) 653 return(-1); 654 } else { 655 if (__icheckhost(raddr, salen, 656 (char *)&buf[1])) 657 return(-1); 658 } 659 break; 660 default: /* if no '+' or '-', do a simple match */ 661 hostok = __icheckhost(raddr, salen, buf); 662 break; 663 } 664 switch(*user) { 665 case '+': 666 if (!*(user+1)) { /* '+' matches all users */ 667 userok = 1; 668 break; 669 } 670 if (*(user+1) == '@') /* match a user by netgroup */ 671 userok = innetgr(user+2, NULL, ruser, ypdomain); 672 else /* match a user by direct specification */ 673 userok = !(strcmp(ruser, user+1)); 674 break; 675 case '-': /* if we matched a hostname, */ 676 if (hostok) { /* check for user field rejections */ 677 if (!*(user+1)) 678 return(-1); 679 if (*(user+1) == '@') { 680 if (innetgr(user+2, NULL, 681 ruser, ypdomain)) 682 return(-1); 683 } else { 684 if (!strcmp(ruser, user+1)) 685 return(-1); 686 } 687 } 688 break; 689 default: /* no rejections: try to match the user */ 690 if (hostok) 691 userok = !(strcmp(ruser,*user ? user : luser)); 692 break; 693 } 694 if (hostok && userok) 695 return(0); 696 } 697 return (-1); 698 } 699 700 /* 701 * Returns "true" if match, 0 if no match. 702 * 703 * NI_WITHSCOPEID is useful for comparing sin6_scope_id portion 704 * if af == AF_INET6. 705 */ 706 static int 707 __icheckhost(raddr, salen, lhost) 708 const struct sockaddr *raddr; 709 socklen_t salen; 710 const char *lhost; 711 { 712 struct sockaddr_in sin; 713 struct sockaddr_in6 *sin6; 714 struct addrinfo hints, *res, *r; 715 int error; 716 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 717 718 if (raddr->sa_family == AF_INET6) { 719 sin6 = (struct sockaddr_in6 *)raddr; 720 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 721 memset(&sin, 0, sizeof(sin)); 722 sin.sin_family = AF_INET; 723 sin.sin_len = sizeof(struct sockaddr_in); 724 memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], 725 sizeof(sin.sin_addr)); 726 raddr = (struct sockaddr *)&sin; 727 salen = sin.sin_len; 728 } 729 } 730 731 h1[0] = '\0'; 732 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 733 NI_NUMERICHOST | NI_WITHSCOPEID) != 0) 734 return (0); 735 736 /* Resolve laddr into sockaddr */ 737 memset(&hints, 0, sizeof(hints)); 738 hints.ai_family = raddr->sa_family; 739 hints.ai_socktype = SOCK_DGRAM; /*XXX dummy*/ 740 res = NULL; 741 error = getaddrinfo(lhost, "0", &hints, &res); 742 if (error) 743 return (0); 744 745 for (r = res; r ; r = r->ai_next) { 746 h2[0] = '\0'; 747 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 748 NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0) 749 continue; 750 if (strcmp(h1, h2) == 0) { 751 freeaddrinfo(res); 752 return (1); 753 } 754 } 755 756 /* No match. */ 757 freeaddrinfo(res); 758 return (0); 759 } 760