1 /* 2 * Copyright (c) 1983, 1988, 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1983, 1988, 1989 The Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)rlogind.c 5.46 (Berkeley) 06/21/90"; 26 #endif /* not lint */ 27 28 #ifdef KERBEROS 29 /* From: 30 * $Source: /mit/kerberos/ucb/mit/rlogind/RCS/rlogind.c,v $ 31 * $Header: rlogind.c,v 5.0 89/06/26 18:31:01 kfall Locked $ 32 */ 33 #endif 34 35 /* 36 * remote login server: 37 * \0 38 * remuser\0 39 * locuser\0 40 * terminal_type/speed\0 41 * data 42 */ 43 44 #define FD_SETSIZE 16 /* don't need many bits for select */ 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 #include <sys/socket.h> 48 #include <sys/wait.h> 49 #include <sys/file.h> 50 #include <sys/signal.h> 51 #include <sys/ioctl.h> 52 #include <sys/termios.h> 53 54 #include <netinet/in.h> 55 56 #include <errno.h> 57 #include <pwd.h> 58 #include <netdb.h> 59 #include <syslog.h> 60 #include <string.h> 61 #include <stdio.h> 62 #include <unistd.h> 63 #include "pathnames.h" 64 65 #ifndef TIOCPKT_WINDOW 66 #define TIOCPKT_WINDOW 0x80 67 #endif 68 69 #ifdef KERBEROS 70 #include <kerberosIV/des.h> 71 #include <kerberosIV/krb.h> 72 #define SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n" 73 74 AUTH_DAT *kdata; 75 KTEXT ticket; 76 u_char auth_buf[sizeof(AUTH_DAT)]; 77 u_char tick_buf[sizeof(KTEXT_ST)]; 78 Key_schedule schedule; 79 int encrypt = 0, retval, use_kerberos = 0, vacuous = 0; 80 81 #define ARGSTR "alnkvx" 82 #else 83 #define ARGSTR "aln" 84 #endif /* KERBEROS */ 85 86 char *env[2]; 87 #define NMAX 30 88 char lusername[NMAX+1], rusername[NMAX+1]; 89 static char term[64] = "TERM="; 90 #define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ 91 int keepalive = 1; 92 int check_all = 0; 93 94 extern int errno; 95 int reapchild(); 96 struct passwd *getpwnam(), *pwd; 97 char *malloc(); 98 99 main(argc, argv) 100 int argc; 101 char **argv; 102 { 103 extern int opterr, optind; 104 extern int _check_rhosts_file; 105 int ch; 106 int on = 1, fromlen; 107 struct sockaddr_in from; 108 109 openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); 110 111 opterr = 0; 112 while ((ch = getopt(argc, argv, ARGSTR)) != EOF) 113 switch (ch) { 114 case 'a': 115 check_all = 1; 116 break; 117 case 'l': 118 _check_rhosts_file = 0; 119 break; 120 case 'n': 121 keepalive = 0; 122 break; 123 #ifdef KERBEROS 124 case 'k': 125 use_kerberos = 1; 126 break; 127 case 'v': 128 vacuous = 1; 129 break; 130 case 'x': 131 encrypt = 1; 132 break; 133 #endif 134 case '?': 135 default: 136 usage(); 137 break; 138 } 139 argc -= optind; 140 argv += optind; 141 142 #ifdef KERBEROS 143 if (use_kerberos && vacuous) { 144 usage(); 145 fatal(STDERR_FILENO, "only one of -k and -v allowed", 0); 146 } 147 #endif 148 fromlen = sizeof (from); 149 if (getpeername(0, &from, &fromlen) < 0) { 150 syslog(LOG_ERR,"Can't get peer name of remote host: %m"); 151 fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); 152 } 153 if (keepalive && 154 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) 155 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 156 doit(0, &from); 157 } 158 159 int child; 160 int cleanup(); 161 int netf; 162 char *line; 163 int confirmed; 164 extern char *inet_ntoa(); 165 166 struct winsize win = { 0, 0, 0, 0 }; 167 168 169 doit(f, fromp) 170 int f; 171 struct sockaddr_in *fromp; 172 { 173 int i, p, t, pid, on = 1; 174 int authenticated = 0, hostok = 0; 175 register struct hostent *hp; 176 char remotehost[2 * MAXHOSTNAMELEN + 1]; 177 struct hostent hostent; 178 char c; 179 180 alarm(60); 181 read(f, &c, 1); 182 183 if (c != 0) 184 exit(1); 185 #ifdef KERBEROS 186 if (vacuous) 187 fatal(f, "Remote host requires Kerberos authentication", 0); 188 #endif 189 190 alarm(0); 191 fromp->sin_port = ntohs((u_short)fromp->sin_port); 192 hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr), 193 fromp->sin_family); 194 if (hp == 0) { 195 /* 196 * Only the name is used below. 197 */ 198 hp = &hostent; 199 hp->h_name = inet_ntoa(fromp->sin_addr); 200 hostok++; 201 } else if (check_all || local_domain(hp->h_name)) { 202 /* 203 * If name returned by gethostbyaddr is in our domain, 204 * attempt to verify that we haven't been fooled by someone 205 * in a remote net; look up the name and check that this 206 * address corresponds to the name. 207 */ 208 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); 209 remotehost[sizeof(remotehost) - 1] = 0; 210 hp = gethostbyname(remotehost); 211 if (hp) 212 for (; hp->h_addr_list[0]; hp->h_addr_list++) 213 if (!bcmp(hp->h_addr_list[0], (caddr_t)&fromp->sin_addr, 214 sizeof(fromp->sin_addr))) { 215 hostok++; 216 break; 217 } 218 } else 219 hostok++; 220 221 #ifdef KERBEROS 222 if (use_kerberos) { 223 if (!hostok) 224 fatal(f, "krlogind: Host address mismatch.", 0); 225 retval = do_krb_login(hp->h_name, fromp, encrypt); 226 if (retval == 0) 227 authenticated++; 228 else if (retval > 0) 229 fatal(f, krb_err_txt[retval], 0); 230 write(f, &c, 1); 231 confirmed = 1; /* we sent the null! */ 232 } else 233 #endif 234 { 235 if (fromp->sin_family != AF_INET || 236 fromp->sin_port >= IPPORT_RESERVED || 237 fromp->sin_port < IPPORT_RESERVED/2) { 238 syslog(LOG_NOTICE, "Connection from %s on illegal port", 239 inet_ntoa(fromp->sin_addr)); 240 fatal(f, "Permission denied", 0); 241 } 242 #ifdef IP_OPTIONS 243 { 244 u_char optbuf[BUFSIZ/3], *cp; 245 char lbuf[BUFSIZ], *lp; 246 int optsize = sizeof(optbuf), ipproto; 247 struct protoent *ip; 248 249 if ((ip = getprotobyname("ip")) != NULL) 250 ipproto = ip->p_proto; 251 else 252 ipproto = IPPROTO_IP; 253 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, 254 &optsize) == 0 && optsize != 0) { 255 lp = lbuf; 256 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) 257 sprintf(lp, " %2.2x", *cp); 258 syslog(LOG_NOTICE, 259 "Connection received using IP options (ignored):%s", 260 lbuf); 261 if (setsockopt(0, ipproto, IP_OPTIONS, 262 (char *)NULL, &optsize) != 0) { 263 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); 264 exit(1); 265 } 266 } 267 } 268 #endif 269 if (do_rlogin(hp->h_name) == 0 && hostok) 270 authenticated++; 271 } 272 273 for (c = 'p'; c <= 's'; c++) { 274 struct stat stb; 275 line = "/dev/ptyXX"; 276 line[strlen("/dev/pty")] = c; 277 line[strlen("/dev/ptyp")] = '0'; 278 if (stat(line, &stb) < 0) 279 break; 280 for (i = 0; i < 16; i++) { 281 line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i]; 282 p = open(line, O_RDWR); 283 if (p > 0) 284 goto gotpty; 285 } 286 } 287 fatal(f, "Out of ptys", 0); 288 /*NOTREACHED*/ 289 gotpty: 290 (void) ioctl(p, TIOCSWINSZ, &win); 291 netf = f; 292 line[sizeof(_PATH_DEV) - 1] = 't'; 293 t = open(line, O_RDWR); 294 if (t < 0) 295 fatal(f, line, 1); 296 if (fchmod(t, 0)) 297 fatal(f, line, 1); 298 (void)signal(SIGHUP, SIG_IGN); 299 #ifdef notdef 300 vhangup(); 301 #endif 302 (void)signal(SIGHUP, SIG_DFL); 303 t = open(line, O_RDWR); 304 if (t < 0) 305 fatal(f, line, 1); 306 setup_term(t); 307 if (confirmed == 0) { 308 write(f, "", 1); 309 confirmed = 1; /* we sent the null! */ 310 } 311 #ifdef KERBEROS 312 if (encrypt) 313 (void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE)); 314 315 if (use_kerberos == 0) 316 #endif 317 if (!authenticated && !hostok) 318 write(f, "rlogind: Host address mismatch.\r\n", 319 sizeof("rlogind: Host address mismatch.\r\n") - 1); 320 321 pid = fork(); 322 if (pid < 0) 323 fatal(f, "", 1); 324 if (pid == 0) { 325 if (setsid() < 0) 326 fatal(f, "setsid", 1); 327 if (ioctl(t, TIOCSCTTY, 0) < 0) 328 fatal(f, "ioctl(sctty)", 1); 329 (void)close(f); 330 (void)close(p); 331 dup2(t, STDIN_FILENO); 332 dup2(t, STDOUT_FILENO); 333 dup2(t, STDERR_FILENO); 334 (void)close(t); 335 336 if (authenticated) { 337 if (use_kerberos && (pwd->pw_uid == 0)) 338 syslog(LOG_INFO|LOG_AUTH, 339 "ROOT Kerberos login from %s.%s@%s on %s\n", 340 kdata->pname, kdata->pinst, kdata->prealm, 341 hp->h_name); 342 343 execl(_PATH_LOGIN, "login", "-p", 344 "-h", hp->h_name, "-f", lusername, 0); 345 } else 346 execl(_PATH_LOGIN, "login", "-p", 347 "-h", hp->h_name, lusername, 0); 348 fatal(STDERR_FILENO, _PATH_LOGIN, 1); 349 /*NOTREACHED*/ 350 } 351 close(t); 352 353 #ifdef KERBEROS 354 /* 355 * If encrypted, don't turn on NBIO or the des read/write 356 * routines will croak. 357 */ 358 359 if (!encrypt) 360 #endif 361 ioctl(f, FIONBIO, &on); 362 ioctl(p, FIONBIO, &on); 363 ioctl(p, TIOCPKT, &on); 364 signal(SIGCHLD, cleanup); 365 protocol(f, p); 366 signal(SIGCHLD, SIG_IGN); 367 cleanup(); 368 } 369 370 char magic[2] = { 0377, 0377 }; 371 char oobdata[] = {TIOCPKT_WINDOW}; 372 373 /* 374 * Handle a "control" request (signaled by magic being present) 375 * in the data stream. For now, we are only willing to handle 376 * window size changes. 377 */ 378 control(pty, cp, n) 379 int pty; 380 char *cp; 381 int n; 382 { 383 struct winsize w; 384 385 if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') 386 return (0); 387 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ 388 bcopy(cp+4, (char *)&w, sizeof(w)); 389 w.ws_row = ntohs(w.ws_row); 390 w.ws_col = ntohs(w.ws_col); 391 w.ws_xpixel = ntohs(w.ws_xpixel); 392 w.ws_ypixel = ntohs(w.ws_ypixel); 393 (void)ioctl(pty, TIOCSWINSZ, &w); 394 return (4+sizeof (w)); 395 } 396 397 /* 398 * rlogin "protocol" machine. 399 */ 400 protocol(f, p) 401 register int f, p; 402 { 403 char pibuf[1024+1], fibuf[1024], *pbp, *fbp; 404 register pcc = 0, fcc = 0; 405 int cc, nfd, n; 406 char cntl; 407 408 /* 409 * Must ignore SIGTTOU, otherwise we'll stop 410 * when we try and set slave pty's window shape 411 * (our controlling tty is the master pty). 412 */ 413 (void) signal(SIGTTOU, SIG_IGN); 414 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ 415 if (f > p) 416 nfd = f + 1; 417 else 418 nfd = p + 1; 419 if (nfd > FD_SETSIZE) { 420 syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE"); 421 fatal(f, "internal error (select mask too small)", 0); 422 } 423 for (;;) { 424 fd_set ibits, obits, ebits, *omask; 425 426 FD_ZERO(&ebits); 427 FD_ZERO(&ibits); 428 FD_ZERO(&obits); 429 omask = (fd_set *)NULL; 430 if (fcc) { 431 FD_SET(p, &obits); 432 omask = &obits; 433 } else 434 FD_SET(f, &ibits); 435 if (pcc >= 0) 436 if (pcc) { 437 FD_SET(f, &obits); 438 omask = &obits; 439 } else 440 FD_SET(p, &ibits); 441 FD_SET(p, &ebits); 442 if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) { 443 if (errno == EINTR) 444 continue; 445 fatal(f, "select", 1); 446 } 447 if (n == 0) { 448 /* shouldn't happen... */ 449 sleep(5); 450 continue; 451 } 452 #define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) 453 if (FD_ISSET(p, &ebits)) { 454 cc = read(p, &cntl, 1); 455 if (cc == 1 && pkcontrol(cntl)) { 456 cntl |= oobdata[0]; 457 send(f, &cntl, 1, MSG_OOB); 458 if (cntl & TIOCPKT_FLUSHWRITE) { 459 pcc = 0; 460 FD_CLR(p, &ibits); 461 } 462 } 463 } 464 if (FD_ISSET(f, &ibits)) { 465 #ifdef KERBEROS 466 if (encrypt) 467 fcc = des_read(f, fibuf, sizeof(fibuf)); 468 else 469 #endif 470 fcc = read(f, fibuf, sizeof(fibuf)); 471 if (fcc < 0 && errno == EWOULDBLOCK) 472 fcc = 0; 473 else { 474 register char *cp; 475 int left, n; 476 477 if (fcc <= 0) 478 break; 479 fbp = fibuf; 480 481 top: 482 for (cp = fibuf; cp < fibuf+fcc-1; cp++) 483 if (cp[0] == magic[0] && 484 cp[1] == magic[1]) { 485 left = fcc - (cp-fibuf); 486 n = control(p, cp, left); 487 if (n) { 488 left -= n; 489 if (left > 0) 490 bcopy(cp+n, cp, left); 491 fcc -= n; 492 goto top; /* n^2 */ 493 } 494 } 495 FD_SET(p, &obits); /* try write */ 496 } 497 } 498 499 if (FD_ISSET(p, &obits) && fcc > 0) { 500 cc = write(p, fbp, fcc); 501 if (cc > 0) { 502 fcc -= cc; 503 fbp += cc; 504 } 505 } 506 507 if (FD_ISSET(p, &ibits)) { 508 pcc = read(p, pibuf, sizeof (pibuf)); 509 pbp = pibuf; 510 if (pcc < 0 && errno == EWOULDBLOCK) 511 pcc = 0; 512 else if (pcc <= 0) 513 break; 514 else if (pibuf[0] == 0) { 515 pbp++, pcc--; 516 #ifdef KERBEROS 517 if (!encrypt) 518 #endif 519 FD_SET(f, &obits); /* try write */ 520 } else { 521 if (pkcontrol(pibuf[0])) { 522 pibuf[0] |= oobdata[0]; 523 send(f, &pibuf[0], 1, MSG_OOB); 524 } 525 pcc = 0; 526 } 527 } 528 if ((FD_ISSET(f, &obits)) && pcc > 0) { 529 #ifdef KERBEROS 530 if (encrypt) 531 cc = des_write(f, pbp, pcc); 532 else 533 #endif 534 cc = write(f, pbp, pcc); 535 if (cc < 0 && errno == EWOULDBLOCK) { 536 /* 537 * This happens when we try write after read 538 * from p, but some old kernels balk at large 539 * writes even when select returns true. 540 */ 541 if (!FD_ISSET(p, &ibits)) 542 sleep(5); 543 continue; 544 } 545 if (cc > 0) { 546 pcc -= cc; 547 pbp += cc; 548 } 549 } 550 } 551 } 552 553 cleanup() 554 { 555 char *p; 556 557 p = line + sizeof(_PATH_DEV) - 1; 558 if (logout(p)) 559 logwtmp(p, "", ""); 560 (void)chmod(line, 0666); 561 (void)chown(line, 0, 0); 562 *p = 'p'; 563 (void)chmod(line, 0666); 564 (void)chown(line, 0, 0); 565 shutdown(netf, 2); 566 exit(1); 567 } 568 569 fatal(f, msg, syserr) 570 int f, syserr; 571 char *msg; 572 { 573 int len; 574 char buf[BUFSIZ], *bp = buf; 575 576 /* 577 * Prepend binary one to message if we haven't sent 578 * the magic null as confirmation. 579 */ 580 if (!confirmed) 581 *bp++ = '\01'; /* error indicator */ 582 if (syserr) 583 len = sprintf(bp, "rlogind: %s: %s.\r\n", 584 msg, strerror(errno)); 585 else 586 len = sprintf(bp, "rlogind: %s.\r\n", msg); 587 (void) write(f, buf, bp + len - buf); 588 exit(1); 589 } 590 591 do_rlogin(host) 592 char *host; 593 { 594 getstr(rusername, sizeof(rusername), "remuser too long"); 595 getstr(lusername, sizeof(lusername), "locuser too long"); 596 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); 597 598 pwd = getpwnam(lusername); 599 if (pwd == NULL) 600 return(-1); 601 if (pwd->pw_uid == 0) 602 return(-1); 603 return(ruserok(host, 0, rusername, lusername)); 604 } 605 606 607 getstr(buf, cnt, errmsg) 608 char *buf; 609 int cnt; 610 char *errmsg; 611 { 612 char c; 613 614 do { 615 if (read(0, &c, 1) != 1) 616 exit(1); 617 if (--cnt < 0) 618 fatal(STDOUT_FILENO, errmsg, 0); 619 *buf++ = c; 620 } while (c != 0); 621 } 622 623 extern char **environ; 624 625 setup_term(fd) 626 int fd; 627 { 628 register char *cp = index(term+ENVSIZE, '/'); 629 char *speed; 630 struct termios tt; 631 632 #ifndef notyet 633 tcgetattr(fd, &tt); 634 if (cp) { 635 *cp++ = '\0'; 636 speed = cp; 637 cp = index(speed, '/'); 638 if (cp) 639 *cp++ = '\0'; 640 cfsetspeed(&tt, atoi(speed)); 641 } 642 643 tt.c_iflag = TTYDEF_IFLAG; 644 tt.c_oflag = TTYDEF_OFLAG; 645 tt.c_lflag = TTYDEF_LFLAG; 646 tcsetattr(fd, TCSAFLUSH, &tt); 647 #else 648 if (cp) { 649 *cp++ = '\0'; 650 speed = cp; 651 cp = index(speed, '/'); 652 if (cp) 653 *cp++ = '\0'; 654 tcgetattr(fd, &tt); 655 cfsetspeed(&tt, atoi(speed)); 656 tcsetattr(fd, TCSAFLUSH, &tt); 657 } 658 #endif 659 660 env[0] = term; 661 env[1] = 0; 662 environ = env; 663 } 664 665 #ifdef KERBEROS 666 #define VERSION_SIZE 9 667 668 /* 669 * Do the remote kerberos login to the named host with the 670 * given inet address 671 * 672 * Return 0 on valid authorization 673 * Return -1 on valid authentication, no authorization 674 * Return >0 for error conditions 675 */ 676 do_krb_login(host, dest, encrypt) 677 char *host; 678 struct sockaddr_in *dest; 679 int encrypt; 680 { 681 int rc; 682 char instance[INST_SZ], version[VERSION_SIZE]; 683 long authopts = 0L; /* !mutual */ 684 struct sockaddr_in faddr; 685 686 kdata = (AUTH_DAT *) auth_buf; 687 ticket = (KTEXT) tick_buf; 688 689 instance[0] = '*'; 690 instance[1] = '\0'; 691 692 if (encrypt) { 693 rc = sizeof(faddr); 694 if (getsockname(0, &faddr, &rc)) 695 return(-1); 696 authopts = KOPT_DO_MUTUAL; 697 rc = krb_recvauth( 698 authopts, 0, 699 ticket, "rcmd", 700 instance, dest, &faddr, 701 kdata, "", schedule, version); 702 des_set_key(kdata->session, schedule); 703 704 } else { 705 rc = krb_recvauth( 706 authopts, 0, 707 ticket, "rcmd", 708 instance, dest, (struct sockaddr_in *) 0, 709 kdata, "", (bit_64 *) 0, version); 710 } 711 712 if (rc != KSUCCESS) 713 return(rc); 714 715 getstr(lusername, sizeof(lusername), "locuser"); 716 /* get the "cmd" in the rcmd protocol */ 717 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type"); 718 719 pwd = getpwnam(lusername); 720 if (pwd == NULL) 721 return(-1); 722 723 /* returns nonzero for no access */ 724 if (kuserok(kdata,lusername) != 0) 725 return(-1); 726 727 return(0); 728 729 } 730 #endif /* KERBEROS */ 731 732 usage() 733 { 734 #ifdef KERBEROS 735 syslog(LOG_ERR, "usage: rlogind [-aln] [-k | -v]"); 736 #else 737 syslog(LOG_ERR, "usage: rlogind [-aln]"); 738 #endif 739 } 740 741 /* 742 * Check whether host h is in our local domain, 743 * defined as sharing the last two components of the domain part, 744 * or the entire domain part if the local domain has only one component. 745 * If either name is unqualified (contains no '.'), 746 * assume that the host is local, as it will be 747 * interpreted as such. 748 */ 749 local_domain(h) 750 char *h; 751 { 752 char localhost[MAXHOSTNAMELEN]; 753 char *p1, *p2, *topdomain(); 754 755 localhost[0] = 0; 756 (void) gethostname(localhost, sizeof(localhost)); 757 p1 = topdomain(localhost); 758 p2 = topdomain(h); 759 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 760 return(1); 761 return(0); 762 } 763 764 char * 765 topdomain(h) 766 char *h; 767 { 768 register char *p; 769 char *maybe = NULL; 770 int dots = 0; 771 772 for (p = h + strlen(h); p >= h; p--) { 773 if (*p == '.') { 774 if (++dots == 2) 775 return (p); 776 maybe = p; 777 } 778 } 779 return (maybe); 780 } 781