/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)ypserv_proc.c	1.21	99/03/21 SMI"

/*
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	PROPRIETARY NOTICE (Combined)
 *
 * This source code is unpublished proprietary information
 * constituting, or derived under license from AT&T's UNIX(r) System V.
 * In addition, portions of such source code were derived from Berkeley
 * 4.3 BSD under license from the Regents of the University of
 * California.
 *
 *
 *
 *	Copyright Notice
 *
 * Notice of copyright on this source code product does not indicate
 *  publication.
 *
 *	(c) 1986,1987,1988,1989,1990  Sun Microsystems, Inc
 *	(c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
 *          All rights reserved.
 */

/*
 *
 * This contains YP server code which supplies the set of functions
 * requested using rpc.   The top level functions in this module
 * are those which have symbols of the form YPPROC_xxxx defined in
 * yp_prot.h, and symbols of the form YPOLDPROC_xxxx defined in ypsym.h.
 * The latter exist to provide compatibility to the old version of the yp
 * protocol/server, and may emulate the behaviour of the previous software
 * by invoking some other program.
 *
 * This module also contains functions which are used by (and only by) the
 * top-level functions here.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <limits.h>
#include <sys/systeminfo.h>
#include <rpc/rpc.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include "ypsym.h"
#include "ypdefs.h"
#include <ctype.h>

USE_YP_PREFIX
USE_YP_SECURE
USE_YP_INTERDOMAIN

#ifndef	YPXFR_PROC
#define	YPXFR_PROC "/usr/lib/netsvc/yp/ypxfr"
#endif
static char ypxfr_proc[] = YPXFR_PROC;
#ifndef	YPPUSH_PROC
#define	YPPUSH_PROC "/usr/lib/netsvc/yp/yppush"
#endif
static char yppush_proc[] = YPPUSH_PROC;
struct yppriv_sym {
    char *sym;
    unsigned len;
};
static	char err_fork[] = "ypserv:  %s fork failure.\n";
#define	FORK_ERR logprintf(err_fork, fun)
static char err_execl[] = "ypserv:  %s execl failure.\n";
#define	EXEC_ERR logprintf(err_execl, fun)
static char err_respond[] = "ypserv: %s can't respond to rpc request.\n";
#define	RESPOND_ERR logprintf(err_respond, fun)
static char err_free[] = "ypserv: %s can't free args.\n";
#define	FREE_ERR logprintf(err_free, fun)
static char err_map[] = "ypserv: %s no such map or access denied.\n";
#define	MAP_ERR logprintf(err_map, fun)
static char err_vers[] = "ypserv: %s version not supported.\n";
#define	VERS_ERR logprintf(err_vers, fun)

static void ypfilter(DBM *fdb, datum *inkey, datum *outkey, datum *val,
			uint_t *status);
static bool isypsym(datum *key);
static bool xdrypserv_ypall(XDR *xdrs, struct ypreq_nokey *req);
static int multihomed(struct ypreq_key req, struct ypresp_val *resp,
			SVCXPRT *xprt, DBM *fdb);
static int omultihomed(struct yprequest req, struct ypresponse *resp,
			SVCXPRT *xprt, DBM *fdb);


/* For DNS forwarding */
extern bool dnsforward;
extern int resolv_pid;
extern CLIENT *resolv_client;
extern char *resolv_tp;

/*
 * This determines whether or not a passed domain is served by this
 * server, and returns a boolean.  Used by both old and new protocol
 * versions.
 */
void
ypdomain(SVCXPRT *transp, bool always_respond)
{
	char domain_name[YPMAXDOMAIN + 1];
	char *pdomain_name = domain_name;
	bool isserved;
	char *fun = "ypdomain";

	memset(domain_name, 0, sizeof (domain_name));

	if (!svc_getargs(transp, (xdrproc_t) xdr_ypdomain_wrap_string,
				(caddr_t) &pdomain_name)) {
		svcerr_decode(transp);
		return;
	}

	isserved = ypcheck_domain(domain_name);

	if (isserved || always_respond) {

		if (!svc_sendreply(transp, xdr_bool, (char *) &isserved)) {
		    RESPOND_ERR;
		}
		if (!isserved)
			logprintf("Domain %s not supported\n",
					domain_name);

	} else {
		/*
		 * This case is the one in which the domain is not
		 * supported, and in which we are not to respond in the
		 * unsupported case.  We are going to make an error happen
		 * to allow the portmapper to end his wait without the
		 * normal timeout period.  The assumption here is that
		 * the only process in the world which is using the function
		 * in its no-answer-if-nack form is the portmapper, which is
		 * doing the krock for pseudo-broadcast.  If some poor fool
		 * calls this function as a single-cast message, the nack
		 * case will look like an incomprehensible error.  Sigh...
		 * (The traditional Unix disclaimer)
		 */

		svcerr_decode(transp);
		logprintf("Domain %s not supported (broadcast)\n",
				domain_name);
	}
}

/*
 * This implements the yp "match" function.
 */
