yp_server.c revision 12997
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.1.1.1 1995/12/16 20:54:17 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
284	timeout.tv_sec = 20;
285	timeout.tv_usec = 0;
286	addr->sin_port = htons(port);
287
288	if ((clnt = clntudp_create(addr, prognum, 1, timeout, &sock)) == NULL)
289		yp_error("%s", clnt_spcreateerror("failed to establish \
290callback handle"));
291
292	ypxfr_resp.status = rval;
293	ypxfr_resp.transid = transid;
294
295	if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL)
296		yp_error("%s", clnt_sperror(clnt, "ypxfr callback failed"));
297
298	clnt_destroy(clnt);
299	return;
300}
301
302ypresp_xfr *
303ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp)
304{
305	static ypresp_xfr  result;
306	struct sockaddr_in *rqhost;
307
308	if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) {
309		result.xfrstat = YPXFR_REFUSED;
310		return(&result);
311	}
312
313	if (argp->map_parms.domain == NULL) {
314		result.xfrstat = YPXFR_BADARGS;
315		return (&result);
316	}
317
318	if (yp_validdomain(argp->map_parms.domain)) {
319		result.xfrstat = YPXFR_NODOM;
320		return(&result);
321	}
322
323	rqhost = svc_getcaller(rqstp->rq_xprt);
324
325	switch(fork()) {
326	case 0:
327	{
328		char g[11], t[11], p[11];
329		char ypxfr_command[MAXPATHLEN + 2];
330
331		sprintf (ypxfr_command, "%sypxfr", _PATH_LIBEXEC);
332		sprintf (t, "%u", argp->transid);
333		sprintf (g, "%u", argp->prog);
334		sprintf (p, "%u", argp->port);
335		if (debug)
336			close(0); close(1); close(2);
337		if (strcmp(yp_dir, _PATH_YP)) {
338			execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain,
339		      	"-h", argp->map_parms.peer, "-f", "-p", yp_dir, "-C", t,
340		      	g, inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map,
341		      	NULL);
342		} else {
343			execl(ypxfr_command, "ypxfr", "-d", argp->map_parms.domain,
344		      	"-h", argp->map_parms.peer, "-f", "-C", t, g,
345		      	inet_ntoa(rqhost->sin_addr), p, argp->map_parms.map,
346		      	NULL);
347		}
348		forked++;
349		yp_error("ypxfr execl(): %s", strerror(errno));
350		ypxfr_callback(YPXFR_XFRERR,rqhost,argp->transid,
351			       argp->prog,argp->port);
352		result.xfrstat = YPXFR_XFRERR;
353		return(&result);
354		break;
355	}
356	case -1:
357		yp_error("ypxfr fork(): %s", strerror(errno));
358		ypxfr_callback(YPXFR_XFRERR,rqhost,argp->transid,
359			       argp->prog,argp->port);
360		result.xfrstat = YPXFR_XFRERR;
361		return(&result);
362		break;
363	default:
364		children++;
365		forked = 0;
366		break;
367	}
368	/* Don't return anything -- it's up to ypxfr to do that. */
369	return (NULL);
370}
371
372void *
373ypproc_clear_2_svc(void *argp, struct svc_req *rqstp)
374{
375	static char * result;
376	static char rval = 0;
377
378	/*
379	 * We don't have to do anything for ypproc_clear. Unlike
380	 * the SunOS ypserv, we don't hold out database descriptors
381	 * open forever.
382	 */
383	if (yp_access(NULL, (struct svc_req *)rqstp))
384		return (NULL);
385
386	result = &rval;
387	return((void *) &result);
388}
389
390/*
391 * For ypproc_all, we have to send a stream of ypresp_all structures
392 * via TCP, but the XDR filter generated from the yp.x protocol
393 * definition file only serializes one such structure. This means that
394 * to send the whole stream, you need a wrapper which feeds all the
395 * records into the underlying XDR routine until it hits an 'EOF.'
396 * But to use the wrapper, you have to violate the boundaries between
397 * RPC layers by calling svc_sendreply() directly from the ypproc_all
398 * service routine instead of letting the RPC dispatcher do it.
399 *
400 * Bleah.
401 */
402
403/*
404 * Custom XDR routine for serialzing results of ypproc_all: keep
405 * reading from the database and spew until we run out of records
406 * or encounter an error.
407 */
408static bool_t
409xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp)
410{
411	DBT key, data;
412
413	while (1) {
414		/* Get a record. */
415		key.size = objp->ypresp_all_u.val.key.keydat_len;
416		key.data = objp->ypresp_all_u.val.key.keydat_val;
417
418		if ((objp->ypresp_all_u.val.stat =
419		    yp_next_record(spec_dbp,&key,&data,1)) == YP_TRUE) {
420			objp->ypresp_all_u.val.val.valdat_len = data.size;
421			objp->ypresp_all_u.val.val.valdat_val = data.data;
422			objp->ypresp_all_u.val.key.keydat_len = key.size;
423			objp->ypresp_all_u.val.key.keydat_val = key.data;
424			objp->more = TRUE;
425		} else {
426			objp->more = FALSE;
427		}
428
429		/* Serialize. */
430		if (!xdr_ypresp_all(xdrs, objp))
431			return(FALSE);
432		if (objp->more == FALSE)
433			return(TRUE);
434	}
435}
436
437ypresp_all *
438ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
439{
440	static ypresp_all  result;
441
442	/*
443	 * Set this here so that the client will be forced to make
444	 * at least one attempt to read from us even if all we're
445	 * doing is returning an error.
446	 */
447	result.more = TRUE;
448
449	if (yp_access(argp->map, (struct svc_req *)rqstp)) {
450		result.ypresp_all_u.val.stat = YP_YPERR;
451		return (&result);
452	}
453
454	if (argp->domain == NULL || argp->map == NULL) {
455		result.ypresp_all_u.val.stat = YP_BADARGS;
456		return (&result);
457	}
458
459	if (yp_validdomain(argp->domain)) {
460		result.ypresp_all_u.val.stat = YP_NODOM;
461		return(&result);
462	}
463
464	/*
465	 * The ypproc_all procedure can take a while to complete.
466	 * Best to handle it in a subprocess so the parent doesn't
467	 * block. We fork() here so we don't end up sharing a
468	 * DB file handle with the parent.
469	 */
470
471	if (!debug && children < MAX_CHILDREN && fork()) {
472		children++;
473		forked = 0;
474		return (NULL);
475	} else {
476		forked++;
477	}
478
479	if ((spec_dbp = yp_open_db(argp->domain, argp->map)) == NULL) {
480		result.ypresp_all_u.val.stat = yp_errno;
481		return(&result);
482	}
483
484	/* Kick off the actual data transfer. */
485	svc_sendreply(rqstp->rq_xprt, xdr_my_ypresp_all, (char *)&result);
486
487	/* Close database when done. */
488	(void)(spec_dbp->close)(spec_dbp);
489
490	/*
491	 * Returning NULL prevents the dispatcher from calling
492	 * svc_sendreply() since we already did it.
493	 */
494	return (NULL);
495}
496
497ypresp_master *
498ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
499{
500	static ypresp_master  result;
501	DBT key,data;
502
503	if (yp_access(NULL, (struct svc_req *)rqstp)) {
504		result.stat = YP_YPERR;
505		return(&result);
506	}
507
508	if (argp->domain == NULL) {
509		result.stat = YP_BADARGS;
510		return (&result);
511	}
512
513	if (yp_validdomain(argp->domain)) {
514		result.stat = YP_NODOM;
515		return (&result);
516	}
517
518	key.data = "YP_MASTER_NAME";
519	key.size = sizeof("YP_MASTER_NAME") - 1;
520
521	result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1);
522
523	if (result.stat == YP_TRUE) {
524		result.peer = (char *)data.data;
525		result.peer[data.size] = '\0';
526	} else
527		result.peer = "";
528
529	return (&result);
530}
531
532ypresp_order *
533ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
534{
535	static ypresp_order  result;
536	DBT key,data;
537
538	if (yp_access(NULL, (struct svc_req *)rqstp)) {
539		result.stat = YP_YPERR;
540		return(&result);
541	}
542
543	if (argp->domain == NULL) {
544		result.stat = YP_BADARGS;
545		return (&result);
546	}
547
548	if (yp_validdomain(argp->domain)) {
549		result.stat = YP_NODOM;
550		return (&result);
551	}
552
553	/*
554	 * We could just check the timestamp on the map file,
555	 * but that's a hack: we'll only know the last time the file
556	 * was touched, not the last time the database contents were
557	 * updated.
558	 */
559	key.data = "YP_LAST_MODIFIED";
560	key.size = sizeof("YP_LAST_MODIFIED") - 1;
561
562	result.stat = yp_get_record(argp->domain, argp->map, &key, &data, 1);
563
564	if (result.stat == YP_TRUE)
565		result.ordernum = atoi((char *)data.data);
566	else
567		result.ordernum = 0;
568
569	return (&result);
570}
571
572static void yp_maplist_free(yp_maplist)
573	struct ypmaplist *yp_maplist;
574{
575	register struct ypmaplist *next;
576
577	while(yp_maplist) {
578		next = yp_maplist->next;
579		free(yp_maplist->map);
580		free(yp_maplist);
581		yp_maplist = next;
582	}
583	return;
584}
585
586static struct ypmaplist *yp_maplist_create(domain)
587	const char *domain;
588{
589	char yp_mapdir[MAXPATHLEN + 2];
590	char yp_mapname[MAXPATHLEN + 2];
591	struct ypmaplist *cur = NULL;
592	struct ypmaplist *yp_maplist = NULL;
593	DIR *dird;
594	struct dirent *dirp;
595	struct stat statbuf;
596
597	snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain);
598
599	if ((dird = opendir(yp_mapdir)) == NULL) {
600		yp_error("opendir(%s) failed: %s", strerror(errno));
601		return(NULL);
602	}
603
604	while ((dirp = readdir(dird)) != NULL) {
605		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
606			snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s",yp_mapdir,dirp->d_name);
607			if (stat(yp_mapname, &statbuf) < 0 || !S_ISREG(statbuf.st_mode))
608				continue;
609			if ((cur = (struct ypmaplist *)malloc(sizeof(struct ypmaplist))) < 0) {
610				yp_error("malloc() failed: %s", strerror(errno));
611				closedir(dird);
612				yp_maplist_free(yp_maplist);
613				return(NULL);
614			}
615			if ((cur->map = (char *)strdup(dirp->d_name)) == NULL) {
616				yp_error("strdup() failed: %s", strerror(errno));
617				closedir(dird);
618				yp_maplist_free(yp_maplist);
619				return(NULL);
620			}
621			cur->next = yp_maplist;
622			yp_maplist = cur;
623			if (debug)
624				yp_error("map: %s", yp_maplist->map);
625		}
626
627	}
628	closedir(dird);
629	return(yp_maplist);
630}
631
632ypresp_maplist *
633ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp)
634{
635	static ypresp_maplist  result;
636
637	if (yp_access(NULL, (struct svc_req *)rqstp)) {
638		result.stat = YP_YPERR;
639		return(&result);
640	}
641
642	if (argp == NULL) {
643		result.stat = YP_BADARGS;
644		return (&result);
645	}
646
647	if (yp_validdomain(*argp)) {
648		result.stat = YP_NODOM;
649		return (&result);
650	}
651
652	/*
653	 * We have to construct a linked list for the ypproc_maplist
654	 * procedure using dynamically allocated memory. Since the XDR
655	 * layer won't free this list for us, we have to deal with it
656	 * ourselves. We call yp_maplist_free() first to free any
657	 * previously allocated data we may have accumulated to insure
658	 * that we have only one linked list in memory at any given
659	 * time.
660	 */
661
662	yp_maplist_free(result.maps);
663
664	if ((result.maps = yp_maplist_create(*argp)) == NULL) {
665		yp_error("yp_maplist_create failed");
666		result.stat = YP_YPERR;
667		return(&result);
668	} else
669		result.stat = YP_TRUE;
670
671	return (&result);
672}
673