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