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