void
ypmatch(SVCXPRT *transp, struct svc_req *rqstp)
{
	struct ypreq_key req;
	struct ypresp_val resp;
	char *fun = "ypmatch";
	DBM *fdb;

	memset(&req, 0, sizeof (req));
	memset(&resp, 0, sizeof (resp));
	resp.status = (unsigned)YP_NOKEY;

	if (!svc_getargs(transp, (xdrproc_t)xdr_ypreq_key, (char *)&req)) {
		svcerr_decode(transp);
		return;
	}

	/*
	 * sanity check the map name and to a DBM lookup
	 * also perform an access check...
	 */
	if ((fdb = ypset_current_map(req.map, req.domain,
					&resp.status)) != NULL &&
		yp_map_access(transp, &resp.status, fdb)) {

		/* Check with the DBM database */
		resp.valdat = dbm_fetch(fdb, req.keydat);
		if (resp.valdat.dptr != NULL) {
			resp.status = YP_TRUE;
			if (!silent)
				printf("%s: dbm: %40.40s\n",
					fun, resp.valdat.dptr);
			goto send_reply;
		}

		/*
		 * If we're being asked to match YP_SECURE or YP_INTERDOMAIN
		 * and we haven't found it in the dbm file, then we don't
		 * really want to waste any more time.  Specifically, we don't
		 * want to ask DNS
		 */
		if (req.keydat.dsize == 0 ||
		    req.keydat.dptr == NULL ||
		    req.keydat.dptr[0] == '\0' ||
	strncmp(req.keydat.dptr, yp_secure, req.keydat.dsize) == 0 ||
	strncmp(req.keydat.dptr, yp_interdomain, req.keydat.dsize) == 0) {
			goto send_reply;
		}

		/* Let's try the YP_MULTI_ hack... */
#ifdef MINUS_C_OPTION
		if (multiflag == TRUE && multihomed(req, &resp, transp, fdb))
			goto send_reply;
#else
		if (multihomed(req, &resp, transp, fdb))
			goto send_reply;
#endif

		/* Let's try DNS */
		if (!dnsforward) {
			datum idkey, idval;
			idkey.dptr = yp_interdomain;
			idkey.dsize = yp_interdomain_sz;
			idval = dbm_fetch(fdb, idkey);
			if (idval.dptr)
				dnsforward = TRUE;
		}

		if (dnsforward) {
			if (!resolv_pid)
				setup_resolv(&dnsforward, &resolv_pid,
						&resolv_client, resolv_tp, 0);

			if (resolv_req(&dnsforward, &resolv_client,
						&resolv_pid, resolv_tp,
						rqstp->rq_xprt, &req,
						req.map) == TRUE)
				goto free_args;
		}
	}
	send_reply:

	if (!svc_sendreply(transp, (xdrproc_t) xdr_ypresp_val,
				(caddr_t) &resp)) {
		RESPOND_ERR;
	}

	free_args:

	if (!svc_freeargs(transp, (xdrproc_t) xdr_ypreq_key,
				(char *) &req)) {
		FREE_ERR;
	}
}


/*
 * This implements the yp "get first" function.
 */
void
ypfirst(SVCXPRT *transp)
{
	struct ypreq_nokey req;
	struct ypresp_key_val resp;
	char *fun = "ypfirst";
	DBM *fdb;

	memset(&req, 0, sizeof (req));
	memset(&resp, 0, sizeof (resp));

	if (!svc_getargs(transp,
				(xdrproc_t) xdr_ypreq_nokey,
				(char *) &req)) {
		svcerr_decode(transp);
		return;
	}

	if ((fdb = ypset_current_map(req.map, req.domain,
					&resp.status)) != NULL &&
		yp_map_access(transp, &resp.status, fdb)) {
		ypfilter(fdb, NULL,
				&resp.keydat, &resp.valdat, &resp.status);
	}

	if (!svc_sendreply(transp,
				(xdrproc_t) xdr_ypresp_key_val,
				(char *) &resp)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp, (xdrproc_t) xdr_ypreq_nokey,
				(char *) &req)) {
		FREE_ERR;
	}
}

/*
 * This implements the yp "get next" function.
 */
void
ypnext(SVCXPRT *transp)
{
	struct ypreq_key req;
	struct ypresp_key_val resp;
	char *fun = "ypnext";
	DBM *fdb;

	memset(&req, 0, sizeof (req));
	memset(&resp, 0, sizeof (resp));

	if (!svc_getargs(transp, (xdrproc_t) xdr_ypreq_key, (char *) &req)) {
		svcerr_decode(transp);
		return;
	}

	if ((fdb = ypset_current_map(req.map, req.domain,
					&resp.status)) != NULL &&
		yp_map_access(transp, &resp.status, fdb)) {
		ypfilter(fdb, &req.keydat,
				&resp.keydat, &resp.valdat, &resp.status);
	}

	if (!svc_sendreply(transp,
				(xdrproc_t) xdr_ypresp_key_val,
				(char *) &resp)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) xdr_ypreq_key,
				(char *) &req)) {
		FREE_ERR;
	}
}

/*
 * This implements the "transfer map" function.  It takes the domain
 * and map names and the callback information provided by the
 * requester (yppush on some node), and execs a ypxfr process to do
 * the actual transfer.
 */
