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