yp_server.c revision 12997
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.1.1.1 1995/12/16 20:54:17 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 284 timeout.tv_sec = 20; 285 timeout.tv_usec = 0; 286 addr->sin_port = htons(port); 287 288 if ((clnt = clntudp_create(addr, prognum, 1, timeout, &sock)) == NULL) 289 yp_error("%s", clnt_spcreateerror("failed to establish \ 290callback handle")); 291 292 ypxfr_resp.status = rval; 293 ypxfr_resp.transid = transid; 294 295 if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) 296 yp_error("%s", clnt_sperror(clnt, "ypxfr callback failed")); 297 298 clnt_destroy(clnt); 299 return; 300} 301 302ypresp_xfr * 303ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp) 304{ 305 static ypresp_xfr result; 306 struct sockaddr_in *rqhost; 307 308 if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) { 309 result.xfrstat = YPXFR_REFUSED; 310 return(&result); 311 } 312 313 if (argp->map_parms.domain == NULL) { 314 result.xfrstat = YPXFR_BADARGS; 315 return (&result); 316 } 317 318 if (yp_validdomain(argp->map_parms.domain)) { 319 result.xfrstat = YPXFR_NODOM; 320 return(&result); 321 } 322 323 rqhost = svc_getcaller(rqstp->rq_xprt); 324 325 switch(fork()) { 326 case 0: 327 { 328 char g[11], t[11], p[11]; 329 char ypxfr_command[MAXPATHLEN + 2]; 330 331 sprintf (ypxfr_command, "%sypxfr", _PATH_LIBEXEC); 332 sprintf (t, "%u", argp->transid); 333 sprintf (g, "%u", argp->prog); 334 sprintf (p, "%u", argp->port); 335 if (debug) 336 close(0); close(1); close(2); 337 if (strcmp(yp_dir, _PATH_YP)) { 338 execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain, 339 "-h", argp->map_parms.peer, "-f", "-p", yp_dir, "-C", t, 340 g, inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map, 341 NULL); 342 } else { 343 execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain, 344 "-h", argp->map_parms.peer, "-f", "-C", t, g, 345 inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map, 346 NULL); 347 } 348 forked++; 349 yp_error("ypxfr execl(): %s", strerror(errno)); 350 ypxfr_callback(YPXFR_XFRERR,rqhost,argp->transid, 351 argp->prog,argp->port); 352 result.xfrstat = YPXFR_XFRERR; 353 return(&result); 354 break; 355 } 356 case -1: 357 yp_error("ypxfr fork(): %s", strerror(errno)); 358 ypxfr_callback(YPXFR_XFRERR,rqhost,argp->transid, 359 argp->prog,argp->port); 360 result.xfrstat = YPXFR_XFRERR; 361 return(&result); 362 break; 363 default: 364 children++; 365 forked = 0; 366 break; 367 } 368 /* Don't return anything -- it's up to ypxfr to do that. */ 369 return (NULL); 370} 371 372void * 373ypproc_clear_2_svc(void *argp, struct svc_req *rqstp) 374{ 375 static char * result; 376 static char rval = 0; 377 378 /* 379 * We don't have to do anything for ypproc_clear. Unlike 380 * the SunOS ypserv, we don't hold out database descriptors 381 * open forever. 382 */ 383 if (yp_access(NULL, (struct svc_req *)rqstp)) 384 return (NULL); 385 386 result = &rval; 387 return((void *) &result); 388} 389 390/* 391 * For ypproc_all, we have to send a stream of ypresp_all structures 392 * via TCP, but the XDR filter generated from the yp.x protocol 393 * definition file only serializes one such structure. This means that 394 * to send the whole stream, you need a wrapper which feeds all the 395 * records into the underlying XDR routine until it hits an 'EOF.' 396 * But to use the wrapper, you have to violate the boundaries between 397 * RPC layers by calling svc_sendreply() directly from the ypproc_all 398 * service routine instead of letting the RPC dispatcher do it. 399 * 400 * Bleah. 401 */ 402 403/* 404 * Custom XDR routine for serialzing results of ypproc_all: keep 405 * reading from the database and spew until we run out of records 406 * or encounter an error. 407 */ 408static bool_t 409xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp) 410{ 411 DBT key, data; 412 413 while (1) { 414 /* Get a record. */ 415 key.size = objp->ypresp_all_u.val.key.keydat_len; 416 key.data = objp->ypresp_all_u.val.key.keydat_val; 417 418 if ((objp->ypresp_all_u.val.stat = 419 yp_next_record(spec_dbp,&key,&data,1)) == YP_TRUE) { 420 objp->ypresp_all_u.val.val.valdat_len = data.size; 421 objp->ypresp_all_u.val.val.valdat_val = data.data; 422 objp->ypresp_all_u.val.key.keydat_len = key.size; 423 objp->ypresp_all_u.val.key.keydat_val = key.data; 424 objp->more = TRUE; 425 } else { 426 objp->more = FALSE; 427 } 428 429 /* Serialize. */ 430 if (!xdr_ypresp_all(xdrs, objp)) 431 return(FALSE); 432 if (objp->more == FALSE) 433 return(TRUE); 434 } 435} 436 437ypresp_all * 438ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 439{ 440 static ypresp_all result; 441 442 /* 443 * Set this here so that the client will be forced to make 444 * at least one attempt to read from us even if all we're 445 * doing is returning an error. 446 */ 447 result.more = TRUE; 448 449 if (yp_access(argp->map, (struct svc_req *)rqstp)) { 450 result.ypresp_all_u.val.stat = YP_YPERR; 451 return (&result); 452 } 453 454 if (argp->domain == NULL || argp->map == NULL) { 455 result.ypresp_all_u.val.stat = YP_BADARGS; 456 return (&result); 457 } 458 459 if (yp_validdomain(argp->domain)) { 460 result.ypresp_all_u.val.stat = YP_NODOM; 461 return(&result); 462 } 463 464 /* 465 * The ypproc_all procedure can take a while to complete. 466 * Best to handle it in a subprocess so the parent doesn't 467 * block. We fork() here so we don't end up sharing a 468 * DB file handle with the parent. 469 */ 470 471 if (!debug && children < MAX_CHILDREN && fork()) { 472 children++; 473 forked = 0; 474 return (NULL); 475 } else { 476 forked++; 477 } 478 479 if ((spec_dbp = yp_open_db(argp->domain, argp->map)) == NULL) { 480 result.ypresp_all_u.val.stat = yp_errno; 481 return(&result); 482 } 483 484 /* Kick off the actual data transfer. */ 485 svc_sendreply(rqstp->rq_xprt, xdr_my_ypresp_all, (char *)&result); 486 487 /* Close database when done. */ 488 (void)(spec_dbp->close)(spec_dbp); 489 490 /* 491 * Returning NULL prevents the dispatcher from calling 492 * svc_sendreply() since we already did it. 493 */ 494 return (NULL); 495} 496 497ypresp_master * 498ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 499{ 500 static ypresp_master result; 501 DBT key,data; 502 503 if (yp_access(NULL, (struct svc_req *)rqstp)) { 504 result.stat = YP_YPERR; 505 return(&result); 506 } 507 508 if (argp->domain == NULL) { 509 result.stat = YP_BADARGS; 510 return (&result); 511 } 512 513 if (yp_validdomain(argp->domain)) { 514 result.stat = YP_NODOM; 515 return (&result); 516 } 517 518 key.data = "YP_MASTER_NAME"; 519 key.size = sizeof("YP_MASTER_NAME") - 1; 520 521 result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1); 522 523 if (result.stat == YP_TRUE) { 524 result.peer = (char *)data.data; 525 result.peer[data.size] = '\0'; 526 } else 527 result.peer = ""; 528 529 return (&result); 530} 531 532ypresp_order * 533ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 534{ 535 static ypresp_order result; 536 DBT key,data; 537 538 if (yp_access(NULL, (struct svc_req *)rqstp)) { 539 result.stat = YP_YPERR; 540 return(&result); 541 } 542 543 if (argp->domain == NULL) { 544 result.stat = YP_BADARGS; 545 return (&result); 546 } 547 548 if (yp_validdomain(argp->domain)) { 549 result.stat = YP_NODOM; 550 return (&result); 551 } 552 553 /* 554 * We could just check the timestamp on the map file, 555 * but that's a hack: we'll only know the last time the file 556 * was touched, not the last time the database contents were 557 * updated. 558 */ 559 key.data = "YP_LAST_MODIFIED"; 560 key.size = sizeof("YP_LAST_MODIFIED") - 1; 561 562 result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1); 563 564 if (result.stat == YP_TRUE) 565 result.ordernum = atoi((char *)data.data); 566 else 567 result.ordernum = 0; 568 569 return (&result); 570} 571 572static void yp_maplist_free(yp_maplist) 573 struct ypmaplist *yp_maplist; 574{ 575 register struct ypmaplist *next; 576 577 while(yp_maplist) { 578 next = yp_maplist->next; 579 free(yp_maplist->map); 580 free(yp_maplist); 581 yp_maplist = next; 582 } 583 return; 584} 585 586static struct ypmaplist *yp_maplist_create(domain) 587 const char *domain; 588{ 589 char yp_mapdir[MAXPATHLEN + 2]; 590 char yp_mapname[MAXPATHLEN + 2]; 591 struct ypmaplist *cur = NULL; 592 struct ypmaplist *yp_maplist = NULL; 593 DIR *dird; 594 struct dirent *dirp; 595 struct stat statbuf; 596 597 snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain); 598 599 if ((dird = opendir(yp_mapdir)) == NULL) { 600 yp_error("opendir(%s) failed: %s", strerror(errno)); 601 return(NULL); 602 } 603 604 while ((dirp = readdir(dird)) != NULL) { 605 if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) { 606 snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s",yp_mapdir,dirp->d_name); 607 if (stat(yp_mapname, &statbuf) < 0 || !S_ISREG(statbuf.st_mode)) 608 continue; 609 if ((cur = (struct ypmaplist *)malloc(sizeof(struct ypmaplist))) < 0) { 610 yp_error("malloc() failed: %s", strerror(errno)); 611 closedir(dird); 612 yp_maplist_free(yp_maplist); 613 return(NULL); 614 } 615 if ((cur->map = (char *)strdup(dirp->d_name)) == NULL) { 616 yp_error("strdup() failed: %s", strerror(errno)); 617 closedir(dird); 618 yp_maplist_free(yp_maplist); 619 return(NULL); 620 } 621 cur->next = yp_maplist; 622 yp_maplist = cur; 623 if (debug) 624 yp_error("map: %s", yp_maplist->map); 625 } 626 627 } 628 closedir(dird); 629 return(yp_maplist); 630} 631 632ypresp_maplist * 633ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp) 634{ 635 static ypresp_maplist result; 636 637 if (yp_access(NULL, (struct svc_req *)rqstp)) { 638 result.stat = YP_YPERR; 639 return(&result); 640 } 641 642 if (argp == NULL) { 643 result.stat = YP_BADARGS; 644 return (&result); 645 } 646 647 if (yp_validdomain(*argp)) { 648 result.stat = YP_NODOM; 649 return (&result); 650 } 651 652 /* 653 * We have to construct a linked list for the ypproc_maplist 654 * procedure using dynamically allocated memory. Since the XDR 655 * layer won't free this list for us, we have to deal with it 656 * ourselves. We call yp_maplist_free() first to free any 657 * previously allocated data we may have accumulated to insure 658 * that we have only one linked list in memory at any given 659 * time. 660 */ 661 662 yp_maplist_free(result.maps); 663 664 if ((result.maps = yp_maplist_create(*argp)) == NULL) { 665 yp_error("yp_maplist_create failed"); 666 result.stat = YP_YPERR; 667 return(&result); 668 } else 669 result.stat = YP_TRUE; 670 671 return (&result); 672} 673