1/*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
7 * Reserved.  This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License').  You may not use this file
10 * except in compliance with the License.  Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24/*	$OpenBSD: ypxfr.c,v 1.22 1997/07/30 12:07:02 maja Exp $ */
25
26/*
27 * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
28 * All rights reserved.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 *    notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 *    notice, this list of conditions and the following disclaimer in the
37 *    documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 *    must display the following acknowledgement:
40 *	This product includes software developed by Mats O Jansson
41 * 4. The name of the author may not be used to endorse or promote products
42 *    derived from this software without specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
45 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
46 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
48 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57#include <sys/cdefs.h>
58#ifndef LINT
59__unused static char rcsid[] = "$OpenBSD: ypxfr.c,v 1.22 1997/07/30 12:07:02 maja Exp $";
60#endif
61
62#include <sys/types.h>
63#include <sys/stat.h>
64#include <sys/socket.h>
65#include <sys/param.h>
66#include <netinet/in.h>
67#include <arpa/inet.h>
68
69#include <stdio.h>
70#include <stdlib.h>
71#include <unistd.h>
72#include <fcntl.h>
73#include <string.h>
74#include <netdb.h>
75
76#include <rpc/rpc.h>
77#include <rpc/xdr.h>
78#include <rpcsvc/yp.h>
79#include <rpcsvc/ypclnt.h>
80
81#include "yplib_host.h"
82#include "yplog.h"
83#include "ypdb.h"
84#include "ypdef.h"
85
86extern char *__progname;		/* from crt0.o */
87DBM	*db;
88
89extern bool_t xdr_ypresp_all_seq();
90extern int getrpcport(char *, int, int, int);
91
92extern int (*ypresp_allfn)();
93extern void *ypresp_data;
94
95static int
96ypxfr_foreach(status,keystr,keylen,valstr,vallen,data)
97int status,keylen,vallen;
98char *keystr,*valstr,*data;
99{
100	datum	key,val;
101
102	if (status == YP_NOMORE)
103		return(0);
104
105	keystr[keylen] = '\0';
106	valstr[vallen] = '\0';
107
108	key.dptr = keystr;
109	key.dsize = strlen(keystr);
110
111	val.dptr = valstr;
112	val.dsize = strlen(valstr);
113
114	ypdb_store(db, key, val, YPDB_INSERT);
115
116	return 0;
117}
118
119int
120get_local_ordernum(domain, map, lordernum)
121char *domain;
122char *map;
123u_int32_t *lordernum;
124{
125	char map_path[MAXPATHLEN];
126	char order_key[] = YP_LAST_KEY;
127	char order[MAX_LAST_LEN+1];
128	struct stat finfo;
129	DBM *db;
130	datum k,v;
131	int status;
132
133	/* This routine returns YPPUSH_SUCC or YPPUSH_NODOM */
134
135	status = YPPUSH_SUCC;
136
137	snprintf(map_path, sizeof map_path, "%s/%s", YP_DB_PATH, domain);
138	if (!((stat(map_path, &finfo) == 0) &&
139	      ((finfo.st_mode & S_IFMT) == S_IFDIR))) {
140		fprintf(stderr, "%s: domain %s not found locally\n",
141		    __progname, domain);
142		status = YPPUSH_NODOM;
143	}
144
145	if(status > 0) {
146		snprintf(map_path, sizeof map_path, "%s/%s/%s%s",
147		    YP_DB_PATH, domain, map, YPDB_SUFFIX);
148		if(!(stat(map_path, &finfo) == 0)) {
149			status = YPPUSH_NOMAP;
150		}
151	}
152
153	if(status > 0) {
154		snprintf(map_path, sizeof map_path, "%s/%s/%s",
155		    YP_DB_PATH, domain, map);
156		db = ypdb_open(map_path, O_RDONLY, 0444);
157		if(db == NULL) {
158			status = YPPUSH_DBM;
159		}
160
161	}
162
163	if(status > 0) {
164		k.dptr = (char *)&order_key;
165		k.dsize = YP_LAST_LEN;
166
167		v = ypdb_fetch(db,k);
168		ypdb_close(db);
169
170		if (v.dptr == NULL) {
171			*lordernum = 0;
172		} else {
173	        	strncpy(order, v.dptr, sizeof order-1);
174			order[sizeof order-1] = '\0';
175			*lordernum = (u_int32_t)atol(order);
176		}
177	}
178
179	if((status == YPPUSH_NOMAP) || (status == YPPUSH_DBM)) {
180		*lordernum = 0;
181		status = YPPUSH_SUCC;
182	}
183
184	return(status);
185
186}
187
188int
189get_remote_ordernum(client, domain, map, lordernum, rordernum)
190CLIENT *client;
191char *domain;
192char *map;
193u_int32_t lordernum;
194u_int32_t *rordernum;
195{
196	int status;
197
198	status = yp_order_host(client, domain, map, rordernum);
199
200	if (status == 0) {
201		if(*rordernum <= lordernum) {
202			status = YPPUSH_AGE;
203		} else {
204			status = YPPUSH_SUCC;
205		}
206	}
207
208	return status;
209}
210
211void
212get_map(client,domain,map,incallback)
213CLIENT *client;
214char *domain;
215char *map;
216struct ypall_callback *incallback;
217{
218	(void)yp_all_host(client, domain, map, incallback);
219
220}
221
222DBM *
223create_db(domain,map,temp_map)
224char *domain;
225char *map;
226char *temp_map;
227{
228	return ypdb_open_suf(temp_map, O_RDWR, 0444);
229}
230
231int
232install_db(domain,map,temp_map)
233char *domain;
234char *map;
235char *temp_map;
236{
237	char	db_name[MAXPATHLEN];
238
239	snprintf(db_name, sizeof db_name, "%s/%s/%s%s",
240	    YP_DB_PATH, domain, map, YPDB_SUFFIX);
241	rename(temp_map, db_name);
242
243	return YPPUSH_SUCC;
244}
245
246int
247add_order(db, ordernum)
248DBM *db;
249u_int32_t ordernum;
250{
251	char	datestr[11];
252	datum	key,val;
253	char	keystr[] = YP_LAST_KEY;
254	int	status;
255
256	sprintf(datestr, "%010u", ordernum);
257
258	key.dptr = keystr;
259	key.dsize = strlen(keystr);
260
261	val.dptr = datestr;
262	val.dsize = strlen(datestr);
263
264	status = ypdb_store(db, key, val, YPDB_INSERT);
265	if(status >= 0) {
266		status = YPPUSH_SUCC;
267	} else {
268		status = YPPUSH_DBM;
269	}
270	return(status);
271}
272
273int
274add_master(client, domain, map, db)
275CLIENT *client;
276char *domain;
277char *map;
278DBM *db;
279{
280	char	keystr[] = YP_MASTER_KEY;
281	char	*master;
282	int	status;
283	datum	key,val;
284
285	master = NULL;
286
287	/* Get MASTER */
288
289	status = yp_master_host(client, domain, map, &master);
290
291	if(master != NULL) {
292	  key.dptr = keystr;
293	  key.dsize = strlen(keystr);
294
295	  val.dptr = master;
296	  val.dsize = strlen(master);
297
298	  status = ypdb_store(db, key, val, YPDB_INSERT);
299	  if(status >= 0) {
300	  	status = YPPUSH_SUCC;
301	  } else {
302	  	status = YPPUSH_DBM;
303	  }
304	}
305
306	return status;
307}
308
309int
310add_interdomain(client, domain, map, db)
311CLIENT *client;
312char *domain;
313char *map;
314DBM *db;
315{
316	char	keystr[] = YP_INTERDOMAIN_KEY;
317	char	*value;
318	int	vallen;
319	int	status;
320	datum	k,v;
321
322	/* Get INTERDOMAIN */
323
324	k.dptr = keystr;
325	k.dsize = strlen(keystr);
326
327	status = yp_match_host(client, domain, map,
328			       k.dptr, k.dsize, &value, &vallen);
329
330	if(status == 0 && value) {
331		v.dptr = value;
332		v.dsize = vallen;
333
334		if(v.dptr != NULL) {
335			status = ypdb_store(db,k,v,YPDB_INSERT);
336			if(status >= 0) {
337				status = YPPUSH_SUCC;
338			} else {
339				status = YPPUSH_DBM;
340			}
341		}
342	}
343
344	return 1;
345}
346
347int
348add_secure(client, domain, map, db)
349CLIENT *client;
350char *domain;
351char *map;
352DBM *db;
353{
354	char	keystr[] = YP_SECURE_KEY;
355	char	*value;
356	int	vallen;
357	int	status;
358	datum	k,v;
359
360	/* Get SECURE */
361
362	k.dptr = keystr;
363	k.dsize = strlen(keystr);
364
365	status = yp_match_host(client, domain, map,
366			       k.dptr, k.dsize, &value, &vallen);
367
368	if(status > 0) {
369		v.dptr = value;
370		v.dsize = vallen;
371
372		if(v.dptr != NULL) {
373			status = ypdb_store(db,k,v,YPDB_INSERT);
374			if(status >= 0) {
375				status = YPPUSH_SUCC;
376			} else {
377				status = YPPUSH_DBM;
378			}
379		}
380	}
381
382	return status;
383
384}
385
386int
387send_clear(client)
388CLIENT *client;
389{
390	struct	timeval tv;
391	int	r;
392	int	status;
393
394	status = YPPUSH_SUCC;
395
396	tv.tv_sec = 10;
397	tv.tv_usec = 0;
398
399	/* Send CLEAR */
400
401	r = clnt_call(client, YPPROC_CLEAR,
402		      (xdrproc_t)xdr_void, 0, (xdrproc_t)xdr_void, 0, tv);
403	if(r != RPC_SUCCESS) {
404		clnt_perror(client, "yp_clear: clnt_call");
405	}
406
407	return status;
408
409}
410
411int
412send_reply(client,status,tid)
413CLIENT *client;
414u_long	status;
415u_long  tid;
416{
417	struct	timeval tv;
418	struct	ypresp_xfr resp;
419	int	r;
420
421	tv.tv_sec = 10;
422	tv.tv_usec = 0;
423
424	resp.transid = tid;
425	resp.xfrstat = status;
426
427	/* Send CLEAR */
428
429	r = clnt_call(client, 1,
430		      (xdrproc_t)xdr_ypresp_xfr, &resp, (xdrproc_t)xdr_void, 0, tv);
431	if(r != RPC_SUCCESS) {
432		clnt_perror(client, "yppushresp_xdr: clnt_call");
433	}
434
435	return status;
436
437}
438
439int
440main (argc,argv)
441int argc;
442char *argv[];
443{
444	int	 usage = 0;
445	int	 cflag = 0;
446	int	 fflag = 0;
447	int	 Cflag = 0;
448	int	 ch;
449	extern	 char *optarg;
450	char	 *domain;
451	char	 *host = NULL;
452	char	 *srcdomain = NULL;
453	char	 *tid = NULL;
454	char	 *prog = NULL;
455	char	 *ipadd = NULL;
456	char	 *port = NULL;
457	char	 *map = NULL;
458	u_int32_t ordernum, new_ordernum;
459	struct	 ypall_callback callback;
460	CLIENT   *client;
461	int	 status,xfr_status;
462	int	 srvport;
463
464	status = YPPUSH_SUCC;
465	client = NULL;
466
467	yp_get_default_domain(&domain);
468
469	while ((ch = getopt(argc, argv, "cd:fh:s:C:")) != -1)
470	  switch (ch) {
471	  case 'c':
472	    cflag++;
473	    break;
474	  case 'd':
475	    if (strchr(optarg, '/'))	/* Ha ha, we are not listening */
476		break;
477	    domain = optarg;
478	    break;
479	  case 'f':
480	    fflag++;
481	    break;
482	  case 'h':
483	    host = optarg;
484	    break;
485	  case 's':
486	    if (strchr(optarg, '/'))	/* Ha ha, we are not listening */
487		break;
488	    srcdomain = optarg;
489	    break;
490	  case 'C':
491	    if (optind + 3 >= argc) {
492		usage++;
493		optind = argc;
494		break;
495	    }
496	    Cflag++;
497	    tid = optarg;
498	    prog = argv[optind++];
499	    ipadd = argv[optind++];
500	    port = argv[optind++];
501	    break;
502	  default:
503	    usage++;
504	    break;
505	  }
506
507	if(optind + 1 != argc) {
508	  usage++;
509	} else {
510	  map = argv[optind];
511	}
512
513	if (usage) {
514		status = YPPUSH_BADARGS;
515		fprintf(stderr, "usage: %s %s %s\n",
516		    "[-cf] [-d domain] [-h host] [-s domain]",
517		    "[-C tid prog ipadd port] mapname\n",
518		    __progname);
519	}
520
521	if (status > 0) {
522		ypopenlog();
523
524		yplog("ypxfr: Arguments:");
525		yplog("YP clear to local: %s", (cflag) ? "no" : "yes");
526		yplog("   Force transfer: %s", (fflag) ? "yes" : "no");
527		yplog("           domain: %s", domain);
528		yplog("             host: %s", host);
529		yplog("    source domain: %s", srcdomain);
530		yplog("          transid: %s", tid);
531		yplog("             prog: %s", prog);
532		yplog("             port: %s", port);
533		yplog("            ipadd: %s", ipadd);
534		yplog("              map: %s", map);
535
536		if(fflag != 0) {
537			ordernum = 0;
538		} else {
539			status = get_local_ordernum(domain, map, &ordernum);
540		}
541	}
542
543	if (status > 0) {
544
545	        yplog("Get Master");
546
547		if (host == NULL) {
548			if (srcdomain == NULL) {
549				status = yp_master(domain,map,&host);
550		        } else {
551				status = yp_master(srcdomain,map,&host);
552			}
553			if(status == 0) {
554				status = YPPUSH_SUCC;
555			} else {
556				status = -status;
557			}
558		}
559	};
560
561	/* XXX this is raceable if portmap has holes! */
562	if (status > 0) {
563
564	        yplog("Check for reserved port on host: %s", host);
565
566		srvport = getrpcport(host,YPPROG,YPVERS,IPPROTO_TCP);
567		if (srvport >= IPPORT_RESERVED)
568			status = YPPUSH_REFUSED;
569
570	}
571
572	if (status > 0) {
573
574	        yplog("Connect host: %s", host);
575
576		client = yp_bind_host(host,YPPROG,YPVERS,0,1);
577
578		status = get_remote_ordernum(client, domain, map,
579					     ordernum, &new_ordernum);
580
581	}
582
583	if (status == YPPUSH_SUCC) {
584		char	tmpmapname[MAXPATHLEN];
585		int	fd;
586
587		/* Create temporary db */
588		snprintf(tmpmapname, sizeof tmpmapname,
589		    "%s/%s/ypdbXXXXXXXXXX", YP_DB_PATH, domain);
590		fd = mkstemp(tmpmapname);
591		if (fd == -1)
592			status = YPPUSH_DBM;
593		else
594			close(fd);
595
596		if (status > 0) {
597			db = create_db(domain,map,tmpmapname);
598			if(db == NULL)
599				status = YPPUSH_DBM;
600		}
601
602	  	/* Add ORDER */
603		if(status > 0) {
604			status = add_order(db, new_ordernum);
605		}
606
607		/* Add MASTER */
608		if(status > 0) {
609			status = add_master(client,domain,map,db);
610		}
611
612	        /* Add INTERDOMAIN */
613		if(status > 0) {
614			status = add_interdomain(client,domain,map,db);
615		}
616
617	        /* Add SECURE */
618		if(status > 0) {
619			status = add_secure(client,domain,map,db);
620		}
621
622		if(status > 0) {
623			callback.foreach=ypxfr_foreach;
624			get_map(client,domain,map,&callback);
625		}
626
627		/* Close db */
628		if(db != NULL) {
629			ypdb_close(db);
630		}
631
632		/* Rename db */
633		if(status > 0) {
634			status = install_db(domain,map,tmpmapname);
635		} else {
636			unlink(tmpmapname);
637			status = YPPUSH_SUCC;
638		}
639
640	}
641
642	xfr_status = status;
643
644	if(client != NULL) {
645		clnt_destroy(client);
646	}
647
648	/* YP_CLEAR */
649
650	if(!cflag) {
651		client = yp_bind_local(YPPROG,YPVERS);
652		status = send_clear(client);
653		clnt_destroy(client);
654	}
655
656	if(Cflag > 0) {
657		/* Send Response */
658		client = yp_bind_host(ipadd,
659				      atoi(prog),
660				      1,
661				      atoi(port),
662				      0);
663		status = send_reply(client,xfr_status,atoi(tid));
664		clnt_destroy(client);
665	}
666
667	return(0);
668
669}
670
671