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