/*                                                                       */
/*                                                                       */
/*                      RESTRICTED RIGHTS LEGEND                         */
/*                                                                       */
/* Use, duplication, or disclosure by the Government is subject to       */
/* restrictions as set forth in subdivision (c)(1)(ii) of the Rights in  */
/* Technical Data and Computer Software clause at 252.227-7013.          */
/*                                                                       */
/*                    TEXAS INSTRUMENTS INCORPORATED.                    */
/*                            P.O. BOX 149149                            */
/*                         AUSTIN, TEXAS 78714-9149                      */
/*                              MS 2151                                  */
/*                                                                       */
/*  Copyright (C)   1987,1988,1989,1990 Texas Instruments Incorporated.  */
/*  All rights reserved.                                                 */
/*                                                                       */

#ifndef lint
static	char sccsid[] = "@(#)portmap.c 1.1 86/02/03 Copyr 1984 Sun Micro";
#endif

/*
 * Copyright (c) 1984 by Sun Microsystems, Inc.
 */

/*
 * portmap.c, Implements the program,version to port number mapping for
 * rpc.
 */

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

#include <rpc.h>
#include <pmap_prot.h>
#include "::bsd:netdb.h"
#include "::bsd:sys:socket.h"
#include "::bsd:sys:time.h"
#include <stdio.h>
#include <Micronet-device.h>
#include <Micronet-accessors.h>
#include <errors.h>
#include <serial.h>
#include <sockets.h>
#include <strings.h>
#include <errno.h>

/*unsigned int DEBUGFLAG; */

char *malloc();
int reg_service();
int svc_fds;


#ifdef DEBUG

#include "::mx-lib:wlw.h"

#endif

portmap_initialize()
{
  int error;
  SVCXPRT *xprt;
  int sock;
  struct sockaddr_in addr;
  int len = sizeof(struct sockaddr_in);

  if ((sock = socket(AF_MAC, SOCK_DGRAM, PF_MAC)) == -1)
    {
#ifdef DEBUG
      PrtWarnMsg("\pportmap cannot create socket", 0); /* use notification manager!!!! */
#endif
      return(-1);
    }
  
  addr.sin_addr.S_un.S_addr = 0;
  addr.sin_family = AF_MAC;
  addr.sin_port = htons(PMAPPORT);
  if (error = bind(sock, (struct sockaddr *)&addr, len) != 0)
    {
#ifdef DEBUG
      PrtWarnMsg("\pportmap cannot bind socket", error); 
#endif
      return(-1);
    }
  
  if ((xprt = svcmac_create(sock)) == (SVCXPRT *)NULL)
    {
#ifdef DEBUG
      PrtWarnMsg("\pPortmap error", 0);
#endif
      return(-1);
    }
  
  (void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE);

  DriverData->portmap_initialized = true;
  return(0);
}


struct pmaplist *pmaplist;

static struct pmaplist *
  find_service(prog, vers, prot)
u_long prog;
u_long vers;
{
  register struct pmaplist *hit = NULL;
  register struct pmaplist *pml;
  
  for (pml = pmaplist; pml != NULL; pml = pml->pml_next) {
    if ((pml->pml_map.pm_prog != prog) ||
	(pml->pml_map.pm_prot != prot))
      continue;
    hit = pml;
    if (pml->pml_map.pm_vers == vers)
      break;
  }
  return (hit);
}

/*
  The next three routines are used to implement portmap calls from the micronet driver. They are coded
  specially so that RPC will never have to recurse, or wait for a reply from some other process. reg_service
  will still be used to answer foreign (ie. Mx) portmap requests.
*/

#define SUCCESS -1

unsigned long
ioctl_pmap_set(parms)
     struct pmap *parms;
{
	struct pmaplist *pml, *fnd;
	fnd = find_service(parms->pm_prog, parms->pm_vers, parms->pm_prot);
	if (fnd && fnd->pml_map.pm_vers == parms->pm_vers)
	  {
		  if (fnd->pml_map.pm_port == parms->pm_port)
		    /* Already there with same port. */
		    return SUCCESS;
		  else
		    /* Already there but with a different port. */
		    return false;
	  }
	else
	  /* Not already there, add an element. */
	  {
		  pml = (struct pmaplist *) malloc((u_int) sizeof(struct pmaplist));
		  pml->pml_map = *parms; /* This should make a copy of parms. */
		  pml->pml_next = 0;
		  if (pmaplist == 0)
		    /* This is the first element added to the list. */
		    {
			    pmaplist = pml;
		    }
		  else
		    /* Tack it on the end. */
		    {
			    for (fnd= pmaplist; fnd->pml_next != 0;
				 fnd = fnd->pml_next);
			    fnd->pml_next = pml;
		    }
		  return SUCCESS;
	  }
}

