xref: /illumos-gate/usr/src/cmd/ypcmd/ypserv_proc.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * Portions of this source code were derived from Berkeley 4.3 BSD
32  * under license from the Regents of the University of California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  *
39  * This contains YP server code which supplies the set of functions
40  * requested using rpc.   The top level functions in this module
41  * are those which have symbols of the form YPPROC_xxxx defined in
42  * yp_prot.h, and symbols of the form YPOLDPROC_xxxx defined in ypsym.h.
43  * The latter exist to provide compatibility to the old version of the yp
44  * protocol/server, and may emulate the behavior of the previous software
45  * by invoking some other program.
46  *
47  * This module also contains functions which are used by (and only by) the
48  * top-level functions here.
49  */
50 
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
55 #include <dirent.h>
56 #include <limits.h>
57 #include <sys/systeminfo.h>
58 #include <rpc/rpc.h>
59 #include <string.h>
60 #include <malloc.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <stdio.h>
64 #include "ypsym.h"
65 #include "ypdefs.h"
66 #include <ctype.h>
67 
68 /* Use shim version of DBM calls */
69 #include "shim.h"
70 #include "shim_hooks.h"
71 
72 USE_YP_PREFIX
73 USE_YP_SECURE
74 USE_YP_INTERDOMAIN
75 
76 #ifndef	YPXFR_PROC
77 #define	YPXFR_PROC "/usr/lib/netsvc/yp/ypxfr"
78 #endif
79 static char ypxfr_proc[] = YPXFR_PROC;
80 #ifndef	YPPUSH_PROC
81 #define	YPPUSH_PROC "/usr/lib/netsvc/yp/yppush"
82 #endif
83 static char yppush_proc[] = YPPUSH_PROC;
84 struct yppriv_sym {
85     char *sym;
86     unsigned len;
87 };
88 static	char err_fork[] = "ypserv:  %s fork failure.\n";
89 #define	FORK_ERR logprintf(err_fork, fun)
90 static char err_execl[] = "ypserv:  %s execl failure.\n";
91 #define	EXEC_ERR logprintf(err_execl, fun)
92 static char err_respond[] = "ypserv: %s can't respond to rpc request.\n";
93 #define	RESPOND_ERR logprintf(err_respond, fun)
94 static char err_free[] = "ypserv: %s can't free args.\n";
95 #define	FREE_ERR logprintf(err_free, fun)
96 static char err_map[] = "ypserv: %s no such map or access denied.\n";
97 #define	MAP_ERR logprintf(err_map, fun)
98 static char err_vers[] = "ypserv: %s version not supported.\n";
99 #define	VERS_ERR logprintf(err_vers, fun)
100 
101 static void ypfilter(DBM *fdb, datum *inkey, datum *outkey, datum *val,
102 			uint_t *status, bool_t update);
103 static bool isypsym(datum *key);
104 static bool xdrypserv_ypall(XDR *xdrs, struct ypreq_nokey *req);
105 static int multihomed(struct ypreq_key req, struct ypresp_val *resp,
106 			SVCXPRT *xprt, DBM *fdb);
107 static int omultihomed(struct yprequest req, struct ypresponse *resp,
108 			SVCXPRT *xprt, DBM *fdb);
109 
110 
111 /* For DNS forwarding */
112 extern bool dnsforward;
113 extern bool client_setup_failure;
114 extern int resolv_pid;
115 extern CLIENT *resolv_client;
116 extern char *resolv_tp;
117 
118 /*
119  * This determines whether or not a passed domain is served by this
120  * server, and returns a boolean.  Used by both old and new protocol
121  * versions.
122  */
123 void
124 ypdomain(SVCXPRT *transp, bool always_respond)
125 {
126 	char domain_name[YPMAXDOMAIN + 1];
127 	char *pdomain_name = domain_name;
128 	bool isserved;
129 	char *fun = "ypdomain";
130 	struct netbuf *nbuf;
131 	sa_family_t af;
132 
133 	memset(domain_name, 0, sizeof (domain_name));
134 
135 	if (!svc_getargs(transp, (xdrproc_t)xdr_ypdomain_wrap_string,
136 				(caddr_t)&pdomain_name)) {
137 		svcerr_decode(transp);
138 		return;
139 	}
140 
141 	/*
142 	 * If the file /var/yp/securenets is present on the server, and if
143 	 * the hostname is present in the file, then let the client bind to
144 	 * the server.
145 	 */
146 	nbuf = svc_getrpccaller(transp);
147 	af = ((struct sockaddr_storage *)nbuf->buf)->ss_family;
148 	if (af != AF_INET && af != AF_INET6) {
149 		logprintf("Protocol incorrect\n");
150 		return;
151 	}
152 
153 	if (!(check_secure_net_ti(nbuf, fun))) {
154 		MAP_ERR;
155 		return;
156 	}
157 
158 	isserved = ypcheck_domain(domain_name);
159 
160 	if (isserved || always_respond) {
161 
162 		if (!svc_sendreply(transp, xdr_bool, (char *)&isserved)) {
163 		    RESPOND_ERR;
164 		}
165 		if (!isserved)
166 			logprintf("Domain %s not supported\n",
167 					domain_name);
168 
169 	} else {
170 		/*
171 		 * This case is the one in which the domain is not
172 		 * supported, and in which we are not to respond in the
173 		 * unsupported case.  We are going to make an error happen
174 		 * to allow the portmapper to end his wait without the
175 		 * normal timeout period.  The assumption here is that
176 		 * the only process in the world which is using the function
177 		 * in its no-answer-if-nack form is the portmapper, which is
178 		 * doing the krock for pseudo-broadcast.  If some poor fool
179 		 * calls this function as a single-cast message, the nack
180 		 * case will look like an incomprehensible error.  Sigh...
181 		 * (The traditional Unix disclaimer)
182 		 */
183 
184 		svcerr_decode(transp);
185 		logprintf("Domain %s not supported (broadcast)\n",
186 				domain_name);
187 	}
188 }
189 
190 /*
191  * This implements the yp "match" function.
192  */
193 void
194 ypmatch(SVCXPRT *transp, struct svc_req *rqstp)
195 {
196 	struct ypreq_key req;
197 	struct ypresp_val resp;
198 	char *fun = "ypmatch";
199 	DBM *fdb;
200 
201 	memset(&req, 0, sizeof (req));
202 	memset(&resp, 0, sizeof (resp));
203 	resp.status = (unsigned)YP_NOKEY;
204 
205 	if (!svc_getargs(transp, (xdrproc_t)xdr_ypreq_key, (char *)&req)) {
206 		svcerr_decode(transp);
207 		return;
208 	}
209 
210 	/*
211 	 * sanity check the map name and to a DBM lookup
212 	 * also perform an access check...
213 	 */
214 	if ((fdb = ypset_current_map(req.map, req.domain,
215 					&resp.status)) != NULL &&
216 		yp_map_access(transp, &resp.status, fdb)) {
217 
218 		/* Check with the DBM database */
219 		resp.valdat = dbm_fetch(fdb, req.keydat);
220 		if (resp.valdat.dptr != NULL) {
221 			resp.status = YP_TRUE;
222 			if (!silent)
223 				printf("%s: dbm: %40.40s\n",
224 					fun, resp.valdat.dptr);
225 			goto send_reply;
226 		}
227 
228 		/*
229 		 * If we're being asked to match YP_SECURE or YP_INTERDOMAIN
230 		 * and we haven't found it in the dbm file, then we don't
231 		 * really want to waste any more time.  Specifically, we don't
232 		 * want to ask DNS
233 		 */
234 		if (req.keydat.dsize == 0 ||
235 		    req.keydat.dptr == NULL ||
236 		    req.keydat.dptr[0] == '\0' ||
237 	strncmp(req.keydat.dptr, yp_secure, req.keydat.dsize) == 0 ||
238 	strncmp(req.keydat.dptr, yp_interdomain, req.keydat.dsize) == 0) {
239 			goto send_reply;
240 		}
241 
242 		/* Let's try the YP_MULTI_ hack... */
243 #ifdef MINUS_C_OPTION
244 		if (multiflag == TRUE && multihomed(req, &resp, transp, fdb))
245 			goto send_reply;
246 #else
247 		if (multihomed(req, &resp, transp, fdb))
248 			goto send_reply;
249 #endif
250 
251 		/*
252 		 * Let's try DNS, but if client_setup_failure is set,
253 		 * we have tried DNS in the past and failed, there is
254 		 * no reason in forcing an infinite loop by turning
255 		 * off DNS in setup_resolv() only to turn it back on
256 		 * again here.
257 		 */
258 		if (!dnsforward && !client_setup_failure) {
259 			datum idkey, idval;
260 			idkey.dptr = yp_interdomain;
261 			idkey.dsize = yp_interdomain_sz;
262 			idval = dbm_fetch(fdb, idkey);
263 			if (idval.dptr)
264 				dnsforward = TRUE;
265 		}
266 
267 		if (dnsforward) {
268 			if (!resolv_pid || !resolv_client) {
269 				setup_resolv(&dnsforward, &resolv_pid,
270 						&resolv_client, resolv_tp, 0);
271 				if (resolv_client == NULL)
272 					client_setup_failure = TRUE;
273 			}
274 
275 			if (resolv_req(&dnsforward, &resolv_client,
276 						&resolv_pid, resolv_tp,
277 						rqstp->rq_xprt, &req,
278 						req.map) == TRUE)
279 				goto free_args;
280 		}
281 	}
282 	send_reply:
283 
284 	if (!svc_sendreply(transp, (xdrproc_t)xdr_ypresp_val,
285 				(caddr_t)&resp)) {
286 		RESPOND_ERR;
287 	}
288 
289 	free_args:
290 
291 	if (!svc_freeargs(transp, (xdrproc_t)xdr_ypreq_key,
292 				(char *)&req)) {
293 		FREE_ERR;
294 	}
295 }
296 
297 
298 /*
299  * This implements the yp "get first" function.
300  */
301 void
302 ypfirst(SVCXPRT *transp)
303 {
304 	struct ypreq_nokey req;
305 	struct ypresp_key_val resp;
306 	char *fun = "ypfirst";
307 	DBM *fdb;
308 
309 	memset(&req, 0, sizeof (req));
310 	memset(&resp, 0, sizeof (resp));
311 
312 	if (!svc_getargs(transp,
313 				(xdrproc_t)xdr_ypreq_nokey,
314 				(char *)&req)) {
315 		svcerr_decode(transp);
316 		return;
317 	}
318 
319 	if ((fdb = ypset_current_map(req.map, req.domain,
320 					&resp.status)) != NULL &&
321 		yp_map_access(transp, &resp.status, fdb)) {
322 		ypfilter(fdb, NULL,
323 			&resp.keydat, &resp.valdat, &resp.status, FALSE);
324 	}
325 
326 	if (!svc_sendreply(transp,
327 				(xdrproc_t)xdr_ypresp_key_val,
328 				(char *)&resp)) {
329 		RESPOND_ERR;
330 	}
331 
332 	if (!svc_freeargs(transp, (xdrproc_t)xdr_ypreq_nokey,
333 				(char *)&req)) {
334 		FREE_ERR;
335 	}
336 }
337 
338 /*
339  * This implements the yp "get next" function.
340  */
341 void
342 ypnext(SVCXPRT *transp)
343 {
344 	struct ypreq_key req;
345 	struct ypresp_key_val resp;
346 	char *fun = "ypnext";
347 	DBM *fdb;
348 
349 	memset(&req, 0, sizeof (req));
350 	memset(&resp, 0, sizeof (resp));
351 
352 	if (!svc_getargs(transp, (xdrproc_t)xdr_ypreq_key, (char *)&req)) {
353 		svcerr_decode(transp);
354 		return;
355 	}
356 
357 	if ((fdb = ypset_current_map(req.map, req.domain,
358 					&resp.status)) != NULL &&
359 		yp_map_access(transp, &resp.status, fdb)) {
360 		ypfilter(fdb, &req.keydat,
361 			&resp.keydat, &resp.valdat, &resp.status, FALSE);
362 	}
363 
364 	if (!svc_sendreply(transp,
365 				(xdrproc_t)xdr_ypresp_key_val,
366 				(char *)&resp)) {
367 		RESPOND_ERR;
368 	}
369 
370 	if (!svc_freeargs(transp,
371 				(xdrproc_t)xdr_ypreq_key,
372 				(char *)&req)) {
373 		FREE_ERR;
374 	}
375 }
376 
377 /*
378  * This implements the "transfer map" function.  It takes the domain
379  * and map names and the callback information provided by the
380  * requester (yppush on some node), and execs a ypxfr process to do
381  * the actual transfer.
382  */
383 void
384 ypxfr(SVCXPRT *transp, int prog)
385 {
386 	struct ypreq_newxfr newreq;
387 	struct ypreq_xfr oldreq;
388 	struct ypresp_val resp;  /* not returned to the caller */
389 	char transid[32];
390 	char proto[32];
391 	char name[256];
392 	char *pdomain, *pmap;
393 	pid_t pid = -1;
394 	char *fun = "ypxfr";
395 	DBM *fdb;
396 
397 	if (prog == YPPROC_NEWXFR) {
398 		memset(&newreq, 0, sizeof (newreq));
399 		if (!svc_getargs(transp, (xdrproc_t)xdr_ypreq_newxfr,
400 				(char *)&newreq)) {
401 			svcerr_decode(transp);
402 			return;
403 		}
404 
405 #ifdef OPCOM_DEBUG
406 		fprintf(stderr, "newreq:\n"
407 			"\tmap_parms:\n"
408 			"\t\tdomain:    %s\n"
409 			"\t\tmap:       %s\n"
410 			"\t\tordernum:  %u\n"
411 			"\t\towner:     %s\n"
412 			"\ttransid:    %u\n"
413 			"\tproto:      %u\n"
414 			"\tname:       %s\n\n",
415 			newreq.map_parms.domain,
416 			newreq.map_parms.map,
417 			newreq.map_parms.ordernum,
418 			newreq.map_parms.owner,
419 			newreq.transid,
420 			newreq.proto,
421 			newreq.name);
422 #endif
423 		sprintf(transid, "%u", newreq.transid);
424 		sprintf(proto, "%u", newreq.proto);
425 		sprintf(name, "%s", newreq.ypxfr_owner);
426 		pdomain = newreq.ypxfr_domain;
427 		pmap = newreq.ypxfr_map;
428 	} else if (prog == YPPROC_XFR) {
429 		memset(&oldreq, 0, sizeof (oldreq));
430 		if (!svc_getargs(transp,
431 					(xdrproc_t)xdr_ypreq_xfr,
432 					(char *)&oldreq)) {
433 		    svcerr_decode(transp);
434 		    return;
435 		}
436 
437 #ifdef OPCOM_DEBUG
438 		fprintf(stderr, "oldreq:\n"
439 			"\tmap_parms:\n"
440 			"\t\tdomain:    %s\n"
441 			"\t\tmap:       %s\n"
442 			"\t\tordernum:  %u\n"
443 			"\t\towner:     %s\n"
444 			"\ttransid:    %u\n"
445 			"\tproto:      %u\n"
446 			"\tport:       %u\n\n",
447 			oldreq.map_parms.domain,
448 			oldreq.map_parms.map,
449 			oldreq.map_parms.ordernum,
450 			oldreq.map_parms.owner,
451 			oldreq.transid,
452 			oldreq.proto,
453 			oldreq.port);
454 #endif
455 
456 		sprintf(transid, "%u", oldreq.transid);
457 		sprintf(proto, "%u", oldreq.proto);
458 		sprintf(name, "%s", oldreq.ypxfr_owner);
459 		pdomain = oldreq.ypxfr_domain;
460 		pmap = oldreq.ypxfr_map;
461 	} else {
462 		VERS_ERR;
463 	}
464 
465 	/* Check that the map exists and is accessible */
466 	if ((fdb = ypset_current_map(pmap, pdomain, &resp.status)) != NULL &&
467 		yp_map_access(transp, &resp.status, fdb)) {
468 
469 		pid = vfork();
470 		if (pid == -1) {
471 			FORK_ERR;
472 		} else if (pid == 0) {
473 		    if (prog == YPPROC_NEWXFR || prog == YPPROC_XFR) {
474 #ifdef OPCOM_DEBUG
475 			fprintf(stderr,
476 				"EXECL: %s, -d, %s, -C, %s, %s, %s, %s\n",
477 				ypxfr_proc, pdomain,
478 				transid, proto, name, pmap);
479 #endif
480 			if (execl(ypxfr_proc, "ypxfr", "-d",
481 					pdomain, "-C", transid, proto,
482 					name, pmap, NULL))
483 			    EXEC_ERR;
484 		    } else {
485 			VERS_ERR;
486 		    }
487 		    _exit(1);
488 		}
489 
490 	} else {
491 		MAP_ERR;
492 	}
493 	if (!svc_sendreply(transp, xdr_void, 0)) {
494 		RESPOND_ERR;
495 	}
496 
497 	if (prog == YPPROC_NEWXFR) {
498 		if (!svc_freeargs(transp,
499 					(xdrproc_t)xdr_ypreq_newxfr,
500 					(char *)&newreq)) {
501 		    FREE_ERR;
502 		}
503 	}
504 }
505 
506 /*
507  * This implements the "get all" function.
508  */
509 void
510 ypall(SVCXPRT *transp)
511 {
512 	struct ypreq_nokey req;
513 	struct ypresp_val resp;  /* not returned to the caller */
514 	pid_t pid;
515 	char *fun = "ypall";
516 	DBM *fdb;
517 
518 	req.domain = req.map = NULL;
519 
520 	memset((char *)&req, 0, sizeof (req));
521 
522 	if (!svc_getargs(transp,
523 				(xdrproc_t)xdr_ypreq_nokey,
524 				(char *)&req)) {
525 		svcerr_decode(transp);
526 		return;
527 	}
528 
529 	pid = fork1();
530 
531 	if (pid) {
532 
533 		if (pid == -1) {
534 			FORK_ERR;
535 		}
536 
537 		if (!svc_freeargs(transp,
538 					(xdrproc_t)xdr_ypreq_nokey,
539 					(char *)&req)) {
540 			FREE_ERR;
541 		}
542 
543 		return;
544 	}
545 
546 	/*
547 	 * access control hack:  If denied then invalidate the map name.
548 	 */
549 	ypclr_current_map();
550 	if ((fdb = ypset_current_map(req.map,
551 		req.domain, &resp.status)) != NULL &&
552 		!yp_map_access(transp, &resp.status, fdb)) {
553 
554 		req.map[0] = '-';
555 	}
556 
557 	/*
558 	 * This is the child process.  The work gets done by xdrypserv_ypall/
559 	 * we must clear the "current map" first so that we do not
560 	 * share a seek pointer with the parent server.
561 	 */
562 
563 	if (!svc_sendreply(transp,
564 				(xdrproc_t)xdrypserv_ypall,
565 				(char *)&req)) {
566 		RESPOND_ERR;
567 	}
568 
569 	if (!svc_freeargs(transp,
570 				(xdrproc_t)xdr_ypreq_nokey,
571 				(char *)&req)) {
572 		FREE_ERR;
573 	}
574 
575 	/*
576 	 * In yptol mode we may start a cache update thread within a child
577 	 * process. It is thus important that child processes do not exit,
578 	 * killing any such threads, before the thread has completed.
579 	 */
580 	if (yptol_mode) {
581 		thr_join(0, NULL, NULL);
582 	}
583 
584 	exit(0);
585 }
586 
587 /*
588  * This implements the "get master name" function.
589  */
590 void
591 ypmaster(SVCXPRT *transp)
592 {
593 	struct ypreq_nokey req;
594 	struct ypresp_master resp;
595 	char *nullstring = "";
596 	char *fun = "ypmaster";
597 	DBM *fdb;
598 
599 	memset((char *)&req, 0, sizeof (req));
600 	resp.master = nullstring;
601 	resp.status = YP_TRUE;
602 
603 	if (!svc_getargs(transp,
604 				(xdrproc_t)xdr_ypreq_nokey,
605 				(char *)&req)) {
606 		svcerr_decode(transp);
607 		return;
608 	}
609 
610 	if ((fdb = ypset_current_map(req.map,
611 		req.domain, &resp.status)) != NULL &&
612 		yp_map_access(transp, &resp.status, fdb)) {
613 
614 		if (!ypget_map_master(&resp.master, fdb)) {
615 			resp.status = (unsigned)YP_BADDB;
616 		}
617 	}
618 
619 	if (!svc_sendreply(transp,
620 				(xdrproc_t)xdr_ypresp_master,
621 				(char *)&resp)) {
622 		RESPOND_ERR;
623 	}
624 
625 	if (!svc_freeargs(transp,
626 				(xdrproc_t)xdr_ypreq_nokey,
627 				(char *)&req)) {
628 		FREE_ERR;
629 	}
630 }
631 
632 /*
633  * This implements the "get order number" function.
634  */
635 void
636 yporder(SVCXPRT *transp)
637 {
638 	struct ypreq_nokey req;
639 	struct ypresp_order resp;
640 	char *fun = "yporder";
641 	DBM *fdb;
642 
643 	req.domain = req.map = NULL;
644 	resp.status  = YP_TRUE;
645 	resp.ordernum  = 0;
646 
647 	memset((char *)&req, 0, sizeof (req));
648 
649 	if (!svc_getargs(transp,
650 				(xdrproc_t)xdr_ypreq_nokey,
651 				(char *)&req)) {
652 		svcerr_decode(transp);
653 		return;
654 	}
655 
656 	resp.ordernum = 0;
657 
658 	if ((fdb = ypset_current_map(req.map,
659 					req.domain,
660 					&resp.status)) != NULL &&
661 		yp_map_access(transp, &resp.status, fdb)) {
662 
663 		if (!ypget_map_order(req.map, req.domain, &resp.ordernum)) {
664 			resp.status = (unsigned)YP_BADDB;
665 		}
666 	}
667 
668 	if (!svc_sendreply(transp,
669 				(xdrproc_t)xdr_ypresp_order,
670 				(char *)&resp)) {
671 		RESPOND_ERR;
672 	}
673 
674 	if (!svc_freeargs(transp,
675 				(xdrproc_t)xdr_ypreq_nokey,
676 				(char *)&req)) {
677 		FREE_ERR;
678 	}
679 }
680 
681 void
682 ypmaplist(SVCXPRT *transp)
683 {
684 	char domain_name[YPMAXDOMAIN + 1];
685 	char *pdomain = domain_name;
686 	char *fun = "ypmaplist";
687 	struct ypresp_maplist maplist;
688 	struct ypmaplist *tmp;
689 
690 	maplist.list = (struct ypmaplist *)NULL;
691 
692 	memset(domain_name, 0, sizeof (domain_name));
693 
694 	if (!svc_getargs(transp,
695 				(xdrproc_t)xdr_ypdomain_wrap_string,
696 				(caddr_t)&pdomain)) {
697 		svcerr_decode(transp);
698 		return;
699 	}
700 
701 	maplist.status = yplist_maps(domain_name, &maplist.list);
702 
703 	if (!svc_sendreply(transp,
704 				(xdrproc_t)xdr_ypresp_maplist,
705 				(char *)&maplist)) {
706 		RESPOND_ERR;
707 	}
708 
709 	while (maplist.list) {
710 		tmp = maplist.list->ypml_next;
711 		free((char *)maplist.list);
712 		maplist.list = tmp;
713 	}
714 }
715 
716 /*
717  * Ancillary functions used by the top-level functions within this
718  * module
719  */
720 
721 /*
722  * This returns TRUE if a given key is a yp-private symbol, otherwise
723  * FALSE
724  */
725 static bool
726 isypsym(datum *key)
727 {
728 	if ((key->dptr == NULL) ||
729 		(key->dsize < yp_prefix_sz) ||
730 		memcmp(yp_prefix, key->dptr, yp_prefix_sz) ||
731 		(!memcmp(key->dptr, "YP_MULTI_", 9))) {
732 		return (FALSE);
733 	}
734 	return (TRUE);
735 }
736 
737 /*
738  * This provides private-symbol filtration for the enumeration functions.
739  */
740 static void
741 ypfilter(DBM *fdb, datum *inkey, datum *outkey, datum *val, uint_t *status,
742 							bool_t update)
743 {
744 	datum k;
745 
746 	if (inkey) {
747 
748 		if (isypsym(inkey)) {
749 			*status = (unsigned)YP_BADARGS;
750 			return;
751 		}
752 
753 		k = dbm_do_nextkey(fdb, *inkey);
754 	} else {
755 		k = dbm_firstkey(fdb);
756 	}
757 
758 	while (k.dptr && isypsym(&k)) {
759 		k = dbm_nextkey(fdb);
760 	}
761 
762 	if (k.dptr == NULL) {
763 		*status = YP_NOMORE;
764 		return;
765 	}
766 
767 	*outkey = k;
768 
769 	/*
770 	 * In N2L mode we must call a version of dbm_fetch() that either does
771 	 * or does not check for entry updates. In non N2L mode both of these
772 	 * will end up doing a normal dbm_fetch().
773 	 */
774 	if (update)
775 		*val = shim_dbm_fetch(fdb, k);
776 	else
777 		*val = shim_dbm_fetch_noupdate(fdb, k);
778 
779 	if (val->dptr != NULL) {
780 		*status = YP_TRUE;
781 	} else {
782 		*status = (unsigned)YP_BADDB;
783 	}
784 }
785 
786 /*
787  * Serializes a stream of struct ypresp_key_val's.  This is used
788  * only by the ypserv side of the transaction.
789  */
790 static bool
791 xdrypserv_ypall(XDR *xdrs, struct ypreq_nokey *req)
792 {
793 	bool_t more = TRUE;
794 	struct ypresp_key_val resp;
795 	DBM *fdb;
796 
797 	resp.keydat.dptr = resp.valdat.dptr = (char *)NULL;
798 	resp.keydat.dsize = resp.valdat.dsize = 0;
799 
800 	if ((fdb = ypset_current_map(req->map, req->domain,
801 					&resp.status)) != NULL) {
802 		ypfilter(fdb, (datum *) NULL, &resp.keydat, &resp.valdat,
803 				&resp.status, FALSE);
804 
805 		while (resp.status == YP_TRUE) {
806 			if (!xdr_bool(xdrs, &more)) {
807 				return (FALSE);
808 			}
809 
810 			if (!xdr_ypresp_key_val(xdrs, &resp)) {
811 				return (FALSE);
812 			}
813 
814 			ypfilter(fdb, &resp.keydat, &resp.keydat, &resp.valdat,
815 					&resp.status, FALSE);
816 		}
817 	}
818 
819 	if (!xdr_bool(xdrs, &more)) {
820 		return (FALSE);
821 	}
822 
823 	if (!xdr_ypresp_key_val(xdrs, &resp)) {
824 		return (FALSE);
825 	}
826 
827 	more = FALSE;
828 
829 	if (!xdr_bool(xdrs, &more)) {
830 		return (FALSE);
831 	}
832 
833 	return (TRUE);
834 }
835 
836 /*
837  * Additions for sparc cluster support
838  */
839 
840 /*
841  * Check for special multihomed host cookie in the key.  If there,
842  * collect the addresses from the comma separated list and return
843  * the one that's nearest the client.
844  */
845 static int
846 multihomed(struct ypreq_key req, struct ypresp_val *resp,
847 		SVCXPRT *xprt, DBM *fdb)
848 {
849 	char *cp, *bp;
850 	ulong_t bestaddr, call_addr;
851 	struct netbuf *nbuf;
852 	char name[PATH_MAX];
853 	static char localbuf[_PBLKSIZ];	/* buffer for multihomed IPv6 addr */
854 
855 	if (strcmp(req.map, "hosts.byname") &&
856 			strcmp(req.map, "ipnodes.byname"))
857 		/* default status is YP_NOKEY */
858 		return (0);
859 
860 	if (strncmp(req.keydat.dptr, "YP_MULTI_", 9)) {
861 		datum tmpname;
862 
863 		strncpy(name, "YP_MULTI_", 9);
864 		strncpy(name + 9, req.keydat.dptr, req.keydat.dsize);
865 		tmpname.dsize = req.keydat.dsize + 9;
866 		tmpname.dptr = name;
867 		resp->valdat = dbm_fetch(fdb, tmpname);
868 	} else {
869 		/*
870 		 * Return whole line (for debugging) if YP_MULTI_hostnam
871 		 * is specified.
872 		 */
873 		resp->valdat = dbm_fetch(fdb, req.keydat);
874 		if (resp->valdat.dptr != NULL)
875 			return (1);
876 	}
877 
878 	if (resp->valdat.dptr == NULL)
879 		return (0);
880 
881 	strncpy(name, req.keydat.dptr, req.keydat.dsize);
882 	name[req.keydat.dsize] = NULL;
883 
884 	if (strcmp(req.map, "ipnodes.byname") == 0) {
885 		/*
886 		 * This section handles multihomed IPv6 addresses.
887 		 * It returns all the IPv6 addresses one per line and only
888 		 * the requested hostname is returned.  NO aliases will be
889 		 * returned.  This is done exactly the same way DNS forwarding
890 		 * daemon handles multihomed hosts.
891 		 * New IPv6 enabled clients should be able to handle this
892 		 * information returned.  The sorting is also the client's
893 		 * responsibility.
894 		 */
895 
896 		char *buf, *endbuf;
897 
898 		if ((buf = strdup(resp->valdat.dptr)) == NULL) /* no memory */
899 			return (0);
900 		if ((bp = strtok(buf, " \t")) == NULL) { /* no address field */
901 			free(buf);
902 			return (0);
903 		}
904 		if ((cp = strtok(NULL, "")) == NULL) { /* no host field */
905 			free(buf);
906 			return (0);
907 		}
908 		if ((cp = strtok(bp, ",")) != NULL) { /* multihomed host */
909 			int bsize;
910 
911 			localbuf[0] = '\0';
912 			bsize = sizeof (localbuf);
913 			endbuf = localbuf;
914 
915 			while (cp) {
916 				if ((strlen(cp) + strlen(name)) >= bsize) {
917 					/* out of range */
918 					break;
919 				}
920 				sprintf(endbuf, "%s %s\n", cp, name);
921 				cp = strtok(NULL, ",");
922 				endbuf = &endbuf[strlen(endbuf)];
923 				bsize = &localbuf[sizeof (localbuf)] - endbuf;
924 			}
925 			resp->valdat.dptr = localbuf;
926 			resp->valdat.dsize = strlen(localbuf);
927 		}
928 
929 		free(buf);
930 		/* remove trailing newline */
931 		if (resp->valdat.dsize &&
932 			resp->valdat.dptr[resp->valdat.dsize-1] == '\n') {
933 			resp->valdat.dptr[resp->valdat.dsize-1] = '\0';
934 			resp->valdat.dsize -= 1;
935 		}
936 
937 		resp->status = YP_TRUE;
938 		return (1);
939 	}
940 	nbuf = svc_getrpccaller(xprt);
941 	/*
942 	 * OK, now I have a netbuf structure which I'm supposed to
943 	 * treat as opaque...  I hate transport independance!
944 	 * So, we're just gonna doit wrong...  By wrong I mean that
945 	 * we assume that the buf part of the netbuf structure is going
946 	 * to be a sockaddr_in.  We'll then check the assumed family
947 	 * member and hope that we find AF_INET in there...  if not
948 	 * then we can't continue.
949 	 */
950 	if (((struct sockaddr_in *)(nbuf->buf))->sin_family != AF_INET)
951 		return (0);
952 
953 	call_addr = ((struct sockaddr_in *)(nbuf->buf))->sin_addr.s_addr;
954 
955 	cp = resp->valdat.dptr;
956 	if ((bp = strtok(cp, " \t")) == NULL) /* no address field */
957 		return (0);
958 	if ((cp = strtok(NULL, "")) == NULL)  /* no host field */
959 		return (0);
960 	bp = strtok(bp, ",");
961 
962 	bestaddr = inet_addr(bp);
963 	while (cp = strtok(NULL, ",")) {
964 		ulong_t taddr;
965 
966 		taddr = inet_addr(cp);
967 		if (ntohl(call_addr ^ taddr) < ntohl(call_addr ^ bestaddr))
968 			bestaddr = taddr;
969 	}
970 	cp = resp->valdat.dptr;
971 	sprintf(cp, "%s %s", inet_ntoa(*(struct in_addr *)&bestaddr), name);
972 	resp->valdat.dsize = strlen(cp);
973 
974 	resp->status = YP_TRUE;
975 
976 	return (1);
977 }
978 
979 /* V1 dispatch routines */
980 void
981 ypoldmatch(SVCXPRT *transp, struct svc_req *rqstp)
982 {
983 	bool dbmop_ok = TRUE;
984 	struct yprequest req;
985 	struct ypreq_key nrq;
986 	struct ypresponse resp;
987 	char *fun = "ypoldmatch";
988 	DBM *fdb;
989 
990 	memset((void *) &req, 0, sizeof (req));
991 	memset((void *) &resp, 0, sizeof (resp));
992 
993 	if (!svc_getargs(transp,
994 				(xdrproc_t)_xdr_yprequest,
995 				(caddr_t)&req)) {
996 		svcerr_decode(transp);
997 		return;
998 	}
999 
1000 	if (req.yp_reqtype != YPMATCH_REQTYPE) {
1001 		resp.ypmatch_resp_status = (unsigned)YP_BADARGS;
1002 		dbmop_ok = FALSE;
1003 	}
1004 
1005 	if (dbmop_ok &&
1006 		(((fdb = ypset_current_map(req.ypmatch_req_map,
1007 						req.ypmatch_req_domain,
1008 						&resp.ypmatch_resp_status))
1009 						!= NULL) &&
1010 						yp_map_access(transp,
1011 						&resp.ypmatch_resp_status,
1012 						fdb))) {
1013 
1014 		/* Check with the DBM database */
1015 		resp.ypmatch_resp_valdat = dbm_fetch(fdb,
1016 						req.ypmatch_req_keydat);
1017 
1018 		if (resp.ypmatch_resp_valptr != NULL) {
1019 			resp.ypmatch_resp_status = YP_TRUE;
1020 			if (!silent)
1021 				printf("%s: dbm: %s\n",
1022 					fun, resp.ypmatch_resp_valptr);
1023 			goto send_oldreply;
1024 		}
1025 
1026 		/*
1027 		 * If we're being asked to match YP_SECURE or YP_INTERDOMAIN
1028 		 * and we haven't found it in the dbm file, then we don't
1029 		 * really want to waste any more time.  Specifically, we don't
1030 		 * want to ask DNS
1031 		 */
1032 		if (req.ypmatch_req_keysize == 0 ||
1033 		    req.ypmatch_req_keyptr == NULL ||
1034 		    req.ypmatch_req_keyptr[0] == '\0' ||
1035 		    strncmp(req.ypmatch_req_keyptr, "YP_SECURE", 9) == 0 ||
1036 		    strncmp(req.ypmatch_req_keyptr, "YP_INTERDOMAIN", 14) == 0)
1037 
1038 		    goto send_oldreply;
1039 
1040 		/* Let's try the YP_MULTI_ hack... */
1041 #ifdef MINUS_C_OPTION
1042 		if (multiflag == TRUE && omultihomed(req, &resp, transp, fdb))
1043 			goto send_oldreply;
1044 #else
1045 		if (omultihomed(req, &resp, transp, fdb))
1046 			goto send_oldreply;
1047 #endif
1048 
1049 		/* Let's try DNS */
1050 		if (!dnsforward) {
1051 			USE_YP_INTERDOMAIN
1052 			datum idkey, idval;
1053 			idkey.dptr = yp_interdomain;
1054 			idkey.dsize = yp_interdomain_sz;
1055 			idval = dbm_fetch(fdb, idkey);
1056 			if (idval.dptr)
1057 				dnsforward = TRUE;
1058 		}
1059 
1060 		if (dnsforward) {
1061 		    if (!resolv_pid)
1062 			setup_resolv(&dnsforward, &resolv_pid, &resolv_client,
1063 					resolv_tp, 0);
1064 
1065 		    if (req.yp_reqtype == YPREQ_KEY) {
1066 			nrq = req.yp_reqbody.yp_req_keytype;
1067 
1068 			resolv_req(&dnsforward, &resolv_client, &resolv_pid,
1069 					resolv_tp, rqstp->rq_xprt,
1070 					&nrq, nrq.map);
1071 		    }
1072 		    return;
1073 		}
1074 	}
1075 
1076 	send_oldreply:
1077 
1078 	if (!svc_sendreply(transp,
1079 				(xdrproc_t)_xdr_ypresponse,
1080 				(caddr_t)&resp)) {
1081 		RESPOND_ERR;
1082 	}
1083 
1084 	if (!svc_freeargs(transp,
1085 				(xdrproc_t)_xdr_yprequest,
1086 				(char *)&req)) {
1087 		FREE_ERR;
1088 	}
1089 }
1090 
1091 void
1092 ypoldfirst(SVCXPRT *transp)
1093 {
1094 	bool dbmop_ok = TRUE;
1095 	struct yprequest req;
1096 	struct ypresponse resp;
1097 	char *fun = "ypoldfirst";
1098 	DBM *fdb;
1099 
1100 	memset((void *) &req, 0, sizeof (req));
1101 	memset((void *) &resp, 0, sizeof (resp));
1102 
1103 	if (!svc_getargs(transp,
1104 				(xdrproc_t)_xdr_yprequest,
1105 				(caddr_t)&req)) {
1106 		svcerr_decode(transp);
1107 		return;
1108 	}
1109 
1110 	if (req.yp_reqtype != YPFIRST_REQTYPE) {
1111 		resp.ypfirst_resp_status = (unsigned)YP_BADARGS;
1112 		dbmop_ok = FALSE;
1113 	}
1114 
1115 	if (dbmop_ok &&
1116 		((fdb = ypset_current_map(req.ypfirst_req_map,
1117 						req.ypfirst_req_domain,
1118 						&resp.ypfirst_resp_status))
1119 						!= NULL) &&
1120 						yp_map_access(transp,
1121 						&resp.ypfirst_resp_status,
1122 						fdb)) {
1123 
1124 		resp.ypfirst_resp_keydat = dbm_firstkey(fdb);
1125 
1126 		if (resp.ypfirst_resp_keyptr != NULL) {
1127 			resp.ypfirst_resp_valdat =
1128 				dbm_fetch(fdb, resp.ypfirst_resp_keydat);
1129 
1130 			if (resp.ypfirst_resp_valptr != NULL) {
1131 				resp.ypfirst_resp_status = YP_TRUE;
1132 			} else {
1133 				resp.ypfirst_resp_status = (unsigned)YP_BADDB;
1134 			}
1135 		} else {
1136 			resp.ypfirst_resp_status = (unsigned)YP_NOKEY;
1137 		}
1138 	}
1139 
1140 	resp.yp_resptype = YPFIRST_RESPTYPE;
1141 
1142 	if (!svc_sendreply(transp,
1143 				(xdrproc_t)_xdr_ypresponse,
1144 				(caddr_t)&resp)) {
1145 		RESPOND_ERR;
1146 	}
1147 
1148 	if (!svc_freeargs(transp,
1149 				(xdrproc_t)_xdr_yprequest,
1150 				(caddr_t)&req)) {
1151 		FREE_ERR;
1152 	}
1153 }
1154 
1155 void
1156 ypoldnext(SVCXPRT *transp)
1157 {
1158 	bool dbmop_ok = TRUE;
1159 	struct yprequest req;
1160 	struct ypresponse resp;
1161 	char *fun = "ypoldnext";
1162 	DBM *fdb;
1163 
1164 	memset((void *) &req, 0, sizeof (req));
1165 	memset((void *) &resp, 0, sizeof (resp));
1166 
1167 	if (!svc_getargs(transp,
1168 				(xdrproc_t)_xdr_yprequest,
1169 				(caddr_t)&req)) {
1170 		svcerr_decode(transp);
1171 		return;
1172 	}
1173 
1174 	if (req.yp_reqtype != YPNEXT_REQTYPE) {
1175 		resp.ypnext_resp_status = (unsigned)YP_BADARGS;
1176 		dbmop_ok = FALSE;
1177 	}
1178 
1179 	if (dbmop_ok &&
1180 		((fdb = ypset_current_map(req.ypnext_req_map,
1181 					req.ypnext_req_domain,
1182 					&resp.ypnext_resp_status)) != NULL &&
1183 		yp_map_access(transp, &resp.ypnext_resp_status, fdb))) {
1184 
1185 		resp.ypnext_resp_keydat = dbm_nextkey(fdb);
1186 
1187 		if (resp.ypnext_resp_keyptr != NULL) {
1188 			resp.ypnext_resp_valdat =
1189 			dbm_fetch(fdb, resp.ypnext_resp_keydat);
1190 
1191 			if (resp.ypnext_resp_valptr != NULL) {
1192 				resp.ypnext_resp_status = YP_TRUE;
1193 			} else {
1194 				resp.ypnext_resp_status = (unsigned)YP_BADDB;
1195 			}
1196 		} else {
1197 			resp.ypnext_resp_status = (unsigned)YP_NOMORE;
1198 		}
1199 	}
1200 
1201 	resp.yp_resptype = YPNEXT_RESPTYPE;
1202 
1203 	if (!svc_sendreply(transp,
1204 				(xdrproc_t)_xdr_ypresponse,
1205 				(caddr_t)&resp)) {
1206 		RESPOND_ERR;
1207 	}
1208 
1209 	if (!svc_freeargs(transp,
1210 				(xdrproc_t)_xdr_yprequest,
1211 				(caddr_t)&req)) {
1212 		FREE_ERR;
1213 	}
1214 }
1215 
1216 /*
1217  * This retrieves the order number and master peer name from the map.
1218  * The conditions for the various message fields are: domain is filled
1219  * in iff the domain exists.  map is filled in iff the map exists.
1220  * order number is filled in iff it's in the map.  owner is filled in
1221  * iff the master peer is in the map.
1222  */
1223 void
1224 ypoldpoll(SVCXPRT *transp)
1225 {
1226 	struct yprequest req;
1227 	struct ypresponse resp;
1228 	char *map = "";
1229 	char *domain = "";
1230 	char *owner = "";
1231 	uint_t error;
1232 	char *fun = "ypoldpoll";
1233 	DBM *fdb;
1234 
1235 	memset((void *) &req, 0, sizeof (req));
1236 	memset((void *) &resp, 0, sizeof (resp));
1237 
1238 	if (!svc_getargs(transp,
1239 				(xdrproc_t)_xdr_yprequest,
1240 				(caddr_t)&req)) {
1241 		svcerr_decode(transp);
1242 		return;
1243 	}
1244 
1245 	if (req.yp_reqtype == YPPOLL_REQTYPE) {
1246 		if (strcmp(req.yppoll_req_domain, "yp_private") == 0 ||
1247 			strcmp(req.yppoll_req_map, "ypdomains") == 0 ||
1248 			strcmp(req.yppoll_req_map, "ypmaps") == 0) {
1249 
1250 			/*
1251 			 * Backward comatibility for 2.0 NIS servers
1252 			 */
1253 			domain = req.yppoll_req_domain;
1254 			map = req.yppoll_req_map;
1255 		} else if ((fdb = ypset_current_map(req.yppoll_req_map,
1256 				req.yppoll_req_domain,
1257 				&error)) != NULL) {
1258 			domain = req.yppoll_req_domain;
1259 			map = req.yppoll_req_map;
1260 			ypget_map_order(map, domain,
1261 					&resp.yppoll_resp_ordernum);
1262 			ypget_map_master(&owner, fdb);
1263 		} else {
1264 			switch ((int)error) {
1265 			case YP_BADDB:
1266 				map = req.yppoll_req_map;
1267 				/* Fall through to set the domain too. */
1268 
1269 			case YP_NOMAP:
1270 				domain = req.yppoll_req_domain;
1271 				break;
1272 			}
1273 		}
1274 	}
1275 
1276 	resp.yp_resptype = YPPOLL_RESPTYPE;
1277 	resp.yppoll_resp_domain = domain;
1278 	resp.yppoll_resp_map = map;
1279 	resp.yppoll_resp_owner = owner;
1280 
1281 	if (!svc_sendreply(transp,
1282 				(xdrproc_t)_xdr_ypresponse,
1283 				(caddr_t)&resp)) {
1284 		RESPOND_ERR;
1285 	}
1286 
1287 	if (!svc_freeargs(transp,
1288 				(xdrproc_t)_xdr_yprequest,
1289 				(caddr_t)&req)) {
1290 		FREE_ERR;
1291 	}
1292 }
1293 
1294 void
1295 ypoldpush(SVCXPRT *transp)
1296 {
1297 	struct yprequest req;
1298 	struct ypresp_val resp;
1299 	pid_t pid = -1;
1300 	char *fun = "ypoldpush";
1301 	DBM *fdb;
1302 
1303 	memset((void *) &req, 0, sizeof (req));
1304 
1305 	if (!svc_getargs(transp,
1306 				(xdrproc_t)_xdr_yprequest,
1307 				(caddr_t)&req)) {
1308 		svcerr_decode(transp);
1309 		return;
1310 	}
1311 
1312 	if (((fdb = ypset_current_map(req.yppush_req_map,
1313 					req.yppush_req_domain,
1314 					&resp.status)) != NULL) &&
1315 		(yp_map_access(transp, &resp.status, fdb))) {
1316 
1317 		pid = vfork();
1318 	}
1319 
1320 	if (pid == -1) {
1321 		FORK_ERR;
1322 	} else if (pid == 0) {
1323 		ypclr_current_map();
1324 
1325 		if (execl(yppush_proc, "yppush", "-d", req.yppush_req_domain,
1326 				req.yppush_req_map, NULL)) {
1327 			EXEC_ERR;
1328 		}
1329 		_exit(1);
1330 	}
1331 
1332 	if (!svc_sendreply(transp,
1333 				(xdrproc_t)xdr_void,
1334 				(caddr_t)NULL)) {
1335 		RESPOND_ERR;
1336 	}
1337 
1338 	if (!svc_freeargs(transp,
1339 				(xdrproc_t)_xdr_yprequest,
1340 				(caddr_t)&req)) {
1341 		FREE_ERR;
1342 	}
1343 }
1344 
1345 void
1346 ypoldpull(SVCXPRT *transp)
1347 {
1348 	struct yprequest req;
1349 	struct ypresp_val resp;
1350 	pid_t pid = -1;
1351 	char *fun = "ypoldpull";
1352 	DBM *fdb;
1353 
1354 	memset((void *) &req, 0, sizeof (req));
1355 
1356 	if (!svc_getargs(transp,
1357 				(xdrproc_t)_xdr_yprequest,
1358 				(caddr_t)&req)) {
1359 		svcerr_decode(transp);
1360 		return;
1361 	}
1362 
1363 	if (req.yp_reqtype == YPPULL_REQTYPE) {
1364 
1365 		if (((fdb = ypset_current_map(req.yppull_req_map,
1366 						req.yppull_req_domain,
1367 						&resp.status)) == NULL) ||
1368 			(yp_map_access(transp, &resp.status, fdb))) {
1369 			pid = vfork();
1370 		}
1371 
1372 		if (pid == -1) {
1373 			FORK_ERR;
1374 		} else if (pid == 0) {
1375 			ypclr_current_map();
1376 
1377 			if (execl(ypxfr_proc, "ypxfr", "-d",
1378 					req.yppull_req_domain,
1379 					req.yppull_req_map, NULL)) {
1380 				EXEC_ERR;
1381 			}
1382 			_exit(1);
1383 		}
1384 	}
1385 
1386 	if (!svc_freeargs(transp,
1387 				(xdrproc_t)_xdr_yprequest,
1388 				(caddr_t)&req)) {
1389 		FREE_ERR;
1390 	}
1391 }
1392 
1393 void
1394 ypoldget(SVCXPRT *transp)
1395 {
1396 	struct yprequest req;
1397 	struct ypresp_val resp;
1398 	pid_t pid = -1;
1399 	char *fun = "ypoldget";
1400 	DBM *fdb;
1401 
1402 	memset((void *) &req, 0, sizeof (req));
1403 
1404 	if (!svc_getargs(transp,
1405 				(xdrproc_t)_xdr_yprequest,
1406 				(caddr_t)&req)) {
1407 		svcerr_decode(transp);
1408 		return;
1409 	}
1410 
1411 	if (!svc_sendreply(transp, xdr_void, 0)) {
1412 		RESPOND_ERR;
1413 	}
1414 
1415 	if (req.yp_reqtype == YPGET_REQTYPE) {
1416 
1417 		if (((fdb = ypset_current_map(req.ypget_req_map,
1418 						req.ypget_req_domain,
1419 						&resp.status)) == NULL) ||
1420 			(yp_map_access(transp, &resp.status, fdb))) {
1421 
1422 			pid = vfork();
1423 		}
1424 
1425 		if (pid == -1) {
1426 			FORK_ERR;
1427 		} else if (pid == 0) {
1428 
1429 			ypclr_current_map();
1430 
1431 			if (execl(ypxfr_proc, "ypxfr", "-d",
1432 					req.ypget_req_domain, "-h",
1433 					req.ypget_req_owner,
1434 					req.ypget_req_map, NULL)) {
1435 
1436 				EXEC_ERR;
1437 			}
1438 			_exit(1);
1439 		}
1440 	}
1441 
1442 	if (!svc_freeargs(transp,
1443 				(xdrproc_t)_xdr_yprequest,
1444 				(caddr_t)&req)) {
1445 		RESPOND_ERR;
1446 	}
1447 }
1448 
1449 static int
1450 omultihomed(struct yprequest req,
1451 	    struct ypresponse *resp, SVCXPRT *xprt, DBM *fdb)
1452 {
1453 	char *cp, *bp;
1454 	char name[PATH_MAX];
1455 	struct netbuf *nbuf;
1456 	ulong_t bestaddr, call_addr;
1457 
1458 	if (strcmp(req.ypmatch_req_map, "hosts.byname"))
1459 		return (0);
1460 
1461 	if (strncmp(req.ypmatch_req_keyptr, "YP_MULTI_", 9)) {
1462 		datum tmpname;
1463 
1464 		strncpy(name, "YP_MULTI_", 9);
1465 		strncpy(name + 9, req.ypmatch_req_keyptr,
1466 			req.ypmatch_req_keysize);
1467 		tmpname.dsize = req.ypmatch_req_keysize + 9;
1468 		tmpname.dptr = name;
1469 		resp->ypmatch_resp_valdat = dbm_fetch(fdb, tmpname);
1470 	} else {
1471 		resp->ypmatch_resp_valdat =
1472 			dbm_fetch(fdb, req.ypmatch_req_keydat);
1473 		if (resp->ypmatch_resp_valptr != NULL)
1474 			return (1);
1475 	}
1476 
1477 	if (resp->ypmatch_resp_valptr == NULL)
1478 		return (0);
1479 
1480 	strncpy(name, req.ypmatch_req_keyptr, req.ypmatch_req_keysize);
1481 	name[req.ypmatch_req_keysize] = NULL;
1482 
1483 	nbuf = svc_getrpccaller(xprt);
1484 
1485 	/*
1486 	 * OK, now I have a netbuf structure which I'm supposed to treat
1487 	 * as opaque...  I hate transport independance!  So, we're just
1488 	 * gonna doit wrong...  By wrong I mean that we assume that the
1489 	 * buf part of the netbuf structure is going to be a sockaddr_in.
1490 	 * We'll then check the assumed family member and hope that we
1491 	 * find AF_INET in there...  if not then we can't continue.
1492 	 */
1493 	if (((struct sockaddr_in *)(nbuf->buf))->sin_family != AF_INET)
1494 		return (0);
1495 
1496 	call_addr = ((struct sockaddr_in *)(nbuf->buf))->sin_addr.s_addr;
1497 
1498 	cp = resp->ypmatch_resp_valptr;
1499 	if ((bp = strtok(cp, "\t")) == NULL)	/* No address field */
1500 		return (0);
1501 	if ((cp = strtok(NULL, "")) == NULL)	/* No host field */
1502 		return (0);
1503 	bp = strtok(bp, ",");
1504 
1505 	bestaddr = inet_addr(bp);
1506 	while (cp = strtok(NULL, ",")) {
1507 		ulong_t taddr;
1508 
1509 		taddr = inet_addr(cp);
1510 		if (ntohl(call_addr ^ taddr) < ntohl(call_addr ^ bestaddr))
1511 			bestaddr = taddr;
1512 	}
1513 
1514 	cp = resp->ypmatch_resp_valptr;
1515 	sprintf(cp, "%s %s", inet_ntoa(*(struct in_addr *)&bestaddr), name);
1516 	resp->ypmatch_resp_valsize = strlen(cp);
1517 
1518 	resp->ypmatch_resp_status = YP_TRUE;
1519 
1520 	return (1);
1521 }
1522