/*
    YPS-0.2, NIS-Server for Linux
    Copyright (C) 1994  Tobias Reber

    This program 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.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
static char rcsid[]="(#)$Id: ypserv.c,v 2.0 1994/01/06 16:58:08 root Exp $";

/*
 *	$Author: root $
 *	$Log: ypserv.c,v $
 * Revision 2.0  1994/01/06  16:58:08  root
 * Version 2.0
 *
 * Revision 0.19  1994/01/02  23:01:31  root
 * Use MAXPATHLEN
 *
 * Revision 0.18  1994/01/02  21:59:08  root
 * Strict prototypes
 *
 * Revision 0.17  1994/01/02  20:10:08  root
 * Added GPL notice
 *
 * Revision 0.16  1994/01/02  18:00:38  root
 * Changed arguments to ypxfr
 *
 * Revision 0.15  1993/12/31  15:12:09  root
 * Use static buffer for key and data, free DBM buffers
 *
 * Revision 0.14  1993/12/30  22:21:49  root
 * Switch to GDBM
 *
 * Revision 0.13  1993/06/12  10:49:35  root
 * Align with include-4.4
 *
 */

#include <sys/types.h>
#include <sys/param.h>
#include <malloc.h>
#include <sys/dir.h>
#include <stdio.h>
#include <rpc/rpc.h>
#include <rpcsvc/yp.h>
#include <arpa/inet.h>

#ifdef GDBM
#include "dbmcompat.h"
#else
#include <dbm.h>
#endif

#ifdef DEBUG
#define PRINTF(x) printf x
#define FWRITE(x) fwrite x
#define PRLINENO printf(__FILE__ "(%d): ", __LINE__);
#else
#define PRINTF(x)
#define FWRITE(x)
#define PRLINENO
#endif

#ifndef YPDIR
#define YPDIR "/var/yp"
#endif
#ifndef YPETCDIR
#define YPETCDIR "/usr/local/sbin"
 
#endif

#undef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))

struct ypall_callback {
	int (*foreach)(char *, int, char **, int *, char **, int *);
	char *data;
};

static char staticKey[YPMAXRECORD];
static char staticVal[YPMAXRECORD];

static bool_t
ServesDomain(char *domain)
{
   char dbname[MAXPATHLEN];
   sprintf(dbname, "%s/%s", YPDIR, domain);
   PRINTF (("ServesDomain: dbname=%s\n", dbname));
   if (access(dbname, R_OK | X_OK)==0) {
	PRINTF(("TRUE(%d)\n", TRUE));
      return(TRUE);
   } else {
      return(FALSE);
   }
}

static bool_t
ServesMap(char * domain, char * map)
{
   char dbname[MAXPATHLEN];
   sprintf(dbname, "%s/%s/%s.dir", YPDIR, domain, map);
   PRINTF (("ServesMap: dbname=%s\n", dbname));
#ifndef GDBM
   if (access(dbname, R_OK)!=0) {
      return(FALSE);
   } else
#endif
   {
      sprintf(dbname, "%s/%s/%s.pag", YPDIR, domain, map);
      PRINTF (("ServesMap: dbname=%s\n", dbname));
      if (access(dbname, R_OK)!=0) {
         return(FALSE);
      } else {
         return(TRUE);
      }
   }
}

static enum ypstat
ypserv_match(char * domain, char * map, char * inkey, int inkeylen,
   char **outval, int *outvallen)
{
   char dbname[MAXPATHLEN];
   datum output, input;
   sprintf(dbname, "%s/%s/%s", YPDIR, domain, map);
   PRINTF (("ypserv_match: domain=%s, map=%s, keylen=%d key=",
      domain, dbname, inkeylen));
   FWRITE ((inkey, sizeof(char), inkeylen, stdout));
   PRINTF (("\n"));
   if (dbminit(dbname)) {
      return(YP_BADDB);
   }
   input.dptr=inkey;
   input.dsize=inkeylen;
   PRINTF (("ypserv_match: inkey=%08x dptr=%08x\n", (int) inkey,
        (int) input.dptr));
   PRINTF (("ypserv_match: domain=%s, map=%s, keylen=%d key=",
      domain, map, inkeylen));
   FWRITE ((inkey, sizeof(char), inkeylen, stdout));
   PRINTF (("\n"));
   PRINTF (("ypserv_match: domain=%s, map=%s, keylen=%d key=",
      domain, map, input.dsize));
   FWRITE ((input.dptr, sizeof(char), input.dsize, stdout));
   PRINTF (("\n"));
   output=fetch(input);
   if (output.dptr==NULL) {
      dbmclose();
      return(YP_NOKEY);
   }
   memcpy(staticVal, output.dptr, MIN(output.dsize, sizeof staticVal));
   free(output.dptr);
   *outval=staticVal;
   *outvallen=output.dsize;
   dbmclose();
   return(YP_TRUE);
}