void
ypxfr(SVCXPRT *transp, int prog)
{
	struct ypreq_newxfr newreq;
	struct ypreq_xfr oldreq;
	struct ypresp_val resp;  /* not returned to the caller */
	char transid[32];
	char proto[32];
	char name[256];
	char *pdomain, *pmap;
	pid_t pid = -1;
	char *fun = "ypxfr";
	DBM *fdb;

	if (prog == YPPROC_NEWXFR) {
		memset(&newreq, 0, sizeof (newreq));
		if (!svc_getargs(transp, (xdrproc_t) xdr_ypreq_newxfr,
				(char *) &newreq)) {
			svcerr_decode(transp);
			return;
		}

#ifdef OPCOM_DEBUG
		fprintf(stderr, "newreq:\n"
			"\tmap_parms:\n"
			"\t\tdomain:    %s\n"
			"\t\tmap:       %s\n"
			"\t\tordernum:  %u\n"
			"\t\towner:     %s\n"
			"\ttransid:    %u\n"
			"\tproto:      %u\n"
			"\tname:       %s\n\n",
			newreq.map_parms.domain,
			newreq.map_parms.map,
			newreq.map_parms.ordernum,
			newreq.map_parms.owner,
			newreq.transid,
			newreq.proto,
			newreq.name);
#endif
		sprintf(transid, "%u", newreq.transid);
		sprintf(proto, "%u", newreq.proto);
		sprintf(name, "%s", newreq.ypxfr_owner);
		pdomain = newreq.ypxfr_domain;
		pmap = newreq.ypxfr_map;
	} else if (prog == YPPROC_XFR) {
		memset(&oldreq, 0, sizeof (oldreq));
		if (!svc_getargs(transp,
					(xdrproc_t) xdr_ypreq_xfr,
					(char *) &oldreq)) {
		    svcerr_decode(transp);
		    return;
		}

#ifdef OPCOM_DEBUG
		fprintf(stderr, "oldreq:\n"
			"\tmap_parms:\n"
			"\t\tdomain:    %s\n"
			"\t\tmap:       %s\n"
			"\t\tordernum:  %u\n"
			"\t\towner:     %s\n"
			"\ttransid:    %u\n"
			"\tproto:      %u\n"
			"\tport:       %u\n\n",
			oldreq.map_parms.domain,
			oldreq.map_parms.map,
			oldreq.map_parms.ordernum,
			oldreq.map_parms.owner,
			oldreq.transid,
			oldreq.proto,
			oldreq.port);
#endif

		sprintf(transid, "%u", oldreq.transid);
		sprintf(proto, "%u", oldreq.proto);
		sprintf(name, "%s", oldreq.ypxfr_owner);
		pdomain = oldreq.ypxfr_domain;
		pmap = oldreq.ypxfr_map;
	} else {
		VERS_ERR;
	}

	/* Check that the map exists and is accessable */
	if ((fdb = ypset_current_map(pmap, pdomain, &resp.status)) != NULL &&
		yp_map_access(transp, &resp.status, fdb)) {

		pid = vfork();
		if (pid == -1) {
			FORK_ERR;
		} else if (pid == 0) {
		    if (prog == YPPROC_NEWXFR || prog == YPPROC_XFR) {
#ifdef OPCOM_DEBUG
			fprintf(stderr,
				"EXECL: %s, -d, %s, -C, %s, %s, %s, %s\n",
				ypxfr_proc, pdomain,
				transid, proto, name, pmap);
#endif
			if (execl(ypxfr_proc, "ypxfr", "-d",
					pdomain, "-C", transid, proto,
					name, pmap, NULL))
			    EXEC_ERR;
		    } else {
			VERS_ERR;
		    }
		    _exit(1);
		}

	} else {
		MAP_ERR;
	}
	if (!svc_sendreply(transp, xdr_void, 0)) {
		RESPOND_ERR;
	}

	if (prog == YPPROC_NEWXFR) {
		if (!svc_freeargs(transp,
					(xdrproc_t) xdr_ypreq_newxfr,
					(char *) &newreq)) {
		    FREE_ERR;
		}
	}
}

/*
 * This implements the "get all" function.
 */
void
ypall(SVCXPRT *transp)
{
	struct ypreq_nokey req;
	struct ypresp_val resp;  /* not returned to the caller */
	pid_t pid;
	char *fun = "ypall";
	DBM *fdb;

	req.domain = req.map = NULL;

	memset((char *)&req, 0, sizeof (req));

	if (!svc_getargs(transp,
				(xdrproc_t)xdr_ypreq_nokey,
				(char *) &req)) {
		svcerr_decode(transp);
		return;
	}

	pid = fork();

	if (pid) {

		if (pid == -1) {
			FORK_ERR;
		}

		if (!svc_freeargs(transp,
					(xdrproc_t) xdr_ypreq_nokey,
					(char *) &req)) {
			FREE_ERR;
		}

		return;
	}

	/*
	* access control hack:  If denied then invalidate the map name.
	*/
	ypclr_current_map();
	if ((fdb = ypset_current_map(req.map,
		req.domain, &resp.status)) != NULL &&
		!yp_map_access(transp, &resp.status, fdb)) {

		req.map[0] = '-';
	}

	/*
	* This is the child process.  The work gets done by xdrypserv_ypall/
	* we must clear the "current map" first so that we do not
	* share a seek pointer with the parent server.
	*/

	if (!svc_sendreply(transp,
				(xdrproc_t) xdrypserv_ypall,
				(char *) &req)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) xdr_ypreq_nokey,
				(char *) &req)) {
		FREE_ERR;
	}

	exit(0);
}

