/* 
 *
 * yp.c
 *
 * YP call responses and related functions
 *
 * Author: Landon Fuller <landonf@go2net.com>
 *
 * Copyright (c) 2000-2001 InfoSpace, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *         This product includes software developed by InfoSpace, Inc.
 *         and its contributors.
 * 4. Neither the name of InfoSpace, Inc nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "../autoconf.h"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>

#include <lber.h>
#include <ldap.h>

#include "yp.h"
#include "../include/config.h"
#include "../include/transit.h"
#include "../include/access.h"
#include "../include/servconf.h"
#include "../include/log.h"
#include "../include/util.h"
#include "../include/modules.h"
#include "../include/yp.h"

int children = 0;

/*
 * NIS v2 Support
 */

ypstat yp_select_map(const char *map, const char *domain)
{
	struct ypmaplist *current = prefs.yp_maplist;

	if(yp_validdomain(domain))
	{
		return (YP_NODOM);
	}

	while(current)
	{
		if(!strcmp(current->map, map))
		{
			return (YP_TRUE);
		}
		current = current->next;
	}
	return (YP_NOMAP);
}

void * ypproc_null_2_svc (void *dummy, struct svc_req *rqstp)
{
	static bool_t result;

	if (yp_access(NULL, (struct svc_req *) rqstp))
		return (NULL);
	result = YP_TRUE;
	return (&result);
}

bool_t * ypproc_domain_2_svc (domainname *domain, struct svc_req *rqstp)
{
	static bool_t result;

	if (yp_access(NULL, (struct svc_req *) rqstp))
		return (NULL);
	if(*domain == NULL || yp_validdomain(*domain))
		result = YP_FALSE;
	else
		result = YP_TRUE;

	return (&result);
}

bool_t * ypproc_domain_nonack_2_svc (domainname *domain, struct svc_req *rqstp)
{
	static bool_t result;

	if (yp_access(NULL, (struct svc_req *) rqstp))
		return (NULL);
	if(*domain == NULL || yp_validdomain(*domain))
		result = YP_FALSE;
	else
		result = YP_TRUE;

	return (&result);
}

ypresp_val * ypproc_match_2_svc (ypreq_key *key, struct svc_req *rqstp)
{
	static ypresp_val result;
	struct ypmoduledata *moduledata;

	if(result.val.valdat_len != 0)
	{
		free(result.val.valdat_val);
		result.val.valdat_len = 0;
	}

	if (yp_access(key->map, (struct svc_req *) rqstp))
	{
		result.stat = YP_YPERR;
		return (&result);
	}
	if (key->domain == NULL || key->map == NULL)
	{
		result.stat = YP_BADARGS;
		return (&result);
	}
	if ((result.stat = yp_select_map(key->map, key->domain)) != YP_TRUE)
	{
		return (&result);
	}

	moduledata = yp_get_module((char *) key->map);
	if(!moduledata)
	{
		warn("No module for map: %s\n", key->map);
		result.stat = YP_YPERR;
		return (&result);
	}

	result.stat = moduledata->yp_getbykey(&key->key, (mapname *) key->map, &result.val);

	return (&result);
}

#ifdef FREEBSD
ypresp_key_val * ypproc_first_2_svc (ypreq_nokey *nokey, struct svc_req *rqstp)
#else
ypresp_key_val * ypproc_first_2_svc (ypreq_key *nokey, struct svc_req *rqstp)
#endif
{
	static ypresp_key_val result;
	struct ypmoduledata *moduledata;

	if(result.val.valdat_len != 0)
	{
		free(result.val.valdat_val);
		result.val.valdat_len = 0;
	}
	if(result.key.keydat_len != 0)
	{
		free(result.key.keydat_val);
		result.key.keydat_len = 0;
	}

	if (yp_access(nokey->map, (struct svc_req *) rqstp))
	{
		result.stat = YP_YPERR;
		return (&result);
	}
	if (nokey->domain == NULL || nokey->map == NULL)
	{
		result.stat = YP_BADARGS;
		return (&result);
	}
	if ((result.stat = yp_select_map(nokey->map, nokey->domain)) != YP_TRUE)
	{
		return (&result);
	}

	moduledata = yp_get_module((char *) nokey->map);
	if(!moduledata)
	{
		warn("No module for map: %s\n", nokey->map);
		result.stat = YP_YPERR;
		return (&result);
	}

	result.stat = moduledata->yp_firstbykey(&result.key, (mapname *) nokey->map, &result.val);

	return (&result);
}

