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