/*
** ypserver.c			YP server routines.
**
** Copyright (c) 1993 Signum Support AB, Sweden
**
** This file is part of the NYS YP Server.
**
** The NYS YP Server is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License as
** published by the Free Software Foundation; either version 2 of the
** License, or (at your option) any later version.
**
** The NYS YP Server is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with the NYS YP Server; see the file COPYING.  If
** not, write to the Free Software Foundation, Inc., 675 Mass Ave,
** Cambridge, MA 02139, USA.
**
** Author: Peter Eriksson <pen@signum.se>
*/

#include "system.h"

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <gdbm.h>
#include <unistd.h>
#include <sys/stat.h>
#include "yp.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>

#ifndef _PATH_YP
#define _PATH_YP   "/var/yp"
#endif
char *path_ypdb = _PATH_YP;

int debug_flag = 0;
int dns_flag   = 0;


static void Gperror(const char *str)
{
    fprintf(stderr, "%s: GDBM Error Code #%d\n", str, gdbm_errno);
}


/*
** Return 1 if request comes from an authorized host
**
** XXX This function should implement the "securenets" functionality
*/
static int is_valid_host(struct sockaddr_in *sin)
{
    return 1;
}


void *ypproc_null_2_svc(void *dummy,
			struct svc_req *rqstp)
{
    static int foo;
    struct sockaddr_in *rqhost;
    

    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
	fprintf(stderr, "ypproc_null() [From: %s:%d]\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));

    return (void *) &foo;
}

    
/*
** Return 1 if the name is a valid domain name served by us, else 0.
*/
static int is_valid_domain(const char *domain)
{
    char buf[1025];
    struct stat sbuf;
    

    if (strcmp(domain, "binding") == 0 ||
	strcmp(domain, "..") == 0 ||
	strcmp(domain, ".") == 0 ||
	strchr(domain, '/'))
	return 0;
    
    sprintf(buf, "%s/%s", path_ypdb, domain);
    if (stat(buf, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode))
	return 0;

    return 1;
}



bool_t *ypproc_domain_2_svc(domainname *name,
			    struct svc_req *rqstp)
{
    static bool_t result;
    struct sockaddr_in *rqhost;
    
    
    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;
	
    if (debug_flag)
	fprintf(stderr, "ypproc_domain(\"%s\") [From: %s:%d]\n",
		*name,
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));

    if (is_valid_domain(*name))
	result = TRUE;
    else
	result = FALSE;
    
    if (debug_flag)
	fprintf(stderr, "\t-> %s.\n",
		(result == TRUE ? "Ok" : "Not served by us"));
    
    return &result;
}