ypresp_key_val * ypproc_next_2_svc (ypreq_key *key, struct svc_req *rqstp)
{
	static ypresp_key_val result;
	struct ypmoduledata *moduledata;

	if(result.val.valdat_len != 0)
	{
		free(result.val.valdat_val);
		result.val.valdat_len = 0;
	}
	if(result.key.keydat_len != 0)
	{
		free(result.key.keydat_val);
		result.key.keydat_len = 0;
	}

	if (yp_access(key->map, (struct svc_req *) rqstp))
	{
		result.stat = YP_YPERR;
		return (&result);
	}
	if (key->domain == NULL || key->map == NULL)
	{
		result.stat = YP_BADARGS;
		return (&result);
	}
	if ((result.stat = yp_select_map(key->map, key->domain)) != YP_TRUE)
	{
		return (&result);
	}

	moduledata = yp_get_module((char *) key->map);
	if(!moduledata)
	{
		warn("No module for map: %s\n", key->map);
		result.stat = YP_YPERR;
		return (&result);
	}

	result.key.keydat_val = key->key.keydat_val;
	result.key.keydat_len = key->key.keydat_len;
	/*
	 * XXX Remember - result.key.keydat_len must be set to zero if you do not replace
	 * result.key.keydat_val with your own malloc'd string!
	 */
	result.stat = moduledata->yp_nextbykey(&result.key, (mapname *) key->map, &result.val);

	return(&result);
}

ypresp_xfr * ypproc_xfr_2_svc (ypreq_xfr *xfr, struct svc_req *rqstp)
{
	static ypresp_xfr result;

	error("ypproc_xfr_2\n");
	result.transid = xfr->transid;

	if (yp_access(xfr->map_parms.map, (struct svc_req *) rqstp))
		return (NULL);
	if (yp_access(xfr->map_parms.map, (struct svc_req *) rqstp))
	{
		result.xfrstat = YP_YPERR;
		return (&result);
	}
	if (xfr->map_parms.domain == NULL || xfr->map_parms.map == NULL)
	{
		result.xfrstat = YP_BADARGS;
		return (&result);
	}
	if ((result.xfrstat = yp_select_map(xfr->map_parms.domain, xfr->map_parms.map)) != YP_TRUE)
	{
		return (&result);
	}
	/* We don't support ypxfrs yet.
	 * In Bill Paul's BSD ypserv, he does a svc_sendreply, then has a ypxfr_callback
	 * routine. His reasoning for this follows:
	 * Add a ypxfr_callback() function that we can use to signal failure to
	 * yppush(8) in the event that we can't fork()/exec() ypxfr(8). yppush
	 * only checks the return status from YPPROC_XFR enough to determine
	 * that the RPC succeded: it relies on its callback service to figure
	 * out whether or not the transfer actually worked.
	 * (from CVS comments of Revision 1.3 of ypserv)
	 *
	 * I include this so that I do not forget this important bit of information.
	 */
	result.transid = xfr->transid;
	result.xfrstat = YPXFR_REFUSED;
	return (&result);
}

void * ypproc_clear_2_svc (void *dummy, struct svc_req *rqstp)
{
	static bool_t result = 0;
	struct ypmodulelist *module = prefs.yp_modulelist;

	if (yp_access(NULL, (struct svc_req *) rqstp))
		return (NULL);
	while (module)
	{
		module->yp_moduledata->yp_clear();
		module = module->next;
	}

	return (&result);
}

/*
 * Custom XDR routine for serializing results of ypproc_all: keep
 * reading from the data source and spew until we run out of records
 * or encounter an error. Idea borrowed from Bill Paul's BSD ypserv.
 */
static bool_t xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *result)
{
	struct ypmoduledata *moduledata;
	mapname *map;
	/* XXX We use the result stat pointer to hold a pointer to module data. Evil hack */
	/* XXX We also use the valdat structure to hold the map name. Also an evil hack */
	moduledata = (struct ypmoduledata *) result->ypresp_all_u.val.key.keydat_val;
	map = (mapname *) result->ypresp_all_u.val.val.valdat_val;

	while (1)
	{
		/* Get a record */
		if((result->ypresp_all_u.val.stat = moduledata->yp_nextbykey(&result->ypresp_all_u.val.key, map, &result->ypresp_all_u.val.val)) == YP_TRUE)
		{
			result->more = TRUE;
		} else {
			result->more = FALSE;
		}
		/* Serialize */
		if (!xdr_ypresp_all(xdrs, result))
			return (FALSE);
		if (result->more == FALSE)
			return (TRUE);
	}
}


