rlogind.c revision 24349
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 * $Id: rlogind.c,v 1.16 1997/03/24 06:01:39 imp Exp $ 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)) != -1) 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)strncpy(hostname, hp->h_name, sizeof(hostname)); 231 } else { 232 (void)strncpy(hostname, inet_ntoa(fromp->sin_addr), sizeof(hostname)); 233 } 234 hostname[sizeof(hostname) - 1] = '\0'; 235 236#ifdef KERBEROS 237 if (use_kerberos) { 238 retval = do_krb_login(fromp); 239 if (retval == 0) 240 authenticated++; 241 else if (retval > 0) 242 fatal(f, krb_err_txt[retval], 0); 243 write(f, &c, 1); 244 confirmed = 1; /* we sent the null! */ 245 } else 246#endif 247 { 248 if (fromp->sin_family != AF_INET || 249 fromp->sin_port >= IPPORT_RESERVED || 250 fromp->sin_port < IPPORT_RESERVED/2) { 251 syslog(LOG_NOTICE, "Connection from %s on illegal port", 252 inet_ntoa(fromp->sin_addr)); 253 fatal(f, "Permission denied", 0); 254 } 255#ifdef IP_OPTIONS 256 { 257 u_char optbuf[BUFSIZ/3]; 258 int optsize = sizeof(optbuf), ipproto, i; 259 struct protoent *ip; 260 261 if ((ip = getprotobyname("ip")) != NULL) 262 ipproto = ip->p_proto; 263 else 264 ipproto = IPPROTO_IP; 265 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, 266 &optsize) == 0 && optsize != 0) { 267 for (i = 0; i < optsize; ) { 268 u_char c = optbuf[i]; 269 if (c == IPOPT_LSRR || c == IPOPT_SSRR) { 270 syslog(LOG_NOTICE, 271 "Connection refused from %s with IP option %s", 272 inet_ntoa(fromp->sin_addr), 273 c == IPOPT_LSRR ? "LSRR" : "SSRR"); 274 exit(1); 275 } 276 if (c == IPOPT_EOL) 277 break; 278 i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; 279 } 280 } 281 } 282#endif 283 if (do_rlogin(fromp) == 0) 284 authenticated++; 285 } 286 if (confirmed == 0) { 287 write(f, "", 1); 288 confirmed = 1; /* we sent the null! */ 289 } 290#ifdef KERBEROS 291#ifdef CRYPT 292 if (doencrypt) 293 (void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE) - 1); 294#endif 295#endif 296 netf = f; 297 298 pid = forkpty(&master, line, NULL, &win); 299 if (pid < 0) { 300 if (errno == ENOENT) 301 fatal(f, "Out of ptys", 0); 302 else 303 fatal(f, "Forkpty", 1); 304 } 305 if (pid == 0) { 306 if (f > 2) /* f should always be 0, but... */ 307 (void) close(f); 308 setup_term(0); 309 if (*lusername=='-') { 310 syslog(LOG_ERR, "tried to pass user \"%s\" to login", 311 lusername); 312 fatal(STDERR_FILENO, "invalid user", 0); 313 } 314 if (authenticated) { 315#ifdef KERBEROS 316 if (use_kerberos && (pwd->pw_uid == 0)) 317 syslog(LOG_INFO|LOG_AUTH, 318 "ROOT Kerberos login from %s.%s@%s on %s\n", 319 kdata->pname, kdata->pinst, kdata->prealm, 320 hostname); 321#endif 322 323 execl(_PATH_LOGIN, "login", "-p", 324 "-h", hostname, "-f", lusername, (char *)NULL); 325 } else 326 execl(_PATH_LOGIN, "login", "-p", 327 "-h", hostname, lusername, (char *)NULL); 328 fatal(STDERR_FILENO, _PATH_LOGIN, 1); 329 /*NOTREACHED*/ 330 } 331#ifdef CRYPT 332#ifdef KERBEROS 333 /* 334 * If encrypted, don't turn on NBIO or the des read/write 335 * routines will croak. 336 */ 337 338 if (!doencrypt) 339#endif 340#endif 341 ioctl(f, FIONBIO, &on); 342 ioctl(master, FIONBIO, &on); 343 ioctl(master, TIOCPKT, &on); 344 signal(SIGCHLD, cleanup); 345 protocol(f, master); 346 signal(SIGCHLD, SIG_IGN); 347 cleanup(0); 348} 349 350char magic[2] = { 0377, 0377 }; 351char oobdata[] = {TIOCPKT_WINDOW}; 352 353/* 354 * Handle a "control" request (signaled by magic being present) 355 * in the data stream. For now, we are only willing to handle 356 * window size changes. 357 */ 358int 359control(pty, cp, n) 360 int pty; 361 char *cp; 362 int n; 363{ 364 struct winsize w; 365 366 if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') 367 return (0); 368 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ 369 bcopy(cp+4, (char *)&w, sizeof(w)); 370 w.ws_row = ntohs(w.ws_row); 371 w.ws_col = ntohs(w.ws_col); 372 w.ws_xpixel = ntohs(w.ws_xpixel); 373 w.ws_ypixel = ntohs(w.ws_ypixel); 374 (void)ioctl(pty, TIOCSWINSZ, &w); 375 return (4+sizeof (w)); 376} 377 378/* 379 * rlogin "protocol" machine. 380 */ 381void 382protocol(f, p) 383 register int f, p; 384{ 385 char pibuf[1024+1], fibuf[1024], *pbp, *fbp; 386 register pcc = 0, fcc = 0; 387 int cc, nfd, n; 388 char cntl; 389 390 /* 391 * Must ignore SIGTTOU, otherwise we'll stop 392 * when we try and set slave pty's window shape 393 * (our controlling tty is the master pty). 394 */ 395 (void) signal(SIGTTOU, SIG_IGN); 396 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ 397 if (f > p) 398 nfd = f + 1; 399 else 400 nfd = p + 1; 401 if (nfd > FD_SETSIZE) { 402 syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE"); 403 fatal(f, "internal error (select mask too small)", 0); 404 } 405 for (;;) { 406 fd_set ibits, obits, ebits, *omask; 407 408 FD_ZERO(&ebits); 409 FD_ZERO(&ibits); 410 FD_ZERO(&obits); 411 omask = (fd_set *)NULL; 412 if (fcc) { 413 FD_SET(p, &obits); 414 omask = &obits; 415 } else 416 FD_SET(f, &ibits); 417 if (pcc >= 0) 418 if (pcc) { 419 FD_SET(f, &obits); 420 omask = &obits; 421 } else 422 FD_SET(p, &ibits); 423 FD_SET(p, &ebits); 424 if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) { 425 if (errno == EINTR) 426 continue; 427 fatal(f, "select", 1); 428 } 429 if (n == 0) { 430 /* shouldn't happen... */ 431 sleep(5); 432 continue; 433 } 434#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) 435 if (FD_ISSET(p, &ebits)) { 436 cc = read(p, &cntl, 1); 437 if (cc == 1 && pkcontrol(cntl)) { 438 cntl |= oobdata[0]; 439 send(f, &cntl, 1, MSG_OOB); 440 if (cntl & TIOCPKT_FLUSHWRITE) { 441 pcc = 0; 442 FD_CLR(p, &ibits); 443 } 444 } 445 } 446 if (FD_ISSET(f, &ibits)) { 447#ifdef CRYPT 448#ifdef KERBEROS 449 if (doencrypt) 450 fcc = des_read(f, fibuf, sizeof(fibuf)); 451 else 452#endif 453#endif 454 fcc = read(f, fibuf, sizeof(fibuf)); 455 if (fcc < 0 && errno == EWOULDBLOCK) 456 fcc = 0; 457 else { 458 register char *cp; 459 int left, n; 460 461 if (fcc <= 0) 462 break; 463 fbp = fibuf; 464 465 top: 466 for (cp = fibuf; cp < fibuf+fcc-1; cp++) 467 if (cp[0] == magic[0] && 468 cp[1] == magic[1]) { 469 left = fcc - (cp-fibuf); 470 n = control(p, cp, left); 471 if (n) { 472 left -= n; 473 if (left > 0) 474 bcopy(cp+n, cp, left); 475 fcc -= n; 476 goto top; /* n^2 */ 477 } 478 } 479 FD_SET(p, &obits); /* try write */ 480 } 481 } 482 483 if (FD_ISSET(p, &obits) && fcc > 0) { 484 cc = write(p, fbp, fcc); 485 if (cc > 0) { 486 fcc -= cc; 487 fbp += cc; 488 } 489 } 490 491 if (FD_ISSET(p, &ibits)) { 492 pcc = read(p, pibuf, sizeof (pibuf)); 493 pbp = pibuf; 494 if (pcc < 0 && errno == EWOULDBLOCK) 495 pcc = 0; 496 else if (pcc <= 0) 497 break; 498 else if (pibuf[0] == 0) { 499 pbp++, pcc--; 500#ifdef CRYPT 501#ifdef KERBEROS 502 if (!doencrypt) 503#endif 504#endif 505 FD_SET(f, &obits); /* try write */ 506 } else { 507 if (pkcontrol(pibuf[0])) { 508 pibuf[0] |= oobdata[0]; 509 send(f, &pibuf[0], 1, MSG_OOB); 510 } 511 pcc = 0; 512 } 513 } 514 if ((FD_ISSET(f, &obits)) && pcc > 0) { 515#ifdef CRYPT 516#ifdef KERBEROS 517 if (doencrypt) 518 cc = des_write(f, pbp, pcc); 519 else 520#endif 521#endif 522 cc = write(f, pbp, pcc); 523 if (cc < 0 && errno == EWOULDBLOCK) { 524 /* 525 * This happens when we try write after read 526 * from p, but some old kernels balk at large 527 * writes even when select returns true. 528 */ 529 if (!FD_ISSET(p, &ibits)) 530 sleep(5); 531 continue; 532 } 533 if (cc > 0) { 534 pcc -= cc; 535 pbp += cc; 536 } 537 } 538 } 539} 540 541void 542cleanup(signo) 543 int signo; 544{ 545 char *p; 546 547 p = line + sizeof(_PATH_DEV) - 1; 548 if (logout(p)) 549 logwtmp(p, "", ""); 550 (void)chmod(line, 0666); 551 (void)chown(line, 0, 0); 552 *p = 'p'; 553 (void)chmod(line, 0666); 554 (void)chown(line, 0, 0); 555 shutdown(netf, 2); 556 exit(1); 557} 558 559void 560fatal(f, msg, syserr) 561 int f; 562 char *msg; 563 int syserr; 564{ 565 int len; 566 char buf[BUFSIZ], *bp = buf; 567 568 /* 569 * Prepend binary one to message if we haven't sent 570 * the magic null as confirmation. 571 */ 572 if (!confirmed) 573 *bp++ = '\01'; /* error indicator */ 574 if (syserr) 575 len = sprintf(bp, "rlogind: %s: %s.\r\n", 576 msg, strerror(errno)); 577 else 578 len = sprintf(bp, "rlogind: %s.\r\n", msg); 579 (void) write(f, buf, bp + len - buf); 580 exit(1); 581} 582 583int 584do_rlogin(dest) 585 struct sockaddr_in *dest; 586{ 587 getstr(rusername, sizeof(rusername), "remuser too long"); 588 getstr(lusername, sizeof(lusername), "locuser too long"); 589 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); 590 591 pwd = getpwnam(lusername); 592 if (pwd == NULL) 593 return (-1); 594 /* XXX why don't we syslog() failure? */ 595 return (iruserok(dest->sin_addr.s_addr, pwd->pw_uid == 0, 596 rusername, lusername)); 597} 598 599void 600getstr(buf, cnt, errmsg) 601 char *buf; 602 int cnt; 603 char *errmsg; 604{ 605 char c; 606 607 do { 608 if (read(0, &c, 1) != 1) 609 exit(1); 610 if (--cnt < 0) 611 fatal(STDOUT_FILENO, errmsg, 0); 612 *buf++ = c; 613 } while (c != 0); 614} 615 616extern char **environ; 617 618void 619setup_term(fd) 620 int fd; 621{ 622 register char *cp = index(term+ENVSIZE, '/'); 623 char *speed; 624 struct termios tt; 625 626#ifndef notyet 627 tcgetattr(fd, &tt); 628 if (cp) { 629 *cp++ = '\0'; 630 speed = cp; 631 cp = index(speed, '/'); 632 if (cp) 633 *cp++ = '\0'; 634 cfsetspeed(&tt, atoi(speed)); 635 } 636 637 tt.c_iflag = TTYDEF_IFLAG; 638 tt.c_oflag = TTYDEF_OFLAG; 639 tt.c_lflag = TTYDEF_LFLAG; 640 tcsetattr(fd, TCSAFLUSH, &tt); 641#else 642 if (cp) { 643 *cp++ = '\0'; 644 speed = cp; 645 cp = index(speed, '/'); 646 if (cp) 647 *cp++ = '\0'; 648 tcgetattr(fd, &tt); 649 cfsetspeed(&tt, atoi(speed)); 650 tcsetattr(fd, TCSAFLUSH, &tt); 651 } 652#endif 653 654 env[0] = term; 655 env[1] = 0; 656 environ = env; 657} 658 659#ifdef KERBEROS 660#define VERSION_SIZE 9 661 662/* 663 * Do the remote kerberos login to the named host with the 664 * given inet address 665 * 666 * Return 0 on valid authorization 667 * Return -1 on valid authentication, no authorization 668 * Return >0 for error conditions 669 */ 670int 671do_krb_login(dest) 672 struct sockaddr_in *dest; 673{ 674 int rc; 675 char instance[INST_SZ], version[VERSION_SIZE]; 676 long authopts = 0L; /* !mutual */ 677 struct sockaddr_in faddr; 678 679 kdata = (AUTH_DAT *) auth_buf; 680 ticket = (KTEXT) tick_buf; 681 682 instance[0] = '*'; 683 instance[1] = '\0'; 684 685#ifdef CRYPT 686 if (doencrypt) { 687 rc = sizeof(faddr); 688 if (getsockname(0, (struct sockaddr *)&faddr, &rc)) 689 return (-1); 690 authopts = KOPT_DO_MUTUAL; 691 rc = krb_recvauth( 692 authopts, 0, 693 ticket, "rcmd", 694 instance, dest, &faddr, 695 kdata, "", schedule, version); 696 des_set_key_krb(&kdata->session, schedule); 697 698 } else 699#endif 700 rc = krb_recvauth( 701 authopts, 0, 702 ticket, "rcmd", 703 instance, dest, (struct sockaddr_in *) 0, 704 kdata, "", NULL, version); 705 706 if (rc != KSUCCESS) 707 return (rc); 708 709 getstr(lusername, sizeof(lusername), "locuser"); 710 /* get the "cmd" in the rcmd protocol */ 711 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type"); 712 713 pwd = getpwnam(lusername); 714 if (pwd == NULL) 715 return (-1); 716 717 /* returns nonzero for no access */ 718 if (kuserok(kdata, lusername) != 0) 719 return (-1); 720 721 return (0); 722 723} 724#endif /* KERBEROS */ 725 726void 727usage() 728{ 729#ifdef KERBEROS 730 syslog(LOG_ERR, "usage: rlogind [-Daln] [-k | -v]"); 731#else 732 syslog(LOG_ERR, "usage: rlogind [-Daln]"); 733#endif 734} 735 736/* 737 * Check whether host h is in our local domain, 738 * defined as sharing the last two components of the domain part, 739 * or the entire domain part if the local domain has only one component. 740 * If either name is unqualified (contains no '.'), 741 * assume that the host is local, as it will be 742 * interpreted as such. 743 */ 744int 745local_domain(h) 746 char *h; 747{ 748 char localhost[MAXHOSTNAMELEN]; 749 char *p1, *p2; 750 751 localhost[0] = 0; 752 (void) gethostname(localhost, sizeof(localhost)); 753 p1 = topdomain(localhost); 754 p2 = topdomain(h); 755 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 756 return (1); 757 return (0); 758} 759 760char * 761topdomain(h) 762 char *h; 763{ 764 register char *p; 765 char *maybe = NULL; 766 int dots = 0; 767 768 for (p = h + strlen(h); p >= h; p--) { 769 if (*p == '.') { 770 if (++dots == 2) 771 return (p); 772 maybe = p; 773 } 774 } 775 return (maybe); 776} 777