bool_t *ypproc_domain_nonack_2_svc(domainname *name,
				   struct svc_req *rqstp)
{
    static bool_t result;
    struct sockaddr_in *rqhost;

    
    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
	fprintf(stderr, "ypproc_domain_nonack(\"%s\") [From: %s:%d]\n",
		*name,
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
    
    if (!is_valid_domain(*name))
    {
	if (debug_flag)
	    fprintf(stderr, "\t-> Not a valid domain.\n");

	/* Bail out and don't return any RPC value */
	return NULL;
    }
    
    if (debug_flag)
	fprintf(stderr, "\t-> OK.\n");
    
    result = TRUE;
    return &result;
}


/*
** Open a GDBM database
*/
static GDBM_FILE open_database(const char *domain,
			       const char *map)
{
    GDBM_FILE dbp;
    char buf[1025];


    /* Better be safe than sorry... */
    if (strcmp(domain, "binding") == 0 ||
	strcmp(domain, "..") == 0 ||
	strcmp(domain, ".") == 0 ||
	strchr(domain, '/'))
	return 0;

    if (map[0] == '.' || strchr(map, '/'))
	return 0;

    sprintf(buf, "%s/%s/%s", path_ypdb, domain, map);
    
    dbp = gdbm_open(buf, 0, O_RDONLY, 0, NULL);

    if (debug_flag && dbp == NULL)
	Gperror("gdbm_open()");
    
    return dbp;
}


#define F_ALL   0x01
#define F_NEXT  0x02

/*
** Get a record from a GDBM database.
*/
int read_database(GDBM_FILE dbp,
		  const datum *ikey,
		  datum *okey,
		  datum *dval,
		  int flags)
{
    int first_flag = 0;
    datum nkey, ckey;
    
    
    if (ikey == NULL || ikey->dptr == NULL)
    {
	ckey = gdbm_firstkey(dbp);
	first_flag = 1;
    }
    else
    {
	if ((flags & F_NEXT))
	    ckey = gdbm_nextkey(dbp, *ikey);
	else
	    ckey = *ikey;
    }
    
    if (ckey.dptr == NULL)
    {
	return YP_NOKEY;
    }

    while (1)
    {
	*dval = gdbm_fetch(dbp, ckey);
	if (dval->dptr == NULL)
	{
	    /* Free key, unless it comes from the caller! */
	    if (ikey == NULL || ckey.dptr != ikey->dptr)
		free(ckey.dptr);

	    if (ikey && ikey->dptr != NULL)
	    {
		return YP_NOKEY;
	    }
	    else
		if (first_flag)
		    return YP_BADDB;
		else
		    return YP_FALSE;
	}
	
	if ((flags & F_ALL) || strncmp(dval->dptr, "YP_", 3) != 0)
	{
	    if (okey)
		*okey = ckey;
	    else if (ikey == NULL || ikey->dptr != ckey.dptr)
		free(ckey.dptr);
	    
	    return YP_TRUE;
	}

	/* Free old value */
	free(dval->dptr);
	
	nkey = gdbm_nextkey(dbp, ckey);

	/* Free old key, unless it comes from the caller! */
	if (ikey == NULL || ckey.dptr != ikey->dptr)
	    free(ckey.dptr);

	if (ckey.dptr == NULL)
	    return YP_NOMORE;

	ckey = nkey;
    }
}


/*
** Get the DateTimeModified value for a certain map database
*/
static unsigned long get_dtm(const char *domain,
			     const char *map)
{
    struct stat sbuf;
    char buf[1025];

    sprintf(buf, "%s/%s/%s", path_ypdb, domain, map);
    if (stat(buf, &sbuf) < 0)
	return 0;
    else
	return (unsigned long) sbuf.st_mtime;
}


/*
** YP function "MATCH" implementation
*/
ypresp_val *ypproc_match_2_svc(ypreq_key *key,
			       struct svc_req *rqstp)
{
    static ypresp_val result;
    struct sockaddr_in *rqhost;

    
    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
    {
	fprintf(stderr, "ypproc_match(): [From: %s:%d]\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
		
	fprintf(stderr, "\t\tdomainname = \"%s\"\n",
		key->domain);
	fprintf(stderr, "\t\tmapname = \"%s\"\n",
		key->map);
	fprintf(stderr, "\t\tkeydat = \"%.*s\"\n",
		(int) key->key.keydat_len,
		key->key.keydat_val);
    }

    result.val.valdat_len = 0;
    if (result.val.valdat_val)
    {
	free(result.val.valdat_val);
	result.val.valdat_val = NULL;
    }
    
    if (key->domain[0] == '\0' || key->map[0] == '\0')
	result.stat = YP_BADARGS;
    else if (!is_valid_domain(key->domain))
	result.stat = YP_NODOM;
    else
    {
	datum rdat, qdat;
	
	GDBM_FILE dbp = open_database(key->domain, key->map);
	if (dbp == NULL)
	    result.stat = YP_NOMAP;
	else
	{
	    qdat.dsize = key->key.keydat_len;
	    qdat.dptr = key->key.keydat_val;

	    result.stat = read_database(dbp, &qdat, NULL, &rdat, F_ALL);

	    if (result.stat == YP_TRUE)
	    {
		result.val.valdat_len = rdat.dsize;
		result.val.valdat_val = rdat.dptr;
	    }

	    gdbm_close(dbp);
	}
    }
	
	if (debug_flag)
	{
	if (result.stat == YP_TRUE)
	    fprintf(stderr, "\t-> Value = \"%.*s\"\n",
		    (int) result.val.valdat_len,
		    result.val.valdat_val);
	else
	    fprintf(stderr, "\t-> Error #%d\n", result.stat);
    }


    /*
    ** Do the jive thing if we didn't find the host in the YP map
    ** and we have enabled the magic DNS lookup stuff.
    **
    ** XXX Perhaps this should be done in a sub-process for performance
    **     reasons. Later.
    */
    if (result.stat != YP_TRUE && dns_flag)
    {
	struct hostent *he;
	key->key.keydat_val[key->key.keydat_len] = '\0';


	if (debug_flag)
	    fprintf(stderr, "Doing DNS lookup of %s\n", key->key.keydat_val);
	
	if (strcmp(key->map, "hosts.byname") == 0)
	    he = gethostbyname(key->key.keydat_val);
	else  if (strcmp(key->map, "hosts.byaddr") == 0)
	    he = gethostbyaddr(inet_addr(key->key.keydat_val), sizeof(long),
			       AF_INET);
	else
	    he = NULL;
	    
	if (he)
	{
	    int len, i;
	    char *cp;
	    
	    
	    if (debug_flag)
		fprintf(stderr, "\t-> OK (%s/%s)\n",
			he->h_name,
			inet_ntoa(he->h_addr));

	    len = 16; /* Numerical IP address */
	    len += strlen(he->h_name) + 1;
	    for (i = 0; he->h_aliases[i]; i++)
		len += strlen(he->h_aliases[i]) + 1;
	    

	    cp = malloc(len+1);
	    strcpy(cp, inet_ntoa(he->h_addr));
	    strcat(cp, " ");
	    strcat(cp, he->h_name);

	    for (i = 0; he->h_aliases[i]; i++)
	    {
		strcat(cp, " ");
		strcat(cp, he->h_aliases[i]);
	    }

	    result.val.valdat_len = len;
	    result.val.valdat_val = cp;
	    result.stat = YP_TRUE;
	}
	else
	{
	    if (debug_flag)
		fprintf(stderr, "\t-> Not Found\n");

	    result.stat = YP_NOKEY;
	}
    }
    
    return &result;
}



ypresp_key_val *ypproc_first_2_svc(ypreq_key *key,
				   struct svc_req *rqstp)
{
    static ypresp_key_val result;
    struct sockaddr_in *rqhost;
    

    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
    {
	fprintf(stderr, "ypproc_first(): [From: %s:%d]\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
		
	fprintf(stderr, "\tdomainname = \"%s\"\n", key->domain);
	fprintf(stderr, "\tmapname = \"%s\"\n", key->map);
	fprintf(stderr, "\tkeydat = \"%.*s\"\n",
		(int) key->key.keydat_len,
		key->key.keydat_val);
    }

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

    if (key->map[0] == '\0' || key->domain[0] == '\0')
	result.stat = YP_BADARGS;
    else if (!is_valid_domain(key->domain))
	result.stat = YP_NODOM;
    else
    {
	datum dkey, dval;
	
	GDBM_FILE dbp = open_database(key->domain, key->map);
	if (dbp == NULL)
	    result.stat = YP_NOMAP;
	else
	{
	    result.stat = read_database(dbp, NULL, &dkey, &dval, 0);

	    if (result.stat == YP_TRUE)
	    {
		result.key.keydat_len = dkey.dsize;
		result.key.keydat_val = dkey.dptr;
		
		result.val.valdat_len = dval.dsize;
		result.val.valdat_val = dval.dptr;
	    }
	    
	    gdbm_close(dbp);
	}
    }
    
    if (debug_flag)
    {
	if (result.stat == YP_TRUE)
	    fprintf(stderr, "\t-> Key = \"%.*s\", Value = \"%.*s\"\n",
		    (int) result.key.keydat_len,
		    result.key.keydat_val,
		    (int) result.val.valdat_len,
		    result.val.valdat_val);
		    
	else
	    fprintf(stderr, "\t-> Error #%d\n", result.stat);
    }
    
    return &result;
}


ypresp_key_val *ypproc_next_2_svc(ypreq_key *key,
				  struct svc_req *rqstp)
{
    static ypresp_key_val result;
    struct sockaddr_in *rqhost;
    
    
    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
    {
	fprintf(stderr, "ypproc_match(): [From: %s:%d]\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
		
	fprintf(stderr, "\tdomainname = \"%s\"\n", key->domain);
	fprintf(stderr, "\tmapname = \"%s\"\n", key->map);
	fprintf(stderr, "\tkeydat = \"%.*s\"\n",
		(int) key->key.keydat_len,
		key->key.keydat_val);
    }

    result.key.keydat_len = 0;
    if (result.key.keydat_val)
    {
	free(result.key.keydat_val);
	result.key.keydat_val = NULL;
    }
    
    result.val.valdat_len = 0;
    if (result.val.valdat_val)
    {
	free(result.val.valdat_val);
	result.val.valdat_val = NULL;
    }
    
    if (key->map[0] == '\0' || key->domain[0] == '\0')
	result.stat = YP_BADARGS;
    else if (!is_valid_domain(key->domain))
	result.stat = YP_NODOM;
    else
    {
	datum dkey, dval, okey;


	GDBM_FILE dbp = open_database(key->domain, key->map);
	if (dbp == NULL)
	    result.stat = YP_NOMAP;
	else
	{
	    dkey.dsize = key->key.keydat_len;
	    dkey.dptr  = key->key.keydat_val;
	    
	    result.stat = read_database(dbp, &dkey, &okey, &dval, 0);

	    if (result.stat == YP_TRUE)
	    {
		result.key.keydat_len = okey.dsize;
		result.key.keydat_val = okey.dptr;
		
		result.val.valdat_len = dval.dsize;
		result.val.valdat_val = dval.dptr;
	    }
	    gdbm_close(dbp);
	}
    }
    
    if (debug_flag)
    {
	if (result.stat == YP_TRUE)
	    fprintf(stderr, "\t-> Key = \"%.*s\", Value = \"%.*s\"\n",
		    (int) result.key.keydat_len,
		    result.key.keydat_val,
		    (int) result.val.valdat_len,
		    result.val.valdat_val);
	else
	    fprintf(stderr, "\t-> Error #%d\n", result.stat);
    }
    
    return &result;
}



static void print_ypmap_parms(const struct ypmap_parms *pp)
{
    fprintf(stderr, "\t\tdomain   = \"%s\"\n", pp->domain);
    fprintf(stderr, "\t\tmap      = \"%s\"\n", pp->map);
    fprintf(stderr, "\t\tordernum = %u\n", pp->ordernum);
    fprintf(stderr, "\t\tpeer     = \"%s\"\n", pp->peer);
}


/*
** XXXX Implement Me!
*/
ypresp_xfr *ypproc_xfr_2_svc(ypreq_xfr *xfr,
			     struct svc_req *rqstp)
{
    static ypresp_xfr result;
    struct sockaddr_in *rqhost;


    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
    {
	fprintf(stderr, "ypproc_xfr_2_svc(): [From: %s:%d]\n\tmap_parms:\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
	
	print_ypmap_parms(&xfr->map_parms);
	fprintf(stderr, "\t\ttransid = %u\n", xfr->transid);
	fprintf(stderr, "\t\tprog = %u\n", xfr->prog);
	fprintf(stderr, "\t\tport = %u\n", xfr->port);
	fprintf(stderr, "\t-> Request denied.\n");
    }

    result.transid = xfr->transid;
    result.xfrstat = YPXFR_REFUSED;
    
    return &result;
}


void *ypproc_clear_2_svc(void *dummy,
			 struct svc_req *rqstp)
{
    static int foo;
    struct sockaddr_in *rqhost;
    

    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
    	fprintf(stderr, "ypproc_clear_2_svc() [From: %s:%d]\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
		

    return (void *) &foo;
}


static int ypall_close(void *data)
{
    if (debug_flag && data == NULL)
	fprintf(stderr, "ypall_close() called with NULL pointer.\n");
	    
    gdbm_close((GDBM_FILE) data);
    return 0;
}


static int ypall_encode(ypresp_key_val *val,
			void *data)
{
    datum dkey, dval, okey;

    
    dkey.dptr = val->key.keydat_val;
    dkey.dsize = val->key.keydat_len;
    
    val->stat = read_database((GDBM_FILE) data, &dkey, &okey, &dval, F_NEXT);

    if (val->stat == YP_TRUE)
    {
	val->key.keydat_val = okey.dptr;
	val->key.keydat_len = okey.dsize;

	val->val.valdat_val = dval.dptr;
	val->val.valdat_len = dval.dsize;
    }

    
    return val->stat;
}


ypresp_all *ypproc_all_2_svc(ypreq_nokey *nokey,
			     struct svc_req *rqstp)
{
    static ypresp_all result;
    extern xdr_ypall_cb_t xdr_ypall_cb;
    struct sockaddr_in *rqhost;

    
    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
    {
	fprintf(stderr, "ypproc_all_2_svc(): [From: %s:%d]\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
		
	fprintf(stderr, "\t\tdomain = \"%s\"\n", nokey->domain);
	fprintf(stderr, "\t\tmap = \"%s\"\n", nokey->map);
    }

    xdr_ypall_cb.u.encode = NULL;
    xdr_ypall_cb.u.close  = NULL;
    xdr_ypall_cb.data = NULL;
    
    result.more = TRUE;
    
    if (nokey->map[0] == '\0' || nokey->domain[0] == '\0')
	result.ypresp_all_u.val.stat = YP_BADARGS;
    else if (!is_valid_domain(nokey->domain))
	result.ypresp_all_u.val.stat = YP_NODOM;
    else
    {
	datum dkey, dval;
	
	GDBM_FILE dbp = open_database(nokey->domain, nokey->map);
	if (dbp == NULL)
	    result.ypresp_all_u.val.stat = YP_NOMAP;
	else
	{
	    result.ypresp_all_u.val.stat = read_database(dbp,
							 NULL,
							 &dkey,
							 &dval,
							 0);

	    if (result.ypresp_all_u.val.stat == YP_TRUE)
	    {
		result.ypresp_all_u.val.key.keydat_len = dkey.dsize;
		result.ypresp_all_u.val.key.keydat_val = dkey.dptr;
		
		result.ypresp_all_u.val.val.valdat_len = dval.dsize;
		result.ypresp_all_u.val.val.valdat_val = dval.dptr;

		xdr_ypall_cb.u.encode = ypall_encode;
		xdr_ypall_cb.u.close  = ypall_close;
		xdr_ypall_cb.data = (void *) dbp;

		return &result;
	    }

	    gdbm_close(dbp);
	}
    }
    
    return &result;
}


ypresp_master *ypproc_master_2_svc(ypreq_nokey *nokey,
				   struct svc_req *rqstp)
{
    static ypresp_master result;
    struct sockaddr_in *rqhost;
    
    
    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
    {
	fprintf(stderr, "ypproc_master_2_svc(): [From: %s:%d]\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
		
	fprintf(stderr, "\t\tdomain = \"%s\"\n", nokey->domain);
	fprintf(stderr, "\t\tmap = \"%s\"\n", nokey->map);
    }

    if (result.peer)
    {
	free(result.peer);
	result.peer = NULL;
    }

    if (nokey->domain[0] == '\0')
	result.stat = YP_BADARGS;
    else if (!is_valid_domain(nokey->domain))
	result.stat = YP_NODOM;
    else
    {
	GDBM_FILE dbp = open_database(nokey->domain, nokey->map);
	if (dbp == NULL)
	    result.stat = YP_NOMAP;
	else
	{
	    datum key, val;

	    key.dsize = sizeof("YP_MASTER_NAME")-1;
	    key.dptr = "YP_MASTER_NAME";
	    
	    val = gdbm_fetch(dbp, key);
	    if (val.dptr == NULL)
	    {
		/* No YP_MASTER_NAME record in map? Assume we are Master */
		static char hostbuf[1025];

		gethostname(hostbuf, sizeof(hostbuf)-1);
		result.peer = strdup(hostbuf);
	    }
	    else
		result.peer = val.dptr;

	    result.stat = YP_TRUE;
	    gdbm_close(dbp);
	}
    }

    if (result.peer == NULL)
	result.peer = strdup("");

    if (debug_flag)
	fprintf(stderr, "\t-> Peer = \"%s\"\n", result.peer);
    
    return &result;
}


ypresp_order *ypproc_order_2_svc(ypreq_nokey *nokey,
				 struct svc_req *rqstp)
{
    static ypresp_order result;
    struct sockaddr_in *rqhost;
    
    
    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
    {
	fprintf(stderr, "ypproc_order_2_svc(): [From: %s:%d]\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
		
	fprintf(stderr, "\t\tdomain = \"%s\"\n", nokey->domain);
	fprintf(stderr, "\t\tmap = \"%s\"\n", nokey->map);
    }

    result.ordernum = 0;

    if (nokey->domain[0] == '\0')
	result.stat = YP_BADARGS;
    else if (!is_valid_domain(nokey->domain))
	result.stat = YP_NODOM;
    else
    {
	GDBM_FILE dbp = open_database(nokey->domain, nokey->map);
	if (dbp == NULL)
	    result.stat = YP_NOMAP;
	else
	{
	    datum key, val;

	    key.dsize = sizeof("YP_LAST_MODIFIED")-1;
	    key.dptr = "YP_LAST_MODIFIED";
	    
	    val = gdbm_fetch(dbp, key);
	    if (val.dptr == NULL)
	    {
		/* No YP_LAST_MODIFIED record in map? Use DTM timestamp.. */
		result.ordernum = get_dtm(nokey->domain, nokey->map);
	    }
	    else
	    {
		result.ordernum = atoi(val.dptr);
		free(val.dptr);
	    }

	    result.stat = YP_TRUE;
	    gdbm_close(dbp);
	}
    }

    if (debug_flag)
	fprintf(stderr, "-> Order # %d\n", result.ordernum);
    
    return &result;
}


static void free_maplist(ypmaplist *mlp)
{
    ypmaplist *next;

    while (mlp != NULL)
    {
	next = mlp->next;
	free(mlp->map);
	free(mlp);
	mlp = next;
    }
}

static int add_maplist(ypmaplist **mlhp,
		       char *map)
{
    ypmaplist *mlp;

    mlp = malloc(sizeof(*mlp));
    if (mlp == NULL)
	return -1;

    mlp->map = strdup(map);
    if (mlp->map == NULL)
    {
	free(mlp);
	return -1;
    }

    mlp->next = *mlhp;
    *mlhp = mlp;

    return 0;
}


ypresp_maplist *ypproc_maplist_2_svc(domainname *name,
				     struct svc_req *rqstp)
{
    static ypresp_maplist result;
    struct sockaddr_in *rqhost;
    

