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 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 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 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 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 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 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 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 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 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 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 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 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 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