yp_server.c revision 12891
1/*
2 * Copyright (c) 1995
3 *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34#include "yp_extern.h"
35#include "yp.h"
36#include <stdlib.h>
37#include <dirent.h>
38#include <sys/stat.h>
39#include <sys/param.h>
40#include <errno.h>
41#include <sys/types.h>
42#include <sys/socket.h>
43#include <netinet/in.h>
44#include <arpa/inet.h>
45
46#ifndef lint
47static char rcsid[] = "$Id: yp_server.c,v 1.18 1995/12/16 04:01:55 wpaul Exp $";
48#endif /* not lint */
49
50int forked = 0;
51int children = 0;
52DB *spec_dbp = NULL;	/* Special global DB handle for ypproc_all. */
53
54void *
55ypproc_null_2_svc(void *argp, struct svc_req *rqstp)
56{
57	static char * result;
58	static char rval = 0;
59
60	if (yp_access(NULL, (struct svc_req *)rqstp))
61		return(NULL);
62
63	result = &rval;
64
65	return((void *) &result);
66}
67
68bool_t *
69ypproc_domain_2_svc(domainname *argp, struct svc_req *rqstp)
70{
71	static bool_t  result;
72
73	if (yp_access(NULL, (struct svc_req *)rqstp)) {
74		result = FALSE;
75		return (&result);
76	}
77
78	if (argp == NULL || yp_validdomain(*argp))
79		result = FALSE;
80	else
81		result = TRUE;
82
83	return (&result);
84}
85
86bool_t *
87ypproc_domain_nonack_2_svc(domainname *argp, struct svc_req *rqstp)
88{
89	static bool_t  result;
90
91	if (yp_access(NULL, (struct svc_req *)rqstp))
92		return (NULL);
93
94	if (argp == NULL || yp_validdomain(*argp))
95		return (NULL);
96	else
97		result = TRUE;
98
99	return (&result);
100}
101
102ypresp_val *
103ypproc_match_2_svc(ypreq_key *argp, struct svc_req *rqstp)
104{
105	static ypresp_val  result;
106	DBT key, data;
107
108	if (yp_access(argp->map, (struct svc_req *)rqstp)) {
109		result.stat = YP_YPERR;
110		return (&result);
111	}
112
113	if (argp->domain == NULL || argp->map == NULL) {
114		result.stat = YP_BADARGS;
115		return (&result);
116	}
117
118	if (yp_validdomain(argp->domain)) {
119		result.stat = YP_NODOM;
120		return(&result);
121	}
122
123	key.size = argp->key.keydat_len;
124	key.data = argp->key.keydat_val;
125
126	result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 0);
127
128	if (result.stat == YP_TRUE) {
129		result.val.valdat_len = data.size;
130		result.val.valdat_val = data.data;
131	}
132
133	/*
134	 * Do DNS lookups for hosts maps if database lookup failed.
135	 */
136
137	if (do_dns && result.stat != YP_TRUE && strstr(argp->map, "hosts")) {
138		char *rval;
139
140	/* DNS lookups can take time -- do them in a subprocess */
141
142		if (!debug && children < MAX_CHILDREN && fork()) {
143			children++;
144			forked = 0;
145			/*
146			 * Returning NULL here prevents svc_sendreply()
147			 * from being called by the parent. This is vital
148			 * since having both the parent and the child process
149			 * call it would confuse the client.
150			 */
151			return (NULL);
152		} else {
153			forked++;
154		}
155
156		if (debug)
157			yp_error("Doing DNS lookup of %.*s",
158			 	  argp->key.keydat_len,
159				  argp->key.keydat_val);
160
161		/* NUL terminate! NUL terminate!! NUL TERMINATE!!! */
162		argp->key.keydat_val[argp->key.keydat_len] = '\0';
163
164		if (!strcmp(argp->map, "hosts.byname"))
165			rval = yp_dnsname((char *)argp->key.keydat_val);
166		else if (!strcmp(argp->map, "hosts.byaddr"))
167			rval = yp_dnsaddr((const char *)argp->key.keydat_val);
168
169
170		if (rval) {
171			if (debug)
172				yp_error("DNS lookup successful. Result: %s", rval);
173			result.val.valdat_len = strlen(rval);
174			result.val.valdat_val = rval;
175			result.stat = YP_TRUE;
176		} else {
177			if (debug)
178				yp_error("DNS lookup failed.");
179			result.stat = YP_NOKEY;
180		}
181	}
182
183	return (&result);
184}
185
186ypresp_key_val *
187ypproc_first_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
188{
189	static ypresp_key_val  result;
190	DBT key, data;
191	DB *dbp;
192
193	if (yp_access(argp->map, (struct svc_req *)rqstp)) {
194		result.stat = YP_YPERR;
195		return (&result);
196	}
197
198	if (argp->domain == NULL) {
199		result.stat = YP_BADARGS;
200		return (&result);
201	}
202
203	if (yp_validdomain(argp->domain)) {
204		result.stat = YP_NODOM;
205		return(&result);
206	}
207
208	if ((dbp = yp_open_db(argp->domain, argp->map)) == NULL) {
209		result.stat = yp_errno;
210		return(&result);
211	}
212
213	key.data = NULL;
214	key.size = 0;
215	result.stat = yp_first_record(dbp, &key, &data);
216	(void)(dbp->close)(dbp);
217
218	if (result.stat == YP_TRUE) {
219		result.key.keydat_len = key.size;
220		result.key.keydat_val = key.data;
221		result.val.valdat_len = data.size;
222		result.val.valdat_val = data.data;
223	}
224
225	return (&result);
226}
227
228ypresp_key_val *
229ypproc_next_2_svc(ypreq_key *argp, struct svc_req *rqstp)
230{
231	static ypresp_key_val  result;
232	DBT key, data;
233	DB *dbp;
234
235	if (yp_access(argp->map, (struct svc_req *)rqstp)) {
236		result.stat = YP_YPERR;
237		return (&result);
238	}
239
240	if (argp->domain == NULL || argp->map == NULL) {
241		result.stat = YP_BADARGS;
242		return (&result);
243	}
244
245	if (yp_validdomain(argp->domain)) {
246		result.stat = YP_NODOM;
247		return(&result);
248	}
249
250	if ((dbp = yp_open_db(argp->domain, argp->map)) == NULL) {
251		result.stat = yp_errno;
252		return(&result);
253	}
254
255	key.size = argp->key.keydat_len;
256	key.data = argp->key.keydat_val;
257
258	result.stat = yp_next_record(dbp, &key, &data, 0);
259	(void)(dbp->close)(dbp);
260
261	if (result.stat == YP_TRUE) {
262		result.key.keydat_len = key.size;
263		result.key.keydat_val = key.data;
264		result.val.valdat_len = data.size;
265		result.val.valdat_val = data.data;
266	}
267
268	return (&result);
269}
270
271ypresp_xfr *
272ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp)
273{
274	static ypresp_xfr  result;
275
276	if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) {
277		result.xfrstat = YPXFR_REFUSED;
278		return(&result);
279	}
280
281	if (argp->map_parms.domain == NULL) {
282		result.xfrstat = YPXFR_BADARGS;
283		return (&result);
284	}
285
286	if (yp_validdomain(argp->map_parms.domain)) {
287		result.xfrstat = YPXFR_NODOM;
288		return(&result);
289	}
290
291	switch(fork()) {
292	case 0:
293	{
294		char g[11], t[11], p[11];
295		struct sockaddr_in *rqhost;
296		char ypxfr_command[MAXPATHLEN + 2];
297
298		rqhost = svc_getcaller(rqstp->rq_xprt);
299		sprintf (ypxfr_command, "%sypxfr", _PATH_LIBEXEC);
300		sprintf (t, "%u", argp->transid);
301		sprintf (g, "%u", argp->prog);
302		sprintf (p, "%u", argp->port);
303		children++;
304		forked = 0;
305		execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain,
306		      "-h", argp->map_parms.peer, "-f", "-C", t, g,
307		      inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map,
308		      NULL);
309		yp_error("ypxfr execl(): %s", strerror(errno));
310		return(NULL);
311	}
312	case -1:
313		yp_error("ypxfr fork(): %s", strerror(errno));
314		result.xfrstat = YPXFR_XFRERR;
315		break;
316	default:
317		result.xfrstat = YPXFR_SUCC;
318		forked++;
319		break;
320	}
321
322	result.transid = argp->transid;
323	return (&result);
324}
325
326void *
327ypproc_clear_2_svc(void *argp, struct svc_req *rqstp)
328{
329	static char * result;
330	static char rval = 0;
331
332	/*
333	 * We don't have to do anything for ypproc_clear. Unlike
334	 * the SunOS ypserv, we don't hold out database descriptors
335	 * open forever.
336	 */
337	if (yp_access(NULL, (struct svc_req *)rqstp))
338		return (NULL);
339
340	result = &rval;
341	return((void *) &result);
342}
343
344/*
345 * For ypproc_all, we have to send a stream of ypresp_all structures
346 * via TCP, but the XDR filter generated from the yp.x protocol
347 * definition file only serializes one such structure. This means that
348 * to send the whole stream, you need a wrapper which feeds all the
349 * records into the underlying XDR routine until it hits an 'EOF.'
350 * But to use the wrapper, you have to violate the boundaries between
351 * RPC layers by calling svc_sendreply() directly from the ypproc_all
352 * service routine instead of letting the RPC dispatcher do it.
353 *
354 * Bleah.
355 */
356
357/*
358 * Custom XDR routine for serialzing results of ypproc_all: keep
359 * reading from the database and spew until we run out of records
360 * or encounter an error.
361 */
362static bool_t
363xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp)
364{
365	DBT key, data;
366
367	while (1) {
368		/* Get a record. */
369		key.size = objp->ypresp_all_u.val.key.keydat_len;
370		key.data = objp->ypresp_all_u.val.key.keydat_val;
371
372		if ((objp->ypresp_all_u.val.stat =
373		    yp_next_record(spec_dbp,&key,&data,1)) == YP_TRUE) {
374			objp->ypresp_all_u.val.val.valdat_len = data.size;
375			objp->ypresp_all_u.val.val.valdat_val = data.data;
376			objp->ypresp_all_u.val.key.keydat_len = key.size;
377			objp->ypresp_all_u.val.key.keydat_val = key.data;
378			objp->more = TRUE;
379		} else {
380			objp->more = FALSE;
381		}
382
383		/* Serialize. */
384		if (!xdr_ypresp_all(xdrs, objp))
385			return(FALSE);
386		if (objp->more == FALSE)
387			return(TRUE);
388	}
389}
390
391ypresp_all *
392ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
393{
394	static ypresp_all  result;
395
396	/*
397	 * Set this here so that the client will be forced to make
398	 * at least one attempt to read from us even if all we're
399	 * doing is returning an error.
400	 */
401	result.more = TRUE;
402
403	if (yp_access(argp->map, (struct svc_req *)rqstp)) {
404		result.ypresp_all_u.val.stat = YP_YPERR;
405		return (&result);
406	}
407
408	if (argp->domain == NULL || argp->map == NULL) {
409		result.ypresp_all_u.val.stat = YP_BADARGS;
410		return (&result);
411	}
412
413	if (yp_validdomain(argp->domain)) {
414		result.ypresp_all_u.val.stat = YP_NODOM;
415		return(&result);
416	}
417
418	/*
419	 * The ypproc_all procedure can take a while to complete.
420	 * Best to handle it in a subprocess so the parent doesn't
421	 * block. We fork() here so we don't end up sharing a
422	 * DB file handle with the parent.
423	 */
424
425	if (!debug && children < MAX_CHILDREN && fork()) {
426		children++;
427		forked = 0;
428		return (NULL);
429	} else {
430		forked++;
431	}
432
433	if ((spec_dbp = yp_open_db(argp->domain, argp->map)) == NULL) {
434		result.ypresp_all_u.val.stat = yp_errno;
435		return(&result);
436	}
437
438	/* Kick off the actual data transfer. */
439	svc_sendreply(rqstp->rq_xprt, xdr_my_ypresp_all, (char *)&result);
440
441	/* Close database when done. */
442	(void)(spec_dbp->close)(spec_dbp);
443
444	/*
445	 * Returning NULL prevents the dispatcher from calling
446	 * svc_sendreply() since we already did it.
447	 */
448	return (NULL);
449}
450
451ypresp_master *
452ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
453{
454	static ypresp_master  result;
455	DBT key,data;
456
457	if (yp_access(NULL, (struct svc_req *)rqstp)) {
458		result.stat = YP_YPERR;
459		return(&result);
460	}
461
462	if (argp->domain == NULL) {
463		result.stat = YP_BADARGS;
464		return (&result);
465	}
466
467	if (yp_validdomain(argp->domain)) {
468		result.stat = YP_NODOM;
469		return (&result);
470	}
471
472	key.data = "YP_MASTER_NAME";
473	key.size = sizeof("YP_MASTER_NAME") - 1;
474
475	result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1);
476
477	if (result.stat == YP_TRUE) {
478		result.peer = (char *)data.data;
479		result.peer[data.size] = '\0';
480	} else
481		result.peer = "";
482
483	return (&result);
484}
485
486ypresp_order *
487ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
488{
489	static ypresp_order  result;
490	DBT key,data;
491
492	if (yp_access(NULL, (struct svc_req *)rqstp)) {
493		result.stat = YP_YPERR;
494		return(&result);
495	}
496
497	if (argp->domain == NULL) {
498		result.stat = YP_BADARGS;
499		return (&result);
500	}
501
502	if (yp_validdomain(argp->domain)) {
503		result.stat = YP_NODOM;
504		return (&result);
505	}
506
507	/*
508	 * We could just check the timestamp on the map file,
509	 * but that's a hack: we'll only know the last time the file
510	 * was touched, not the last time the database contents were
511	 * updated.
512	 */
513	key.data = "YP_LAST_MODIFIED";
514	key.size = sizeof("YP_LAST_MODIFIED") - 1;
515
516	result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1);
517
518	if (result.stat == YP_TRUE)
519		result.ordernum = atoi((char *)data.data);
520	else
521		result.ordernum = 0;
522
523	return (&result);
524}
525
526static void yp_maplist_free(yp_maplist)
527	struct ypmaplist *yp_maplist;
528{
529	register struct ypmaplist *next;
530
531	while(yp_maplist) {
532		next = yp_maplist->next;
533		free(yp_maplist->map);
534		free(yp_maplist);
535		yp_maplist = next;
536	}
537	return;
538}
539
540static struct ypmaplist *yp_maplist_create(domain)
541	const char *domain;
542{
543	char yp_mapdir[MAXPATHLEN + 2];
544	char yp_mapname[MAXPATHLEN + 2];
545	struct ypmaplist *cur = NULL;
546	struct ypmaplist *yp_maplist = NULL;
547	DIR *dird;
548	struct dirent *dirp;
549	struct stat statbuf;
550
551	snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain);
552
553	if ((dird = opendir(yp_mapdir)) == NULL) {
554		yp_error("opendir(%s) failed: %s", strerror(errno));
555		return(NULL);
556	}
557
558	while ((dirp = readdir(dird)) != NULL) {
559		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
560			snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s",yp_mapdir,dirp->d_name);
561			if (stat(yp_mapname, &statbuf) < 0 || !S_ISREG(statbuf.st_mode))
562				continue;
563			if ((cur = (struct ypmaplist *)malloc(sizeof(struct ypmaplist))) < 0) {
564				yp_error("malloc() failed: %s", strerror(errno));
565				closedir(dird);
566				yp_maplist_free(yp_maplist);
567				return(NULL);
568			}
569			if ((cur->map = (char *)strdup(dirp->d_name)) == NULL) {
570				yp_error("strdup() failed: %s", strerror(errno));
571				closedir(dird);
572				yp_maplist_free(yp_maplist);
573				return(NULL);
574			}
575			cur->next = yp_maplist;
576			yp_maplist = cur;
577			if (debug)
578				yp_error("map: %s", yp_maplist->map);
579		}
580
581	}
582	closedir(dird);
583	return(yp_maplist);
584}
585
586ypresp_maplist *
587ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp)
588{
589	static ypresp_maplist  result;
590
591	if (yp_access(NULL, (struct svc_req *)rqstp)) {
592		result.stat = YP_YPERR;
593		return(&result);
594	}
595
596	if (argp == NULL) {
597		result.stat = YP_BADARGS;
598		return (&result);
599	}
600
601	if (yp_validdomain(*argp)) {
602		result.stat = YP_NODOM;
603		return (&result);
604	}
605
606	/*
607	 * We have to construct a linked list for the ypproc_maplist
608	 * procedure using dynamically allocated memory. Since the XDR
609	 * layer won't free this list for us, we have to deal with it
610	 * ourselves. We call yp_maplist_free() first to free any
611	 * previously allocated data we may have accumulated to insure
612	 * that we have only one linked list in memory at any given
613	 * time.
614	 */
615
616	yp_maplist_free(result.maps);
617
618	if ((result.maps = yp_maplist_create(*argp)) == NULL) {
619		yp_error("yp_maplist_create failed");
620		result.stat = YP_YPERR;
621		return(&result);
622	} else
623		result.stat = YP_TRUE;
624
625	return (&result);
626}
627