unsigned long
ioctl_pmap_unset(parms)
     struct pmap *parms;
{
	struct pmaplist *pml, *prevpml;
	caddr_t t;
	
	for (prevpml = NULL, pml = pmaplist; pml != NULL; )
	  {
		  if ((pml->pml_map.pm_prog != parms->pm_prog) || (pml->pml_map.pm_vers != parms->pm_vers))
		    {
			    prevpml = pml;
			    pml = pml->pml_next;
			    continue;
		    }
		  /* Found it. */
		  t = (caddr_t)pml;
		  pml = pml->pml_next;
		  if (prevpml == NULL)
		    /* This is now the first element in the list. */
		    pmaplist = pml;
		  else
		    /* Close the link. */
		    prevpml->pml_next = pml;
		  free(t);
		  return SUCCESS;
	  }
	return false;
}

unsigned long
ioctl_pmap_get(parms)
     struct pmap *parms;
{
	struct pmaplist *fnd;
	
	if (fnd = find_service(parms->pm_prog, parms->pm_vers, parms->pm_prot))
	  parms->pm_port = fnd->pml_map.pm_port;
	else
	  parms->pm_port = 0;
	return SUCCESS;

}

reg_service(rqstp, xprt)
     struct svc_req *rqstp;
     SVCXPRT *xprt;
{
  struct pmap reg;
  struct pmaplist *pml, *prevpml, *fnd;
  int ans, port;
  caddr_t t;
  
#ifdef DEBUG
  if(DEBUGFLAG)
    printf("server: about do a switch\n");
#endif
  switch (rqstp->rq_proc) {
    
  case PMAPPROC_NULL:
    if ((!svc_sendreply(xprt, xdr_void, NULL)) && DEBUGFLAG) {
/*
      exit();
*/
    }
    break;
    
  case PMAPPROC_SET:
    if (!svc_getargs(xprt, xdr_pmap, &reg))
      svcerr_decode(xprt);
    else {
      fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
      if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) {
	if (fnd->pml_map.pm_port == reg.pm_port) {
	  ans = 1;
	  goto done;
	}
	else {
	  ans = 0;
	  goto done;
	}
      } else {
	pml = (struct pmaplist *)
	  malloc((u_int)sizeof(struct pmaplist));
	pml->pml_map = reg;
	pml->pml_next = 0;
	if (pmaplist == 0) {
	  pmaplist = pml;
	} else {
	  for (fnd= pmaplist; fnd->pml_next != 0;
	       fnd = fnd->pml_next);
	  fnd->pml_next = pml;
	}
	ans = 1;
      }
    done:
      if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
	  DEBUGFLAG) {
#ifdef DEBUG
	printf("svc_sendreply\n");
/*
	exit();
*/
#endif
      }
    }
    break;
    
  case PMAPPROC_UNSET:
    if (!svc_getargs(xprt, xdr_pmap, &reg))
      svcerr_decode(xprt);
    else {
      ans = 0;
      for (prevpml = NULL, pml = pmaplist; pml != NULL; ) {
	if ((pml->pml_map.pm_prog != reg.pm_prog) ||
	    (pml->pml_map.pm_vers != reg.pm_vers)) {
	  prevpml = pml;
	  pml = pml->pml_next;
	  continue;
	}
	ans = 1;
	t = (caddr_t)pml;
	pml = pml->pml_next;
	if (prevpml == NULL)
	  pmaplist = pml;
	else
	  prevpml->pml_next = pml;
	free(t);
      }
      if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
	  DEBUGFLAG) {
#ifdef DEBUG
	printf("svc_sendreply\n");
/*
	exit();
*/
#endif
      }
    }
    break;
    
  case PMAPPROC_GETPORT:
    if (!svc_getargs(xprt, xdr_pmap, &reg))
      svcerr_decode(xprt);
    else {
      fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
      if (fnd)
	port = fnd->pml_map.pm_port;
      else
	port = 0;
      if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&port)) &&
	  DEBUGFLAG) {
#ifdef DEBUG
	printf("svc_sendreply: port = %x\n", port);
/*
	exit();
*/
#endif
      }
    }
    break;
    
  case PMAPPROC_DUMP:
    if (!svc_getargs(xprt, xdr_void, NULL))
      svcerr_decode(xprt);
    else {
      if ((!svc_sendreply(xprt, xdr_pmaplist,
			  (caddr_t)&pmaplist)) && DEBUGFLAG) {
#ifdef DEBUG
	printf("svc_sendreply\n");
/*
	exit();
*/
#endif
      }
    }
    break;
  default:
    svcerr_noproc(xprt);
    break;
  }