/*
 * This implements the "get master name" function.
 */
void
ypmaster(SVCXPRT *transp)
{
	struct ypreq_nokey req;
	struct ypresp_master resp;
	char *nullstring = "";
	char *fun = "ypmaster";
	DBM *fdb;

	memset((char *)&req, 0, sizeof (req));
	resp.master = nullstring;
	resp.status = YP_TRUE;

	if (!svc_getargs(transp,
				(xdrproc_t) xdr_ypreq_nokey,
				(char *) &req)) {
		svcerr_decode(transp);
		return;
	}

	if ((fdb = ypset_current_map(req.map,
		req.domain, &resp.status)) != NULL &&
		yp_map_access(transp, &resp.status, fdb)) {

		if (!ypget_map_master(&resp.master, fdb)) {
			resp.status = (unsigned)YP_BADDB;
		}
	}

	if (!svc_sendreply(transp,
				(xdrproc_t) xdr_ypresp_master,
				(char *) &resp)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) xdr_ypreq_nokey,
				(char *) &req)) {
		FREE_ERR;
	}
}

/*
 * This implements the "get order number" function.
 */
void
yporder(SVCXPRT *transp)
{
	struct ypreq_nokey req;
	struct ypresp_order resp;
	char *fun = "yporder";
	DBM *fdb;

	req.domain = req.map = NULL;
	resp.status  = YP_TRUE;
	resp.ordernum  = 0;

	memset((char *)&req, 0, sizeof (req));

	if (!svc_getargs(transp,
				(xdrproc_t) xdr_ypreq_nokey,
				(char *) &req)) {
		svcerr_decode(transp);
		return;
	}

	resp.ordernum = 0;

	if ((fdb = ypset_current_map(req.map,
					req.domain,
					&resp.status)) != NULL &&
		yp_map_access(transp, &resp.status, fdb)) {

		if (!ypget_map_order(req.map, req.domain, &resp.ordernum)) {
			resp.status = (unsigned)YP_BADDB;
		}
	}

	if (!svc_sendreply(transp,
				(xdrproc_t) xdr_ypresp_order,
				(char *) &resp)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) xdr_ypreq_nokey,
				(char *)&req)) {
		FREE_ERR;
	}
}

void
ypmaplist(SVCXPRT *transp)
{
	char domain_name[YPMAXDOMAIN + 1];
	char *pdomain = domain_name;
	char *fun = "ypmaplist";
	struct ypresp_maplist maplist;
	struct ypmaplist *tmp;

	maplist.list = (struct ypmaplist *) NULL;

	memset(domain_name, 0, sizeof (domain_name));

	if (!svc_getargs(transp,
				(xdrproc_t) xdr_ypdomain_wrap_string,
				(caddr_t) &pdomain)) {
		svcerr_decode(transp);
		return;
	}

	maplist.status = yplist_maps(domain_name, &maplist.list);

	if (!svc_sendreply(transp,
				(xdrproc_t) xdr_ypresp_maplist,
				(char *) &maplist)) {
		RESPOND_ERR;
	}

	while (maplist.list) {
		tmp = maplist.list->ypml_next;
		free((char *) maplist.list);
		maplist.list = tmp;
	}
}

/*
 * Ancillary functions used by the top-level functions within this
 * module
 */

/*
 * This returns TRUE if a given key is a yp-private symbol, otherwise
 * FALSE
 */
static bool
isypsym(datum *key)
{
	if ((key->dptr == NULL) ||
		(key->dsize < yp_prefix_sz) ||
		memcmp(yp_prefix, key->dptr, yp_prefix_sz) ||
		(!memcmp(key->dptr, "YP_MULTI_", 9))) {
		return (FALSE);
	}
	return (TRUE);
}

/*
 * This provides private-symbol filtration for the enumeration functions.
 */
static void
ypfilter(DBM *fdb, datum *inkey, datum *outkey, datum *val, uint_t *status)
{
	datum k;

	if (inkey) {

		if (isypsym(inkey)) {
			*status = (unsigned)YP_BADARGS;
			return;
		}

		k = dbm_do_nextkey(fdb, *inkey);
	} else {
		k = dbm_firstkey(fdb);
	}

	while (k.dptr && isypsym(&k)) {
		k = dbm_nextkey(fdb);
	}

	if (k.dptr == NULL) {
		*status = YP_NOMORE;
		return;
	}

	*outkey = k;
	*val = dbm_fetch(fdb, k);

	if (val->dptr != NULL) {
		*status = YP_TRUE;
	} else {
		*status = (unsigned)YP_BADDB;
	}
}

/*
 * Serializes a stream of struct ypresp_key_val's.  This is used
 * only by the ypserv side of the transaction.
 */
