xref: /netbsd-src/lib/libc/net/rcmd.c (revision 7b4851649d5c70bd2eae32e8ea14d0dd1f0d92d7)
1 /*	$NetBSD: rcmd.c,v 1.23 1998/01/18 06:22:19 lukem Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Matthew R. Green.
5  * Copyright (c) 1983, 1993, 1994
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 #if 0
40 static char sccsid[] = "@(#)rcmd.c	8.3 (Berkeley) 3/26/94";
41 #else
42 __RCSID("$NetBSD: rcmd.c,v 1.23 1998/01/18 06:22:19 lukem Exp $");
43 #endif
44 #endif /* LIBC_SCCS and not lint */
45 
46 #include "namespace.h"
47 #include <sys/param.h>
48 #include <sys/socket.h>
49 #include <sys/stat.h>
50 #include <sys/poll.h>
51 #include <sys/wait.h>
52 
53 #include <netinet/in.h>
54 #include <rpc/rpc.h>
55 #include <arpa/inet.h>
56 #include <netgroup.h>
57 
58 #include <signal.h>
59 #include <fcntl.h>
60 #include <netdb.h>
61 #include <unistd.h>
62 #include <pwd.h>
63 #include <errno.h>
64 #include <stdio.h>
65 #include <ctype.h>
66 #include <string.h>
67 #include <syslog.h>
68 #include <stdlib.h>
69 #include <paths.h>
70 #include <err.h>
71 
72 #include "pathnames.h"
73 
74 int	orcmd __P((char **, u_short, const char *, const char *, const char *,
75 	    int *));
76 int	__ivaliduser __P((FILE *, u_int32_t, const char *, const char *));
77 static	int rshrcmd __P((char **, u_short, const char *, const char *,
78 	    const char *, int *, char *));
79 static	int hprcmd __P((struct hostent *, char **, u_short, const char *,
80 	    const char *, const char *, int *));
81 static	int __icheckhost __P((u_int32_t, const char *));
82 static	char *__gethostloop __P((u_int32_t));
83 
84 int
85 rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
86 	char **ahost;
87 	u_short rport;
88 	const char *locuser, *remuser, *cmd;
89 	int *fd2p;
90 {
91 	struct hostent *hp;
92 	struct servent *sp;
93 
94 	/*
95 	 * Canonicalise hostname.
96 	 * XXX: Should we really do this?
97 	 */
98 	hp = gethostbyname(*ahost);
99 	if (hp == NULL) {
100 		herror(*ahost);
101 		return (-1);
102 	}
103 	*ahost = hp->h_name;
104 
105 	/*
106 	 * Check if rport is the same as the shell port, and that the fd2p.  If
107 	 * it is not, the program isn't expecting 'rsh' and so we can't use the
108 	 * RCMD_CMD environment.
109 	 */
110 	sp = getservbyname("shell", "tcp");
111 	if (sp != NULL && sp->s_port == rport)
112 		return (rshrcmd(ahost, rport, locuser, remuser, cmd, fd2p,
113 		    getenv("RCMD_CMD")));
114 	else
115 		return (hprcmd(hp, ahost, rport, locuser, remuser, cmd, fd2p));
116 }
117 
118 /* this is simply a wrapper around hprcmd() that handles ahost first */
119 int
120 orcmd(ahost, rport, locuser, remuser, cmd, fd2p)
121 	char **ahost;
122 	u_short rport;
123 	const char *locuser, *remuser, *cmd;
124 	int *fd2p;
125 {
126 	struct hostent *hp;
127 
128 	hp = gethostbyname(*ahost);
129 	if (hp == NULL) {
130 		herror(*ahost);
131 		return (-1);
132 	}
133 	*ahost = hp->h_name;
134 
135 	return (hprcmd(hp, ahost, rport, locuser, remuser, cmd, fd2p));
136 }
137 
138 static int
139 hprcmd(hp, ahost, rport, locuser, remuser, cmd, fd2p)
140 	struct hostent *hp;
141 	char **ahost;
142 	u_short rport;
143 	const char *locuser, *remuser, *cmd;
144 	int *fd2p;
145 {
146 	struct sockaddr_in sin, from;
147 	struct pollfd reads[2];
148 	int oldmask;
149 	pid_t pid;
150 	int s, lport, timo;
151 	int pollr;
152 	char c;
153 
154 	pid = getpid();
155 	oldmask = sigblock(sigmask(SIGURG));
156 	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
157 		s = rresvport(&lport);
158 		if (s < 0) {
159 			if (errno == EAGAIN)
160 				warnx("rcmd: socket: All ports in use");
161 			else
162 				warn("rcmd: socket");
163 			sigsetmask(oldmask);
164 			return (-1);
165 		}
166 		fcntl(s, F_SETOWN, pid);
167 		sin.sin_len = sizeof(struct sockaddr_in);
168 		sin.sin_family = hp->h_addrtype;
169 		sin.sin_port = rport;
170 		memmove(&sin.sin_addr, hp->h_addr_list[0], hp->h_length);
171 		if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
172 			break;
173 		(void)close(s);
174 		if (errno == EADDRINUSE) {
175 			lport--;
176 			continue;
177 		}
178 		if (errno == ECONNREFUSED && timo <= 16) {
179 			(void)sleep(timo);
180 			timo *= 2;
181 			continue;
182 		}
183 		if (hp->h_addr_list[1] != NULL) {
184 			int oerrno = errno;
185 
186 			warnx("rcmd: connect to address %s",
187 			    inet_ntoa(sin.sin_addr));
188 			errno = oerrno;
189 			perror(0);
190 			hp->h_addr_list++;
191 			memmove(&sin.sin_addr, hp->h_addr_list[0],
192 			    hp->h_length);
193 			(void)fprintf(stderr, "Trying %s...\n",
194 			    inet_ntoa(sin.sin_addr));
195 			continue;
196 		}
197 		(void)fprintf(stderr, "%s: %s\n", hp->h_name, strerror(errno));
198 		sigsetmask(oldmask);
199 		return (-1);
200 	}
201 	lport--;
202 	if (fd2p == 0) {
203 		write(s, "", 1);
204 		lport = 0;
205 	} else {
206 		char num[8];
207 		int s2 = rresvport(&lport), s3;
208 		int len = sizeof(from);
209 
210 		if (s2 < 0)
211 			goto bad;
212 		listen(s2, 1);
213 		(void)snprintf(num, sizeof(num), "%d", lport);
214 		if (write(s, num, strlen(num) + 1) != strlen(num) + 1) {
215 			warn("rcmd: write (setting up stderr)");
216 			(void)close(s2);
217 			goto bad;
218 		}
219 		reads[0].fd = s;
220 		reads[0].events = POLLIN;
221 		reads[1].fd = s2;
222 		reads[1].events = POLLIN;
223 		errno = 0;
224 		pollr = poll(reads, 2, INFTIM);
225 		if (pollr < 1 || (reads[1].revents & POLLIN) == 0) {
226 			if (errno != 0)
227 				warn("poll: setting up stderr");
228 			else
229 				warnx("poll: protocol failure in circuit setup");
230 			(void)close(s2);
231 			goto bad;
232 		}
233 		s3 = accept(s2, (struct sockaddr *)&from, &len);
234 		(void)close(s2);
235 		if (s3 < 0) {
236 			warn("rcmd: accept");
237 			lport = 0;
238 			goto bad;
239 		}
240 		*fd2p = s3;
241 		from.sin_port = ntohs(from.sin_port);
242 		if (from.sin_family != AF_INET ||
243 		    from.sin_port >= IPPORT_RESERVED ||
244 		    from.sin_port < IPPORT_RESERVED / 2) {
245 			warnx("rcmd: protocol failure in circuit setup.");
246 			goto bad2;
247 		}
248 	}
249 
250 	(void)write(s, locuser, strlen(locuser)+1);
251 	(void)write(s, remuser, strlen(remuser)+1);
252 	(void)write(s, cmd, strlen(cmd)+1);
253 	if (read(s, &c, 1) != 1) {
254 		warn("%s", *ahost);
255 		goto bad2;
256 	}
257 	if (c != 0) {
258 		while (read(s, &c, 1) == 1) {
259 			(void)write(STDERR_FILENO, &c, 1);
260 			if (c == '\n')
261 				break;
262 		}
263 		goto bad2;
264 	}
265 	sigsetmask(oldmask);
266 	return (s);
267 bad2:
268 	if (lport)
269 		(void)close(*fd2p);
270 bad:
271 	(void)close(s);
272 	sigsetmask(oldmask);
273 	return (-1);
274 }
275 
276 /*
277  * based on code written by Chris Siebenmann <cks@utcc.utoronto.ca>
278  */
279 static int
280 rshrcmd(ahost, rport, locuser, remuser, cmd, fd2p, rshcmd)
281 	char  	**ahost;
282 	u_short	rport;
283 	const	char *locuser, *remuser, *cmd;
284 	int	*fd2p;
285 	char	*rshcmd;
286 {
287 	pid_t pid;
288 	int sp[2], ep[2];
289 	char *p;
290 	struct passwd *pw;
291 
292 	/* What rsh/shell to use. */
293 	if (rshcmd == NULL)
294 		rshcmd = _PATH_BIN_RCMD;
295 
296 	/* locuser must exist on this host. */
297 	if ((pw = getpwnam(locuser)) == NULL) {
298 		warnx("rshrcmd: unknown user: %s", locuser);
299 		return(-1);
300 	}
301 
302 	/* get a socketpair we'll use for stdin and stdout. */
303 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) {
304 		warn("rshrcmd: socketpair");
305 		return (-1);
306 	}
307 	/* we will use this for the fd2 pointer */
308 	if (fd2p) {
309 		if (socketpair(AF_UNIX, SOCK_STREAM, 0, ep) < 0) {
310 			warn("rshrcmd: socketpair");
311 			return (-1);
312 		}
313 		*fd2p = ep[0];
314 	}
315 
316 	pid = fork();
317 	if (pid < 0) {
318 		warn("rshrcmd: fork");
319 		return (-1);
320 	}
321 	if (pid == 0) {
322 		/*
323 		 * child
324 		 * - we use sp[1] to be stdin/stdout, and close sp[0]
325 		 * - with fd2p, we use ep[1] for stderr, and close ep[0]
326 		 */
327 		(void)close(sp[0]);
328 		if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0) {
329 			warn("rshrcmd: dup2");
330 			_exit(1);
331 		}
332 		if (fd2p) {
333 			if (dup2(ep[1], 2) < 0) {
334 				warn("rshrcmd: dup2");
335 				_exit(1);
336 			}
337 			(void)close(ep[0]);
338 			(void)close(ep[1]);
339 		} else if (dup2(0, 2) < 0) {
340 			warn("rshrcmd: dup2");
341 			_exit(1);
342 		}
343 		/* fork again to lose parent. */
344 		pid = fork();
345 		if (pid < 0) {
346 			warn("rshrcmd: second fork");
347 			_exit(1);
348 		}
349 		if (pid > 0)
350 			_exit(0);
351 
352 		/* Orphan.  Become local user for rshprog. */
353 		if (setuid(pw->pw_uid)) {
354 			warn("rshrcmd: setuid(%u)", pw->pw_uid);
355 			_exit(1);
356 		}
357 
358 		/*
359 		 * If we are rcmd'ing to "localhost" as the same user as we are,
360 		 * then avoid running remote shell for efficiency.
361 		 */
362 		if (strcmp(*ahost, "localhost") == 0 &&
363 		    strcmp(locuser, remuser) == 0) {
364 			if (pw->pw_shell[0] == '\0')
365 				rshcmd = _PATH_BSHELL;
366 			else
367 				rshcmd = pw->pw_shell;
368 			p = strrchr(rshcmd, '/');
369 			execlp(rshcmd, p ? p + 1 : rshcmd, "-c", cmd, NULL);
370 		} else {
371 			p = strrchr(rshcmd, '/');
372 			execlp(rshcmd, p ? p + 1 : rshcmd, *ahost, "-l",
373 			    remuser, cmd, NULL);
374 		}
375 		warn("rshrcmd: exec %s", rshcmd);
376 		_exit(1);
377 	}
378 	/* Parent */
379 	(void)close(sp[1]);
380 	if (fd2p)
381 		(void)close(ep[1]);
382 
383 	(void)waitpid(pid, NULL, 0);
384 	return (sp[0]);
385 }
386 
387 int
388 rresvport(alport)
389 	int *alport;
390 {
391 	struct sockaddr_in sin;
392 	int s;
393 
394 	sin.sin_len = sizeof(struct sockaddr_in);
395 	sin.sin_family = AF_INET;
396 	sin.sin_addr.s_addr = INADDR_ANY;
397 	s = socket(AF_INET, SOCK_STREAM, 0);
398 	if (s < 0)
399 		return (-1);
400 #if 0
401 	for (;;) {
402 		sin.sin_port = htons((u_short)*alport);
403 		if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
404 			return (s);
405 		if (errno != EADDRINUSE) {
406 			(void)close(s);
407 			return (-1);
408 		}
409 		(*alport)--;
410 		if (*alport == IPPORT_RESERVED/2) {
411 			(void)close(s);
412 			errno = EAGAIN;		/* close */
413 			return (-1);
414 		}
415 	}
416 #else
417 	sin.sin_port = 0;
418 	if (bindresvport(s, &sin) < 0) {
419 		int sverr = errno;
420 
421 		(void)close(s);
422 		errno = sverr;
423 		return (-1);
424 	}
425 	*alport = (int)ntohs(sin.sin_port);
426 	return (s);
427 #endif
428 }
429 
430 int	__check_rhosts_file = 1;
431 char	*__rcmd_errstr;
432 
433 int
434 ruserok(rhost, superuser, ruser, luser)
435 	const char *rhost, *ruser, *luser;
436 	int superuser;
437 {
438 	struct hostent *hp;
439 	char **ap;
440 	int i;
441 #define MAXADDRS	35
442 	u_int32_t addrs[MAXADDRS + 1];
443 
444 	if ((hp = gethostbyname(rhost)) == NULL)
445 		return (-1);
446 	for (i = 0, ap = hp->h_addr_list; *ap && i < MAXADDRS; ++ap, ++i)
447 		memmove(&addrs[i], *ap, sizeof(addrs[i]));
448 	addrs[i] = 0;
449 
450 	for (i = 0; i < MAXADDRS && addrs[i]; i++)
451 		if (iruserok(addrs[i], superuser, ruser, luser) == 0)
452 			return (0);
453 	return (-1);
454 }
455 
456 /*
457  * New .rhosts strategy: We are passed an ip address. We spin through
458  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
459  * has ip addresses, we don't have to trust a nameserver.  When it
460  * contains hostnames, we spin through the list of addresses the nameserver
461  * gives us and look for a match.
462  *
463  * Returns 0 if ok, -1 if not ok.
464  */
465 int
466 iruserok(raddr, superuser, ruser, luser)
467 	u_int32_t raddr;
468 	int superuser;
469 	const char *ruser, *luser;
470 {
471 	register char *cp;
472 	struct stat sbuf;
473 	struct passwd *pwd;
474 	FILE *hostf;
475 	uid_t uid;
476 	gid_t gid;
477 	int first;
478 	char pbuf[MAXPATHLEN];
479 
480 	first = 1;
481 	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
482 again:
483 	if (hostf) {
484 		if (__ivaliduser(hostf, raddr, luser, ruser) == 0) {
485 			(void)fclose(hostf);
486 			return (0);
487 		}
488 		(void)fclose(hostf);
489 	}
490 	if (first == 1 && (__check_rhosts_file || superuser)) {
491 		first = 0;
492 		if ((pwd = getpwnam(luser)) == NULL)
493 			return (-1);
494 		(void)strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
495 		(void)strncat(pbuf, "/.rhosts", sizeof(pbuf) - strlen(pbuf) - 1);
496 
497 		/*
498 		 * Change effective uid while opening .rhosts.  If root and
499 		 * reading an NFS mounted file system, can't read files that
500 		 * are protected read/write owner only.
501 		 */
502 		uid = geteuid();
503 		gid = getegid();
504 		(void)setegid(pwd->pw_gid);
505 		initgroups(pwd->pw_name, pwd->pw_gid);
506 		(void)seteuid(pwd->pw_uid);
507 		hostf = fopen(pbuf, "r");
508 		(void)seteuid(uid);
509 		(void)setegid(gid);
510 
511 		if (hostf == NULL)
512 			return (-1);
513 		/*
514 		 * If not a regular file, or is owned by someone other than
515 		 * user or root or if writeable by anyone but the owner, quit.
516 		 */
517 		cp = NULL;
518 		if (lstat(pbuf, &sbuf) < 0)
519 			cp = ".rhosts lstat failed";
520 		else if (!S_ISREG(sbuf.st_mode))
521 			cp = ".rhosts not regular file";
522 		else if (fstat(fileno(hostf), &sbuf) < 0)
523 			cp = ".rhosts fstat failed";
524 		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
525 			cp = "bad .rhosts owner";
526 		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
527 			cp = ".rhosts writeable by other than owner";
528 		/* If there were any problems, quit. */
529 		if (cp) {
530 			__rcmd_errstr = cp;
531 			(void)fclose(hostf);
532 			return (-1);
533 		}
534 		goto again;
535 	}
536 	return (-1);
537 }
538 
539 /*
540  * XXX
541  * Don't make static, used by lpd(8).
542  *
543  * Returns 0 if ok, -1 if not ok.
544  */
545 int
546 __ivaliduser(hostf, raddr, luser, ruser)
547 	FILE *hostf;
548 	u_int32_t raddr;
549 	const char *luser, *ruser;
550 {
551 	register char *user, *p;
552 	int ch;
553 	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
554 	const char *auser, *ahost;
555 	int hostok, userok;
556 	char *rhost = NULL;
557 	int firsttime = 1;
558 	char domain[MAXHOSTNAMELEN];
559 
560 	getdomainname(domain, sizeof(domain));
561 
562 	while (fgets(buf, sizeof(buf), hostf)) {
563 		p = buf;
564 		/* Skip lines that are too long. */
565 		if (strchr(p, '\n') == NULL) {
566 			while ((ch = getc(hostf)) != '\n' && ch != EOF)
567 				;
568 			continue;
569 		}
570 		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
571 			*p = isupper(*p) ? tolower(*p) : *p;
572 			p++;
573 		}
574 		if (*p == ' ' || *p == '\t') {
575 			*p++ = '\0';
576 			while (*p == ' ' || *p == '\t')
577 				p++;
578 			user = p;
579 			while (*p != '\n' && *p != ' ' &&
580 			    *p != '\t' && *p != '\0')
581 				p++;
582 		} else
583 			user = p;
584 		*p = '\0';
585 
586 		if (p == buf)
587 			continue;
588 
589 		auser = *user ? user : luser;
590 		ahost = buf;
591 
592 		if (ahost[0] == '+')
593 			switch (ahost[1]) {
594 			case '\0':
595 				hostok = 1;
596 				break;
597 
598 			case '@':
599 				if (firsttime) {
600 					rhost = __gethostloop(raddr);
601 					firsttime = 0;
602 				}
603 				if (rhost)
604 					hostok = innetgr(&ahost[2], rhost,
605 					    NULL, domain);
606 				else
607 					hostok = 0;
608 				break;
609 
610 			default:
611 				hostok = __icheckhost(raddr, &ahost[1]);
612 				break;
613 			}
614 		else if (ahost[0] == '-')
615 			switch (ahost[1]) {
616 			case '\0':
617 				hostok = -1;
618 				break;
619 
620 			case '@':
621 				if (firsttime) {
622 					rhost = __gethostloop(raddr);
623 					firsttime = 0;
624 				}
625 				if (rhost)
626 					hostok = -innetgr(&ahost[2], rhost,
627 					    NULL, domain);
628 				else
629 					hostok = 0;
630 				break;
631 
632 			default:
633 				hostok = -__icheckhost(raddr, &ahost[1]);
634 				break;
635 			}
636 		else
637 			hostok = __icheckhost(raddr, ahost);
638 
639 
640 		if (auser[0] == '+')
641 			switch (auser[1]) {
642 			case '\0':
643 				userok = 1;
644 				break;
645 
646 			case '@':
647 				userok = innetgr(&auser[2], NULL, ruser,
648 				    domain);
649 				break;
650 
651 			default:
652 				userok = strcmp(ruser, &auser[1]) == 0;
653 				break;
654 			}
655 		else if (auser[0] == '-')
656 			switch (auser[1]) {
657 			case '\0':
658 				userok = -1;
659 				break;
660 
661 			case '@':
662 				userok = -innetgr(&auser[2], NULL, ruser,
663 				    domain);
664 				break;
665 
666 			default:
667 				userok = -(strcmp(ruser, &auser[1]) == 0);
668 				break;
669 			}
670 		else
671 			userok = strcmp(ruser, auser) == 0;
672 
673 		/* Check if one component did not match */
674 		if (hostok == 0 || userok == 0)
675 			continue;
676 
677 		/* Check if we got a forbidden pair */
678 		if (userok == -1 || hostok == -1)
679 			return -1;
680 
681 		/* Check if we got a valid pair */
682 		if (hostok == 1 && userok == 1)
683 			return 0;
684 	}
685 	return -1;
686 }
687 
688 /*
689  * Returns "true" if match, 0 if no match.
690  */
691 static int
692 __icheckhost(raddr, lhost)
693 	u_int32_t raddr;
694 	const char *lhost;
695 {
696 	struct hostent *hp;
697 	struct in_addr laddr;
698 	char **pp;
699 
700 	/* Try for raw ip address first. */
701 	if (isdigit(*lhost) && inet_aton(lhost, &laddr) != 0)
702 		return (raddr == laddr.s_addr);
703 
704 	/* Better be a hostname. */
705 	if ((hp = gethostbyname(lhost)) == NULL)
706 		return (0);
707 
708 	/* Spin through ip addresses. */
709 	for (pp = hp->h_addr_list; *pp; ++pp)
710 		if (!memcmp(&raddr, *pp, sizeof(u_int32_t)))
711 			return (1);
712 
713 	/* No match. */
714 	return (0);
715 }
716 
717 /*
718  * Return the hostname associated with the supplied address.
719  * Do a reverse lookup as well for security. If a loop cannot
720  * be found, pack the result of inet_ntoa() into the string.
721  */
722 static char *
723 __gethostloop(raddr)
724 	u_int32_t raddr;
725 {
726 	static char remotehost[MAXHOSTNAMELEN];
727 	struct hostent *hp;
728 	struct in_addr in;
729 
730 	hp = gethostbyaddr((char *) &raddr, sizeof(raddr), AF_INET);
731 	if (hp == NULL)
732 		return (NULL);
733 
734 	/*
735 	 * Look up the name and check that the supplied
736 	 * address is in the list
737 	 */
738 	strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
739 	remotehost[sizeof(remotehost) - 1] = '\0';
740 	hp = gethostbyname(remotehost);
741 	if (hp == NULL)
742 		return (NULL);
743 
744 	for (; hp->h_addr_list[0] != NULL; hp->h_addr_list++)
745 		if (!memcmp(hp->h_addr_list[0], (caddr_t)&raddr, sizeof(raddr)))
746 			return (remotehost);
747 
748 	/*
749 	 * either the DNS adminstrator has made a configuration
750 	 * mistake, or someone has attempted to spoof us
751 	 */
752 	in.s_addr = raddr;
753 	syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s",
754 	    inet_ntoa(in), hp->h_name);
755 	return (NULL);
756 }
757