static enum ypstat
ypserv_first( char *domain, char *map, char **outkey, int *outkeylen,
   char **outval, int *outvallen)
{
   char dbname[MAXPATHLEN];
   datum output;
   PRINTF (("ypserv_first: domain=%s, map=%s\n", domain, map));
   sprintf(dbname, "%s/%s/%s", YPDIR, domain, map);
   if (dbminit(dbname)) {
      return(YP_BADDB);
   }
   output=firstkey();
   if (output.dptr==NULL) {
      dbmclose();
      return(YP_NOMORE);
   }
   while(strncmp(output.dptr, "YP_", 3)==0) {
      char *o=output.dptr;
      output=nextkey(output);
      free(o);
      if (output.dptr==NULL) {
         dbmclose();
         return(YP_NOMORE);
      }
   }
   memcpy(staticKey, output.dptr, MIN(output.dsize, sizeof staticKey));
   *outkey=staticKey;
   *outkeylen=output.dsize;
   {
      char *o=output.dptr;
      output=fetch(output);
      free (o);
   }
   if (output.dptr==NULL) {
      dbmclose();
      return(YP_NOKEY);
   }
   memcpy(staticVal, output.dptr, MIN(output.dsize, sizeof staticVal));
   free(output.dptr);
   *outval=staticVal;
   *outvallen=output.dsize;
   dbmclose();
   return(YP_TRUE);
}

static enum ypstat
ypserv_next( char *domain, char *map, char *inkey, int inkeylen, char **outkey,
   int *outkeylen, char **outval, int *outvallen)
{
   char dbname[MAXPATHLEN];
   datum output, input;
   PRINTF (("ypserv_next: domain=%s, map=%s, keylen=%d key=",
      domain, map, inkeylen));
   FWRITE ((inkey, sizeof(*inkey), inkeylen, stdout));
   PRINTF (("\n"));
   sprintf(dbname, "%s/%s/%s", YPDIR, domain, map);
   if (dbminit(dbname)) {
      return(YP_BADDB);
   }
   input.dptr=inkeylen?inkey:"";
   input.dsize=inkeylen;
   output=nextkey(input);
   if (output.dptr==NULL) {
      dbmclose();
      return(YP_NOMORE);
   }
   while(strncmp(output.dptr, "YP_", 3)==0) {
      char *o=output.dptr;
      output=nextkey(output);
      free (o);
      if (output.dptr==NULL) {
         dbmclose();
         return(YP_NOMORE);
      }
   }
   memcpy(staticKey, output.dptr, MIN(output.dsize, sizeof staticKey));
   *outkey=staticKey;
   *outkeylen=output.dsize;
   {
      char *o=output.dptr;
      output=fetch(output);
      free(o);
   }
   if (output.dptr==NULL) {
      dbmclose();
      return(YP_NOKEY);
   }
   memcpy(staticVal, output.dptr, MIN(output.dsize, sizeof staticVal));
   free(output.dptr);
   *outval=staticVal;
   *outvallen=output.dsize;
   dbmclose();
   return(YP_TRUE);
}

static enum ypstat
ypserv_all_start( char *domain, char *map, char **outkey, int *outkeylen,
   char **outval, int *outvallen)
{
   char dbname[MAXPATHLEN];
   datum output;
   static char o[]="";

   *outkey=o; *outkeylen=strlen(*outkey);
   *outval=o; *outvallen=strlen(*outval);

   PRINTF (("ypserv_all_start: domain=%s, map=%s\n", domain, map));
   sprintf(dbname, "%s/%s/%s", YPDIR, domain, map);
   if (dbminit(dbname)) {
      return(YP_BADDB);
   }
   output=firstkey();
   if (output.dptr==NULL) {
      dbmclose();
      return(YP_NOMORE);
   }
   while(strncmp(output.dptr, "YP_", 3)==0) {
      char *o=output.dptr;
      output=nextkey(output);
      free(o);
      if (output.dptr==NULL) {
         dbmclose();
         return(YP_NOMORE);
      }
   }
   memcpy(staticKey, output.dptr, MIN(output.dsize, sizeof staticKey));
   *outkey=staticKey;
   *outkeylen=output.dsize;
   PRINTF (("ypserv_all_start: outkeylen=%d, outkey=", *outkeylen));
   FWRITE ((*outkey, 1, *outkeylen, stdout));
   PRINTF (("\n"));
   {
      char *o=output.dptr;
      output=fetch(output);
      free (o);
   }
   if (output.dptr==NULL) {
      dbmclose();
      return(YP_NOKEY);
   }
   memcpy(staticVal, output.dptr, MIN(output.dsize, sizeof staticVal));
   free(output.dptr);
   *outval=staticVal;
   *outvallen=output.dsize;
   PRINTF (("ypserv_all_start: outvallen=%d, outval=", *outvallen));
   FWRITE ((*outval, 1, *outvallen, stdout));
   PRINTF (("\n"));
   return(YP_TRUE);
}

