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