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