static int
ypserv_all_foreach(char *inkey, int inkeylen, char **outkey,
   int *outkeylen, char **outval, int *outvallen)
{
   datum output, input;

   *outkey=""; *outkeylen=strlen(*outkey);
   *outval=""; *outvallen=strlen(*outval);

   PRINTF (("ypserv_all_foreach: keylen=%d key=", inkeylen));
   FWRITE ((inkey, sizeof(*inkey), inkeylen, stdout));
   PRINTF (("\n"));
   input.dptr=inkey;
   input.dsize=inkeylen;
   output=nextkey(input);
   while(strncmp(output.dptr, "YP_", 3)==0) {
      char *o=output.dptr;
      output=nextkey(output);
      free(o);
      if (output.dptr==NULL) {
         dbmclose();
         return((int) YP_NOMORE);
      }
   }
   if (output.dptr==NULL) {
      dbmclose();
      PRINTF (("ypserv_all_foreach: returning YP_NOMORE\n"));
      return((int)YP_NOMORE);
   }
   memcpy(staticKey, output.dptr, MIN(output.dsize, sizeof staticKey));
   *outkey=staticKey;
   *outkeylen=output.dsize;
   PRINTF (("ypserv_all_foreach: outkeylen=%d outkey=", *outkeylen));
   FWRITE ((*outkey, sizeof(**outkey), *outkeylen, stdout));
   PRINTF (("\n"));
   {
      char *o=output.dptr;
      output=fetch(output);
      free(o);
   }
   if (output.dptr==NULL) {
      dbmclose();
      PRINTF (("ypserv_all_foreach: returning YP_NOKEY\n"));
      return((int)YP_NOKEY);
   }
   memcpy(staticVal, output.dptr, MIN(output.dsize, sizeof staticVal));
   free(output.dptr);
   *outval=staticVal;
   *outvallen=output.dsize;
   PRINTF (("ypserv_all_foreach: outvallen=%d outval=", *outvallen));
   FWRITE ((*outval, sizeof(**outval), *outvallen, stdout));
   PRINTF (("\n"));
   return((int)YP_TRUE);
}

static enum ypstat
ypserv_master( char *domain, char *map, char **outmaster)
{
   char dbname[MAXPATHLEN];
   datum output, input;
   sprintf(dbname, "%s/%s/%s", YPDIR, domain, map);
   if (dbminit(dbname)) {
      return(YP_BADDB);
   }
   input.dptr="YP_MASTER_NAME";
   input.dsize=strlen(input.dptr);
   output=fetch(input);
   if (output.dptr==NULL) {
      dbmclose();
      return(YP_NOKEY);
   }
   *outmaster=output.dptr;
   *((*outmaster)+output.dsize)='\0';
   dbmclose();
   PRINTF (("ypserv_master: domain=%s, map=%s master=%s\n",
      domain, map, *outmaster));
   return(YP_TRUE);
}

static enum ypstat
ypserv_order( char *domain, char *map, int *outorder)
{
   char dbname[MAXPATHLEN];
   datum output, input;
   sprintf(dbname, "%s/%s/%s", YPDIR, domain, map);
   PRINTF (("%s\n", dbname));
   if (dbminit(dbname)) {
      return(YP_BADDB);
   }
   input.dptr="YP_LAST_MODIFIED";
   input.dsize=strlen(input.dptr);
   output=fetch(input);
   if (output.dptr==NULL) {
      dbmclose();
      return(YP_NOKEY);
   }
   *(output.dptr+output.dsize)='\0';
   dbmclose();
   if (sscanf(output.dptr, "%d", outorder)==0) {
      return(YP_BADDB);
   }
   PRINTF (("ypserv_order: domain=%s, map=%s ordernum=%d\n",
      domain, map, *outorder));
   return(YP_TRUE);
}