ypresp_all * ypproc_all_2_svc (ypreq_nokey *nokey, struct svc_req *rqstp)
{
	static ypresp_all result;
	struct ypmoduledata *moduledata;

	result.more = TRUE;
	result.ypresp_all_u.val.key.keydat_len = 0;
	result.ypresp_all_u.val.key.keydat_val = "";

	if (yp_access(nokey->map, (struct svc_req *) rqstp))
	{
		result.ypresp_all_u.val.stat = YP_YPERR;
		return (&result);
	}
	if (nokey->domain == NULL || nokey->map == NULL)
	{
		result.ypresp_all_u.val.stat = YP_BADARGS;
		return (&result);
	}
	if ((result.ypresp_all_u.val.stat = yp_select_map(nokey->map, nokey->domain)) != YP_TRUE)
	{
		return (&result);
	}

	moduledata = yp_get_module((char *) nokey->map);
	if(!moduledata)
	{
		warn("No module for map: %s\n", nokey->map);
		result.ypresp_all_u.val.stat = YP_YPERR;
		return (&result);
	}
	if (children >= MAX_CHILDREN)
	{
		result.ypresp_all_u.val.stat = YP_YPERR;
		return (&result);
	}

	switch(fork())
	{
		case 0:
			break;
		case -1:
			error("ypproc_all fork(): %s", strerror(errno));
			result.ypresp_all_u.val.stat = YP_YPERR;
			return (&result);
			break;
		default:
			children++;
			return (NULL);
			break;
	}

	/* XXX
	 * Feed the moduledata struct in via result.ypresp_all_u.val.key.keydat_val
	 * Feed the map name in via result.ypresp_all_u.val.val.valdal_val
	 * This is an evil hack */

	result.ypresp_all_u.val.key.keydat_val = (char *) moduledata;
	result.ypresp_all_u.val.val.valdat_val = (char *) nokey->map;
	svc_sendreply(rqstp->rq_xprt, (xdrproc_t) xdr_my_ypresp_all, (char *)&result);
	
	_exit(0);
}

ypresp_master * ypproc_master_2_svc (ypreq_nokey *nokey, struct svc_req *rqstp)
{
	static ypresp_master result;

	if (yp_access(nokey->map, (struct svc_req *) rqstp))
		return (NULL);
	/* Return the prefs.master as master. */
	if (nokey->domain == NULL || nokey->map == NULL)
	{
		result.stat = YP_BADARGS;
		return (&result);
	}
	result.peer = prefs.master;
	result.stat = YP_TRUE;
	return (&result);
}

ypresp_order * ypproc_order_2_svc (ypreq_nokey *nokey, struct svc_req *rqstp)
{
	static ypresp_order result;
	if (yp_access(nokey->map, (struct svc_req *) rqstp))
		return (NULL);
	if(nokey->domain == NULL || nokey->map == NULL)
	{
		result.stat = YP_BADARGS;
		return (&result);
	}
	/* we always return that NIS is updated. How are we to tell (yet)
	 * In the future, we'll be able to tell. And get smarter. */
	result.ordernum = time(NULL);
	result.stat = YP_TRUE;
	return (&result);
}

void yp_maplist_free (ypmaplist *yp_maplist)
{
	register ypmaplist *next;

	while(yp_maplist)
	{
		next = yp_maplist->next;
		free (yp_maplist->map);
		free (yp_maplist);
		yp_maplist = next;
	}
	return;
}

struct ypmaplist *yp_maplist_create(char *mapstrings)
{
	char *cp;
	struct ypmaplist *current = NULL;
	struct ypmaplist *head = NULL;

	cp = strtok((char *) mapstrings, WHITESPACE);
	if(!*cp)
		return(NULL);
	while(cp)
	{
		current = safe_malloc(sizeof(struct ypmaplist));
		current->map = safe_strdup(cp);
		current->next = head; 
		head = current;
		cp = strtok(NULL, WHITESPACE);
	}
	return (head);
}

