/*
 * 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
 */

/*
 * Copyright (C) 1986, Sun Microsystems, Inc.
 */

#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif

#include <stdio.h>
#include <locale.h>
#include <libintl.h>
#include <rpc/rpc.h>
#include <rpc/key_prot.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>

#ifndef _
#define _(String) gettext (String)
#endif

/*
 * Talk to the keyserver on a privileged port on the part of a calling program.
 *
 * Protocol is for caller to send through stdin the procedure number
 * to call followed by the argument data.  We call the keyserver, and
 * send the results back to the caller through stdout.
 * Non-zero exit status means something went wrong.
 */

#ifndef DEBUG
#define debug(msg)
#endif

#define TOTAL_TIMEOUT 30	/* total timeout talking to keyserver */
#define TOTAL_TRIES	  10	/* Number of tries */

/*
 * Opaque data that we send and receive
 */
#define MAXOPAQUE 256
struct opaqn
  {
    u_int len;
    u_int data[MAXOPAQUE];
  };

static bool_t xdr_opaqn (XDR *, struct opaqn *);
static int callkeyserver (u_long proc, xdrproc_t xdr_args, void *args,
			  xdrproc_t xdr_rslt, void *rslt);


int
main (void)
{
  XDR xdrs_args;
  XDR xdrs_rslt;
  long proc;
  struct opaqn args, rslt;


  if (isatty (0))
    {
      setlocale (LC_MESSAGES, "");
      bindtextdomain (PACKAGE, LOCALEDIR);
      textdomain (PACKAGE);

      fputs (_("This program cannot be used interactively.\n"), stderr);
      exit (1);
    }

#ifdef DEBUG
  close (2);
  open ("/dev/console", O_WRONLY, 0);
#endif

  xdrstdio_create (&xdrs_args, stdin, XDR_DECODE);
  xdrstdio_create (&xdrs_rslt, stdout, XDR_ENCODE);

  if (!xdr_u_long (&xdrs_args, &proc))
    {
      debug ("no proc");
      exit (1);
    }
  if (!xdr_opaqn (&xdrs_args, &args))
    {
      debug ("recving args failed");
      exit (1);
    }
  if (!callkeyserver (proc, (xdrproc_t)xdr_opaqn, &args,
		      (xdrproc_t)xdr_opaqn, &rslt))
    {
      debug ("rpc_call failed");
      exit (1);
    }
  if (!xdr_opaqn (&xdrs_rslt, &rslt))
    {
      debug ("sending args failed");
      exit (1);
    }
  exit (0);
}



static int
callkeyserver (u_long proc, xdrproc_t xdr_args, void *args,
	       xdrproc_t xdr_rslt, void *rslt)

{
  struct sockaddr_in remote;
  struct timeval wait;
  enum clnt_stat stat;
  CLIENT *client;
  int sd;

  /*
   * set up the remote address
   * and create client
   */
  remote.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
  remote.sin_family = AF_INET;
  remote.sin_port = 0;
  wait.tv_sec = TOTAL_TIMEOUT / TOTAL_TRIES;
  wait.tv_usec = 0;
  sd = RPC_ANYSOCK;
  if (proc == KEY_SET || proc == KEY_ENCRYPT || proc == KEY_DECRYPT ||
      proc == KEY_GEN || proc == KEY_GETCRED)
    client = clntudp_create (&remote, KEY_PROG, KEY_VERS, wait, &sd);
  else
    client = clntudp_create (&remote, KEY_PROG, KEY_VERS2, wait, &sd);
  if (client == NULL)
    {
      debug ("no client");
      return 0;
    }

  /*
   * Check that server is bound to a reserved port, so
   * that noone can masquerade as the keyserver.
   */
  if (ntohs (remote.sin_port) >= IPPORT_RESERVED)
    {
      debug ("insecure port");
      return 0;
    }

  /*
   * Create authentication
   * All we care about really is sending the real uid
   */
  client->cl_auth = authunix_create ((char *) "", getuid (), 0, 0, NULL);
  if (client->cl_auth == NULL)
    {
      debug ("no auth");
      return 0;
    }
  wait.tv_sec = TOTAL_TIMEOUT;
  wait.tv_usec = 0;
  stat = clnt_call (client, proc, (xdrproc_t) xdr_args, args,
		    (xdrproc_t) xdr_rslt, rslt, wait);

#ifdef DEBUG
  if (stat != RPC_SUCCESS)
    debug ("clnt_call failed");
#endif

  return (stat == RPC_SUCCESS);
}


/*
 * XDR opaque data
 * Don't know the length on decode, so just keep receiving until failure.
 */
static bool_t
xdr_opaqn (XDR * xdrs, struct opaqn *objp)
{
  unsigned int i;

  switch (xdrs->x_op)
    {
    case XDR_FREE:
      break;
    case XDR_DECODE:
      for (i = 0; i < MAXOPAQUE && xdr_int (xdrs, &objp->data[i]); ++i)
	{
	}
      if (i == MAXOPAQUE)
	return FALSE;
      objp->len = i;
      break;
    case XDR_ENCODE:
      for (i = 0; i < objp->len; ++i)
	{
	  if (!xdr_int (xdrs, &objp->data[i]))
	    return FALSE;
	}
      break;
    }
  return TRUE;
}


#ifdef DEBUG
debug (msg)
     char *msg;
{
  fprintf (stderr, "%s\n", msg);
}
#endif