static enum ypstat
ypserv_maplist( char *domain, ypmaplist **first)
{
   static ypmaplist *current=(ypmaplist *)0;
   DIR *DomainDir;
   struct dirent *dp;
   char dbname[MAXNAMLEN+1];

   sprintf(dbname, "%s/%s", YPDIR, domain);

   while (current) {
      ypmaplist *n=current->next;
      free(current->map);
      free(current);
      current=n;
   }

   if ((DomainDir=opendir(dbname))==NULL) {
      return(YP_NODOM);
   }

   for (dp=readdir(DomainDir); dp!=NULL; dp=readdir(DomainDir)) {
      if ((dp->d_namlen>4) && (strcmp(".pag", dp->d_name+(dp->d_namlen-4))==0)) {
         ypmaplist *n=(ypmaplist *)malloc(sizeof (*n));
         n->next=current;
         n->map=malloc(dp->d_namlen-3);
         memcpy(n->map,dp->d_name, dp->d_namlen-4);
         n->map[dp->d_namlen-4]='\0';
         current=n;
      }
   }
   closedir(DomainDir);

   *first=current;
   return(YP_TRUE);
}

static enum ypxfrstat
ypserv_xfr(char *domain, char *map, int order, char *master, long transid,
   long prog, struct in_addr *ipadd, short port)
{
   extern char **environ;
   char ypxfr_command[MAXPATHLEN];
   switch(fork()) {
   case 0:
      strcpy(ypxfr_command, YPETCDIR);
      strcat(ypxfr_command, "/");
      strcat(ypxfr_command, "ypxfr");
#if 0
      execle(ypxfr_command, "ypxfr", "-d", domain, "-h", master, "-f",
         "-C", &transid, &prog, ipadd, &port, map, NULL, environ);
#else
      {
      char t[11], g[11], p[11];
      sprintf(t, "%u", transid);
      sprintf(g, "%u", prog);
      sprintf(p, "%u", port);
      PRLINENO; PRINTF(("t=%s g=%s i=%s p=%s\n",  __LINE__, t, g, inet_ntoa(*ipadd), p));
      execle(ypxfr_command, "ypxfr", "-d", domain, "-h", master, "-f",
         "-C", t, g, inet_ntoa(*ipadd), p, map, NULL, environ);
      }
#endif
      perror("execve");
      exit(0);
   case (-1):
      perror("fork");
   default:
      return YPXFR_SUCC;
}
/*
   enum ypxfrstat ypxfr_start();
   PRINTF (("ypserv_xfr: domain=%s map=%s order=%d master=%s\n",
      domain, map, order, master));
   return(ypxfr_start(domain, map, master, order));
*/
}
 
