xref: /openbsd/usr.bin/rpcinfo/rpcinfo.c (revision f6e4162f)
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