static bool
xdrypserv_ypall(XDR *xdrs, struct ypreq_nokey *req)
{
	bool_t more = TRUE;
	struct ypresp_key_val resp;
	DBM *fdb;

	resp.keydat.dptr = resp.valdat.dptr = (char *) NULL;
	resp.keydat.dsize = resp.valdat.dsize = 0;

	if ((fdb = ypset_current_map(req->map, req->domain,
					&resp.status)) != NULL) {
		ypfilter(fdb, (datum *) NULL, &resp.keydat, &resp.valdat,
				&resp.status);

		while (resp.status == YP_TRUE) {
			if (!xdr_bool(xdrs, &more)) {
				return (FALSE);
			}

			if (!xdr_ypresp_key_val(xdrs, &resp)) {
				return (FALSE);
			}

			ypfilter(fdb, &resp.keydat, &resp.keydat, &resp.valdat,
					&resp.status);
		}
	}

	if (!xdr_bool(xdrs, &more)) {
		return (FALSE);
	}

	if (!xdr_ypresp_key_val(xdrs, &resp)) {
		return (FALSE);
	}

	more = FALSE;

	if (!xdr_bool(xdrs, &more)) {
		return (FALSE);
	}

	return (TRUE);
}

/*
 * Additions for sparc cluster support
 */

/*
 * Check for special multihomed host cookie in the key.  If there,
 * collect the addresses from the comma separated list and return
 * the one that's nearest the client.
 */
static int
multihomed(struct ypreq_key req, struct ypresp_val *resp,
		SVCXPRT *xprt, DBM *fdb)
{
	char *cp, *bp;
	u_long bestaddr, call_addr;
	struct netbuf *nbuf;
	char name[PATH_MAX];
	static char localbuf[_PBLKSIZ];	/* buffer for multihomed IPv6 addr */

	if (strcmp(req.map, "hosts.byname") &&
			strcmp(req.map, "ipnodes.byname"))
		/* default status is YP_NOKEY */
		return (0);

	if (strncmp(req.keydat.dptr, "YP_MULTI_", 9)) {
		datum tmpname;

		strncpy(name, "YP_MULTI_", 9);
		strncpy(name + 9, req.keydat.dptr, req.keydat.dsize);
		tmpname.dsize = req.keydat.dsize + 9;
		tmpname.dptr = name;
		resp->valdat = dbm_fetch(fdb, tmpname);
	} else {
		/*
		 * Return whole line (for debugging) if YP_MULTI_hostnam
		 * is specified.
		 */
		resp->valdat = dbm_fetch(fdb, req.keydat);
		if (resp->valdat.dptr != NULL)
			return (1);
	}

	if (resp->valdat.dptr == NULL)
		return (0);

	strncpy(name, req.keydat.dptr, req.keydat.dsize);
	name[req.keydat.dsize] = NULL;

	if (strcmp(req.map, "ipnodes.byname") == 0) {
		/*
		 * This seciton handles multihomed IPv6 addresses.
		 * It returns all the IPv6 addresses one per line and only
		 * the requested hostname is returned.  NO aliases will be
		 * returned.  This is done texactly the same way DNS forwarding
		 * daemon handles multihomed hosts.
		 * New IPv6 enabled clients should be able to handle this
		 * information returned.  The sorting is also the client's
		 * responsibility.
		 */

		char *buf, *endbuf;

		if ((buf = strdup(resp->valdat.dptr)) == NULL) /* no memory */
			return (0);
		if ((bp = strtok(buf, " \t")) == NULL) { /* no address field */
			free(buf);
			return (0);
		}
		if ((cp = strtok(NULL, "")) == NULL) { /* no host field */
			free(buf);
			return (0);
		}
		if ((cp = strtok(bp, ",")) != NULL) { /* multihomed host */
			int bsize;

			localbuf[0] = '\0';
			bsize = sizeof (localbuf);
			endbuf = localbuf;

			while (cp) {
				if ((strlen(cp) + strlen(name)) >= bsize) {
					/* out of range */
					break;
				}
				sprintf(endbuf, "%s %s\n", cp, name);
				cp = strtok(NULL, ",");
				endbuf = &endbuf[strlen(endbuf)];
				bsize = &localbuf[sizeof (localbuf)] - endbuf;
			}
			resp->valdat.dptr = localbuf;
			resp->valdat.dsize = strlen(localbuf);
		}

		free(buf);
		/* remove trailing newline */
		if (resp->valdat.dsize &&
			resp->valdat.dptr[resp->valdat.dsize-1] == '\n') {
			resp->valdat.dptr[resp->valdat.dsize-1] = '\0';
			resp->valdat.dsize -= 1;
		}

		resp->status = YP_TRUE;
		return (1);
	}
	nbuf = svc_getrpccaller(xprt);
	/*
	 * OK, now I have a netbuf structure which I'm supposed to
	 * treat as opaque...  I hate transport independance!
	 * So, we're just gonna doit wrong...  By wrong I mean that
	 * we assume that the buf part of the netbuf structure is going
	 * to be a sockaddr_in.  We'll then check the assumed family
	 * member and hope that we find AF_INET in there...  if not
	 * then we can't continue.
	 */
	if (((struct sockaddr_in *) (nbuf->buf))->sin_family != AF_INET)
		return (0);

	call_addr = ((struct sockaddr_in *) (nbuf->buf))->sin_addr.s_addr;

	cp = resp->valdat.dptr;
	if ((bp = strtok(cp, " \t")) == NULL) /* no address field */
		return (0);
	if ((cp = strtok(NULL, "")) == NULL)  /* no host field */
		return (0);
	bp = strtok(bp, ",");

	bestaddr = inet_addr(bp);
	while (cp = strtok(NULL, ",")) {
		u_long taddr;

		taddr = inet_addr(cp);
		if (ntohl(call_addr ^ taddr) < ntohl(call_addr ^ bestaddr))
			bestaddr = taddr;
	}
	cp = resp->valdat.dptr;
	sprintf(cp, "%s %s", inet_ntoa(*(struct in_addr *) &bestaddr), name);
	resp->valdat.dsize = strlen(cp);

	resp->status = YP_TRUE;

	return (1);
}

