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