ypresp_maplist * ypproc_maplist_2_svc (domainname *domain, struct svc_req *rqstp)
{
	static ypresp_maplist result = {0, NULL};

	if (yp_access(NULL, (struct svc_req *) rqstp))
		return (NULL);
	if (domain == NULL)
	{
		result.stat = YP_BADARGS;
		return (&result);
	}

	if (yp_validdomain(*domain))
	{
		result.stat = YP_NODOM;
		return (&result);
	}
	/* Return the yp_maplist generated by servconf.c */
	result.maps = prefs.yp_maplist;
	result.stat = YP_TRUE;
	return (&result);
}

#ifdef NISV1
/*
 * NIS v1 Support
 */

void * ypoldproc_null_1_svc(void *argp, struct svc_req *rqstp)
{
	return(ypproc_null_2_svc(argp, rqstp));
}

bool_t * ypoldproc_domain_1_svc(domainname *argp, struct svc_req *rqstp)
{
	return(ypproc_domain_2_svc(argp, rqstp));
}

bool_t * ypoldproc_domain_nonack_1_svc(domainname *argp, struct svc_req *rqstp)
{
	return (ypproc_domain_nonack_2_svc(argp, rqstp));
}

/*
 * the 'match' procedure sends a response of type YPRESP_VAL
 */
ypresponse * ypoldproc_match_1_svc(yprequest *argp, struct svc_req *rqstp)
{
	static ypresponse  result;
	ypresp_val *v2_result;

	result.yp_resptype = YPRESP_VAL;
	result.ypresponse_u.yp_resp_valtype.val.valdat_val = "";
	result.ypresponse_u.yp_resp_valtype.val.valdat_len = 0;

	if (argp->yp_reqtype != YPREQ_KEY) {
		result.ypresponse_u.yp_resp_valtype.stat = YP_BADARGS;
		return(&result);
	}

	v2_result = ypproc_match_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp);
	if (v2_result == NULL)
		return(NULL);

	bcopy((char *)v2_result,
	      (char *)&result.ypresponse_u.yp_resp_valtype,
	      sizeof(ypresp_val));

	return (&result);
}

/*
 * the 'first' procedure sends a response of type YPRESP_KEY_VAL
 */
ypresponse * ypoldproc_first_1_svc(yprequest *argp, struct svc_req *rqstp)
{
	static ypresponse  result;
	ypresp_key_val *v2_result;

	result.yp_resptype = YPRESP_KEY_VAL;
	result.ypresponse_u.yp_resp_key_valtype.val.valdat_val =
	result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = "";
	result.ypresponse_u.yp_resp_key_valtype.val.valdat_len =
	result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0;

	if (argp->yp_reqtype != YPREQ_NOKEY) {
		result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS;
		return(&result);
	}

	v2_result = ypproc_first_2_svc(&argp->yprequest_u.yp_req_nokeytype,
									rqstp);
	if (v2_result == NULL)
		return(NULL);

	bcopy((char *)v2_result,
	      (char *)&result.ypresponse_u.yp_resp_key_valtype,
	      sizeof(ypresp_key_val));

	return (&result);
}

/*
 * the 'next' procedure sends a response of type YPRESP_KEY_VAL
 */
ypresponse * ypoldproc_next_1_svc(yprequest *argp, struct svc_req *rqstp)
{
	static ypresponse  result;
	ypresp_key_val *v2_result;

	result.yp_resptype = YPRESP_KEY_VAL;
	result.ypresponse_u.yp_resp_key_valtype.val.valdat_val =
	result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = "";
	result.ypresponse_u.yp_resp_key_valtype.val.valdat_len =
	result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0;

	if (argp->yp_reqtype != YPREQ_KEY) {
		result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS;
		return(&result);
	}

	v2_result = ypproc_next_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp);
	if (v2_result == NULL)
		return(NULL);

	bcopy((char *)v2_result,
	      (char *)&result.ypresponse_u.yp_resp_key_valtype,
	      sizeof(ypresp_key_val));

	return (&result);
}

/*
 * the 'poll' procedure sends a response of type YPRESP_MAP_PARMS
 */
