/*
    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: ypclnt.c,v 2.0 1994/01/06 16:58:48 root Exp $";

/*
 *	$Author: root $
 *	$Log: ypclnt.c,v $
 * Revision 2.0  1994/01/06  16:58:48  root
 * Version 2.0
 *
 * Revision 0.17  1994/01/02  22:48:22  root
 * Added strict prototypes
 *
 * Revision 0.16  1994/01/02  20:09:39  root
 * Added GPL notice
 *
 * Revision 0.15  1993/12/30  22:34:57  root
 * *** empty log message ***
 *
 * Revision 0.14  1993/12/19  12:42:32  root
 * *** empty log message ***
 *
 * Revision 0.13  1993/06/12  09:39:30  root
 * Align with include-4.4
 *
 */

#include <sys/types.h>
#include <sys/time.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#include <rpcsvc/yp.h>
/*
 * ypclnt.h does not have a definition for struct dom_binding,
 * although it is used there. It is defined in yp_prot.h, but
 * we cannot use it here.
 */
struct dom_binding {
	void * m;
};
#include <rpcsvc/ypclnt.h>
 
#ifdef DEBUG
#define PRINTF(x) printf x
#define PRLINENO fprintf(stderr, __FILE__ "(%d): ", __LINE__)
#else
#define PRINTF(x)
#define PRLINENO
#endif

#if 0
#define SOCKSERVER 0x7f000001
#endif

static struct sockaddr_in ServerAddress;
static CLIENT *UdpClient=NULL, *TcpClient=NULL;
 
#ifdef YPBROADCAST
static bool_t
eachresult( caddr_t resultsp, struct sockaddr_in *raddr)
{
   bcopy(raddr, &ServerAddress, sizeof(ServerAddress));
   return((bool_t) TRUE);
}
#endif
 
static struct sockaddr_in *
do_ypbind(domainname d)
{
   static struct sockaddr_in resp;
   int rc;
   ypbind_resp r;
   CLIENT *localBindClient;
   struct sockaddr_in localAddr;
   int s;
   struct timeval t={5,0}, tott={25,0};

   s=RPC_ANYSOCK;
   bzero(&localAddr, sizeof localAddr);
   localAddr.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
   localBindClient=clntudp_create(&localAddr, YPBINDPROG, YPBINDVERS, tott, &s);
   if (!localBindClient) {
      PRLINENO;
      clnt_pcreateerror("");
      return NULL;
   }
   
   rc=clnt_call(localBindClient, YPBINDPROC_DOMAIN, 
      xdr_domainname, (char *)&d, xdr_ypbind_resp, (char *)&r, t);
   if (rc) {
      PRLINENO;
      clnt_perrno(rc);
      return NULL;
   }

   switch (r.ypbind_status) {
   case YPBIND_FAIL_VAL:
      switch(r.ypbind_resp_u.ypbind_error) {
      case YPBIND_ERR_ERR:
         fprintf(stderr, "YPBINDPROC_DOMAIN: Internal error\n");
         break;
      case YPBIND_ERR_NOSERV:
         fprintf(stderr, "YPBINDPROC_DOMAIN: No bound server for passed domain\n");
         break;
      case YPBIND_ERR_RESC:
         fprintf(stderr, "YPBINDPROC_DOMAIN: System resource allocation failure\n");
         break;
      default:
         fprintf(stderr, "YPBINDPROC_DOMAIN: Unknown error\n");
         break;
      }
      return NULL;
   case YPBIND_SUCC_VAL:
      {
         struct ypbind_binding *y=&r.ypbind_resp_u.ypbind_bindinfo;
         bzero(&resp, sizeof resp);
         resp.sin_family=AF_INET;
         resp.sin_addr=*(struct in_addr *)(y->ypbind_binding_addr);
         return &resp;
      }
   }
   return NULL;
}

void
yp_unbind(char *DomainName)
{
   if (UdpClient) clnt_destroy(UdpClient);
   UdpClient=NULL;
   if (TcpClient) clnt_destroy(TcpClient);
   TcpClient=NULL;
}
 