void *
ypproc_null_2( char *req, struct svc_req *rqstp)
{
   PRINTF (("ypproc_null:\n"));
   return((void *)TRUE);
}
bool_t *
ypproc_domain_2( domainname *req, struct svc_req *rqstp)
{
   static bool_t resp;
   PRINTF (("ypproc_domain: domain=%s\n", *req));
   if (ServesDomain(*req)) {
      resp=TRUE;
   } else {
      resp=FALSE;
   }
   return(&resp);
}
bool_t *
ypproc_domain_nonack_2( domainname *req, struct svc_req *rqstp)
{
   static bool_t resp;
   PRINTF (("ypproc_domain_nonack: domain=%s\n", *req));
   PRINTF (("%s,%d\n", inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr),
   	ntohs(rqstp->rq_xprt->xp_raddr.sin_port)));
   if (!ServesDomain(*req)) {
      PRINTF (("ypproc_domain_nonack: returning (nil)\n"));
      return((bool_t *)NULL);
   } else {
      resp=TRUE;
      PRINTF (("ypproc_domain_nonack: returning TRUE\n"));
   }
   return(&resp);
}
ypresp_val *
ypproc_match_2( ypreq_key *req, struct svc_req *rqstp)
{
   static ypresp_val resp;
   if (!ServesDomain(req->domain)) {
      resp.stat=YP_NODOM;
   } else if (!ServesMap(req->domain, req->map)) {
      resp.stat=YP_NOMAP;
   } else {
      resp.stat=ypserv_match(req->domain, req->map,
         req->key.keydat_val, req->key.keydat_len,
         &resp.val.valdat_val, &resp.val.valdat_len);
   }
   return(&resp);
}
ypresp_key_val *
ypproc_first_2( ypreq_nokey *req, struct svc_req *rqstp)
{
   static ypresp_key_val resp;
   if (!ServesDomain(req->domain)) {
      resp.stat=YP_NODOM;
   } else if (!ServesMap(req->domain, req->map)) {
      resp.stat=YP_NOMAP;
   } else {
      resp.stat=ypserv_first(req->domain, req->map,
         &resp.key.keydat_val, &resp.key.keydat_len,
         &resp.val.valdat_val, &resp.val.valdat_len);
   }
   return(&resp);
}
ypresp_key_val *
ypproc_next_2( ypreq_key *req, struct svc_req *rqstp)
{
   static ypresp_key_val resp;
   if (!ServesDomain(req->domain)) {
      resp.stat=YP_NODOM;
   } else if (!ServesMap(req->domain, req->map)) {
      resp.stat=YP_NOMAP;
   } else {
      resp.stat=ypserv_next(req->domain, req->map,
         req->key.keydat_val, req->key.keydat_len,
         &resp.key.keydat_val, &resp.key.keydat_len,
         &resp.val.valdat_val, &resp.val.valdat_len);
   }
   return(&resp);
}
ypresp_xfr *
ypproc_xfr_2( ypreq_xfr *req, struct svc_req *rqstp)
{
   static ypresp_xfr resp;
   PRINTF (("ypproc_xfr: domain=%s map=%s transid=%d\n",
      req->map_parms.domain, req->map_parms.map, req->transid));
   resp.transid=req->transid;
   if (!ServesDomain(req->map_parms.domain)) {
      resp.xfrstat=YPXFR_NODOM;
   } else if (!ServesMap(req->map_parms.domain, req->map_parms.map)) {
      resp.xfrstat=YPXFR_NOMAP;
   } else
   {
      struct sockaddr_in *caller;
      caller=svc_getcaller(rqstp->rq_xprt);
      resp.xfrstat=ypserv_xfr(req->map_parms.domain, req->map_parms.map,
         req->map_parms.ordernum, req->map_parms.peer, req->transid,
         req->prog, &(caller->sin_addr), req->port);
   }
   resp.xfrstat=YPXFR_SUCC;
   PRINTF (("ypproc_xfr: succeed.\n"));
   return(&resp);
}
void *
ypproc_clear_2( char *req, struct svc_req *rqstp)
{
   PRINTF (("ypproc_clear:\n"));
   return((void *)TRUE);
}
ypresp_all *
ypproc_all_2( ypreq_nokey *req, struct svc_req *rqstp)
{
   static ypresp_all resp;
   static struct ypall_callback callback;
   extern struct ypall_callback *xdr_ypall_callback;
   if (!ServesDomain(req->domain)) {
      resp.ypresp_all_u.val.stat=YP_NODOM;
   } else if (!ServesMap(req->domain, req->map)) {
      resp.ypresp_all_u.val.stat=YP_NOMAP;
   } else {
      resp.ypresp_all_u.val.stat=ypserv_all_start(req->domain, req->map,
         &resp.ypresp_all_u.val.key.keydat_val,
         &resp.ypresp_all_u.val.key.keydat_len,
         &resp.ypresp_all_u.val.val.valdat_val,
         &resp.ypresp_all_u.val.val.valdat_len);
   }
   resp.more=TRUE /* (resp.ypresp_all_u.val.stat==YP_TRUE) */;
   PRINTF (("back from ypserv_all_start\n"));
   callback.foreach=ypserv_all_foreach;
   xdr_ypall_callback=(&callback);
   return(&resp);
}
ypresp_master *
ypproc_master_2( ypreq_nokey *req, struct svc_req *rqstp)
{
   static ypresp_master resp;
   if (!ServesDomain(req->domain)) {
      resp.stat=YP_NODOM;
   } else if (!ServesMap(req->domain, req->map)) {
      resp.stat=YP_NOMAP;
   } else {
      resp.stat=ypserv_master(req->domain, req->map,
         &resp.peer);
   }
   return(&resp);
}

ypresp_order *
ypproc_order_2( ypreq_nokey *req, struct svc_req *rqstp)
{
   static ypresp_order resp;
   if (!ServesDomain(req->domain)) {
      resp.stat=YP_NODOM;
   } else if (!ServesMap(req->domain, req->map)) {
      resp.stat=YP_NOMAP;
   } else {
      resp.stat=ypserv_order(req->domain, req->map,
         &resp.ordernum);
   }
   return(&resp);
}

ypresp_maplist *
ypproc_maplist_2( domainname *req, struct svc_req *rqstp)
{
   static ypresp_maplist resp;
   if (!ServesDomain(*req)) {
      resp.stat=YP_NODOM;
   } else {
      resp.stat=ypserv_maplist(*req, &resp.maps);
      PRINTF (("ypproc_maplist: ypserv_maplist returned\n"));
   }
   return(&resp);
}
