xref: /netbsd/usr.sbin/ypserv/ypxfr/ypxfr.c (revision 41e309b9)
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 
59 DBM	*db;
60 
61 static	int yperr2yppush(int);
62 static	int ypxfr_foreach(int, char *, int, char *, int, char *);
63 
64 int	get_local_ordernum(char *, char *, u_int *);
65 int	get_remote_ordernum(CLIENT *, char *, char *, u_int, u_int *);
66 void	get_map(CLIENT *, char *, char *, struct ypall_callback *);
67 DBM	*create_db(char *, char *, char *, size_t);
68 int	install_db(char *, char *, char *);
69 int	unlink_db(char *, char *, char *);
70 int	add_order(DBM *, u_int);
71 int	add_master(CLIENT *, char *, char *, DBM *);
72 int	add_interdomain(CLIENT *, char *, char *, DBM *);
73 int	add_secure(CLIENT *, char *, char *, DBM *);
74 int	send_clear(CLIENT *);
75 int	send_reply(CLIENT *, int, int);
76 
77 int
main(int argc,char ** argv)78 main(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  */
273 static int
yperr2yppush(int yperr)274 yperr2yppush(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 
293 static int
ypxfr_foreach(int status,char * keystr,int keylen,char * valstr,int vallen,char * data)294 ypxfr_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 
317 int
get_local_ordernum(char * domain,char * map,u_int * lordernum)318 get_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 
380 int
get_remote_ordernum(CLIENT * client,char * domain,char * map,u_int lordernum,u_int * rordernum)381 get_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 
400 void
get_map(CLIENT * client,char * domain,char * map,struct ypall_callback * incallback)401 get_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 
408 DBM *
create_db(char * domain,char * map,char * db_temp,size_t db_temp_len)409 create_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 
422 int
install_db(char * domain,char * map,char * db_temp)423 install_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 
438 int
unlink_db(char * domain,char * map,char * db_temp)439 unlink_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 
450 int
add_order(DBM * ldb,u_int ordernum)451 add_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 
475 int
add_master(CLIENT * client,char * domain,char * map,DBM * ldb)476 add_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 
507 int
add_interdomain(CLIENT * client,char * domain,char * map,DBM * ldb)508 add_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 
544 int
add_secure(CLIENT * client,char * domain,char * map,DBM * ldb)545 add_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 
581 int
send_clear(CLIENT * client)582 send_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 
603 int
send_reply(CLIENT * client,int status,int tid)604 send_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