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