yp_server.c revision 13375
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#include <rpc/rpc.h>
46
47#ifndef lint
48static char rcsid[] = "$Id: yp_server.c,v 1.2 1995/12/23 21:35:35 wpaul Exp $";
49#endif /* not lint */
50
51int forked = 0;
52int children = 0;
53DB *spec_dbp = NULL;	/* Special global DB handle for ypproc_all. */
54
55void *
56ypproc_null_2_svc(void *argp, struct svc_req *rqstp)
57{
58	static char * result;
59	static char rval = 0;
60
61	if (yp_access(NULL, (struct svc_req *)rqstp))
62		return(NULL);
63
64	result = &rval;
65
66	return((void *) &result);
67}
68
69bool_t *
70ypproc_domain_2_svc(domainname *argp, struct svc_req *rqstp)
71{
72	static bool_t  result;
73
74	if (yp_access(NULL, (struct svc_req *)rqstp)) {
75		result = FALSE;
76		return (&result);
77	}
78
79	if (argp == NULL || yp_validdomain(*argp))
80		result = FALSE;
81	else
82		result = TRUE;
83
84	return (&result);
85}
86
87bool_t *
88ypproc_domain_nonack_2_svc(domainname *argp, struct svc_req *rqstp)
89{
90	static bool_t  result;
91
92	if (yp_access(NULL, (struct svc_req *)rqstp))
93		return (NULL);
94
95	if (argp == NULL || yp_validdomain(*argp))
96		return (NULL);
97	else
98		result = TRUE;
99
100	return (&result);
101}
102
103ypresp_val *
104ypproc_match_2_svc(ypreq_key *argp, struct svc_req *rqstp)
105{
106	static ypresp_val  result;
107	DBT key, data;
108
109	if (yp_access(argp->map, (struct svc_req *)rqstp)) {
110		result.stat = YP_YPERR;
111		return (&result);
112	}
113
114	if (argp->domain == NULL || argp->map == NULL) {
115		result.stat = YP_BADARGS;
116		return (&result);
117	}
118
119	if (yp_validdomain(argp->domain)) {
120		result.stat = YP_NODOM;
121		return(&result);
122	}
123
124	key.size = argp->key.keydat_len;
125	key.data = argp->key.keydat_val;
126
127	result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 0);
128
129	if (result.stat == YP_TRUE) {
130		result.val.valdat_len = data.size;
131		result.val.valdat_val = data.data;
132	}
133
134	/*
135	 * Do DNS lookups for hosts maps if database lookup failed.
136	 */
137
138	if (do_dns && result.stat != YP_TRUE && strstr(argp->map, "hosts")) {
139		char *rval = NULL;
140
141	/* DNS lookups can take time -- do them in a subprocess */
142
143		if (!debug && children < MAX_CHILDREN && fork()) {
144			children++;
145			forked = 0;
146			/*
147			 * Returning NULL here prevents svc_sendreply()
148			 * from being called by the parent. This is vital
149			 * since having both the parent and the child process
150			 * call it would confuse the client.
151			 */
152			return (NULL);
153		} else {
154			forked++;
155		}
156
157		if (debug)
158			yp_error("Doing DNS lookup of %.*s",
159			 	  argp->key.keydat_len,
160				  argp->key.keydat_val);
161
162		/* NUL terminate! NUL terminate!! NUL TERMINATE!!! */
163		argp->key.keydat_val[argp->key.keydat_len] = '\0';
164
165		if (!strcmp(argp->map, "hosts.byname"))
166			rval = yp_dnsname((char *)argp->key.keydat_val);
167		else if (!strcmp(argp->map, "hosts.byaddr"))
168			rval = yp_dnsaddr((const char *)argp->key.keydat_val);
169
170
171		if (rval) {
172			if (debug)
173				yp_error("DNS lookup successful. Result: %s", rval);
174			result.val.valdat_len = strlen(rval);
175			result.val.valdat_val = rval;
176			result.stat = YP_TRUE;
177		} else {
178			if (debug)
179				yp_error("DNS lookup failed.");
180			result.stat = YP_NOKEY;
181		}
182	}
183
184	return (&result);
185}
186
187ypresp_key_val *
188ypproc_first_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
189{
190	static ypresp_key_val  result;
191	DBT key, data;
192	DB *dbp;
193
194	if (yp_access(argp->map, (struct svc_req *)rqstp)) {
195		result.stat = YP_YPERR;
196		return (&result);
197	}
198
199	if (argp->domain == NULL) {
200		result.stat = YP_BADARGS;
201		return (&result);
202	}
203
204	if (yp_validdomain(argp->domain)) {
205		result.stat = YP_NODOM;
206		return(&result);
207	}
208
209	if ((dbp = yp_open_db(argp->domain, argp->map)) == NULL) {
210		result.stat = yp_errno;
211		return(&result);
212	}
213
214	key.data = NULL;
215	key.size = 0;
216	result.stat = yp_first_record(dbp, &key, &data);
217	(void)(dbp->close)(dbp);
218
219	if (result.stat == YP_TRUE) {
220		result.key.keydat_len = key.size;
221		result.key.keydat_val = key.data;
222		result.val.valdat_len = data.size;
223		result.val.valdat_val = data.data;
224	}
225
226	return (&result);
227}
228
229ypresp_key_val *
230ypproc_next_2_svc(ypreq_key *argp, struct svc_req *rqstp)
231{
232	static ypresp_key_val  result;
233	DBT key, data;
234	DB *dbp;
235
236	if (yp_access(argp->map, (struct svc_req *)rqstp)) {
237		result.stat = YP_YPERR;
238		return (&result);
239	}
240
241	if (argp->domain == NULL || argp->map == NULL) {
242		result.stat = YP_BADARGS;
243		return (&result);
244	}
245
246	if (yp_validdomain(argp->domain)) {
247		result.stat = YP_NODOM;
248		return(&result);
249	}
250
251	if ((dbp = yp_open_db(argp->domain, argp->map)) == NULL) {
252		result.stat = yp_errno;
253		return(&result);
254	}
255
256	key.size = argp->key.keydat_len;
257	key.data = argp->key.keydat_val;
258
259	result.stat = yp_next_record(dbp, &key, &data, 0);
260	(void)(dbp->close)(dbp);
261
262	if (result.stat == YP_TRUE) {
263		result.key.keydat_len = key.size;
264		result.key.keydat_val = key.data;
265		result.val.valdat_len = data.size;
266		result.val.valdat_val = data.data;
267	}
268
269	return (&result);
270}
271
272static void ypxfr_callback(rval,addr,transid,prognum,port)
273	ypxfrstat rval;
274	struct sockaddr_in *addr;
275	unsigned int transid;
276	unsigned int prognum;
277	unsigned long port;
278{
279	CLIENT *clnt;
280	int sock = RPC_ANYSOCK;
281	struct timeval timeout;
282	yppushresp_xfr ypxfr_resp;
283	struct rpc_err err;
284
285	timeout.tv_sec = 5;
286	timeout.tv_usec = 0;
287	addr->sin_port = htons(port);
288
289	if ((clnt = clntudp_create(addr, prognum, 1, timeout, &sock)) == NULL)
290		yp_error("%s", clnt_spcreateerror("failed to establish \
291callback handle"));
292
293	ypxfr_resp.status = rval;
294	ypxfr_resp.transid = transid;
295
296	/* Turn the timeout off -- we don't want to block. */
297	timeout.tv_sec = 0;
298	if (clnt_control(clnt, CLSET_TIMEOUT, (char *)&timeout) == FALSE)
299		yp_error("failed to set timeout on ypproc_xfr callback");
300
301	if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) {
302		clnt_geterr(clnt, &err);
303		if (err.re_status != RPC_SUCCESS &&
304		    err.re_status != RPC_TIMEDOUT)
305			yp_error("%s", clnt_sperror(clnt,
306				"ypxfr callback failed"));
307	}
308
309	clnt_destroy(clnt);
310	return;
311}
312
313ypresp_xfr *
314ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp)
315{
316	static ypresp_xfr  result;
317	struct sockaddr_in *rqhost;
318
319	result.transid = argp->transid;
320	rqhost = svc_getcaller(rqstp->rq_xprt);
321
322	if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) {
323		/* Order is important: send regular RPC reply, then callback */
324		result.xfrstat = YPXFR_REFUSED;
325		svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result);
326		ypxfr_callback(YPXFR_REFUSED,rqhost,argp->transid,
327			       argp->prog,argp->port);
328		return(NULL);
329	}
330
331	if (argp->map_parms.domain == NULL) {
332		result.xfrstat = YPXFR_BADARGS;
333		svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result);
334		ypxfr_callback(YPXFR_BADARGS,rqhost,argp->transid,
335			       argp->prog,argp->port);
336		return(NULL);
337	}
338
339	if (yp_validdomain(argp->map_parms.domain)) {
340		result.xfrstat = YPXFR_NODOM;
341		svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result);
342		ypxfr_callback(YPXFR_NODOM,rqhost,argp->transid,
343			       argp->prog,argp->port);
344		return(NULL);
345	}
346
347	switch(fork()) {
348	case 0:
349	{
350		char g[11], t[11], p[11];
351		char ypxfr_command[MAXPATHLEN + 2];
352
353		sprintf (ypxfr_command, "%sypxfr", _PATH_LIBEXEC);
354		sprintf (t, "%u", argp->transid);
355		sprintf (g, "%u", argp->prog);
356		sprintf (p, "%u", argp->port);
357		if (debug)
358			close(0); close(1); close(2);
359		if (strcmp(yp_dir, _PATH_YP)) {
360			execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain,
361		      	"-h", argp->map_parms.peer, "-p", yp_dir, "-C", t,
362		      	g, inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map,
363		      	NULL);
364		} else {
365			execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain,
366		      	"-h", argp->map_parms.peer, "-C", t, g,
367		      	inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map,
368		      	NULL);
369		}
370		forked++;
371		result.xfrstat = YPXFR_XFRERR;
372		yp_error("ypxfr execl(): %s", strerror(errno));
373		svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result);
374		ypxfr_callback(YPXFR_XFRERR,rqhost,argp->transid,
375			       argp->prog,argp->port);
376		return(NULL);
377		break;
378	}
379	case -1:
380		yp_error("ypxfr fork(): %s", strerror(errno));
381		result.xfrstat = YPXFR_XFRERR;
382		svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result);
383		ypxfr_callback(YPXFR_XFRERR,rqhost,argp->transid,
384			       argp->prog,argp->port);
385		return(NULL);
386		break;
387	default:
388		result.xfrstat = YPXFR_SUCC;
389		children++;
390		forked = 0;
391		break;
392	}
393
394	return (&result);
395}
396
397void *
398ypproc_clear_2_svc(void *argp, struct svc_req *rqstp)
399{
400	static char * result;
401	static char rval = 0;
402
403	/*
404	 * We don't have to do anything for ypproc_clear. Unlike
405	 * the SunOS ypserv, we don't hold out database descriptors
406	 * open forever.
407	 */
408	if (yp_access(NULL, (struct svc_req *)rqstp))
409		return (NULL);
410
411	result = &rval;
412	return((void *) &result);
413}
414
415/*
416 * For ypproc_all, we have to send a stream of ypresp_all structures
417 * via TCP, but the XDR filter generated from the yp.x protocol
418 * definition file only serializes one such structure. This means that
419 * to send the whole stream, you need a wrapper which feeds all the
420 * records into the underlying XDR routine until it hits an 'EOF.'
421 * But to use the wrapper, you have to violate the boundaries between
422 * RPC layers by calling svc_sendreply() directly from the ypproc_all
423 * service routine instead of letting the RPC dispatcher do it.
424 *
425 * Bleah.
426 */
427
428/*
429 * Custom XDR routine for serialzing results of ypproc_all: keep
430 * reading from the database and spew until we run out of records
431 * or encounter an error.
432 */
433static bool_t
434xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp)
435{
436	DBT key, data;
437
438	while (1) {
439		/* Get a record. */
440		key.size = objp->ypresp_all_u.val.key.keydat_len;
441		key.data = objp->ypresp_all_u.val.key.keydat_val;
442
443		if ((objp->ypresp_all_u.val.stat =
444		    yp_next_record(spec_dbp,&key,&data,1)) == YP_TRUE) {
445			objp->ypresp_all_u.val.val.valdat_len = data.size;
446			objp->ypresp_all_u.val.val.valdat_val = data.data;
447			objp->ypresp_all_u.val.key.keydat_len = key.size;
448			objp->ypresp_all_u.val.key.keydat_val = key.data;
449			objp->more = TRUE;
450		} else {
451			objp->more = FALSE;
452		}
453
454		/* Serialize. */
455		if (!xdr_ypresp_all(xdrs, objp))
456			return(FALSE);
457		if (objp->more == FALSE)
458			return(TRUE);
459	}
460}
461
462ypresp_all *
463ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
464{
465	static ypresp_all  result;
466
467	/*
468	 * Set this here so that the client will be forced to make
469	 * at least one attempt to read from us even if all we're
470	 * doing is returning an error.
471	 */
472	result.more = TRUE;
473
474	if (yp_access(argp->map, (struct svc_req *)rqstp)) {
475		result.ypresp_all_u.val.stat = YP_YPERR;
476		return (&result);
477	}
478
479	if (argp->domain == NULL || argp->map == NULL) {
480		result.ypresp_all_u.val.stat = YP_BADARGS;
481		return (&result);
482	}
483
484	if (yp_validdomain(argp->domain)) {
485		result.ypresp_all_u.val.stat = YP_NODOM;
486		return(&result);
487	}
488
489	/*
490	 * The ypproc_all procedure can take a while to complete.
491	 * Best to handle it in a subprocess so the parent doesn't
492	 * block. We fork() here so we don't end up sharing a
493	 * DB file handle with the parent.
494	 */
495
496	if (!debug && children < MAX_CHILDREN && fork()) {
497		children++;
498		forked = 0;
499		return (NULL);
500	} else {
501		forked++;
502	}
503
504	if ((spec_dbp = yp_open_db(argp->domain, argp->map)) == NULL) {
505		result.ypresp_all_u.val.stat = yp_errno;
506		return(&result);
507	}
508
509	/* Kick off the actual data transfer. */
510	svc_sendreply(rqstp->rq_xprt, xdr_my_ypresp_all, (char *)&result);
511
512	/* Close database when done. */
513	(void)(spec_dbp->close)(spec_dbp);
514
515	/*
516	 * Returning NULL prevents the dispatcher from calling
517	 * svc_sendreply() since we already did it.
518	 */
519	return (NULL);
520}
521
522ypresp_master *
523ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
524{
525	static ypresp_master  result;
526	DBT key,data;
527
528	if (yp_access(NULL, (struct svc_req *)rqstp)) {
529		result.stat = YP_YPERR;
530		return(&result);
531	}
532
533	if (argp->domain == NULL) {
534		result.stat = YP_BADARGS;
535		return (&result);
536	}
537
538	if (yp_validdomain(argp->domain)) {
539		result.stat = YP_NODOM;
540		return (&result);
541	}
542
543	key.data = "YP_MASTER_NAME";
544	key.size = sizeof("YP_MASTER_NAME") - 1;
545
546	result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1);
547
548	if (result.stat == YP_TRUE) {
549		result.peer = (char *)data.data;
550		result.peer[data.size] = '\0';
551	} else
552		result.peer = "";
553
554	return (&result);
555}
556
557ypresp_order *
558ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
559{
560	static ypresp_order  result;
561	DBT key,data;
562
563	if (yp_access(NULL, (struct svc_req *)rqstp)) {
564		result.stat = YP_YPERR;
565		return(&result);
566	}
567
568	if (argp->domain == NULL) {
569		result.stat = YP_BADARGS;
570		return (&result);
571	}
572
573	if (yp_validdomain(argp->domain)) {
574		result.stat = YP_NODOM;
575		return (&result);
576	}
577
578	/*
579	 * We could just check the timestamp on the map file,
580	 * but that's a hack: we'll only know the last time the file
581	 * was touched, not the last time the database contents were
582	 * updated.
583	 */
584	key.data = "YP_LAST_MODIFIED";
585	key.size = sizeof("YP_LAST_MODIFIED") - 1;
586
587	result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1);
588
589	if (result.stat == YP_TRUE)
590		result.ordernum = atoi((char *)data.data);
591	else
592		result.ordernum = 0;
593
594	return (&result);
595}
596
597static void yp_maplist_free(yp_maplist)
598	struct ypmaplist *yp_maplist;
599{
600	register struct ypmaplist *next;
601
602	while(yp_maplist) {
603		next = yp_maplist->next;
604		free(yp_maplist->map);
605		free(yp_maplist);
606		yp_maplist = next;
607	}
608	return;
609}
610
611static struct ypmaplist *yp_maplist_create(domain)
612	const char *domain;
613{
614	char yp_mapdir[MAXPATHLEN + 2];
615	char yp_mapname[MAXPATHLEN + 2];
616	struct ypmaplist *cur = NULL;
617	struct ypmaplist *yp_maplist = NULL;
618	DIR *dird;
619	struct dirent *dirp;
620	struct stat statbuf;
621
622	snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain);
623
624	if ((dird = opendir(yp_mapdir)) == NULL) {
625		yp_error("opendir(%s) failed: %s", strerror(errno));
626		return(NULL);
627	}
628
629	while ((dirp = readdir(dird)) != NULL) {
630		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
631			snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s",yp_mapdir,dirp->d_name);
632			if (stat(yp_mapname, &statbuf) < 0 || !S_ISREG(statbuf.st_mode))
633				continue;
634			if ((cur = (struct ypmaplist *)malloc(sizeof(struct ypmaplist))) < 0) {
635				yp_error("malloc() failed: %s", strerror(errno));
636				closedir(dird);
637				yp_maplist_free(yp_maplist);
638				return(NULL);
639			}
640			if ((cur->map = (char *)strdup(dirp->d_name)) == NULL) {
641				yp_error("strdup() failed: %s", strerror(errno));
642				closedir(dird);
643				yp_maplist_free(yp_maplist);
644				return(NULL);
645			}
646			cur->next = yp_maplist;
647			yp_maplist = cur;
648			if (debug)
649				yp_error("map: %s", yp_maplist->map);
650		}
651
652	}
653	closedir(dird);
654	return(yp_maplist);
655}
656
657ypresp_maplist *
658ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp)
659{
660	static ypresp_maplist  result;
661
662	if (yp_access(NULL, (struct svc_req *)rqstp)) {
663		result.stat = YP_YPERR;
664		return(&result);
665	}
666
667	if (argp == NULL) {
668		result.stat = YP_BADARGS;
669		return (&result);
670	}
671
672	if (yp_validdomain(*argp)) {
673		result.stat = YP_NODOM;
674		return (&result);
675	}
676
677	/*
678	 * We have to construct a linked list for the ypproc_maplist
679	 * procedure using dynamically allocated memory. Since the XDR
680	 * layer won't free this list for us, we have to deal with it
681	 * ourselves. We call yp_maplist_free() first to free any
682	 * previously allocated data we may have accumulated to insure
683	 * that we have only one linked list in memory at any given
684	 * time.
685	 */
686
687	yp_maplist_free(result.maps);
688
689	if ((result.maps = yp_maplist_create(*argp)) == NULL) {
690		yp_error("yp_maplist_create failed");
691		result.stat = YP_YPERR;
692		return(&result);
693	} else
694		result.stat = YP_TRUE;
695
696	return (&result);
697}
698