1 /* $OpenBSD: rpcinfo.c,v 1.20 2024/09/15 07:14:58 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2010, Oracle America, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 * * Neither the name of the "Oracle America, Inc." nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * rpcinfo: ping a particular rpc program
36 * or dump the portmapper
37 */
38
39 #include <rpc/rpc.h>
40 #include <stdio.h>
41 #include <sys/socket.h>
42 #include <netdb.h>
43 #include <rpc/pmap_prot.h>
44 #include <rpc/pmap_clnt.h>
45 #include <signal.h>
46 #include <string.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <ctype.h>
50 #include <errno.h>
51 #include <limits.h>
52 #include <arpa/inet.h>
53 #include <err.h>
54
55 #define MAXHOSTLEN 256
56
57 #define MIN_VERS ((u_long) 0)
58 #define MAX_VERS ((u_long) 4294967295UL)
59
60 void udpping(u_short portflag, int argc, char **argv);
61 void tcpping(u_short portflag, int argc, char **argv);
62 int pstatus(CLIENT *client, u_long prognum, u_long vers);
63 void pmapdump(int argc, char **argv);
64 bool_t reply_proc(caddr_t res, struct sockaddr_in *who);
65 void brdcst(int argc, char **argv);
66 void deletereg(int argc, char **argv);
67 void setreg(int argc, char **argv);
68 void usage(char *);
69 int getprognum(char *arg, u_long *ulp);
70 int getul(char *arg, u_long *ulp);
71 void get_inet_address(struct sockaddr_in *addr, char *host);
72
73 /*
74 * Functions to be performed.
75 */
76 #define NONE 0 /* no function */
77 #define PMAPDUMP 1 /* dump portmapper registrations */
78 #define TCPPING 2 /* ping TCP service */
79 #define UDPPING 3 /* ping UDP service */
80 #define BRDCST 4 /* ping broadcast UDP service */
81 #define DELETES 5 /* delete registration for the service */
82 #define SETS 6 /* set registration for the service */
83
84 int
main(int argc,char * argv[])85 main(int argc, char *argv[])
86 {
87 int c;
88 extern char *optarg;
89 extern int optind;
90 int errflg;
91 int function;
92 u_short portnum;
93 u_long tmp;
94
95 function = NONE;
96 portnum = 0;
97 errflg = 0;
98
99 if (unveil("/etc/rpc", "r") == -1)
100 err(1, "unveil /");
101 if (unveil(NULL, NULL) == -1)
102 err(1, "unveil");
103
104 if (pledge("stdio inet dns rpath", NULL) == -1)
105 err(1, "pledge");
106
107 while ((c = getopt(argc, argv, "ptubdsn:")) != -1) {
108 switch (c) {
109
110 case 'p':
111 if (function != NONE)
112 errflg = 1;
113 else
114 function = PMAPDUMP;
115 break;
116
117 case 't':
118 if (function != NONE)
119 errflg = 1;
120 else
121 function = TCPPING;
122 break;
123
124 case 'u':
125 if (function != NONE)
126 errflg = 1;
127 else
128 function = UDPPING;
129 break;
130
131 case 'b':
132 if (function != NONE)
133 errflg = 1;
134 else
135 function = BRDCST;
136 break;
137
138 case 'n':
139 if (getul(optarg, &tmp))
140 usage("invalid port number");
141 if (tmp >= 65536)
142 usage("port number out of range");
143 portnum = (u_short)tmp;
144 break;
145
146 case 'd':
147 if (function != NONE)
148 errflg = 1;
149 else
150 function = DELETES;
151 break;
152
153 case 's':
154 if (function != NONE)
155 errflg = 1;
156 else
157 function = SETS;
158 break;
159
160
161 case '?':
162 errflg = 1;
163 }
164 }
165
166 if (errflg || function == NONE)
167 usage(NULL);
168
169 switch (function) {
170
171 case PMAPDUMP:
172 if (portnum != 0)
173 usage(NULL);
174 pmapdump(argc - optind, argv + optind);
175 break;
176
177 case UDPPING:
178 udpping(portnum, argc - optind, argv + optind);
179 break;
180
181 case TCPPING:
182 tcpping(portnum, argc - optind, argv + optind);
183 break;
184
185 case BRDCST:
186 if (portnum != 0)
187 usage(NULL);
188
189 brdcst(argc - optind, argv + optind);
190 break;
191
192 case DELETES:
193 deletereg(argc - optind, argv + optind);
194 break;
195
196 case SETS:
197 setreg(argc - optind, argv + optind);
198 break;
199 }
200
201 return (0);
202 }
203
204 void
udpping(u_short portnum,int argc,char ** argv)205 udpping(u_short portnum, int argc, char **argv)
206 {
207 struct timeval to;
208 struct sockaddr_in addr;
209 enum clnt_stat rpc_stat;
210 CLIENT *client;
211 u_long prognum, vers, minvers, maxvers;
212 int sock = RPC_ANYSOCK;
213 struct rpc_err rpcerr;
214 int failure;
215
216 if (argc < 2)
217 usage("too few arguments");
218 if (argc > 3)
219 usage("too many arguments");
220 if (getprognum(argv[1], &prognum))
221 usage("program number out of range");
222
223 get_inet_address(&addr, argv[0]);
224 /* Open the socket here so it will survive calls to clnt_destroy */
225 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
226 if (sock == -1) {
227 perror("rpcinfo: socket");
228 exit(1);
229 }
230 if (getuid() == 0)
231 bindresvport(sock, NULL);
232 failure = 0;
233 if (argc == 2) {
234 /*
235 * A call to version 0 should fail with a program/version
236 * mismatch, and give us the range of versions supported.
237 */
238 addr.sin_port = htons(portnum);
239 to.tv_sec = 5;
240 to.tv_usec = 0;
241 if ((client = clntudp_create(&addr, prognum, (u_long)0,
242 to, &sock)) == NULL) {
243 clnt_pcreateerror("rpcinfo");
244 printf("program %lu is not available\n",
245 prognum);
246 exit(1);
247 }
248 to.tv_sec = 10;
249 to.tv_usec = 0;
250 rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
251 xdr_void, (char *)NULL, to);
252 if (rpc_stat == RPC_PROGVERSMISMATCH) {
253 clnt_geterr(client, &rpcerr);
254 minvers = rpcerr.re_vers.low;
255 maxvers = rpcerr.re_vers.high;
256 } else if (rpc_stat == RPC_SUCCESS) {
257 /*
258 * Oh dear, it DOES support version 0.
259 * Let's try version MAX_VERS.
260 */
261 addr.sin_port = htons(portnum);
262 to.tv_sec = 5;
263 to.tv_usec = 0;
264 if ((client = clntudp_create(&addr, prognum, MAX_VERS,
265 to, &sock)) == NULL) {
266 clnt_pcreateerror("rpcinfo");
267 printf("program %lu version %lu is not available\n",
268 prognum, MAX_VERS);
269 exit(1);
270 }
271 to.tv_sec = 10;
272 to.tv_usec = 0;
273 rpc_stat = clnt_call(client, NULLPROC, xdr_void,
274 (char *)NULL, xdr_void, (char *)NULL, to);
275 if (rpc_stat == RPC_PROGVERSMISMATCH) {
276 clnt_geterr(client, &rpcerr);
277 minvers = rpcerr.re_vers.low;
278 maxvers = rpcerr.re_vers.high;
279 } else if (rpc_stat == RPC_SUCCESS) {
280 /*
281 * It also supports version MAX_VERS.
282 * Looks like we have a wise guy.
283 * OK, we give them information on all
284 * 4 billion versions they support...
285 */
286 minvers = 0;
287 maxvers = MAX_VERS;
288 } else {
289 (void) pstatus(client, prognum, MAX_VERS);
290 exit(1);
291 }
292 } else {
293 (void) pstatus(client, prognum, (u_long)0);
294 exit(1);
295 }
296 clnt_destroy(client);
297 for (vers = minvers; vers <= maxvers; vers++) {
298 addr.sin_port = htons(portnum);
299 to.tv_sec = 5;
300 to.tv_usec = 0;
301 if ((client = clntudp_create(&addr, prognum, vers,
302 to, &sock)) == NULL) {
303 clnt_pcreateerror("rpcinfo");
304 printf("program %lu version %lu is not available\n",
305 prognum, vers);
306 exit(1);
307 }
308 to.tv_sec = 10;
309 to.tv_usec = 0;
310 rpc_stat = clnt_call(client, NULLPROC, xdr_void,
311 (char *)NULL, xdr_void, (char *)NULL, to);
312 if (pstatus(client, prognum, vers) < 0)
313 failure = 1;
314 clnt_destroy(client);
315 }
316 } else {
317 getul(argv[2], &vers); /* XXX */
318 addr.sin_port = htons(portnum);
319 to.tv_sec = 5;
320 to.tv_usec = 0;
321 if ((client = clntudp_create(&addr, prognum, vers,
322 to, &sock)) == NULL) {
323 clnt_pcreateerror("rpcinfo");
324 printf("program %lu version %lu is not available\n",
325 prognum, vers);
326 exit(1);
327 }
328 to.tv_sec = 10;
329 to.tv_usec = 0;
330 rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
331 xdr_void, (char *)NULL, to);
332 if (pstatus(client, prognum, vers) < 0)
333 failure = 1;
334 }
335 (void) close(sock); /* Close it up again */
336 if (failure)
337 exit(1);
338 }
339
340 void
tcpping(u_short portnum,int argc,char ** argv)341 tcpping(u_short portnum, int argc, char **argv)
342 {
343 struct timeval to;
344 struct sockaddr_in addr;
345 enum clnt_stat rpc_stat;
346 CLIENT *client;
347 u_long prognum, vers, minvers, maxvers;
348 int sock = RPC_ANYSOCK;
349 struct rpc_err rpcerr;
350 int failure;
351
352 if (argc < 2)
353 usage("too few arguments");
354 if (argc > 3)
355 usage("too many arguments");
356 if (getprognum(argv[1], &prognum))
357 usage("program number out of range");
358
359 get_inet_address(&addr, argv[0]);
360 failure = 0;
361 if (argc == 2) {
362 /*
363 * A call to version 0 should fail with a program/version
364 * mismatch, and give us the range of versions supported.
365 */
366 addr.sin_port = htons(portnum);
367 if ((client = clnttcp_create(&addr, prognum, MIN_VERS,
368 &sock, 0, 0)) == NULL) {
369 clnt_pcreateerror("rpcinfo");
370 printf("program %lu is not available\n",
371 prognum);
372 exit(1);
373 }
374 to.tv_sec = 10;
375 to.tv_usec = 0;
376 rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
377 xdr_void, (char *)NULL, to);
378 if (rpc_stat == RPC_PROGVERSMISMATCH) {
379 clnt_geterr(client, &rpcerr);
380 minvers = rpcerr.re_vers.low;
381 maxvers = rpcerr.re_vers.high;
382 } else if (rpc_stat == RPC_SUCCESS) {
383 /*
384 * Oh dear, it DOES support version 0.
385 * Let's try version MAX_VERS.
386 */
387 addr.sin_port = htons(portnum);
388 if ((client = clnttcp_create(&addr, prognum, MAX_VERS,
389 &sock, 0, 0)) == NULL) {
390 clnt_pcreateerror("rpcinfo");
391 printf("program %lu version %lu is not available\n",
392 prognum, MAX_VERS);
393 exit(1);
394 }
395 to.tv_sec = 10;
396 to.tv_usec = 0;
397 rpc_stat = clnt_call(client, NULLPROC, xdr_void,
398 (char *)NULL, xdr_void, (char *)NULL, to);
399 if (rpc_stat == RPC_PROGVERSMISMATCH) {
400 clnt_geterr(client, &rpcerr);
401 minvers = rpcerr.re_vers.low;
402 maxvers = rpcerr.re_vers.high;
403 } else if (rpc_stat == RPC_SUCCESS) {
404 /*
405 * It also supports version MAX_VERS.
406 * Looks like we have a wise guy.
407 * OK, we give them information on all
408 * 4 billion versions they support...
409 */
410 minvers = 0;
411 maxvers = MAX_VERS;
412 } else {
413 (void) pstatus(client, prognum, MAX_VERS);
414 exit(1);
415 }
416 } else {
417 (void) pstatus(client, prognum, MIN_VERS);
418 exit(1);
419 }
420 clnt_destroy(client);
421 (void) close(sock);
422 sock = RPC_ANYSOCK; /* Re-initialize it for later */
423 for (vers = minvers; vers <= maxvers; vers++) {
424 addr.sin_port = htons(portnum);
425 if ((client = clnttcp_create(&addr, prognum, vers,
426 &sock, 0, 0)) == NULL) {
427 clnt_pcreateerror("rpcinfo");
428 printf("program %lu version %lu is not available\n",
429 prognum, vers);
430 exit(1);
431 }
432 to.tv_usec = 0;
433 to.tv_sec = 10;
434 rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
435 xdr_void, (char *)NULL, to);
436 if (pstatus(client, prognum, vers) < 0)
437 failure = 1;
438 clnt_destroy(client);
439 (void) close(sock);
440 sock = RPC_ANYSOCK;
441 }
442 } else {
443 getul(argv[2], &vers); /* XXX */
444 addr.sin_port = htons(portnum);
445 if ((client = clnttcp_create(&addr, prognum, vers, &sock,
446 0, 0)) == NULL) {
447 clnt_pcreateerror("rpcinfo");
448 printf("program %lu version %lu is not available\n",
449 prognum, vers);
450 exit(1);
451 }
452 to.tv_usec = 0;
453 to.tv_sec = 10;
454 rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
455 xdr_void, (char *)NULL, to);
456 if (pstatus(client, prognum, vers) < 0)
457 failure = 1;
458 }
459 if (failure)
460 exit(1);
461 }
462
463 /*
464 * This routine should take a pointer to an "rpc_err" structure, rather than
465 * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to
466 * a CLIENT structure rather than a pointer to an "rpc_err" structure.
467 * As such, we have to keep the CLIENT structure around in order to print
468 * a good error message.
469 */
470 int
pstatus(CLIENT * client,u_long prognum,u_long vers)471 pstatus(CLIENT *client, u_long prognum, u_long vers)
472 {
473 struct rpc_err rpcerr;
474
475 clnt_geterr(client, &rpcerr);
476 if (rpcerr.re_status != RPC_SUCCESS) {
477 clnt_perror(client, "rpcinfo");
478 printf("program %lu version %lu is not available\n",
479 prognum, vers);
480 return (-1);
481 } else {
482 printf("program %lu version %lu ready and waiting\n",
483 prognum, vers);
484 return (0);
485 }
486 }
487
488 void
pmapdump(int argc,char ** argv)489 pmapdump(int argc, char **argv)
490 {
491 struct sockaddr_in server_addr;
492 struct pmaplist *head = NULL;
493 int socket = RPC_ANYSOCK;
494 struct timeval minutetimeout;
495 CLIENT *client;
496 struct rpcent *rpc;
497
498 if (argc > 1)
499 usage("too many arguments");
500
501 if (argc == 1)
502 get_inet_address(&server_addr, argv[0]);
503 else
504 get_inet_address(&server_addr, "127.0.0.1");
505
506 minutetimeout.tv_sec = 60;
507 minutetimeout.tv_usec = 0;
508 server_addr.sin_port = htons(PMAPPORT);
509 if ((client = clnttcp_create(&server_addr, PMAPPROG,
510 PMAPVERS, &socket, 50, 500)) == NULL) {
511 clnt_pcreateerror("rpcinfo: can't contact portmapper");
512 exit(1);
513 }
514 if (clnt_call(client, PMAPPROC_DUMP, xdr_void, NULL,
515 xdr_pmaplist, &head, minutetimeout) != RPC_SUCCESS) {
516 fprintf(stderr, "rpcinfo: can't contact portmapper: ");
517 clnt_perror(client, "rpcinfo");
518 exit(1);
519 }
520 if (head == NULL) {
521 printf("No remote programs registered.\n");
522 } else {
523 printf(" program vers proto port\n");
524 for (; head != NULL; head = head->pml_next) {
525 printf("%10ld%5ld",
526 head->pml_map.pm_prog,
527 head->pml_map.pm_vers);
528 if (head->pml_map.pm_prot == IPPROTO_UDP)
529 printf("%6s", "udp");
530 else if (head->pml_map.pm_prot == IPPROTO_TCP)
531 printf("%6s", "tcp");
532 else
533 printf("%6ld", head->pml_map.pm_prot);
534 printf("%7ld", head->pml_map.pm_port);
535 rpc = getrpcbynumber(head->pml_map.pm_prog);
536 if (rpc)
537 printf(" %s\n", rpc->r_name);
538 else
539 printf("\n");
540 }
541 }
542 }
543
544 /*
545 * reply_proc collects replies from the broadcast.
546 * to get a unique list of responses the output of rpcinfo should
547 * be piped through sort(1) and then uniq(1).
548 */
549 bool_t
reply_proc(caddr_t res,struct sockaddr_in * who)550 reply_proc(caddr_t res, struct sockaddr_in *who)
551 {
552 struct hostent *hp;
553
554 hp = gethostbyaddr((char *) &who->sin_addr, sizeof who->sin_addr,
555 AF_INET);
556 printf("%s %s\n", inet_ntoa(who->sin_addr),
557 (hp == NULL) ? "(unknown)" : hp->h_name);
558 return(FALSE);
559 }
560
561 void
brdcst(int argc,char ** argv)562 brdcst(int argc, char **argv)
563 {
564 enum clnt_stat rpc_stat;
565 u_long prognum, vers_num;
566
567 if (argc != 2)
568 usage("incorrect number of arguments");
569 if (getprognum(argv[1], &prognum))
570 usage("program number out of range");
571 if (getul(argv[1], &vers_num))
572 usage("version number out of range");
573
574 rpc_stat = clnt_broadcast(prognum, vers_num, NULLPROC, xdr_void,
575 (char *)NULL, xdr_void, (char *)NULL, reply_proc);
576 if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) {
577 fprintf(stderr, "rpcinfo: broadcast failed: %s\n",
578 clnt_sperrno(rpc_stat));
579 exit(1);
580 }
581 exit(0);
582 }
583
584 void
deletereg(int argc,char ** argv)585 deletereg(int argc, char **argv)
586 {
587 u_long prog_num, version_num;
588
589 if (argc != 2)
590 usage("incorrect number of arguments");
591 if (getprognum(argv[0], &prog_num))
592 usage("program number out of range");
593 if (getul(argv[1], &version_num))
594 usage("version number out of range");
595
596 if ((pmap_unset(prog_num, version_num)) == 0) {
597 fprintf(stderr, "rpcinfo: Could not delete "
598 "registration for prog %s version %s\n",
599 argv[0], argv[1]);
600 exit(1);
601 }
602 }
603
604 void
setreg(int argc,char ** argv)605 setreg(int argc, char **argv)
606 {
607 u_long prog_num, version_num, port_num;
608
609 if (argc != 3)
610 usage("incorrect number of arguments");
611 if (getprognum(argv[0], &prog_num))
612 usage("cannot parse program number");
613 if (getul(argv[1], &version_num))
614 usage("cannot parse version number");
615 if (getul(argv[2], &port_num))
616 usage("cannot parse port number");
617 if (port_num >= 65536)
618 usage("port number out of range");
619
620 if ((pmap_set(prog_num, version_num, IPPROTO_TCP,
621 (u_short)port_num)) == 0) {
622 fprintf(stderr, "rpcinfo: Could not set registration "
623 "for prog %s version %s port %s protocol IPPROTO_TCP\n",
624 argv[0], argv[1], argv[2]);
625 exit(1);
626 }
627 if ((pmap_set(prog_num, version_num, IPPROTO_UDP,
628 (u_short)port_num)) == 0) {
629 fprintf(stderr, "rpcinfo: Could not set registration "
630 "for prog %s version %s port %s protocol IPPROTO_UDP\n",
631 argv[0], argv[1], argv[2]);
632 exit(1);
633 }
634 }
635
636 void
usage(char * msg)637 usage(char *msg)
638 {
639 if (msg)
640 fprintf(stderr,
641 "rpcinfo: %s\n", msg);
642 fprintf(stderr, "usage: rpcinfo -b program version\n");
643 fprintf(stderr, " rpcinfo -d program version\n");
644 fprintf(stderr, " rpcinfo -p [host]\n");
645 fprintf(stderr, " rpcinfo -s program version port\n");
646 fprintf(stderr,
647 " rpcinfo [-n portnum] -t host program [version]\n");
648 fprintf(stderr,
649 " rpcinfo [-n portnum] -u host program [version]\n");
650 exit(1);
651 }
652
653 int
getprognum(char * arg,u_long * ulp)654 getprognum(char *arg, u_long *ulp)
655 {
656 struct rpcent *rpc;
657
658 if (isalpha(*arg)) {
659 rpc = getrpcbyname(arg);
660 if (rpc == NULL) {
661 fprintf(stderr, "rpcinfo: %s is unknown service\n",
662 arg);
663 exit(1);
664 }
665 *ulp = rpc->r_number;
666 return 0;
667 }
668 return getul(arg, ulp);
669 }
670
671 int
getul(char * arg,u_long * ulp)672 getul(char *arg, u_long *ulp)
673 {
674 u_long ul;
675 int save_errno = errno;
676 char *ep;
677 int ret = 1;
678
679 errno = 0;
680 ul = strtoul(arg, &ep, 10);
681 if (arg[0] == '\0' || *ep != '\0')
682 goto fail;
683 if (errno == ERANGE && ul == ULONG_MAX)
684 goto fail;
685 *ulp = ul;
686 ret = 0;
687 fail:
688 errno = save_errno;
689 return (ret);
690 }
691
692 void
get_inet_address(struct sockaddr_in * addr,char * host)693 get_inet_address(struct sockaddr_in *addr, char *host)
694 {
695 struct addrinfo hints, *res;
696 int error;
697
698 memset(&hints, 0, sizeof(hints));
699 hints.ai_family = AF_INET;
700
701 if ((error = getaddrinfo(host, NULL, &hints, &res))) {
702 fprintf(stderr, "rpcinfo: %s is unknown host: %s\n",
703 host, gai_strerror(error));
704 exit(1);
705 }
706
707 addr->sin_family = AF_INET;
708 addr->sin_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
709 freeaddrinfo(res);
710 }
711