    rqhost = svc_getcaller(rqstp->rq_xprt);
    if (!is_valid_host(rqhost))
	return NULL;

    if (debug_flag)
    {
	fprintf(stderr, "ypproc_maplist_2_svc(): [From: %s:%d]\n",
		inet_ntoa(rqhost->sin_addr),
		ntohs(rqhost->sin_port));
		
	fprintf(stderr, "\t\tdomain = \"%s\"\n", *name);
    }

    if (result.maps)
	free_maplist(result.maps);
    
    result.maps = NULL;

    if ((*name)[0] == '\0')
	result.stat = YP_BADARGS;
    else if (!is_valid_domain(*name))
	result.stat = YP_NODOM;
    else
    {
	DIR *dp;

	dp = opendir(path_ypdb);
	if (dp == NULL)
	{
	    if (debug_flag)
		perror("opendir");
	    
	    result.stat = YP_BADDB;
	}
	else
	{
	    struct dirent *dep;
	    
	    while ((dep = readdir(dp)) != NULL)
		if (add_maplist(&result.maps, dep->d_name) < 0)
		{
		    result.stat = YP_YPERR;
		    break;
		}
	    
	    closedir(dp);
	}
    }
    
    if (debug_flag)
    {
	if (result.stat == YP_TRUE)
	{
	    ypmaplist *p;

	    p = result.maps;
	    fprintf(stderr, "-> ");
	    while (p)
	    {
		fprintf(stderr, "%s", p->map);
		if (p->next)
		    fprintf(stderr, ", ");
	    }
	    putc('\n', stderr);
	}
	else
	    fprintf(stderr, "\t-> Error #%d\n", result.stat);
    }
    
    return &result;
}