/* V1 dispatch routines */
void
ypoldmatch(SVCXPRT *transp, struct svc_req *rqstp)
{
	bool dbmop_ok = TRUE;
	struct yprequest req;
	struct ypreq_key nrq;
	struct ypresponse resp;
	char *fun = "ypoldmatch";
	DBM *fdb;

	memset((void *) &req, 0, sizeof (req));
	memset((void *) &resp, 0, sizeof (resp));

	if (!svc_getargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		svcerr_decode(transp);
		return;
	}

	if (req.yp_reqtype != YPMATCH_REQTYPE) {
		resp.ypmatch_resp_status = (unsigned)YP_BADARGS;
		dbmop_ok = FALSE;
	}

	if (dbmop_ok &&
		(((fdb = ypset_current_map(req.ypmatch_req_map,
						req.ypmatch_req_domain,
						&resp.ypmatch_resp_status))
						!= NULL) &&
						yp_map_access(transp,
						&resp.ypmatch_resp_status,
						fdb))) {

		/* Check with the DBM database */
		resp.ypmatch_resp_valdat = dbm_fetch(fdb,
						req.ypmatch_req_keydat);

		if (resp.ypmatch_resp_valptr != NULL) {
			resp.ypmatch_resp_status = YP_TRUE;
			if (!silent)
				printf("%s: dbm: %s\n",
					fun, resp.ypmatch_resp_valptr);
			goto send_oldreply;
		}

		/*
		 * If we're being asked to match YP_SECURE or YP_INTERDOMAIN
		 * and we haven't found it in the dbm file, then we don't
		 * really want to waste any more time.  Specifically, we don't
		 * want to ask DNS
		 */
		if (req.ypmatch_req_keysize == 0 ||
		    req.ypmatch_req_keyptr == NULL ||
		    req.ypmatch_req_keyptr[0] == '\0' ||
		    strncmp(req.ypmatch_req_keyptr, "YP_SECURE", 9) == 0 ||
		    strncmp(req.ypmatch_req_keyptr, "YP_INTERDOMAIN", 14) == 0)

		    goto send_oldreply;

		/* Let's try the YP_MULTI_ hack... */
#ifdef MINUS_C_OPTION
		if (multiflag == TRUE && omultihomed(req, &resp, transp, fdb))
			goto send_oldreply;
#else
		if (omultihomed(req, &resp, transp, fdb))
			goto send_oldreply;
#endif

		/* Let's try DNS */
		if (!dnsforward) {
			USE_YP_INTERDOMAIN
			datum idkey, idval;
			idkey.dptr = yp_interdomain;
			idkey.dsize = yp_interdomain_sz;
			idval = dbm_fetch(fdb, idkey);
			if (idval.dptr)
				dnsforward = TRUE;
		}

		if (dnsforward) {
		    if (!resolv_pid)
			setup_resolv(&dnsforward, &resolv_pid, &resolv_client,
					resolv_tp, 0);

		    if (req.yp_reqtype == YPREQ_KEY) {
			nrq = req.yp_reqbody.yp_req_keytype;

			resolv_req(&dnsforward, &resolv_client, &resolv_pid,
					resolv_tp, rqstp->rq_xprt,
					&nrq, nrq.map);
		    }
		    return;
		}
	}

	send_oldreply:

	if (!svc_sendreply(transp,
				(xdrproc_t) _xdr_ypresponse,
				(caddr_t) &resp)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(char *) &req)) {
		FREE_ERR;
	}
}

void
ypoldfirst(SVCXPRT *transp)
{
	bool dbmop_ok = TRUE;
	struct yprequest req;
	struct ypresponse resp;
	char *fun = "ypoldfirst";
	DBM *fdb;

	memset((void *) &req, 0, sizeof (req));
	memset((void *) &resp, 0, sizeof (resp));

	if (!svc_getargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		svcerr_decode(transp);
		return;
	}

	if (req.yp_reqtype != YPFIRST_REQTYPE) {
		resp.ypfirst_resp_status = (unsigned)YP_BADARGS;
		dbmop_ok = FALSE;
	}

	if (dbmop_ok &&
		((fdb = ypset_current_map(req.ypfirst_req_map,
						req.ypfirst_req_domain,
						&resp.ypfirst_resp_status))
						!= NULL) &&
						yp_map_access(transp,
						&resp.ypfirst_resp_status,
						fdb)) {

		resp.ypfirst_resp_keydat = dbm_firstkey(fdb);

		if (resp.ypfirst_resp_keyptr != NULL) {
			resp.ypfirst_resp_valdat =
				dbm_fetch(fdb, resp.ypfirst_resp_keydat);

			if (resp.ypfirst_resp_valptr != NULL) {
				resp.ypfirst_resp_status = YP_TRUE;
			} else {
				resp.ypfirst_resp_status = (unsigned)YP_BADDB;
			}
		} else {
			resp.ypfirst_resp_status = (unsigned)YP_NOKEY;
		}
	}

	resp.yp_resptype = YPFIRST_RESPTYPE;

	if (!svc_sendreply(transp,
				(xdrproc_t) _xdr_ypresponse,
				(caddr_t) &resp)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		FREE_ERR;
	}
}

