yp_server.c revision 12891
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 46#ifndef lint 47static char rcsid[] = "$Id: yp_server.c,v 1.18 1995/12/16 04:01:55 wpaul Exp $"; 48#endif /* not lint */ 49 50int forked = 0; 51int children = 0; 52DB *spec_dbp = NULL; /* Special global DB handle for ypproc_all. */ 53 54void * 55ypproc_null_2_svc(void *argp, struct svc_req *rqstp) 56{ 57 static char * result; 58 static char rval = 0; 59 60 if (yp_access(NULL, (struct svc_req *)rqstp)) 61 return(NULL); 62 63 result = &rval; 64 65 return((void *) &result); 66} 67 68bool_t * 69ypproc_domain_2_svc(domainname *argp, struct svc_req *rqstp) 70{ 71 static bool_t result; 72 73 if (yp_access(NULL, (struct svc_req *)rqstp)) { 74 result = FALSE; 75 return (&result); 76 } 77 78 if (argp == NULL || yp_validdomain(*argp)) 79 result = FALSE; 80 else 81 result = TRUE; 82 83 return (&result); 84} 85 86bool_t * 87ypproc_domain_nonack_2_svc(domainname *argp, struct svc_req *rqstp) 88{ 89 static bool_t result; 90 91 if (yp_access(NULL, (struct svc_req *)rqstp)) 92 return (NULL); 93 94 if (argp == NULL || yp_validdomain(*argp)) 95 return (NULL); 96 else 97 result = TRUE; 98 99 return (&result); 100} 101 102ypresp_val * 103ypproc_match_2_svc(ypreq_key *argp, struct svc_req *rqstp) 104{ 105 static ypresp_val result; 106 DBT key, data; 107 108 if (yp_access(argp->map, (struct svc_req *)rqstp)) { 109 result.stat = YP_YPERR; 110 return (&result); 111 } 112 113 if (argp->domain == NULL || argp->map == NULL) { 114 result.stat = YP_BADARGS; 115 return (&result); 116 } 117 118 if (yp_validdomain(argp->domain)) { 119 result.stat = YP_NODOM; 120 return(&result); 121 } 122 123 key.size = argp->key.keydat_len; 124 key.data = argp->key.keydat_val; 125 126 result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 0); 127 128 if (result.stat == YP_TRUE) { 129 result.val.valdat_len = data.size; 130 result.val.valdat_val = data.data; 131 } 132 133 /* 134 * Do DNS lookups for hosts maps if database lookup failed. 135 */ 136 137 if (do_dns && result.stat != YP_TRUE && strstr(argp->map, "hosts")) { 138 char *rval; 139 140 /* DNS lookups can take time -- do them in a subprocess */ 141 142 if (!debug && children < MAX_CHILDREN && fork()) { 143 children++; 144 forked = 0; 145 /* 146 * Returning NULL here prevents svc_sendreply() 147 * from being called by the parent. This is vital 148 * since having both the parent and the child process 149 * call it would confuse the client. 150 */ 151 return (NULL); 152 } else { 153 forked++; 154 } 155 156 if (debug) 157 yp_error("Doing DNS lookup of %.*s", 158 argp->key.keydat_len, 159 argp->key.keydat_val); 160 161 /* NUL terminate! NUL terminate!! NUL TERMINATE!!! */ 162 argp->key.keydat_val[argp->key.keydat_len] = '\0'; 163 164 if (!strcmp(argp->map, "hosts.byname")) 165 rval = yp_dnsname((char *)argp->key.keydat_val); 166 else if (!strcmp(argp->map, "hosts.byaddr")) 167 rval = yp_dnsaddr((const char *)argp->key.keydat_val); 168 169 170 if (rval) { 171 if (debug) 172 yp_error("DNS lookup successful. Result: %s", rval); 173 result.val.valdat_len = strlen(rval); 174 result.val.valdat_val = rval; 175 result.stat = YP_TRUE; 176 } else { 177 if (debug) 178 yp_error("DNS lookup failed."); 179 result.stat = YP_NOKEY; 180 } 181 } 182 183 return (&result); 184} 185 186ypresp_key_val * 187ypproc_first_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 188{ 189 static ypresp_key_val result; 190 DBT key, data; 191 DB *dbp; 192 193 if (yp_access(argp->map, (struct svc_req *)rqstp)) { 194 result.stat = YP_YPERR; 195 return (&result); 196 } 197 198 if (argp->domain == NULL) { 199 result.stat = YP_BADARGS; 200 return (&result); 201 } 202 203 if (yp_validdomain(argp->domain)) { 204 result.stat = YP_NODOM; 205 return(&result); 206 } 207 208 if ((dbp = yp_open_db(argp->domain, argp->map)) == NULL) { 209 result.stat = yp_errno; 210 return(&result); 211 } 212 213 key.data = NULL; 214 key.size = 0; 215 result.stat = yp_first_record(dbp, &key, &data); 216 (void)(dbp->close)(dbp); 217 218 if (result.stat == YP_TRUE) { 219 result.key.keydat_len = key.size; 220 result.key.keydat_val = key.data; 221 result.val.valdat_len = data.size; 222 result.val.valdat_val = data.data; 223 } 224 225 return (&result); 226} 227 228ypresp_key_val * 229ypproc_next_2_svc(ypreq_key *argp, struct svc_req *rqstp) 230{ 231 static ypresp_key_val result; 232 DBT key, data; 233 DB *dbp; 234 235 if (yp_access(argp->map, (struct svc_req *)rqstp)) { 236 result.stat = YP_YPERR; 237 return (&result); 238 } 239 240 if (argp->domain == NULL || argp->map == NULL) { 241 result.stat = YP_BADARGS; 242 return (&result); 243 } 244 245 if (yp_validdomain(argp->domain)) { 246 result.stat = YP_NODOM; 247 return(&result); 248 } 249 250 if ((dbp = yp_open_db(argp->domain, argp->map)) == NULL) { 251 result.stat = yp_errno; 252 return(&result); 253 } 254 255 key.size = argp->key.keydat_len; 256 key.data = argp->key.keydat_val; 257 258 result.stat = yp_next_record(dbp, &key, &data, 0); 259 (void)(dbp->close)(dbp); 260 261 if (result.stat == YP_TRUE) { 262 result.key.keydat_len = key.size; 263 result.key.keydat_val = key.data; 264 result.val.valdat_len = data.size; 265 result.val.valdat_val = data.data; 266 } 267 268 return (&result); 269} 270 271ypresp_xfr * 272ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp) 273{ 274 static ypresp_xfr result; 275 276 if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) { 277 result.xfrstat = YPXFR_REFUSED; 278 return(&result); 279 } 280 281 if (argp->map_parms.domain == NULL) { 282 result.xfrstat = YPXFR_BADARGS; 283 return (&result); 284 } 285 286 if (yp_validdomain(argp->map_parms.domain)) { 287 result.xfrstat = YPXFR_NODOM; 288 return(&result); 289 } 290 291 switch(fork()) { 292 case 0: 293 { 294 char g[11], t[11], p[11]; 295 struct sockaddr_in *rqhost; 296 char ypxfr_command[MAXPATHLEN + 2]; 297 298 rqhost = svc_getcaller(rqstp->rq_xprt); 299 sprintf (ypxfr_command, "%sypxfr", _PATH_LIBEXEC); 300 sprintf (t, "%u", argp->transid); 301 sprintf (g, "%u", argp->prog); 302 sprintf (p, "%u", argp->port); 303 children++; 304 forked = 0; 305 execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain, 306 "-h", argp->map_parms.peer, "-f", "-C", t, g, 307 inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map, 308 NULL); 309 yp_error("ypxfr execl(): %s", strerror(errno)); 310 return(NULL); 311 } 312 case -1: 313 yp_error("ypxfr fork(): %s", strerror(errno)); 314 result.xfrstat = YPXFR_XFRERR; 315 break; 316 default: 317 result.xfrstat = YPXFR_SUCC; 318 forked++; 319 break; 320 } 321 322 result.transid = argp->transid; 323 return (&result); 324} 325 326void * 327ypproc_clear_2_svc(void *argp, struct svc_req *rqstp) 328{ 329 static char * result; 330 static char rval = 0; 331 332 /* 333 * We don't have to do anything for ypproc_clear. Unlike 334 * the SunOS ypserv, we don't hold out database descriptors 335 * open forever. 336 */ 337 if (yp_access(NULL, (struct svc_req *)rqstp)) 338 return (NULL); 339 340 result = &rval; 341 return((void *) &result); 342} 343 344/* 345 * For ypproc_all, we have to send a stream of ypresp_all structures 346 * via TCP, but the XDR filter generated from the yp.x protocol 347 * definition file only serializes one such structure. This means that 348 * to send the whole stream, you need a wrapper which feeds all the 349 * records into the underlying XDR routine until it hits an 'EOF.' 350 * But to use the wrapper, you have to violate the boundaries between 351 * RPC layers by calling svc_sendreply() directly from the ypproc_all 352 * service routine instead of letting the RPC dispatcher do it. 353 * 354 * Bleah. 355 */ 356 357/* 358 * Custom XDR routine for serialzing results of ypproc_all: keep 359 * reading from the database and spew until we run out of records 360 * or encounter an error. 361 */ 362static bool_t 363xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp) 364{ 365 DBT key, data; 366 367 while (1) { 368 /* Get a record. */ 369 key.size = objp->ypresp_all_u.val.key.keydat_len; 370 key.data = objp->ypresp_all_u.val.key.keydat_val; 371 372 if ((objp->ypresp_all_u.val.stat = 373 yp_next_record(spec_dbp,&key,&data,1)) == YP_TRUE) { 374 objp->ypresp_all_u.val.val.valdat_len = data.size; 375 objp->ypresp_all_u.val.val.valdat_val = data.data; 376 objp->ypresp_all_u.val.key.keydat_len = key.size; 377 objp->ypresp_all_u.val.key.keydat_val = key.data; 378 objp->more = TRUE; 379 } else { 380 objp->more = FALSE; 381 } 382 383 /* Serialize. */ 384 if (!xdr_ypresp_all(xdrs, objp)) 385 return(FALSE); 386 if (objp->more == FALSE) 387 return(TRUE); 388 } 389} 390 391ypresp_all * 392ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 393{ 394 static ypresp_all result; 395 396 /* 397 * Set this here so that the client will be forced to make 398 * at least one attempt to read from us even if all we're 399 * doing is returning an error. 400 */ 401 result.more = TRUE; 402 403 if (yp_access(argp->map, (struct svc_req *)rqstp)) { 404 result.ypresp_all_u.val.stat = YP_YPERR; 405 return (&result); 406 } 407 408 if (argp->domain == NULL || argp->map == NULL) { 409 result.ypresp_all_u.val.stat = YP_BADARGS; 410 return (&result); 411 } 412 413 if (yp_validdomain(argp->domain)) { 414 result.ypresp_all_u.val.stat = YP_NODOM; 415 return(&result); 416 } 417 418 /* 419 * The ypproc_all procedure can take a while to complete. 420 * Best to handle it in a subprocess so the parent doesn't 421 * block. We fork() here so we don't end up sharing a 422 * DB file handle with the parent. 423 */ 424 425 if (!debug && children < MAX_CHILDREN && fork()) { 426 children++; 427 forked = 0; 428 return (NULL); 429 } else { 430 forked++; 431 } 432 433 if ((spec_dbp = yp_open_db(argp->domain, argp->map)) == NULL) { 434 result.ypresp_all_u.val.stat = yp_errno; 435 return(&result); 436 } 437 438 /* Kick off the actual data transfer. */ 439 svc_sendreply(rqstp->rq_xprt, xdr_my_ypresp_all, (char *)&result); 440 441 /* Close database when done. */ 442 (void)(spec_dbp->close)(spec_dbp); 443 444 /* 445 * Returning NULL prevents the dispatcher from calling 446 * svc_sendreply() since we already did it. 447 */ 448 return (NULL); 449} 450 451ypresp_master * 452ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 453{ 454 static ypresp_master result; 455 DBT key,data; 456 457 if (yp_access(NULL, (struct svc_req *)rqstp)) { 458 result.stat = YP_YPERR; 459 return(&result); 460 } 461 462 if (argp->domain == NULL) { 463 result.stat = YP_BADARGS; 464 return (&result); 465 } 466 467 if (yp_validdomain(argp->domain)) { 468 result.stat = YP_NODOM; 469 return (&result); 470 } 471 472 key.data = "YP_MASTER_NAME"; 473 key.size = sizeof("YP_MASTER_NAME") - 1; 474 475 result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1); 476 477 if (result.stat == YP_TRUE) { 478 result.peer = (char *)data.data; 479 result.peer[data.size] = '\0'; 480 } else 481 result.peer = ""; 482 483 return (&result); 484} 485 486ypresp_order * 487ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) 488{ 489 static ypresp_order result; 490 DBT key,data; 491 492 if (yp_access(NULL, (struct svc_req *)rqstp)) { 493 result.stat = YP_YPERR; 494 return(&result); 495 } 496 497 if (argp->domain == NULL) { 498 result.stat = YP_BADARGS; 499 return (&result); 500 } 501 502 if (yp_validdomain(argp->domain)) { 503 result.stat = YP_NODOM; 504 return (&result); 505 } 506 507 /* 508 * We could just check the timestamp on the map file, 509 * but that's a hack: we'll only know the last time the file 510 * was touched, not the last time the database contents were 511 * updated. 512 */ 513 key.data = "YP_LAST_MODIFIED"; 514 key.size = sizeof("YP_LAST_MODIFIED") - 1; 515 516 result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1); 517 518 if (result.stat == YP_TRUE) 519 result.ordernum = atoi((char *)data.data); 520 else 521 result.ordernum = 0; 522 523 return (&result); 524} 525 526static void yp_maplist_free(yp_maplist) 527 struct ypmaplist *yp_maplist; 528{ 529 register struct ypmaplist *next; 530 531 while(yp_maplist) { 532 next = yp_maplist->next; 533 free(yp_maplist->map); 534 free(yp_maplist); 535 yp_maplist = next; 536 } 537 return; 538} 539 540static struct ypmaplist *yp_maplist_create(domain) 541 const char *domain; 542{ 543 char yp_mapdir[MAXPATHLEN + 2]; 544 char yp_mapname[MAXPATHLEN + 2]; 545 struct ypmaplist *cur = NULL; 546 struct ypmaplist *yp_maplist = NULL; 547 DIR *dird; 548 struct dirent *dirp; 549 struct stat statbuf; 550 551 snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain); 552 553 if ((dird = opendir(yp_mapdir)) == NULL) { 554 yp_error("opendir(%s) failed: %s", strerror(errno)); 555 return(NULL); 556 } 557 558 while ((dirp = readdir(dird)) != NULL) { 559 if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) { 560 snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s",yp_mapdir,dirp->d_name); 561 if (stat(yp_mapname, &statbuf) < 0 || !S_ISREG(statbuf.st_mode)) 562 continue; 563 if ((cur = (struct ypmaplist *)malloc(sizeof(struct ypmaplist))) < 0) { 564 yp_error("malloc() failed: %s", strerror(errno)); 565 closedir(dird); 566 yp_maplist_free(yp_maplist); 567 return(NULL); 568 } 569 if ((cur->map = (char *)strdup(dirp->d_name)) == NULL) { 570 yp_error("strdup() failed: %s", strerror(errno)); 571 closedir(dird); 572 yp_maplist_free(yp_maplist); 573 return(NULL); 574 } 575 cur->next = yp_maplist; 576 yp_maplist = cur; 577 if (debug) 578 yp_error("map: %s", yp_maplist->map); 579 } 580 581 } 582 closedir(dird); 583 return(yp_maplist); 584} 585 586ypresp_maplist * 587ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp) 588{ 589 static ypresp_maplist result; 590 591 if (yp_access(NULL, (struct svc_req *)rqstp)) { 592 result.stat = YP_YPERR; 593 return(&result); 594 } 595 596 if (argp == NULL) { 597 result.stat = YP_BADARGS; 598 return (&result); 599 } 600 601 if (yp_validdomain(*argp)) { 602 result.stat = YP_NODOM; 603 return (&result); 604 } 605 606 /* 607 * We have to construct a linked list for the ypproc_maplist 608 * procedure using dynamically allocated memory. Since the XDR 609 * layer won't free this list for us, we have to deal with it 610 * ourselves. We call yp_maplist_free() first to free any 611 * previously allocated data we may have accumulated to insure 612 * that we have only one linked list in memory at any given 613 * time. 614 */ 615 616 yp_maplist_free(result.maps); 617 618 if ((result.maps = yp_maplist_create(*argp)) == NULL) { 619 yp_error("yp_maplist_create failed"); 620 result.stat = YP_YPERR; 621 return(&result); 622 } else 623 result.stat = YP_TRUE; 624 625 return (&result); 626} 627