#ifdef DEBUG
  if(DEBUGFLAG)
    printf("pmaplist = %x\n", pmaplist);
#endif
}


#define ARGSIZE 9000

typedef struct encap_parms {
	u_long arglen;
	char *args;
};

static bool_t
xdr_encap_parms(xdrs, epp)
	XDR *xdrs;
	struct encap_parms *epp;
{

	return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE));
}

typedef struct rmtcallargs {
	u_long	rmt_prog;
	u_long	rmt_vers;
	u_long	rmt_port;
	u_long	rmt_proc;
	struct encap_parms rmt_args;
};

static bool_t
xdr_rmtcall_args(xdrs, cap)
	register XDR *xdrs;
	register struct rmtcallargs *cap;
{

	if (xdr_u_long(xdrs, &(cap->rmt_prog)) &&
	    xdr_u_long(xdrs, &(cap->rmt_vers)) &&
	    xdr_u_long(xdrs, &(cap->rmt_proc))) {
		return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
	}
	return (FALSE);
}

static bool_t
xdr_rmtcall_result(xdrs, cap)
	register XDR *xdrs;
	register struct rmtcallargs *cap;
{
	if (xdr_u_long(xdrs, &(cap->rmt_port)))
		return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
	return (FALSE);
}


static bool_t
xdr_opaque_parms(xdrs, cap)
	XDR *xdrs;
	struct rmtcallargs *cap;
{

	return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen));
}


static bool_t
xdr_len_opaque_parms(xdrs, cap)
	register XDR *xdrs;
	struct rmtcallargs *cap;
{
	register u_int beginpos, lowpos, highpos, currpos, pos;

	beginpos = lowpos = pos = xdr_getpos(xdrs);
	highpos = lowpos + ARGSIZE;
	while ((int)(highpos - lowpos) >= 0) {
		currpos = (lowpos + highpos) / 2;
		if (xdr_setpos(xdrs, currpos)) {
			pos = currpos;
			lowpos = currpos + 1;
		} else {
			highpos = currpos - 1;
		}
	}
	xdr_setpos(xdrs, beginpos);
	cap->rmt_args.arglen = pos - beginpos;
	return (xdr_opaque_parms(xdrs, cap));
}


static
callit(rqstp, xprt)
	struct svc_req *rqstp;
	SVCXPRT *xprt;
{
	char buf[2000];
	struct rmtcallargs a;
	struct pmaplist *pml;
	u_short port;
	struct sockaddr_in me;
	int socket = -1;
	CLIENT *client;
	struct authunix_parms *au = (struct authunix_parms *)rqstp->rq_clntcred;
	struct timeval timeout;

	timeout.tv_sec = 5;
	timeout.tv_usec = 0;
	a.rmt_args.args = buf;
	if (!svc_getargs(xprt, xdr_rmtcall_args, &a))
	    return;
	if ((pml = find_service(a.rmt_prog, a.rmt_vers, PF_MAC)) == NULL)
	    return;
	port = pml->pml_map.pm_port;
	get_myaddress(&me);
	me.sin_port = htons(port);
	client = clntudp_create(&me, a.rmt_prog, a.rmt_vers, timeout, &socket);
	if (client != (CLIENT *)NULL) {
		if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
			client->cl_auth = authunix_create(au->aup_machname,
			   au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids);
		}
		a.rmt_port = (u_long)port;
		if (clnt_call(client, a.rmt_proc, xdr_opaque_parms, &a,
		    xdr_len_opaque_parms, &a, timeout) == RPC_SUCCESS) {
			svc_sendreply(xprt, xdr_rmtcall_result, &a);
		}
		AUTH_DESTROY(client->cl_auth);
		clnt_destroy(client);
	}
	sockclose(socket);
}


