Merge from vendor branch BIND:
[dragonfly.git] / usr.bin / rpcinfo / rpcinfo.c
1 /*
2  * Copyright (C) 1986, Sun Microsystems, Inc.
3  *
4  * @(#)rpcinfo.c 1.22 87/08/12 SMI
5  * @(#)rpcinfo.c        2.2 88/08/11 4.0 RPCSRC
6  * $FreeBSD: src/usr.bin/rpcinfo/rpcinfo.c,v 1.9.2.1 2001/03/04 09:00:23 kris Exp $
7  * $DragonFly: src/usr.bin/rpcinfo/rpcinfo.c,v 1.2 2003/06/17 04:29:31 dillon Exp $
8  */
9 /*
10  * rpcinfo: ping a particular rpc program
11  *     or dump the portmapper
12  */
13
14 /*
15  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
16  * unrestricted use provided that this legend is included on all tape
17  * media and as a part of the software program in whole or part.  Users
18  * may copy or modify Sun RPC without charge, but are not authorized
19  * to license or distribute it to anyone else except as part of a product or
20  * program developed by the user.
21  *
22  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
23  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
25  *
26  * Sun RPC is provided with no support and without any obligation on the
27  * part of Sun Microsystems, Inc. to assist in its use, correction,
28  * modification or enhancement.
29  *
30  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
31  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
32  * OR ANY PART THEREOF.
33  *
34  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
35  * or profits or other special, indirect and consequential damages, even if
36  * Sun has been advised of the possibility of such damages.
37  *
38  * Sun Microsystems, Inc.
39  * 2550 Garcia Avenue
40  * Mountain View, California  94043
41  */
42
43 #include <err.h>
44 #include <ctype.h>
45 #include <rpc/rpc.h>
46 #include <stdio.h>
47 #include <sys/socket.h>
48 #include <netdb.h>
49 #include <rpc/pmap_prot.h>
50 #include <rpc/pmap_clnt.h>
51 #include <signal.h>
52 #include <ctype.h>
53 #include <unistd.h>
54 #include <sys/param.h>
55 #include <arpa/inet.h>
56
57 #define MAXHOSTLEN 256
58
59 #define MIN_VERS        ((u_long) 0)
60 #define MAX_VERS        ((u_long) 4294967295UL)
61
62 static void     udpping(/*u_short portflag, int argc, char **argv*/);
63 static void     tcpping(/*u_short portflag, int argc, char **argv*/);
64 static int      pstatus(/*CLIENT *client, u_long prognum, u_long vers*/);
65 static void     pmapdump(/*int argc, char **argv*/);
66 static bool_t   reply_proc(/*void *res, struct sockaddr_in *who*/);
67 static void     brdcst(/*int argc, char **argv*/);
68 static void     deletereg(/* int argc, char **argv */) ;
69 static void     usage(/*void*/);
70 static u_long   getprognum(/*char *arg*/);
71 static u_long   getvers(/*char *arg*/);
72 static void     get_inet_address(/*struct sockaddr_in *addr, char *host*/);
73
74 /*
75  * Functions to be performed.
76  */
77 #define NONE            0       /* no function */
78 #define PMAPDUMP        1       /* dump portmapper registrations */
79 #define TCPPING         2       /* ping TCP service */
80 #define UDPPING         3       /* ping UDP service */
81 #define BRDCST          4       /* ping broadcast UDP service */
82 #define DELETES         5       /* delete registration for the service */
83
84 int
85 main(argc, argv)
86         int argc;
87         char **argv;
88 {
89         register int c;
90         int errflg;
91         int function;
92         u_short portnum;
93
94         function = NONE;
95         portnum = 0;
96         errflg = 0;
97         while ((c = getopt(argc, argv, "ptubdn:")) != -1) {
98                 switch (c) {
99
100                 case 'p':
101                         if (function != NONE)
102                                 errflg = 1;
103                         else
104                                 function = PMAPDUMP;
105                         break;
106
107                 case 't':
108                         if (function != NONE)
109                                 errflg = 1;
110                         else
111                                 function = TCPPING;
112                         break;
113
114                 case 'u':
115                         if (function != NONE)
116                                 errflg = 1;
117                         else
118                                 function = UDPPING;
119                         break;
120
121                 case 'b':
122                         if (function != NONE)
123                                 errflg = 1;
124                         else
125                                 function = BRDCST;
126                         break;
127
128                 case 'n':
129                         portnum = (u_short) atoi(optarg);   /* hope we don't get bogus # */
130                         break;
131
132                 case 'd':
133                         if (function != NONE)
134                                 errflg = 1;
135                         else
136                                 function = DELETES;
137                         break;
138
139                 case '?':
140                         errflg = 1;
141                 }
142         }
143
144         if (errflg || function == NONE) {
145                 usage();
146                 return (1);
147         }
148
149         switch (function) {
150
151         case PMAPDUMP:
152                 if (portnum != 0) {
153                         usage();
154                         return (1);
155                 }
156                 pmapdump(argc - optind, argv + optind);
157                 break;
158
159         case UDPPING:
160                 udpping(portnum, argc - optind, argv + optind);
161                 break;
162
163         case TCPPING:
164                 tcpping(portnum, argc - optind, argv + optind);
165                 break;
166
167         case BRDCST:
168                 if (portnum != 0) {
169                         usage();
170                         return (1);
171                 }
172                 brdcst(argc - optind, argv + optind);
173                 break;
174
175         case DELETES:
176                 deletereg(argc - optind, argv + optind);
177                 break;
178         }
179
180         return (0);
181 }
182
183 static void
184 udpping(portnum, argc, argv)
185         u_short portnum;
186         int argc;
187         char **argv;
188 {
189         struct timeval to;
190         struct sockaddr_in addr;
191         enum clnt_stat rpc_stat;
192         CLIENT *client;
193         u_long prognum, vers, minvers, maxvers;
194         int sock = RPC_ANYSOCK;
195         struct rpc_err rpcerr;
196         int failure;
197
198         if (argc < 2 || argc > 3) {
199                 usage();
200                 exit(1);
201         }
202         prognum = getprognum(argv[1]);
203         get_inet_address(&addr, argv[0]);
204         /* Open the socket here so it will survive calls to clnt_destroy */
205         sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP);
206         if (sock < 0) {
207                 perror("rpcinfo: socket");
208                 exit(1);
209         }
210         failure = 0;
211         if (argc == 2) {
212                 /*
213                  * A call to version 0 should fail with a program/version
214                  * mismatch, and give us the range of versions supported.
215                  */
216                 addr.sin_port = htons(portnum);
217                 to.tv_sec = 5;
218                 to.tv_usec = 0;
219                 if ((client = clntudp_create(&addr, prognum, (u_long)0,
220                     to, &sock)) == NULL) {
221                         clnt_pcreateerror("rpcinfo");
222                         printf("program %lu is not available\n",
223                             prognum);
224                         exit(1);
225                 }
226                 to.tv_sec = 10;
227                 to.tv_usec = 0;
228                 rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
229                     xdr_void, (char *)NULL, to);
230                 if (rpc_stat == RPC_PROGVERSMISMATCH) {
231                         clnt_geterr(client, &rpcerr);
232                         minvers = rpcerr.re_vers.low;
233                         maxvers = rpcerr.re_vers.high;
234                 } else if (rpc_stat == RPC_SUCCESS) {
235                         /*
236                          * Oh dear, it DOES support version 0.
237                          * Let's try version MAX_VERS.
238                          */
239                         addr.sin_port = htons(portnum);
240                         to.tv_sec = 5;
241                         to.tv_usec = 0;
242                         if ((client = clntudp_create(&addr, prognum, MAX_VERS,
243                             to, &sock)) == NULL) {
244                                 clnt_pcreateerror("rpcinfo");
245                                 printf("program %lu version %lu is not available\n",
246                                     prognum, MAX_VERS);
247                                 exit(1);
248                         }
249                         to.tv_sec = 10;
250                         to.tv_usec = 0;
251                         rpc_stat = clnt_call(client, NULLPROC, xdr_void,
252                             (char *)NULL, xdr_void, (char *)NULL, to);
253                         if (rpc_stat == RPC_PROGVERSMISMATCH) {
254                                 clnt_geterr(client, &rpcerr);
255                                 minvers = rpcerr.re_vers.low;
256                                 maxvers = rpcerr.re_vers.high;
257                         } else if (rpc_stat == RPC_SUCCESS) {
258                                 /*
259                                  * It also supports version MAX_VERS.
260                                  * Looks like we have a wise guy.
261                                  * OK, we give them information on all
262                                  * 4 billion versions they support...
263                                  */
264                                 minvers = 0;
265                                 maxvers = MAX_VERS;
266                         } else {
267                                 (void) pstatus(client, prognum, MAX_VERS);
268                                 exit(1);
269                         }
270                 } else {
271                         (void) pstatus(client, prognum, (u_long)0);
272                         exit(1);
273                 }
274                 clnt_destroy(client);
275                 for (vers = minvers; vers <= maxvers; vers++) {
276                         addr.sin_port = htons(portnum);
277                         to.tv_sec = 5;
278                         to.tv_usec = 0;
279                         if ((client = clntudp_create(&addr, prognum, vers,
280                             to, &sock)) == NULL) {
281                                 clnt_pcreateerror("rpcinfo");
282                                 printf("program %lu version %lu is not available\n",
283                                     prognum, vers);
284                                 exit(1);
285                         }
286                         to.tv_sec = 10;
287                         to.tv_usec = 0;
288                         rpc_stat = clnt_call(client, NULLPROC, xdr_void,
289                             (char *)NULL, xdr_void, (char *)NULL, to);
290                         if (pstatus(client, prognum, vers) < 0)
291                                 failure = 1;
292                         clnt_destroy(client);
293                 }
294         }
295         else {
296                 vers = getvers(argv[2]);
297                 addr.sin_port = htons(portnum);
298                 to.tv_sec = 5;
299                 to.tv_usec = 0;
300                 if ((client = clntudp_create(&addr, prognum, vers,
301                     to, &sock)) == NULL) {
302                         clnt_pcreateerror("rpcinfo");
303                         printf("program %lu version %lu is not available\n",
304                             prognum, vers);
305                         exit(1);
306                 }
307                 to.tv_sec = 10;
308                 to.tv_usec = 0;
309                 rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
310                     xdr_void, (char *)NULL, to);
311                 if (pstatus(client, prognum, vers) < 0)
312                         failure = 1;
313         }
314         (void) close(sock); /* Close it up again */
315         if (failure)
316                 exit(1);
317 }
318
319 static void
320 tcpping(portnum, argc, argv)
321         u_short portnum;
322         int argc;
323         char **argv;
324 {
325         struct timeval to;
326         struct sockaddr_in addr;
327         enum clnt_stat rpc_stat;
328         CLIENT *client;
329         u_long prognum, vers, minvers, maxvers;
330         int sock = RPC_ANYSOCK;
331         struct rpc_err rpcerr;
332         int failure;
333
334         if (argc < 2 || argc > 3) {
335                 usage();
336                 exit(1);
337         }
338         prognum = getprognum(argv[1]);
339         get_inet_address(&addr, argv[0]);
340         failure = 0;
341         if (argc == 2) {
342                 /*
343                  * A call to version 0 should fail with a program/version
344                  * mismatch, and give us the range of versions supported.
345                  */
346                 addr.sin_port = htons(portnum);
347                 if ((client = clnttcp_create(&addr, prognum, MIN_VERS,
348                     &sock, 0, 0)) == NULL) {
349                         clnt_pcreateerror("rpcinfo");
350                         printf("program %lu is not available\n",
351                             prognum);
352                         exit(1);
353                 }
354                 to.tv_sec = 10;
355                 to.tv_usec = 0;
356                 rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
357                     xdr_void, (char *)NULL, to);
358                 if (rpc_stat == RPC_PROGVERSMISMATCH) {
359                         clnt_geterr(client, &rpcerr);
360                         minvers = rpcerr.re_vers.low;
361                         maxvers = rpcerr.re_vers.high;
362                 } else if (rpc_stat == RPC_SUCCESS) {
363                         /*
364                          * Oh dear, it DOES support version 0.
365                          * Let's try version MAX_VERS.
366                          */
367                         addr.sin_port = htons(portnum);
368                         if ((client = clnttcp_create(&addr, prognum, MAX_VERS,
369                             &sock, 0, 0)) == NULL) {
370                                 clnt_pcreateerror("rpcinfo");
371                                 printf("program %lu version %lu is not available\n",
372                                     prognum, MAX_VERS);
373                                 exit(1);
374                         }
375                         to.tv_sec = 10;
376                         to.tv_usec = 0;
377                         rpc_stat = clnt_call(client, NULLPROC, xdr_void,
378                             (char *)NULL, xdr_void, (char *)NULL, to);
379                         if (rpc_stat == RPC_PROGVERSMISMATCH) {
380                                 clnt_geterr(client, &rpcerr);
381                                 minvers = rpcerr.re_vers.low;
382                                 maxvers = rpcerr.re_vers.high;
383                         } else if (rpc_stat == RPC_SUCCESS) {
384                                 /*
385                                  * It also supports version MAX_VERS.
386                                  * Looks like we have a wise guy.
387                                  * OK, we give them information on all
388                                  * 4 billion versions they support...
389                                  */
390                                 minvers = 0;
391                                 maxvers = MAX_VERS;
392                         } else {
393                                 (void) pstatus(client, prognum, MAX_VERS);
394                                 exit(1);
395                         }
396                 } else {
397                         (void) pstatus(client, prognum, MIN_VERS);
398                         exit(1);
399                 }
400                 clnt_destroy(client);
401                 (void) close(sock);
402                 sock = RPC_ANYSOCK; /* Re-initialize it for later */
403                 for (vers = minvers; vers <= maxvers; vers++) {
404                         addr.sin_port = htons(portnum);
405                         if ((client = clnttcp_create(&addr, prognum, vers,
406                             &sock, 0, 0)) == NULL) {
407                                 clnt_pcreateerror("rpcinfo");
408                                 printf("program %lu version %lu is not available\n",
409                                     prognum, vers);
410                                 exit(1);
411                         }
412                         to.tv_usec = 0;
413                         to.tv_sec = 10;
414                         rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
415                             xdr_void, (char *)NULL, to);
416                         if (pstatus(client, prognum, vers) < 0)
417                                 failure = 1;
418                         clnt_destroy(client);
419                         (void) close(sock);
420                         sock = RPC_ANYSOCK;
421                 }
422         }
423         else {
424                 vers = getvers(argv[2]);
425                 addr.sin_port = htons(portnum);
426                 if ((client = clnttcp_create(&addr, prognum, vers, &sock,
427                     0, 0)) == NULL) {
428                         clnt_pcreateerror("rpcinfo");
429                         printf("program %lu version %lu is not available\n",
430                             prognum, vers);
431                         exit(1);
432                 }
433                 to.tv_usec = 0;
434                 to.tv_sec = 10;
435                 rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
436                     xdr_void, (char *)NULL, to);
437                 if (pstatus(client, prognum, vers) < 0)
438                         failure = 1;
439         }
440         if (failure)
441                 exit(1);
442 }
443
444 /*
445  * This routine should take a pointer to an "rpc_err" structure, rather than
446  * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to
447  * a CLIENT structure rather than a pointer to an "rpc_err" structure.
448  * As such, we have to keep the CLIENT structure around in order to print
449  * a good error message.
450  */
451 static int
452 pstatus(client, prognum, vers)
453         register CLIENT *client;
454         u_long prognum;
455         u_long vers;
456 {
457         struct rpc_err rpcerr;
458
459         clnt_geterr(client, &rpcerr);
460         if (rpcerr.re_status != RPC_SUCCESS) {
461                 clnt_perror(client, "rpcinfo");
462                 printf("program %lu version %lu is not available\n",
463                     prognum, vers);
464                 return (-1);
465         } else {
466                 printf("program %lu version %lu ready and waiting\n",
467                     prognum, vers);
468                 return (0);
469         }
470 }
471
472 static void
473 pmapdump(argc, argv)
474         int argc;
475         char **argv;
476 {
477         struct sockaddr_in server_addr;
478         register struct hostent *hp;
479         struct pmaplist *head = NULL;
480         int socket = RPC_ANYSOCK;
481         struct timeval minutetimeout;
482         register CLIENT *client;
483         struct rpcent *rpc;
484
485         if (argc > 1) {
486                 usage();
487                 exit(1);
488         }
489         if (argc == 1)
490                 get_inet_address(&server_addr, argv[0]);
491         else {
492                 bzero((char *)&server_addr, sizeof server_addr);
493                 server_addr.sin_family = AF_INET;
494                 if ((hp = gethostbyname("localhost")) != NULL)
495                         bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr,
496                             MIN(hp->h_length,sizeof(server_addr.sin_addr)));
497                 else
498                         server_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
499         }
500         minutetimeout.tv_sec = 60;
501         minutetimeout.tv_usec = 0;
502         server_addr.sin_port = htons(PMAPPORT);
503         if ((client = clnttcp_create(&server_addr, PMAPPROG,
504             PMAPVERS, &socket, 50, 500)) == NULL) {
505                 clnt_pcreateerror("rpcinfo: can't contact portmapper");
506                 exit(1);
507         }
508         if (clnt_call(client, PMAPPROC_DUMP, xdr_void, NULL,
509             xdr_pmaplist, &head, minutetimeout) != RPC_SUCCESS) {
510                 fprintf(stderr, "rpcinfo: can't contact portmapper: ");
511                 clnt_perror(client, "rpcinfo");
512                 exit(1);
513         }
514         if (head == NULL) {
515                 printf("No remote programs registered.\n");
516         } else {
517                 printf("   program vers proto   port\n");
518                 for (; head != NULL; head = head->pml_next) {
519                         printf("%10ld%5ld",
520                             head->pml_map.pm_prog,
521                             head->pml_map.pm_vers);
522                         if (head->pml_map.pm_prot == IPPROTO_UDP)
523                                 printf("%6s",  "udp");
524                         else if (head->pml_map.pm_prot == IPPROTO_TCP)
525                                 printf("%6s", "tcp");
526                         else
527                                 printf("%6ld",  head->pml_map.pm_prot);
528                         printf("%7ld",  head->pml_map.pm_port);
529                         rpc = getrpcbynumber(head->pml_map.pm_prog);
530                         if (rpc)
531                                 printf("  %s\n", rpc->r_name);
532                         else
533                                 printf("\n");
534                 }
535         }
536 }
537
538 /*
539  * reply_proc collects replies from the broadcast.
540  * to get a unique list of responses the output of rpcinfo should
541  * be piped through sort(1) and then uniq(1).
542  */
543
544 /*ARGSUSED*/
545 static bool_t
546 reply_proc(res, who)
547         void *res;              /* Nothing comes back */
548         struct sockaddr_in *who; /* Who sent us the reply */
549 {
550         register struct hostent *hp;
551
552         hp = gethostbyaddr((char *) &who->sin_addr, sizeof who->sin_addr,
553             AF_INET);
554         printf("%s %s\n", inet_ntoa(who->sin_addr),
555             (hp == NULL) ? "(unknown)" : hp->h_name);
556         return(FALSE);
557 }
558
559 static void
560 brdcst(argc, argv)
561         int argc;
562         char **argv;
563 {
564         enum clnt_stat rpc_stat;
565         u_long prognum, vers;
566
567         if (argc != 2) {
568                 usage();
569                 exit(1);
570         }
571         prognum = getprognum(argv[0]);
572         vers = getvers(argv[1]);
573         rpc_stat = clnt_broadcast(prognum, vers, NULLPROC, xdr_void,
574             (char *)NULL, xdr_void, (char *)NULL, reply_proc);
575         if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) {
576                 fprintf(stderr, "rpcinfo: broadcast failed: %s\n",
577                     clnt_sperrno(rpc_stat));
578                 exit(1);
579         }
580         exit(0);
581 }
582
583 static void
584 deletereg(argc, argv)
585         int argc;
586         char **argv;
587 {       u_long prog_num, version_num ;
588
589         if (argc != 2) {
590                 usage() ;
591                 exit(1) ;
592         }
593         if (getuid()) /* This command allowed only to root */
594                 errx(1, "sorry, you are not root") ;
595         prog_num = getprognum(argv[0]);
596         version_num = getvers(argv[1]);
597         if ((pmap_unset(prog_num, version_num)) == 0)
598                 errx(1, "could not delete registration for prog %s version %s",
599                         argv[0], argv[1]) ;
600 }
601
602 static void
603 usage()
604 {
605         fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
606                 "usage: rpcinfo [-n portnum] -u host prognum [versnum]",
607                 "       rpcinfo [-n portnum] -t host prognum [versnum]",
608                 "       rpcinfo -p [host]",
609                 "       rpcinfo -b prognum versnum",
610                 "       rpcinfo -d prognum versnum");
611 }
612
613 static u_long
614 getprognum(arg)
615         char *arg;
616 {
617         register struct rpcent *rpc;
618         register u_long prognum;
619
620         if (isalpha(*arg)) {
621                 rpc = getrpcbyname(arg);
622                 if (rpc == NULL)
623                         errx(1, "%s is unknown service", arg);
624                 prognum = rpc->r_number;
625         } else {
626                 prognum = (u_long) atoi(arg);
627         }
628
629         return (prognum);
630 }
631
632 static u_long
633 getvers(arg)
634         char *arg;
635 {
636         register u_long vers;
637
638         vers = (int) atoi(arg);
639         return (vers);
640 }
641
642 static void
643 get_inet_address(addr, host)
644         struct sockaddr_in *addr;
645         char *host;
646 {
647         register struct hostent *hp;
648
649         bzero((char *)addr, sizeof *addr);
650         addr->sin_addr.s_addr = (u_long) inet_addr(host);
651         if (addr->sin_addr.s_addr == -1 || addr->sin_addr.s_addr == 0) {
652                 if ((hp = gethostbyname(host)) == NULL)
653                         errx(1, "%s is unknown host\n", host);
654                 bcopy(hp->h_addr, (char *)&addr->sin_addr, 
655                         MIN(hp->h_length,sizeof(addr->sin_addr)));
656         }
657         addr->sin_family = AF_INET;
658 }