1/* $NetBSD: ypxfr.c,v 1.21 2017/05/04 16:26:10 sevan Exp $ */ 2 3/* 4 * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30#ifndef lint 31__RCSID("$NetBSD: ypxfr.c,v 1.21 2017/05/04 16:26:10 sevan Exp $"); 32#endif 33 34#include <sys/param.h> 35#include <sys/types.h> 36#include <sys/stat.h> 37#include <sys/socket.h> 38 39#include <netinet/in.h> 40#include <arpa/inet.h> 41 42#include <err.h> 43#include <netdb.h> 44#include <string.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <syslog.h> 48#include <unistd.h> 49 50#include <rpc/rpc.h> 51#include <rpc/xdr.h> 52#include <rpcsvc/yp_prot.h> 53#include <rpcsvc/ypclnt.h> 54 55#include "yplib_host.h" 56#include "ypdb.h" 57#include "ypdef.h" 58 59DBM *db; 60 61static int yperr2yppush(int); 62static int ypxfr_foreach(int, char *, int, char *, int, char *); 63 64int get_local_ordernum(char *, char *, u_int *); 65int get_remote_ordernum(CLIENT *, char *, char *, u_int, u_int *); 66void get_map(CLIENT *, char *, char *, struct ypall_callback *); 67DBM *create_db(char *, char *, char *, size_t); 68int install_db(char *, char *, char *); 69int unlink_db(char *, char *, char *); 70int add_order(DBM *, u_int); 71int add_master(CLIENT *, char *, char *, DBM *); 72int add_interdomain(CLIENT *, char *, char *, DBM *); 73int add_secure(CLIENT *, char *, char *, DBM *); 74int send_clear(CLIENT *); 75int send_reply(CLIENT *, int, int); 76 77int 78main(int argc, char **argv) 79{ 80 int need_usage = 0, cflag = 0, fflag = 0, Cflag = 0; 81 int ch; 82 char *domain; 83 char *host = NULL; 84 char *srcdomain = NULL; 85 char *tid = NULL; 86 char *prog = NULL; 87 char *ipadd = NULL; 88 char *port = NULL; 89 char *map = NULL; 90 u_int ordernum, new_ordernum; 91 struct ypall_callback callback; 92 CLIENT *client; 93 char temp_map[MAXPATHLEN]; 94 int status, xfr_status; 95 96 status = YPPUSH_SUCC; 97 client = NULL; 98 99 if (yp_get_default_domain(&domain)) 100 errx(1, "can't get YP domain name"); 101 102 while ((ch = getopt(argc, argv, "cd:fh:s:C:")) != -1) { 103 switch (ch) { 104 case 'c': 105 cflag = 1; 106 break; 107 108 case 'd': 109 domain = optarg; 110 break; 111 112 case 'f': 113 fflag = 1; 114 break; 115 116 case 'h': 117 host = optarg; 118 break; 119 120 case 's': 121 srcdomain = optarg; 122 break; 123 124 case 'C': 125 if (optind + 3 >= argc) { 126 need_usage = 1; 127 optind = argc; 128 break; 129 } 130 Cflag = 1; 131 tid = optarg; 132 prog = argv[optind++]; 133 ipadd = argv[optind++]; 134 port = argv[optind++]; 135 break; 136 137 default: 138 need_usage = 1; 139 } 140 } 141 argc -= optind; argv += optind; 142 143 if (argc != 1) 144 need_usage = 1; 145 146 map = argv[0]; 147 148 if (need_usage) { 149 status = YPPUSH_BADARGS; 150 fprintf(stderr, "usage: %s [-cf] [-C tid prog ipadd port] " 151 "[-d domain] [-h host] [-s domain] mapname\n", 152 getprogname()); 153 exit(1); 154 } 155 156#ifdef DEBUG 157 openlog("ypxfr", LOG_PID, LOG_DAEMON); 158 159 syslog(LOG_DEBUG, "ypxfr: Arguments:"); 160 syslog(LOG_DEBUG, "YP clear to local: %s", (cflag) ? "no" : "yes"); 161 syslog(LOG_DEBUG, " Force transfer: %s", (fflag) ? "yes" : "no"); 162 syslog(LOG_DEBUG, " domain: %s", domain); 163 syslog(LOG_DEBUG, " host: %s", host); 164 syslog(LOG_DEBUG, " source domain: %s", srcdomain); 165 syslog(LOG_DEBUG, " transid: %s", tid); 166 syslog(LOG_DEBUG, " prog: %s", prog); 167 syslog(LOG_DEBUG, " port: %s", port); 168 syslog(LOG_DEBUG, " ipadd: %s", ipadd); 169 syslog(LOG_DEBUG, " map: %s", map); 170#endif 171 172 if (fflag != 0) 173 ordernum = 0; 174 else { 175 status = get_local_ordernum(domain, map, &ordernum); 176 if (status < 0) 177 goto punt; 178 } 179 180#ifdef DEBUG 181 syslog(LOG_DEBUG, "Get Master"); 182#endif 183 184 if (host == NULL) { 185 if (srcdomain == NULL) 186 status = yp_master(domain, map, &host); 187 else 188 status = yp_master(srcdomain, map, &host); 189 190 if (status == 0) 191 status = YPPUSH_SUCC; 192 else { 193 status = YPPUSH_MADDR; 194 goto punt; 195 } 196 } 197 198#ifdef DEBUG 199 syslog(LOG_DEBUG, "Connect host: %s", host); 200#endif 201 202 client = yp_bind_host(host, YPPROG, YPVERS, 0, 1); 203 204 status = get_remote_ordernum(client, domain, map, ordernum, 205 &new_ordernum); 206 207 208 if (status == YPPUSH_SUCC) { 209 /* Create temporary db */ 210 db = create_db(domain, map, temp_map, sizeof(temp_map)); 211 if (db == NULL) 212 status = YPPUSH_DBM; 213 214 /* Add ORDER */ 215 if (status > 0) 216 status = add_order(db, new_ordernum); 217 218 /* Add MASTER */ 219 if (status > 0) 220 status = add_master(client, domain, map, db); 221 222 /* Add INTERDOMAIN */ 223 if (status > 0) 224 status = add_interdomain(client, domain, map, db); 225 226 /* Add SECURE */ 227 if (status > 0) 228 status = add_secure(client, domain, map, db); 229 230 if (status > 0) { 231 callback.foreach = ypxfr_foreach; 232 get_map(client, domain, map, &callback); 233 } 234 235 /* Close db */ 236 if (db != NULL) 237 ypdb_close(db); 238 239 /* Rename db */ 240 if (status > 0) 241 status = install_db(domain, map, temp_map); 242 else 243 (void) unlink_db(domain, map, temp_map); 244 } 245 246 punt: 247 xfr_status = status; 248 249 if (client != NULL) 250 clnt_destroy(client); 251 252 /* YP_CLEAR */ 253 if (!cflag) { 254 client = yp_bind_local(YPPROG, YPVERS); 255 status = send_clear(client); 256 clnt_destroy(client); 257 } 258 259 if (Cflag > 0) { 260 /* Send Response */ 261 client = yp_bind_host(ipadd, atoi(prog), 1, atoi(port), 0); 262 status = send_reply(client, xfr_status, atoi(tid)); 263 clnt_destroy(client); 264 } 265 266 exit (0); 267} 268 269/* 270 * yperr2yppush: convert error codes from functions like yp_order_host, 271 * yp_master_host, and yp_match_host into YPPUSH rpc status values. 272 */ 273static int 274yperr2yppush(int yperr) { 275 switch (yperr) { 276 case YPERR_DOMAIN: 277 return(YPPUSH_NODOM); 278 case YPERR_MAP: 279 return(YPPUSH_NOMAP); 280 case YPERR_KEY: 281 return(YPPUSH_YPERR); 282 case YPERR_BADDB: 283 return(YPPUSH_YPERR); 284 } 285 286 /* 287 * generic error status for the rest (BADARGS, RPC, YPERR, RESRC, 288 * NOMORE, PMAP, YPBIND, YPSERV, NODOM, VERS, ACCESS, BUSY) 289 */ 290 return(YPPUSH_XFRERR); /* generic error status */ 291} 292 293static int 294ypxfr_foreach(int status, char *keystr, int keylen, char *valstr, 295 int vallen, char *data) 296{ 297 datum key, val; 298 299 if (status == YP_NOMORE) 300 return (0); 301 302 keystr[keylen] = '\0'; 303 valstr[vallen] = '\0'; 304 305 key.dptr = keystr; 306 key.dsize = strlen(keystr); 307 308 val.dptr = valstr; 309 val.dsize = strlen(valstr); 310 311 /* XXX: suspect... ignoring return value here */ 312 ypdb_store(db, key, val, YPDB_INSERT); 313 314 return (0); 315} 316 317int 318get_local_ordernum(char *domain, char *map, u_int *lordernum) 319{ 320 char map_path[1024]; 321 char order_key[] = YP_LAST_KEY; 322 char order[MAX_LAST_LEN+1]; 323 struct stat finfo; 324 DBM *ldb; 325 datum k, v; 326 unsigned int status; 327 328 status = YPPUSH_SUCC; 329 330 snprintf(map_path, sizeof(map_path), "%s/%s", YP_DB_PATH, domain); 331 332 /* Make sure we serve the domain. */ 333 if ((stat(map_path, &finfo)) != 0 || 334 (S_ISDIR(finfo.st_mode) == 0)) { 335 warnx("domain `%s' not found locally", domain); 336 status = YPPUSH_NODOM; 337 goto out; 338 } 339 340 /* Make sure we serve the map. */ 341 snprintf(map_path, sizeof(map_path), "%s/%s/%s%s", 342 YP_DB_PATH, domain, map, YPDB_SUFFIX); 343 if (stat(map_path, &finfo) != 0) { 344 status = YPPUSH_NOMAP; 345 goto out; 346 } 347 348 /* Open the map file. */ 349 snprintf(map_path, sizeof(map_path), "%s/%s/%s", 350 YP_DB_PATH, domain, map); 351 ldb = ypdb_open(map_path); 352 if (ldb == NULL) { 353 status = YPPUSH_DBM; 354 goto out; 355 } 356 357 k.dptr = (char *)&order_key; 358 k.dsize = YP_LAST_LEN; 359 360 v = ypdb_fetch(ldb, k); 361 362 if (v.dptr == NULL) 363 *lordernum = 0; 364 else { 365 strncpy(order, v.dptr, v.dsize); 366 order[v.dsize] = '\0'; 367 *lordernum = (u_int)atoi((char *)&order); 368 } 369 ypdb_close(ldb); 370 371 out: 372 if ((status == YPPUSH_NOMAP) || (status == YPPUSH_DBM)) { 373 *lordernum = 0; 374 status = YPPUSH_SUCC; 375 } 376 377 return (status); 378} 379 380int 381get_remote_ordernum(CLIENT *client, char *domain, char *map, 382 u_int lordernum, u_int *rordernum) 383{ 384 int status; 385 386 status = yp_order_host(client, domain, map, (int *)rordernum); 387 388 if (status == 0) { 389 if (*rordernum <= lordernum) 390 status = YPPUSH_AGE; 391 else 392 status = YPPUSH_SUCC; 393 } else { 394 status = yperr2yppush(status); 395 } 396 397 return status; 398} 399 400void 401get_map(CLIENT *client, char *domain, char *map, 402 struct ypall_callback *incallback) 403{ 404 405 (void)yp_all_host(client, domain, map, incallback); 406} 407 408DBM * 409create_db(char *domain, char *map, char *db_temp, size_t db_temp_len) 410{ 411 static const char template[] = "ypdbXXXXXX"; 412 DBM *ldb; 413 414 snprintf(db_temp, db_temp_len, "%s/%s/%s", 415 YP_DB_PATH, domain, template); 416 417 ldb = ypdb_mktemp(db_temp); 418 419 return ldb; 420} 421 422int 423install_db(char *domain, char *map, char *db_temp) 424{ 425 char db_name[MAXPATHLEN]; 426 427 snprintf(db_name, sizeof(db_name), "%s/%s/%s%s", 428 YP_DB_PATH, domain, map, YPDB_SUFFIX); 429 430 if (rename(db_temp, db_name)) { 431 warn("can't rename `%s' -> `%s'", db_temp, db_name); 432 return YPPUSH_YPERR; 433 } 434 435 return YPPUSH_SUCC; 436} 437 438int 439unlink_db(char *domain, char *map, char *db_temp) 440{ 441 442 if (unlink(db_temp)) { 443 warn("can't unlink `%s'", db_temp); 444 return YPPUSH_YPERR; 445 } 446 447 return YPPUSH_SUCC; 448} 449 450int 451add_order(DBM *ldb, u_int ordernum) 452{ 453 char datestr[11]; 454 datum key, val; 455 char keystr[] = YP_LAST_KEY; 456 int status; 457 458 snprintf(datestr, sizeof(datestr), "%010d", ordernum); 459 460 key.dptr = keystr; 461 key.dsize = strlen(keystr); 462 463 val.dptr = datestr; 464 val.dsize = strlen(datestr); 465 466 status = ypdb_store(ldb, key, val, YPDB_INSERT); 467 if(status >= 0) 468 status = YPPUSH_SUCC; 469 else 470 status = YPPUSH_DBM; 471 472 return (status); 473} 474 475int 476add_master(CLIENT *client, char *domain, char *map, DBM *ldb) 477{ 478 char keystr[] = YP_MASTER_KEY; 479 char *master; 480 int status; 481 datum key, val; 482 483 master = NULL; 484 485 /* Get MASTER */ 486 status = yp_master_host(client, domain, map, &master); 487 488 if (master != NULL) { 489 key.dptr = keystr; 490 key.dsize = strlen(keystr); 491 492 val.dptr = master; 493 val.dsize = strlen(master); 494 495 status = ypdb_store(ldb, key, val, YPDB_INSERT); 496 if (status >= 0) 497 status = YPPUSH_SUCC; 498 else 499 status = YPPUSH_DBM; 500 } else { 501 status = yperr2yppush(status); 502 } 503 504 return status; 505} 506 507int 508add_interdomain(CLIENT *client, char *domain, char *map, DBM *ldb) 509{ 510 char keystr[] = YP_INTERDOMAIN_KEY; 511 char *value; 512 int vallen; 513 int status; 514 datum k, v; 515 516 /* Get INTERDOMAIN */ 517 k.dptr = keystr; 518 k.dsize = strlen(keystr); 519 520 status = yp_match_host(client, domain, map, 521 k.dptr, k.dsize, &value, &vallen); 522 523 if (status == YPERR_KEY) { 524 /* this is an optional key/val, so it may not be present */ 525 status = YPPUSH_SUCC; 526 } else if (status == 0 && value) { 527 v.dptr = value; 528 v.dsize = vallen; 529 530 if (v.dptr != NULL) { 531 status = ypdb_store(ldb, k, v, YPDB_INSERT); 532 if (status >= 0) 533 status = YPPUSH_SUCC; 534 else 535 status = YPPUSH_DBM; 536 } 537 } else { 538 status = yperr2yppush(status); 539 } 540 541 return status; 542} 543 544int 545add_secure(CLIENT *client, char *domain, char *map, DBM *ldb) 546{ 547 char keystr[] = YP_SECURE_KEY; 548 char *value; 549 int vallen; 550 int status; 551 datum k, v; 552 553 /* Get SECURE */ 554 k.dptr = keystr; 555 k.dsize = strlen(keystr); 556 557 status = yp_match_host(client, domain, map, 558 k.dptr, k.dsize, &value, &vallen); 559 560 if (status == YPERR_KEY) { 561 /* this is an optional key/val, so it may not be present */ 562 status = YPPUSH_SUCC; 563 } else if (status == 0 && value != 0) { 564 v.dptr = value; 565 v.dsize = vallen; 566 567 if (v.dptr != NULL) { 568 status = ypdb_store(ldb, k, v, YPDB_INSERT); 569 if (status >= 0) 570 status = YPPUSH_SUCC; 571 else 572 status = YPPUSH_DBM; 573 } 574 } else { 575 status = yperr2yppush(status); 576 } 577 578 return status; 579} 580 581int 582send_clear(CLIENT *client) 583{ 584 struct timeval tv; 585 int r; 586 int status; 587 588 status = YPPUSH_SUCC; 589 590 tv.tv_sec = 10; 591 tv.tv_usec = 0; 592 593 /* Send CLEAR */ 594 r = clnt_call(client, YPPROC_CLEAR, xdr_void, 0, xdr_void, 0, tv); 595 if (r != RPC_SUCCESS) { 596 clnt_perror(client, "yp_clear: clnt_call"); 597 status = YPPUSH_RPC; 598 } 599 600 return status; 601} 602 603int 604send_reply(CLIENT *client, int status, int tid) 605{ 606 struct timeval tv; 607 struct ypresp_xfr resp; 608 int r; 609 610 tv.tv_sec = 10; 611 tv.tv_usec = 0; 612 613 resp.transid = tid; 614 resp.xfrstat = status; 615 616 /* Send XFRRESP */ 617 r = clnt_call(client, YPPUSHPROC_XFRRESP, xdr_ypresp_xfr, &resp, 618 xdr_void, 0, tv); 619 if (r != RPC_SUCCESS) { 620 clnt_perror(client, "yppushresp_xdr: clnt_call"); 621 status = YPPUSH_RPC; 622 } 623 624 return status; 625} 626