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