yp_server.c revision 13800
1/* 2 * Copyright (c) 1995 3 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Bill Paul. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 */ 33 34#include "yp_extern.h" 35#include "yp.h" 36#include <stdlib.h> 37#include <dirent.h> 38#include <sys/stat.h> 39#include <sys/param.h> 40#include <errno.h> 41#include <sys/types.h> 42#include <sys/socket.h> 43#include <netinet/in.h> 44#include <arpa/inet.h> 45#include <rpc/rpc.h> 46 47#ifndef lint 48static char rcsid[] = "$Id: yp_server.c,v 1.3 1996/01/10 16:07:39 wpaul Exp $"; 49#endif /* not lint */ 50 51int forked = 0; 52int children = 0; 53DB *spec_dbp = NULL; /* Special global DB handle for ypproc_all. */ 54 55void * 56ypproc_null_2_svc(void *argp, struct svc_req *rqstp) 57{ 58 static char * result; 59 static char rval = 0; 60 61 if (yp_access(NULL, (struct svc_req *)rqstp)) 62 return(NULL); 63 64 result = &rval; 65 66 return((void *) &result); 67} 68 69bool_t * 70ypproc_domain_2_svc(domainname *argp, struct svc_req *rqstp) 71{ 72 static bool_t result; 73 74 if (yp_access(NULL, (struct svc_req *)rqstp)) { 75 result = FALSE; 76 return (&result); 77 } 78 79 if (argp == NULL || yp_validdomain(*argp)) 80 result = FALSE; 81 else 82 result = TRUE; 83 84 return (&result); 85} 86 87bool_t * 88ypproc_domain_nonack_2_svc(domainname *argp, struct svc_req *rqstp) 89{ 90 static bool_t result; 91 92 if (yp_access(NULL, (struct svc_req *)rqstp)) 93 return (NULL); 94 95 if (argp == NULL || yp_validdomain(*argp)) 96 return (NULL); 97 else 98 result = TRUE; 99 100 return (&result); 101} 102 103ypresp_val * 104ypproc_match_2_svc(ypreq_key *argp, struct svc_req *rqstp) 105{ 106 static ypresp_val result; 107 DBT key, data; 108 109 if (yp_access(argp->map, (struct svc_req *)rqstp)) { 110 result.stat = YP_YPERR; 111 return (&result); 112 } 113 114 if (argp->domain == NULL || argp->map == NULL) { 115 result.stat = YP_BADARGS; 116 return (&result); 117 } 118 119 if (yp_validdomain(argp->domain)) { 120 result.stat = YP_NODOM; 121 return(&result); 122 } 123 124 key.size = argp->key.keydat_len; 125 key.data = argp->key.keydat_val; 126 127 result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 0); 128 129 if (result.stat == YP_TRUE) { 130 result.val.valdat_len = data.size; 131 result.val.valdat_val = data.data; 132 } 133 134 /* 135 * Do DNS lookups for hosts maps if database lookup failed. 136 */ 137 138 if (do_dns && result.stat != YP_TRUE && strstr(argp->map, "hosts")) { 139 char *rval = NULL; 140 141 /* DNS lookups can take time -- do them in a subprocess */ 142 143 if (!debug && children < MAX_CHILDREN && fork()) { 144 children++; 145 forked = 0; 146 /* 147 * Returning NULL here prevents svc_sendreply() 148 * from being called by the parent. This is vital 149 * since having both the parent and the child process 150 * call it would confuse the client. 151 */ 152 return (NULL); 153 } else { 154 forked++; 155 } 156 157 if (debug) 158 yp_error("Doing DNS lookup of %.*s", 159 argp->key.keydat_len, 160 argp->key.keydat_val); 161 162 /* NUL terminate! NUL terminate!! NUL TERMINATE!!! */ 163 argp->key.keydat_val[argp->key.keydat_len] = '\0'; 164 165 if (!strcmp(argp->map, "hosts.byname")) 166 rval = yp_dnsname((char *)argp->key.keydat_val); 167 else if (!strcmp(argp->map, "hosts.byaddr")) 168 rval = yp_dnsaddr((const char *)argp->key.keydat_val); 169 170 171 if (rval) { 172 if (debug) 173 yp_error("DNS lookup successful. Result: %s", rval); 174 result.val.valdat_len = strlen(rval); 175 result.val.valdat_val = rval; 176 result.stat = YP_TRUE; 177 } else { 178 if (debug) 179 yp_error("DNS lookup failed."); 180 result.stat = YP_NOKEY; 181 } 182 } 183 184 return (&result); 185} 186 187ypresp_key_val * 188ypproc_first_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 189{ 190 static ypresp_key_val result; 191 DBT key, data; 192 DB *dbp; 193 194 if (yp_access(argp->map, (struct svc_req *)rqstp)) { 195 result.stat = YP_YPERR; 196 return (&result); 197 } 198 199 if (argp->domain == NULL) { 200 result.stat = YP_BADARGS; 201 return (&result); 202 } 203 204 if (yp_validdomain(argp->domain)) { 205 result.stat = YP_NODOM; 206 return(&result); 207 } 208 209 if ((dbp = yp_open_db(argp->domain, argp->map)) == NULL) { 210 result.stat = yp_errno; 211 return(&result); 212 } 213 214 key.data = NULL; 215 key.size = 0; 216 result.stat = yp_first_record(dbp, &key, &data); 217 (void)(dbp->close)(dbp); 218 219 if (result.stat == YP_TRUE) { 220 result.key.keydat_len = key.size; 221 result.key.keydat_val = key.data; 222 result.val.valdat_len = data.size; 223 result.val.valdat_val = data.data; 224 } 225 226 return (&result); 227} 228 229ypresp_key_val * 230ypproc_next_2_svc(ypreq_key *argp, struct svc_req *rqstp) 231{ 232 static ypresp_key_val result; 233 DBT key, data; 234 DB *dbp; 235 236 if (yp_access(argp->map, (struct svc_req *)rqstp)) { 237 result.stat = YP_YPERR; 238 return (&result); 239 } 240 241 if (argp->domain == NULL || argp->map == NULL) { 242 result.stat = YP_BADARGS; 243 return (&result); 244 } 245 246 if (yp_validdomain(argp->domain)) { 247 result.stat = YP_NODOM; 248 return(&result); 249 } 250 251 if ((dbp = yp_open_db(argp->domain, argp->map)) == NULL) { 252 result.stat = yp_errno; 253 return(&result); 254 } 255 256 key.size = argp->key.keydat_len; 257 key.data = argp->key.keydat_val; 258 259 result.stat = yp_next_record(dbp, &key, &data, 0); 260 (void)(dbp->close)(dbp); 261 262 if (result.stat == YP_TRUE) { 263 result.key.keydat_len = key.size; 264 result.key.keydat_val = key.data; 265 result.val.valdat_len = data.size; 266 result.val.valdat_val = data.data; 267 } 268 269 return (&result); 270} 271 272static void ypxfr_callback(rval,addr,transid,prognum,port) 273 ypxfrstat rval; 274 struct sockaddr_in *addr; 275 unsigned int transid; 276 unsigned int prognum; 277 unsigned long port; 278{ 279 CLIENT *clnt; 280 int sock = RPC_ANYSOCK; 281 struct timeval timeout; 282 yppushresp_xfr ypxfr_resp; 283 struct rpc_err err; 284 285 timeout.tv_sec = 5; 286 timeout.tv_usec = 0; 287 addr->sin_port = htons(port); 288 289 if ((clnt = clntudp_create(addr, prognum, 1, timeout, &sock)) == NULL) 290 yp_error("%s", clnt_spcreateerror("failed to establish \ 291callback handle")); 292 293 ypxfr_resp.status = rval; 294 ypxfr_resp.transid = transid; 295 296 /* Turn the timeout off -- we don't want to block. */ 297 timeout.tv_sec = 0; 298 if (clnt_control(clnt, CLSET_TIMEOUT, (char *)&timeout) == FALSE) 299 yp_error("failed to set timeout on ypproc_xfr callback"); 300 301 if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) { 302 clnt_geterr(clnt, &err); 303 if (err.re_status != RPC_SUCCESS && 304 err.re_status != RPC_TIMEDOUT) 305 yp_error("%s", clnt_sperror(clnt, 306 "ypxfr callback failed")); 307 } 308 309 clnt_destroy(clnt); 310 return; 311} 312 313ypresp_xfr * 314ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp) 315{ 316 static ypresp_xfr result; 317 struct sockaddr_in *rqhost; 318 319 result.transid = argp->transid; 320 rqhost = svc_getcaller(rqstp->rq_xprt); 321 322 if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) { 323 /* Order is important: send regular RPC reply, then callback */ 324 result.xfrstat = YPXFR_REFUSED; 325 svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result); 326 ypxfr_callback(YPXFR_REFUSED,rqhost,argp->transid, 327 argp->prog,argp->port); 328 return(NULL); 329 } 330 331 if (argp->map_parms.domain == NULL) { 332 result.xfrstat = YPXFR_BADARGS; 333 svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result); 334 ypxfr_callback(YPXFR_BADARGS,rqhost,argp->transid, 335 argp->prog,argp->port); 336 return(NULL); 337 } 338 339 if (yp_validdomain(argp->map_parms.domain)) { 340 result.xfrstat = YPXFR_NODOM; 341 svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result); 342 ypxfr_callback(YPXFR_NODOM,rqhost,argp->transid, 343 argp->prog,argp->port); 344 return(NULL); 345 } 346 347 switch(fork()) { 348 case 0: 349 { 350 char g[11], t[11], p[11]; 351 char ypxfr_command[MAXPATHLEN + 2]; 352 353 sprintf (ypxfr_command, "%sypxfr", _PATH_LIBEXEC); 354 sprintf (t, "%u", argp->transid); 355 sprintf (g, "%u", argp->prog); 356 sprintf (p, "%u", argp->port); 357 if (debug) 358 close(0); close(1); close(2); 359 if (strcmp(yp_dir, _PATH_YP)) { 360 execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain, 361 "-h", argp->map_parms.peer, "-p", yp_dir, "-C", t, 362 g, inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map, 363 NULL); 364 } else { 365 execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain, 366 "-h", argp->map_parms.peer, "-C", t, g, 367 inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map, 368 NULL); 369 } 370 forked++; 371 result.xfrstat = YPXFR_XFRERR; 372 yp_error("ypxfr execl(): %s", strerror(errno)); 373 svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result); 374 ypxfr_callback(YPXFR_XFRERR,rqhost,argp->transid, 375 argp->prog,argp->port); 376 return(NULL); 377 break; 378 } 379 case -1: 380 yp_error("ypxfr fork(): %s", strerror(errno)); 381 result.xfrstat = YPXFR_XFRERR; 382 svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result); 383 ypxfr_callback(YPXFR_XFRERR,rqhost,argp->transid, 384 argp->prog,argp->port); 385 return(NULL); 386 break; 387 default: 388 result.xfrstat = YPXFR_SUCC; 389 children++; 390 forked = 0; 391 break; 392 } 393 394 return (&result); 395} 396 397void * 398ypproc_clear_2_svc(void *argp, struct svc_req *rqstp) 399{ 400 static char * result; 401 static char rval = 0; 402 403 /* 404 * We don't have to do anything for ypproc_clear. Unlike 405 * the SunOS ypserv, we don't hold out database descriptors 406 * open forever. 407 */ 408 if (yp_access(NULL, (struct svc_req *)rqstp)) 409 return (NULL); 410 411 result = &rval; 412 return((void *) &result); 413} 414 415/* 416 * For ypproc_all, we have to send a stream of ypresp_all structures 417 * via TCP, but the XDR filter generated from the yp.x protocol 418 * definition file only serializes one such structure. This means that 419 * to send the whole stream, you need a wrapper which feeds all the 420 * records into the underlying XDR routine until it hits an 'EOF.' 421 * But to use the wrapper, you have to violate the boundaries between 422 * RPC layers by calling svc_sendreply() directly from the ypproc_all 423 * service routine instead of letting the RPC dispatcher do it. 424 * 425 * Bleah. 426 */ 427 428/* 429 * Custom XDR routine for serialzing results of ypproc_all: keep 430 * reading from the database and spew until we run out of records 431 * or encounter an error. 432 */ 433static bool_t 434xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp) 435{ 436 DBT key, data; 437 438 while (1) { 439 /* Get a record. */ 440 key.size = objp->ypresp_all_u.val.key.keydat_len; 441 key.data = objp->ypresp_all_u.val.key.keydat_val; 442 443 if ((objp->ypresp_all_u.val.stat = 444 yp_next_record(spec_dbp,&key,&data,1)) == YP_TRUE) { 445 objp->ypresp_all_u.val.val.valdat_len = data.size; 446 objp->ypresp_all_u.val.val.valdat_val = data.data; 447 objp->ypresp_all_u.val.key.keydat_len = key.size; 448 objp->ypresp_all_u.val.key.keydat_val = key.data; 449 objp->more = TRUE; 450 } else { 451 objp->more = FALSE; 452 } 453 454 /* Serialize. */ 455 if (!xdr_ypresp_all(xdrs, objp)) 456 return(FALSE); 457 if (objp->more == FALSE) 458 return(TRUE); 459 } 460} 461 462ypresp_all * 463ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 464{ 465 static ypresp_all result; 466 467 /* 468 * Set this here so that the client will be forced to make 469 * at least one attempt to read from us even if all we're 470 * doing is returning an error. 471 */ 472 result.more = TRUE; 473 474 if (yp_access(argp->map, (struct svc_req *)rqstp)) { 475 result.ypresp_all_u.val.stat = YP_YPERR; 476 return (&result); 477 } 478 479 if (argp->domain == NULL || argp->map == NULL) { 480 result.ypresp_all_u.val.stat = YP_BADARGS; 481 return (&result); 482 } 483 484 if (yp_validdomain(argp->domain)) { 485 result.ypresp_all_u.val.stat = YP_NODOM; 486 return(&result); 487 } 488 489 /* 490 * The ypproc_all procedure can take a while to complete. 491 * Best to handle it in a subprocess so the parent doesn't 492 * block. We fork() here so we don't end up sharing a 493 * DB file handle with the parent. 494 */ 495 496 if (!debug && children < MAX_CHILDREN && fork()) { 497 children++; 498 forked = 0; 499 return (NULL); 500 } else { 501 forked++; 502 } 503 504 if ((spec_dbp = yp_open_db(argp->domain, argp->map)) == NULL) { 505 result.ypresp_all_u.val.stat = yp_errno; 506 return(&result); 507 } 508 509 /* Kick off the actual data transfer. */ 510 svc_sendreply(rqstp->rq_xprt, xdr_my_ypresp_all, (char *)&result); 511 512 /* Close database when done. */ 513 (void)(spec_dbp->close)(spec_dbp); 514 515 /* 516 * Returning NULL prevents the dispatcher from calling 517 * svc_sendreply() since we already did it. 518 */ 519 return (NULL); 520} 521 522ypresp_master * 523ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 524{ 525 static ypresp_master result; 526 DBT key,data; 527 528 if (yp_access(NULL, (struct svc_req *)rqstp)) { 529 result.stat = YP_YPERR; 530 return(&result); 531 } 532 533 if (argp->domain == NULL) { 534 result.stat = YP_BADARGS; 535 return (&result); 536 } 537 538 if (yp_validdomain(argp->domain)) { 539 result.stat = YP_NODOM; 540 return (&result); 541 } 542 543 key.data = "YP_MASTER_NAME"; 544 key.size = sizeof("YP_MASTER_NAME") - 1; 545 546 result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1); 547 548 if (result.stat == YP_TRUE) { 549 result.peer = (char *)data.data; 550 result.peer[data.size] = '\0'; 551 } else 552 result.peer = ""; 553 554 return (&result); 555} 556 557ypresp_order * 558ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 559{ 560 static ypresp_order result; 561 DBT key,data; 562 563 if (yp_access(NULL, (struct svc_req *)rqstp)) { 564 result.stat = YP_YPERR; 565 return(&result); 566 } 567 568 if (argp->domain == NULL) { 569 result.stat = YP_BADARGS; 570 return (&result); 571 } 572 573 if (yp_validdomain(argp->domain)) { 574 result.stat = YP_NODOM; 575 return (&result); 576 } 577 578 /* 579 * We could just check the timestamp on the map file, 580 * but that's a hack: we'll only know the last time the file 581 * was touched, not the last time the database contents were 582 * updated. 583 */ 584 key.data = "YP_LAST_MODIFIED"; 585 key.size = sizeof("YP_LAST_MODIFIED") - 1; 586 587 result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1); 588 589 if (result.stat == YP_TRUE) 590 result.ordernum = atoi((char *)data.data); 591 else 592 result.ordernum = 0; 593 594 return (&result); 595} 596 597static void yp_maplist_free(yp_maplist) 598 struct ypmaplist *yp_maplist; 599{ 600 register struct ypmaplist *next; 601 602 while(yp_maplist) { 603 next = yp_maplist->next; 604 free(yp_maplist->map); 605 free(yp_maplist); 606 yp_maplist = next; 607 } 608 return; 609} 610 611static struct ypmaplist *yp_maplist_create(domain) 612 const char *domain; 613{ 614 char yp_mapdir[MAXPATHLEN + 2]; 615 char yp_mapname[MAXPATHLEN + 2]; 616 struct ypmaplist *cur = NULL; 617 struct ypmaplist *yp_maplist = NULL; 618 DIR *dird; 619 struct dirent *dirp; 620 struct stat statbuf; 621 622 snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain); 623 624 if ((dird = opendir(yp_mapdir)) == NULL) { 625 yp_error("opendir(%s) failed: %s", yp_mapdir, strerror(errno)); 626 return(NULL); 627 } 628 629 while ((dirp = readdir(dird)) != NULL) { 630 if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) { 631 snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s",yp_mapdir,dirp->d_name); 632 if (stat(yp_mapname, &statbuf) < 0 || !S_ISREG(statbuf.st_mode)) 633 continue; 634 if ((cur = (struct ypmaplist *)malloc(sizeof(struct ypmaplist))) < 0) { 635 yp_error("malloc() failed: %s", strerror(errno)); 636 closedir(dird); 637 yp_maplist_free(yp_maplist); 638 return(NULL); 639 } 640 if ((cur->map = (char *)strdup(dirp->d_name)) == NULL) { 641 yp_error("strdup() failed: %s", strerror(errno)); 642 closedir(dird); 643 yp_maplist_free(yp_maplist); 644 return(NULL); 645 } 646 cur->next = yp_maplist; 647 yp_maplist = cur; 648 if (debug) 649 yp_error("map: %s", yp_maplist->map); 650 } 651 652 } 653 closedir(dird); 654 return(yp_maplist); 655} 656 657ypresp_maplist * 658ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp) 659{ 660 static ypresp_maplist result; 661 662 if (yp_access(NULL, (struct svc_req *)rqstp)) { 663 result.stat = YP_YPERR; 664 return(&result); 665 } 666 667 if (argp == NULL) { 668 result.stat = YP_BADARGS; 669 return (&result); 670 } 671 672 if (yp_validdomain(*argp)) { 673 result.stat = YP_NODOM; 674 return (&result); 675 } 676 677 /* 678 * We have to construct a linked list for the ypproc_maplist 679 * procedure using dynamically allocated memory. Since the XDR 680 * layer won't free this list for us, we have to deal with it 681 * ourselves. We call yp_maplist_free() first to free any 682 * previously allocated data we may have accumulated to insure 683 * that we have only one linked list in memory at any given 684 * time. 685 */ 686 687 yp_maplist_free(result.maps); 688 689 if ((result.maps = yp_maplist_create(*argp)) == NULL) { 690 yp_error("yp_maplist_create failed"); 691 result.stat = YP_YPERR; 692 return(&result); 693 } else 694 result.stat = YP_TRUE; 695 696 return (&result); 697} 698