void
ypoldnext(SVCXPRT *transp)
{
	bool dbmop_ok = TRUE;
	struct yprequest req;
	struct ypresponse resp;
	char *fun = "ypoldnext";
	DBM *fdb;

	memset((void *) &req, 0, sizeof (req));
	memset((void *) &resp, 0, sizeof (resp));

	if (!svc_getargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		svcerr_decode(transp);
		return;
	}

	if (req.yp_reqtype != YPNEXT_REQTYPE) {
		resp.ypnext_resp_status = (unsigned)YP_BADARGS;
		dbmop_ok = FALSE;
	}

	if (dbmop_ok &&
		((fdb = ypset_current_map(req.ypnext_req_map,
					req.ypnext_req_domain,
					&resp.ypnext_resp_status)) != NULL &&
		yp_map_access(transp, &resp.ypnext_resp_status, fdb))) {

		resp.ypnext_resp_keydat = dbm_nextkey(fdb);

		if (resp.ypnext_resp_keyptr != NULL) {
			resp.ypnext_resp_valdat =
			dbm_fetch(fdb, resp.ypnext_resp_keydat);

			if (resp.ypnext_resp_valptr != NULL) {
				resp.ypnext_resp_status = YP_TRUE;
			} else {
				resp.ypnext_resp_status = (unsigned)YP_BADDB;
			}
		} else {
			resp.ypnext_resp_status = (unsigned)YP_NOMORE;
		}
	}

	resp.yp_resptype = YPNEXT_RESPTYPE;

	if (!svc_sendreply(transp,
				(xdrproc_t) _xdr_ypresponse,
				(caddr_t) &resp)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		FREE_ERR;
	}
}

/*
 * This retrieves the order number and master peer name from the map.
 * The conditions for the various message fields are: domain is filled
 * in iff the domain exists.  map is filled in iff the map exists.
 * order number is filled in iff it's in the map.  owner is filled in
 * iff the master peer is in the map.
 */
void
ypoldpoll(SVCXPRT *transp)
{
	struct yprequest req;
	struct ypresponse resp;
	char *map = "";
	char *domain = "";
	char *owner = "";
	uint_t error;
	char *fun = "ypoldpoll";
	DBM *fdb;

	memset((void *) &req, 0, sizeof (req));
	memset((void *) &resp, 0, sizeof (resp));

	if (!svc_getargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		svcerr_decode(transp);
		return;
	}

	if (req.yp_reqtype == YPPOLL_REQTYPE) {
		if (strcmp(req.yppoll_req_domain, "yp_private") == 0 ||
			strcmp(req.yppoll_req_map, "ypdomains") == 0 ||
			strcmp(req.yppoll_req_map, "ypmaps") == 0) {

			/*
			* Backward comatibility for 2.0 NIS servers
			*/
			domain = req.yppoll_req_domain;
			map = req.yppoll_req_map;
		} else if ((fdb = ypset_current_map(req.yppoll_req_map,
				req.yppoll_req_domain,
				&error)) != NULL) {
			domain = req.yppoll_req_domain;
			map = req.yppoll_req_map;
			ypget_map_order(map, domain,
					&resp.yppoll_resp_ordernum);
			ypget_map_master(&owner, fdb);
		} else {
			switch ((int) error) {
			case YP_BADDB:
				map = req.yppoll_req_map;
				/* Fall through to set the domain too. */

			case YP_NOMAP:
				domain = req.yppoll_req_domain;
				break;
			}
		}
	}

	resp.yp_resptype = YPPOLL_RESPTYPE;
	resp.yppoll_resp_domain = domain;
	resp.yppoll_resp_map = map;
	resp.yppoll_resp_owner = owner;

	if (!svc_sendreply(transp,
				(xdrproc_t) _xdr_ypresponse,
				(caddr_t) &resp)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		FREE_ERR;
	}
}

void
ypoldpush(SVCXPRT *transp)
{
	struct yprequest req;
	struct ypresp_val resp;
	pid_t pid = -1;
	char *fun = "ypoldpush";
	DBM *fdb;

	memset((void *) &req, 0, sizeof (req));

	if (!svc_getargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		svcerr_decode(transp);
		return;
	}

	if (((fdb = ypset_current_map(req.yppush_req_map,
					req.yppush_req_domain,
					&resp.status)) != NULL) &&
		(yp_map_access(transp, &resp.status, fdb))) {

		pid = vfork();
	}

	if (pid == -1) {
		FORK_ERR;
	} else if (pid == 0) {
		ypclr_current_map();

		if (execl(yppush_proc, "yppush", "-d", req.yppush_req_domain,
				req.yppush_req_map, NULL)) {
			EXEC_ERR;
		}
		_exit(1);
	}

	if (!svc_sendreply(transp,
				(xdrproc_t) xdr_void,
				(caddr_t) NULL)) {
		RESPOND_ERR;
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		FREE_ERR;
	}
}

