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