int
_yp_bind(struct sockaddr_in *ServerAddress, char *DomainName)
{
   struct sockaddr_in UdpServerAddress, TcpServerAddress;
   int UdpSockp, TcpSockp;
   static struct timeval Wait = { 5, 0 };
 
   if (UdpClient || TcpClient) yp_unbind(DomainName);
 
   bcopy(ServerAddress, &UdpServerAddress, sizeof(*ServerAddress));
   UdpServerAddress.sin_port=0;
   UdpSockp=(RPC_ANYSOCK);
   bcopy(ServerAddress, &TcpServerAddress, sizeof(*ServerAddress));
   TcpServerAddress.sin_port=0;
   TcpSockp=(RPC_ANYSOCK);
   PRLINENO; PRINTF (("%s,%d\n", inet_ntoa(UdpServerAddress.sin_addr),
      UdpServerAddress.sin_port));
   if ((UdpClient=clntudp_create(&UdpServerAddress, YPPROG, YPVERS,
      Wait, &UdpSockp))==NULL) {
      clnt_pcreateerror("UdpClient");
      return(YPERR_RPC);
   }
   if ((TcpClient=clnttcp_create(&TcpServerAddress, YPPROG, YPVERS,
      &TcpSockp, 0, 0))==NULL) {
      clnt_pcreateerror("TcpClient");
      return(YPERR_RPC);
   }
   PRLINENO; PRINTF (("UdpClient=0x%x\n", (int) UdpClient));
   PRLINENO; PRINTF (("TcpClient=0x%x\n", (int) TcpClient));
   return(0);
 
}
 
int
yp_bind(char *DomainName)
{
#  ifdef YPBROADCAST
   enum clnt_stat clnt_stat;
   static bool_t res;
#  endif
   static domainname domain;
 
   domain=DomainName;
#  ifndef SOCKSERVER
#  ifdef YPBROADCAST
   bzero(&ServerAddress, sizeof ServerAddress);
   if ((clnt_stat=clnt_broadcast(YPPROG, YPVERS,
      YPPROC_DOMAIN_NONACK, xdr_domainname, (char *)&domain, xdr_bool,
      (char *)&res, eachresult))!=RPC_SUCCESS) {
      clnt_perrno(clnt_stat);
      return(YPERR_DOMAIN);
   }
#  else
   {
      struct sockaddr_in *s=do_ypbind(DomainName);
      if (!s) return(YPERR_DOMAIN);
      ServerAddress=*s;
   }
#  endif
#  else
   bzero(&ServerAddress, sizeof ServerAddress);
   ServerAddress.sin_family=AF_INET;
   ServerAddress.sin_addr.s_addr=htonl(SOCKSERVER);
#  endif SOCKSERVER
   return (_yp_bind(&ServerAddress, DomainName));
}
 
int
yp_match( char *DomainName, char *MapName, char *Key,
   int KeyLength, char **Value, int *ValueLength)
{
   static ypreq_key req;
   ypresp_val *resp;
   int Status;
 
   do {
      if (UdpClient==NULL)
         if ((Status=yp_bind(DomainName))) return(Status);
 
      req.domain=DomainName;
      req.map=MapName;
      req.key.keydat_len=KeyLength;
      req.key.keydat_val=Key;
      if ((resp=ypproc_match_2(&req, UdpClient))==NULL) {
         clnt_perror(UdpClient, "ypmatch");
         yp_unbind(DomainName);
      }
   } while(resp==NULL);
   if (resp->stat!=YP_TRUE) {
      Status=ypprot_err(resp->stat);
   } else {
      (*ValueLength)=resp->val.valdat_len;
      if (((*Value)=malloc((*ValueLength)+2))!=NULL) {
         bcopy(resp->val.valdat_val, *Value, *ValueLength);
         bcopy("\n", (*Value)+(*ValueLength), 2);
      }
      Status=(*Value)?0:YPERR_RESRC;
   }
   clnt_freeres(UdpClient, xdr_ypresp_val, resp);
   return(Status);
}
 
int
yp_first( char *DomainName, char *MapName, char **OutKey,
   int *OutKeyLength, char **Value, int *ValueLength)
{
   static ypreq_key req;
   ypresp_key_val *resp;
   int Status;
 
   do {
      if (UdpClient==NULL)
         if ((Status=yp_bind(DomainName))) return(Status);
 
      req.domain=DomainName;
      req.map=MapName;
      req.key.keydat_len=0;
      req.key.keydat_val=NULL;
      if ((resp=ypproc_first_2(&req, UdpClient))==NULL) {
         clnt_perror(UdpClient, "ypfirst");
         yp_unbind(DomainName);
      }
   } while(resp==NULL);
   if (resp->stat!=YP_TRUE) {
      Status=ypprot_err(resp->stat);
   } else {
      (*Value)=(*OutKey)=NULL;
      (*OutKeyLength)=resp->key.keydat_len;
      if (((*OutKey)=malloc((*OutKeyLength)+2))!=NULL) {
         bcopy(resp->key.keydat_val, *OutKey, *OutKeyLength);
         bcopy("\n", (*OutKey)+(*OutKeyLength), 2);
         (*ValueLength)=resp->val.valdat_len;
         if (((*Value)=malloc((*ValueLength)+2))!=NULL) {
            bcopy(resp->val.valdat_val, *Value, *ValueLength);
            bcopy("\n", (*Value)+(*ValueLength), 2);
         } else
            free(*OutKey);
      }
      Status=(*Value)?0:YPERR_RESRC;
   }
   clnt_freeres(UdpClient, xdr_ypresp_key_val, resp);
   return(Status);
}
 