ypresponse * ypoldproc_poll_1_svc(yprequest *argp, struct svc_req *rqstp)
{
	static ypresponse  result;
	ypresp_master *v2_result1;
	ypresp_order *v2_result2;

	result.yp_resptype = YPRESP_MAP_PARMS;
	result.ypresponse_u.yp_resp_map_parmstype.domain =
		argp->yprequest_u.yp_req_nokeytype.domain;
	result.ypresponse_u.yp_resp_map_parmstype.map =
		argp->yprequest_u.yp_req_nokeytype.map;
	/*
	 * Hmm... there is no 'status' value in the
	 * yp_resp_map_parmstype structure, so I have to
	 * guess at what to do to indicate a failure.
	 * I hope this is right.
	 */
	result.ypresponse_u.yp_resp_map_parmstype.ordernum = 0;
	result.ypresponse_u.yp_resp_map_parmstype.peer = "";

	if (argp->yp_reqtype != YPREQ_MAP_PARMS) {
		return(&result);
	}

	v2_result1 = ypproc_master_2_svc(&argp->yprequest_u.yp_req_nokeytype,
									rqstp);
	if (v2_result1 == NULL)
		return(NULL);

	if (v2_result1->stat != YP_TRUE) {
		return(&result);
	}

	v2_result2 = ypproc_order_2_svc(&argp->yprequest_u.yp_req_nokeytype,
									rqstp);
	if (v2_result2 == NULL)
		return(NULL);

	if (v2_result2->stat != YP_TRUE) {
		return(&result);
	}

	result.ypresponse_u.yp_resp_map_parmstype.peer =
		v2_result1->peer;
	result.ypresponse_u.yp_resp_map_parmstype.ordernum =
		v2_result2->ordernum;

	return (&result);
}

ypresponse * ypoldproc_push_1_svc(yprequest *argp, struct svc_req *rqstp)
{
	static ypresponse  result;

	/*
	 * Not implemented.
	 */

	return (&result);
}

ypresponse * ypoldproc_pull_1_svc(yprequest *argp, struct svc_req *rqstp)
{
	static ypresponse  result;

	/*
	 * Not implemented.
	 */

	return (&result);
}

ypresponse * ypoldproc_get_1_svc(yprequest *argp, struct svc_req *rqstp)
{
	static ypresponse  result;

	/*
	 * Not implemented.
	 */

	return (&result);
}
#endif
#ifndef YPSERV_ONLY
/* Empty symbols for Linux and Solaris' generated ypbind / ypproc RPC calls */
void * yppushproc_null_1_svc(void *argp, struct svc_req *dummy) {return NULL;}
#ifdef SOLARIS
yppushresp_xfr * yppushproc_xfrresp_1_svc(void *argp, struct svc_req *dummy) {return NULL;}
#else
void * yppushproc_xfrresp_1_svc(yppushresp_xfr *argp, struct svc_req *dummy) {return NULL;}
#endif
void * ypbindproc_null_2_svc(void *argp, struct svc_req *dummy) {return NULL;}
ypbind_resp * ypbindproc_domain_2_svc(domainname *argp, struct svc_req *dummy) {return NULL;}
void * ypbindproc_setdom_2_svc(ypbind_setdom *argp, struct svc_req *dummy) {return NULL;}
#endif

#ifdef SOLARIS
bool_t xdr_ypxfrstat (XDR *xdrs, ypxfrstat *objp)
{
	return (xdr_enum (xdrs, (enum_t *) objp));
}

bool_t xdr_domainname (XDR *xdrs, domainname *objp)
{
	return (xdr_string (xdrs, objp, YPMAXDOMAIN));
}
bool_t xdr_ypresp_all (XDR *xdrs, struct ypresp_all *objp)
{
	if (!xdr_bool(xdrs, &objp->more))
		return FALSE;
	switch (objp->more) {
		case TRUE:
			return xdr_ypresp_key_val(xdrs, &objp->ypresp_all_u.val);
		case FALSE:
			return (TRUE);
		default:
			return (FALSE);
	}
	/* NOT REACHED */
}

bool_t xdr_ypresp_xfr (XDR *xdrs, ypresp_xfr *objp)
{
	if (!xdr_u_int (xdrs, &objp->transid))
		return (FALSE);
	if (!xdr_ypxfrstat (xdrs, &objp->xfrstat))
		return (FALSE);
	return (TRUE);
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1