void
ypoldpull(SVCXPRT *transp)
{
	struct yprequest req;
	struct ypresp_val resp;
	pid_t pid = -1;
	char *fun = "ypoldpull";
	DBM *fdb;

	memset((void *) &req, 0, sizeof (req));

	if (!svc_getargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		svcerr_decode(transp);
		return;
	}

	if (req.yp_reqtype == YPPULL_REQTYPE) {

		if (((fdb = ypset_current_map(req.yppull_req_map,
						req.yppull_req_domain,
						&resp.status)) == NULL) ||
			(yp_map_access(transp, &resp.status, fdb))) {
			pid = vfork();
		}

		if (pid == -1) {
			FORK_ERR;
		} else if (pid == 0) {
			ypclr_current_map();

			if (execl(ypxfr_proc, "ypxfr", "-d",
					req.yppull_req_domain,
					req.yppull_req_map, NULL)) {
				EXEC_ERR;
			}
			_exit(1);
		}
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		FREE_ERR;
	}
}

void
ypoldget(SVCXPRT *transp)
{
	struct yprequest req;
	struct ypresp_val resp;
	pid_t pid = -1;
	char *fun = "ypoldget";
	DBM *fdb;

	memset((void *) &req, 0, sizeof (req));

	if (!svc_getargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		svcerr_decode(transp);
		return;
	}

	if (!svc_sendreply(transp, xdr_void, 0)) {
		RESPOND_ERR;
	}

	if (req.yp_reqtype == YPGET_REQTYPE) {

		if (((fdb = ypset_current_map(req.ypget_req_map,
						req.ypget_req_domain,
						&resp.status)) == NULL) ||
			(yp_map_access(transp, &resp.status, fdb))) {

			pid = vfork();
		}

		if (pid == -1) {
			FORK_ERR;
		} else if (pid == 0) {

			ypclr_current_map();

			if (execl(ypxfr_proc, "ypxfr", "-d",
					req.ypget_req_domain, "-h",
					req.ypget_req_owner,
					req.ypget_req_map, NULL)) {

				EXEC_ERR;
			}
			_exit(1);
		}
	}

	if (!svc_freeargs(transp,
				(xdrproc_t) _xdr_yprequest,
				(caddr_t) &req)) {
		RESPOND_ERR;
	}
}

static int
omultihomed(struct yprequest req,
	    struct ypresponse *resp, SVCXPRT *xprt, DBM *fdb)
{
	char *cp, *bp;
	char name[PATH_MAX];
	struct netbuf *nbuf;
	u_long bestaddr, call_addr;

	if (strcmp(req.ypmatch_req_map, "hosts.byname"))
		return (0);

	if (strncmp(req.ypmatch_req_keyptr, "YP_MULTI_", 9)) {
		datum tmpname;

		strncpy(name, "YP_MULTI_", 9);
		strncpy(name + 9, req.ypmatch_req_keyptr,
			req.ypmatch_req_keysize);
		tmpname.dsize = req.ypmatch_req_keysize + 9;
		tmpname.dptr = name;
		resp->ypmatch_resp_valdat = dbm_fetch(fdb, tmpname);
	} else {
		resp->ypmatch_resp_valdat =
			dbm_fetch(fdb, req.ypmatch_req_keydat);
		if (resp->ypmatch_resp_valptr != NULL)
			return (1);
	}

	if (resp->ypmatch_resp_valptr == NULL)
		return (0);

	strncpy(name, req.ypmatch_req_keyptr, req.ypmatch_req_keysize);
	name[req.ypmatch_req_keysize] = NULL;

	nbuf = svc_getrpccaller(xprt);

	/*
	 * OK, now I have a netbuf structure which I'm supposed to treat
	 * as opaque...  I hate transport independance!  So, we're just
	 * gonna doit wrong...  By wrong I mean that we assume that the
	 * buf part of the netbuf structure is going to be a sockaddr_in.
	 * We'll then check the assumed family member and hope that we
	 * find AF_INET in there...  if not then we can't continue. */
	if (((struct sockaddr_in *) (nbuf->buf))->sin_family != AF_INET)
		return (0);

	call_addr = ((struct sockaddr_in *) (nbuf->buf))->sin_addr.s_addr;

	cp = resp->ypmatch_resp_valptr;
	if ((bp = strtok(cp, "\t")) == NULL)	/* No address field */
		return (0);
	if ((cp = strtok(NULL, "")) == NULL)	/* No host field */
		return (0);
	bp = strtok(bp, ",");

	bestaddr = inet_addr(bp);
	while (cp = strtok(NULL, ",")) {
		u_long taddr;

		taddr = inet_addr(cp);
		if (ntohl(call_addr ^ taddr) < ntohl(call_addr ^ bestaddr))
			bestaddr = taddr;
	}

	cp = resp->ypmatch_resp_valptr;
	sprintf(cp, "%s %s", inet_ntoa(*(struct in_addr *) &bestaddr), name);
	resp->ypmatch_resp_valsize = strlen(cp);

	resp->ypmatch_resp_status = YP_TRUE;

	return (1);
}