int
yp_next( char *DomainName, char *MapName, char *Key, int KeyLength,
   char **OutKey, int *OutKeyLength, char **Value, int *ValueLength)
{
   static ypreq_key req;
   ypresp_key_val *resp;
   int Status;
 
   do {
      if (UdpClient==NULL)
         if ((Status=yp_bind(DomainName))) return(Status);
 
      req.domain=DomainName;
      req.map=MapName;
      req.key.keydat_len=KeyLength;
      req.key.keydat_val=Key;
      if ((resp=ypproc_next_2(&req, UdpClient))==NULL) {
         clnt_perror(UdpClient, "ypnext");
         yp_unbind(DomainName);
      }
   } while(resp==NULL);
   if (resp->stat!=YP_TRUE) {
      Status=ypprot_err(resp->stat);
   } else {
      (*Value)=(*OutKey)=NULL;
      (*OutKeyLength)=resp->key.keydat_len;
      if (((*OutKey)=malloc((*OutKeyLength)+2))!=NULL) {
         bcopy(resp->key.keydat_val, *OutKey, *OutKeyLength);
         bcopy("\n", (*OutKey)+(*OutKeyLength), 2);
         (*ValueLength)=resp->val.valdat_len;
         if (((*Value)=malloc((*ValueLength)+2))!=NULL) {
            bcopy(resp->val.valdat_val, *Value, *ValueLength);
            bcopy("\n", (*Value)+(*ValueLength), 2);
         } else
            free(*OutKey);
      }
      Status=(*Value)?0:YPERR_RESRC;
   }
   clnt_freeres(UdpClient, xdr_ypresp_key_val, resp);
   return(Status);
}
 
int
yp_all( char *DomainName, char *MapName, struct ypall_callback *CallBack)
{
   static ypreq_nokey req;
   ypresp_all *resp;
   extern struct ypall_callback *xdr_ypall_callback;
   int Status;
 
   do {
      if (TcpClient==NULL)
         if ((Status=yp_bind(DomainName))) return(Status);
 
      req.domain=DomainName;
      req.map=MapName;
      xdr_ypall_callback=CallBack;
#ifdef DEBUG
      {
	int i;
	register char *c=(char *)TcpClient;
	PRLINENO;
	for (i=0; i<sizeof (CLIENT); i++, c++) PRINTF(("%02x", *c));
	PRINTF(("\n"));
      }
#endif
      if ((resp=ypproc_all_2(&req, TcpClient))==NULL) {
         clnt_perror(TcpClient, "ypall");
         yp_unbind(DomainName);
      }
#ifdef DEBUG
      {
	int i;
	register char *c=(char *)TcpClient;
	PRLINENO;
	for (i=0; i<sizeof (CLIENT); i++, c++) PRINTF(("%02x", *c));
	PRINTF(("\n"));
      }
#endif
   } while(resp==NULL);
   PRLINENO; PRINTF(("Status=%d\n", resp->ypresp_all_u.val.stat));
   switch (resp->ypresp_all_u.val.stat) {
   case YP_TRUE:
   case YP_NOMORE:
      Status=0;
      break;
   default:
      Status=ypprot_err(resp->ypresp_all_u.val.stat);
   }
   clnt_freeres(TcpClient, xdr_ypresp_all, resp);
   PRLINENO; PRINTF(("yp_all returns Status=%d\n", Status));
   return(Status);
}
 
int
yp_order( char *DomainName, char *MapName, int *OutOrder)
{
   static ypreq_nokey req;
   ypresp_order *resp;
   int Status;
 
   do {
      if (UdpClient==NULL)
         if ((Status=yp_bind(DomainName))) return(Status);
 
      req.domain=DomainName;
      req.map=MapName;
      if ((resp=ypproc_order_2(&req, UdpClient))==NULL) {
         clnt_perror(UdpClient, "yporder");
         yp_unbind(DomainName);
      }
   } while(resp==NULL);
   if (resp->stat!=YP_TRUE) {
      Status=ypprot_err(resp->stat);
   } else {
      (*OutOrder)=resp->ordernum;
      Status=0;
   }
   clnt_freeres(UdpClient, xdr_ypresp_order, resp);
   return(Status);
}
 
int
yp_master( char *DomainName, char *MapName, char **OutMaster)
{
   static ypreq_nokey req;
   ypresp_master *resp;
   int Status;
 
   do {
      if (UdpClient==NULL)
         if ((Status=yp_bind(DomainName))) return(Status);
 
      req.domain=DomainName;
      req.map=MapName;
      if ((resp=ypproc_master_2(&req, UdpClient))==NULL) {
         clnt_perror(UdpClient, "ypmaster");
         yp_unbind(DomainName);
      }
   } while(resp==NULL);
   if (resp->stat!=YP_TRUE) {
      Status=ypprot_err(resp->stat);
   } else {
      if (((*OutMaster)=malloc(strlen(resp->peer)+1))!=NULL) {
         strcpy(*OutMaster, resp->peer);
         Status=0;
      } else {
         Status=YPERR_RESRC;
      }
   }
   clnt_freeres(UdpClient, xdr_ypresp_master, resp);
   return(Status);
}
 
char *yperr_string(incode)
   int incode;
{
   switch(incode) {
      case YPERR_BADARGS: return("Args to function are bad");
      case YPERR_RPC: return("RPC failure");
      case YPERR_DOMAIN: return("Can't bind to a server which serves this domain.");
      case YPERR_MAP: return("No such map in server's domain");
      case YPERR_KEY: return("No such key in map");
      case YPERR_YPERR: return("Internal yp server or client interface error");
      case YPERR_RESRC: return("Local resource allocation failure");
      case YPERR_NOMORE: return("No more records in map database");
      case YPERR_PMAP: return("Can't communicate with portmapper");
      case YPERR_YPBIND: return("Can't communicate with ypbind");
      case YPERR_YPSERV: return("Can't communicate with ypserv");
      case YPERR_NODOM: return("Local domain name not set");
      case YPERR_BADDB: return(" yp data base is bad");
      case YPERR_VERS: return("YP version mismatch");
      default: return("Args to function are bad");
   }
}
 
int
ypprot_err( unsigned int incode)
{
   switch(incode) {
      case YP_TRUE: return(0);
      case YP_NOMORE: return(YPERR_NOMORE);
      case YP_FALSE: return(YPERR_BADARGS);
      case YP_NOMAP: return(YPERR_MAP);
      case YP_NODOM: return(YPERR_NODOM);
      case YP_NOKEY: return(YPERR_KEY);
      case YP_BADOP: return(YPERR_BADARGS);
      case YP_BADDB: return(YPERR_BADDB);
      case YP_YPERR: return(YPERR_YPERR);
      case YP_BADARGS: return(YPERR_BADARGS);
      case YP_VERS: return(YPERR_VERS);
      default: return(YPERR_BADARGS);
   }
}

int
_yp_clear(char *DomainName)
{
   void *resp;
   int Status;

   do {
      if (UdpClient==NULL)
         if ((Status=yp_bind(DomainName))) return(Status);
      if ((resp=ypproc_clear_2(NULL, UdpClient))==NULL) {
         clnt_perror(UdpClient, "_yp_clear");
         yp_unbind(DomainName);
      }
   } while(resp==NULL);
   return 0;
}

int
_yp_maplist(char *DomainName, ypmaplist **OutMaplist)
{
   ypresp_maplist *resp;
   ypmaplist *m;
   ypmaplist *r=NULL;
   int Status;

   do {
      if (UdpClient==NULL)
         if ((Status=yp_bind(DomainName))) return(Status);
      if ((resp=ypproc_maplist_2(&DomainName, TcpClient))==NULL) {
         clnt_perror(UdpClient, "_yp_maplist");
         yp_unbind(DomainName);
      }
   } while(resp==NULL);
   if (resp->stat!=YP_TRUE) {
      Status=ypprot_err(resp->stat);
   } else {
      Status=0;
      for (m=resp->maps; m; m=m->next) {
         struct ypmaplist *rr=r;
         r=malloc(sizeof (struct ypmaplist));
         if (!r) {
            Status=YPERR_RESRC;
            break;
         }
         r->map=malloc(strlen(m->map)+1);
         if (!r->map) {
            Status=YPERR_RESRC;
            break;
         }
         strcpy(r->map, m->map);
         r->next=rr;
      }
      (*OutMaplist)=NULL;
      if (!Status) while (r) {
         struct ypmaplist *rr=r->next;
         r->next=*OutMaplist;
         (*OutMaplist)=r;
         r=rr;
      }
   }
   clnt_freeres(UdpClient, xdr_ypresp_maplist, resp);

   return(Status);
}
