/* 
 * $Id: qrpc.c,v 1.18 91/05/05 19:59:23 qjb Exp $
 * $Source: /afs/athena.mit.edu/astaff/project/qrpc/src/RCS/qrpc.c,v $
 * $Author: qjb $
 *
 * This is the main file for QRPC RPC package. 
 */

#if !defined(lint) && !defined(SABER) && !defined(RCS_HDRS)
static char *rcsid = "$Id: qrpc.c,v 1.18 91/05/05 19:59:23 qjb Exp $";
#endif /* lint || SABER || RCS_HDRS */

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <syslog.h>

#include "qrpc.h"
#include "qrpc_private.h"

extern char *malloc();

int qrpc_debug;

#ifdef __STDC__
static void qrpci_client_error(char *string)
#else
static void qrpci_client_error(string)
  char *string;
#endif /* __STDC__ */
{
    (void) fprintf(stderr, "%s\n", string);
}
  

#ifdef __STDC__
static void qrpci_server_error(char *string)
#else
static void qrpci_server_error(string)
  char *string;
#endif /* __STDC__ */
{
    (void) syslog(LOG_ERR, "%s", string);
}


#ifdef __STDC__
static qrpc_error_t qrpci_choose_version(qrpc_t qrpc, qrpc_version_t c_minv,
					 qrpc_version_t c_maxv,
					 qrpc_version_t s_minv,
					 qrpc_version_t s_maxv)
#else
static qrpc_error_t qrpci_choose_version(qrpc, c_minv, c_maxv, s_minv, s_maxv)
  qrpc_t qrpc;
  qrpc_version_t c_minv;
  qrpc_version_t c_maxv;
  qrpc_version_t s_minv;
  qrpc_version_t s_maxv;
#endif /* __STDC__ */
{
    qrpc->version = min(c_maxv, s_maxv);
    if (qrpc->version < max(c_minv, s_minv)) {
	sprintf(qrpc->errmsg, 
		"Can't run version %d client against version %d server.",
		c_maxv, s_maxv);
	return(QRPC_ERR_VERSION);
    }

    return(QRPC_SUCCESS);
}


#ifdef __STDC__
static qrpc_error_t qrpci_client_establish_vno(qrpc_t qrpc,
					       qrpc_version_t c_minv,
					       qrpc_version_t c_maxv)
#else
static qrpc_error_t qrpci_client_establish_vno(qrpc, c_minv, c_maxv)
  qrpc_t qrpc;
  qrpc_version_t c_minv;
  qrpc_version_t c_maxv;
#endif /* __STDC__ */
{
    qrpc_error_t status = QRPC_SUCCESS;
    qrpc_length_t length;
    bit16 version_info[2];
    qrpc_version_t s_minv;
    qrpc_version_t s_maxv;

    if (! (IS16BIT(c_minv) && IS16BIT(c_maxv))) {
	sprintf(qrpc->errmsg, 
		"Version numbers must be sixteen-bit quantities.");
	return(QRPC_ERR_ILLEGAL_VNO);
    }

    /* 
     * Put min version first, then max version.  
     */
    (void) qrpci_ushort_to_bit16(c_minv, version_info[0]);
    (void) qrpci_ushort_to_bit16(c_maxv, version_info[1]);

    if (status = qrpci_send_packet(qrpc, QRPC_OP_CVERSION,
				   (qrpc_data_t) version_info,
				   (qrpc_length_t) sizeof(version_info)))
	return (status);

    length = (qrpc_length_t) sizeof(version_info);
    if (status = qrpci_recv_packet(qrpc, QRPC_OP_SVERSION,
				   (qrpc_data_t) version_info, &length))
	return (status);
    
    (void) qrpci_bit16_to_ushort(version_info[0], &s_minv);
    (void) qrpci_bit16_to_ushort(version_info[1], &s_maxv);

    if (status = qrpci_choose_version(qrpc, c_minv, c_maxv, s_minv, s_maxv)) 
	return (status);

    if (qrpc->version < s_maxv) {
	sprintf(qrpc->errmsg, "Protocol version %d is available; %s %d",
		s_maxv, "now running version", qrpc->version);
	return (QRPC_ERR_VWARN);
    }

    return (QRPC_SUCCESS);
}


#ifdef __STDC__
static qrpc_error_t qrpci_server_establish_vno(qrpc_t qrpc, 
					       qrpc_version_t s_minv, 
					       qrpc_version_t s_maxv)
#else
static qrpc_error_t qrpci_server_establish_vno(qrpc, s_minv, s_maxv)
  qrpc_t qrpc;
  qrpc_version_t s_minv;
  qrpc_version_t s_maxv;
#endif /* __STDC__ */
{
    qrpc_error_t status = QRPC_SUCCESS;
    qrpc_length_t length;
    bit16 version_info[2];
    qrpc_version_t c_minv;
    qrpc_version_t c_maxv;

    if (! (IS16BIT(s_minv) && IS16BIT(s_maxv))) {
	sprintf(qrpc->errmsg,
		"Version numbers must be sixteen-bit quantities.");
	return(QRPC_ERR_ILLEGAL_VNO);
    }

    length = (qrpc_length_t) sizeof(version_info);
    if (status = qrpci_recv_packet(qrpc, QRPC_OP_CVERSION, 
				   (qrpc_data_t) version_info, &length))
	return (status);

    /* 
     * Put min version first, then max version.  
     */
    (void) qrpci_bit16_to_ushort(version_info[0], &c_minv);
    (void) qrpci_bit16_to_ushort(version_info[1], &c_maxv);

    (void) qrpci_ushort_to_bit16(s_minv, version_info[0]);
    (void) qrpci_ushort_to_bit16(s_maxv, version_info[1]);

    if (status = qrpci_send_packet(qrpc, QRPC_OP_SVERSION,
				   (qrpc_data_t) version_info,
				   (qrpc_length_t) sizeof(version_info)))
	return (status);

    if (status = qrpci_choose_version(qrpc, c_minv, c_maxv, s_minv, s_maxv))
	return (status);

    if (qrpc->version < c_maxv) {
	sprintf(qrpc->errmsg, "Protocol version %d is available; %s %d",
		c_maxv, "now running version", qrpc->version);
	return (QRPC_ERR_VWARN);
    }

    return (QRPC_SUCCESS);
}


#ifdef __STDC__
static qrpc_error_t qrpci_setup_client_rpc(qrpc_t qrpc, 
					   qrpc_version_t minv, 
					   qrpc_version_t maxv)
  /* 
   * Modifies:
   *   qrpc
   * Effects:
   *   Uses host and service information in qrpc to open the 
   *   connection.  After the connection is succesfullly made,
   *   the version number of the application protocol is 
   *   established.
   */
#else
static qrpc_error_t qrpci_setup_client_rpc(qrpc, minv, maxv)
  qrpc_t qrpc;
  qrpc_version_t minv;
  qrpc_version_t maxv;
#endif /* __STDC__ */
{
    struct hostent *hp;
    struct servent *sp;
    int addrlen = sizeof(qrpc->caddr);

    sprintf(qrpc->errmsg, "Looking up client host: %s", 
	    qrpc->setup.client.host);
    qrpci_dprint(qrpc, qrpc->errmsg);

    if ((hp = gethostbyname(qrpc->setup.client.host)) == NULL) {
	sprintf(qrpc->errmsg, "%s: unknown host.", qrpc->setup.client.host);
	return(QRPC_ERR_HOST);
    }

    BCLEAR(qrpc->setup.client.fullhost);
    strncpy(qrpc->setup.client.fullhost, hp->h_name, 
	    sizeof(qrpc->setup.client.fullhost));

    sprintf(qrpc->errmsg, "Client fullhost: %s", qrpc->setup.client.fullhost);
    qrpci_dprint(qrpc, qrpc->errmsg);

    if (qrpc->setup.client.service) {
	if (sp = getservbyname(qrpc->setup.client.service, "tcp"))
	    qrpc->setup.client.port = sp->s_port;
	/* 
	 * else leave qrpc->setup.client.port as it is--it should be set to a 
	 * known fallback port number 
	 */
    }

    /*
     * Set up the server socket address structure
     */
    qrpc->saddr.sin_family = hp->h_addrtype;
    bcopy(hp->h_addr, (char *)&(qrpc->saddr.sin_addr), hp->h_length);
    qrpc->saddr.sin_port = qrpc->setup.client.port;

    /*
     * Open the socket.  in == out for the client.
     */
    if ((qrpc->out = socket(hp->h_addrtype, SOCK_STREAM, IPPROTO_IP)) < 0) {
	sprintf(qrpc->errmsg, "Failure getting socket: %s", 
		sys_errlist[errno]);
	return(QRPC_ERR_SOCKET);
    }
    else
	qrpc->in = qrpc->out;
    
    /*
     * Connect to the server and indicate that we are connected.
     */
    if (connect(qrpc->out, (char *)&(qrpc->saddr), 
		sizeof (qrpc->saddr)) < 0) {
	sprintf(qrpc->errmsg, "Error connecting to %s: %s", 
		qrpc->setup.client.host, sys_errlist[errno]);
	close(qrpc->out);
	return(QRPC_ERR_CONNECT);
    }

    qrpc->connected = TRUE;

    /*
     * Set up the client socket address structure
     */
    if (getsockname(qrpc->out, &(qrpc->caddr), &addrlen) < 0) {
	sprintf(qrpc->errmsg, "Error getting local socket information: %s", 
		sys_errlist[errno]);
	return(QRPC_ERR_GETSOCK);
    }

    if (qrpc_debug)
    {
	u_long addr;
	addr = htonl(qrpc->caddr.sin_addr.s_addr);
	printf("Client internet address: %d.%d.%d.%d\n", 
	       addr >> 24,
	       (addr >> 16) & 0xff,
	       (addr >> 8) & 0xff,
	       addr & 0xff);
	addr = htonl(qrpc->saddr.sin_addr.s_addr);
	printf("Server internet address: %d.%d.%d.%d\n", 
	       addr >> 24,
	       (addr >> 16) & 0xff,
	       (addr >> 8) & 0xff,
	       addr & 0xff);
    }
    
    return(qrpci_client_establish_vno(qrpc, minv, maxv));
}    


#ifdef __STDC__
static qrpc_error_t qrpci_listen(qrpc_t qrpc)
#else
static qrpc_error_t qrpci_listen(qrpc)
  qrpc_t qrpc;
#endif /* __STDC__ */
{
    struct servent *sp;
    struct sockaddr_in addr;
    int len;
    
    SBCLEAR(addr);
    
    if (qrpc->setup.server.service) {
	if (sp = getservbyname(qrpc->setup.server.service, "tcp"))
	    qrpc->setup.server.port = sp->s_port;
	/* 
	 * else leave qrpc->setup.server.port as it is--it should be set to a 
	 * known fallback port number 
	 */
    }
    
    /*
     * Set up the address structure
     */
    
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = gethostid();
    addr.sin_port = qrpc->setup.server.port;
    
    /*
     * Since this is a listener, allocate and clear a listen_info object
     */
    
    if ((qrpc->listen_info = (qrpc_listen_info) 
	 malloc(sizeof (*(qrpc->listen_info)))) == NULL) {
	sprintf(qrpc->errmsg, 
		"Failure allocating %d bytes of memory for listen_info",
		sizeof (*(qrpc->listen_info)));
	return (QRPC_ERR_MEMORY);
    }

    bzero((char *)qrpc->listen_info, sizeof (*(qrpc->listen_info)));

    /*
     * Open the socket.
     */
    
    if ((qrpc->listen_info->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP))
	== -1) {
	sprintf(qrpc->errmsg, "Failure getting socket: %s", 
		sys_errlist[errno]);
	return(QRPC_ERR_SOCKET);
    }

    if (bind(qrpc->listen_info->sock, &addr, sizeof(addr)) == -1) {
	sprintf(qrpc->errmsg, "Failure calling bind: %s",
		sys_errlist[errno]);
	return(QRPC_ERR_BIND);
    }

    /* Find out real information about socket */
    len = sizeof(addr);
    if (getsockname(qrpc->listen_info->sock, &addr, &len) == 01) {
	sprintf(qrpc->errmsg, "Failure calling getsockname: %s",
		sys_errlist[errno]);
	return(QRPC_ERR_GETSOCK);
    }

    qrpc->setup.server.port = addr.sin_port;

    if (listen(qrpc->listen_info->sock, qrpc->setup.server.backlog) == -1) {
	sprintf(qrpc->errmsg, "Failure calling listen: %s",
		sys_errlist[errno]);
	return(QRPC_ERR_LISTEN);
    }

    return (QRPC_SUCCESS);
}


#ifdef __STDC__
static qrpc_error_t qrpci_complete_server_init(qrpc_t qrpc, 
					       qrpc_version_t minv,
					       qrpc_version_t maxv)
#else
static qrpc_error_t qrpci_complete_server_init(qrpc, minv, maxv)
  qrpc_t qrpc;
  qrpc_version_t minv;
  qrpc_version_t maxv;
#endif /* __STDC__ */
{
    int addrlen;

    qrpc->connected = TRUE;

    /*
     * Figure out information about both sides of connection
     */

    if (! qrpc->caddr.sin_addr.s_addr) {
	addrlen = sizeof(qrpc->caddr);
	if (getpeername(qrpc->in, &(qrpc->caddr), &addrlen) < 0) {
	    sprintf(qrpc->errmsg, "Error getting client's address: %s",
		    sys_errlist[errno]);
	    return(QRPC_ERR_GETPEER);
	}
    }

    addrlen = sizeof(qrpc->caddr);
    if (getsockname(qrpc->out, &(qrpc->saddr), &addrlen) < 0) {
	sprintf(qrpc->errmsg, 
		"Error getting local socket information: %s", 
		sys_errlist[errno]);
	return(QRPC_ERR_GETSOCK);
    }

    if (qrpc_debug)
    {
	u_long addr;
	addr = htonl(qrpc->caddr.sin_addr.s_addr);
	syslog(LOG_NOTICE, "Client internet address: %d.%d.%d.%d\n", 
	       addr >> 24,
	       (addr >> 16) & 0xff,
	       (addr >> 8) & 0xff,
	       addr & 0xff);
	addr = htonl(qrpc->saddr.sin_addr.s_addr);
	syslog(LOG_NOTICE, "Server internet address: %d.%d.%d.%d\n", 
	       addr >> 24,
	       (addr >> 16) & 0xff,
	       (addr >> 8) & 0xff,
	       addr & 0xff);
    }
    
    return(qrpci_server_establish_vno(qrpc, minv, maxv));
}


#ifdef __STDC__
static qrpc_error_t qrpci_setup_server_rpc(qrpc_t qrpc, 
					   qrpc_version_t minv,
					   qrpc_version_t maxv)
  /*
   * Modifies:
   *   qrpc
   * Effects:
   *   Initializes server fields in qrpc object.
   */
#else
static qrpc_error_t qrpci_setup_server_rpc(qrpc, minv, maxv)
  qrpc_t qrpc;
  qrpc_version_t minv;
  qrpc_version_t maxv;
#endif /* __STDC__ */
{
    qrpc_error_t status;

    switch (qrpc->setup.server.server_type) {

      case qst_inetd:
	qrpc->in = 0;
	qrpc->out = 1;
	status = qrpci_complete_server_init(qrpc, minv, maxv);
	break;

      case qst_listen:
	qrpc->save_minv = minv;
	qrpc->save_maxv = maxv;
	status = qrpci_listen(qrpc);
	break;

      default:
	sprintf(qrpc->errmsg, "Unknown server type %d in qrpc_create_server",
		(int) qrpc->setup.server.server_type);
	status = QRPC_ERR_INTERNAL;
    }
    
    return (status);
}

    
#ifdef __STDC__
static void qrpci_global_init(void)
#else
static void qrpci_global_init()
#endif /* __STDC__ */
{
    static qrpc_bool inited = FALSE;

    if (!inited) {
	(void) initialize_qrpc_error_table();
	inited = TRUE;
    }
}


#ifdef __STDC__
static qrpc_error_t qrpci_create_object(qrpc_t *qrpcp)
#else
static qrpc_error_t qrpci_create_object(qrpcp)
  qrpc_t *qrpcp;
#endif /* __STDC__ */
{
    /* 
     * Create qprc object.  Use calloc so that we get cleared memory. 
     */
    
    if ((*qrpcp = (qrpc_t)calloc(1, (sizeof **qrpcp))) == NULL) 
	return(QRPC_ERR_MEMORY_CREATE);

    return (QRPC_SUCCESS);
}


#ifdef __STDC__
qrpc_error_t qrpci_make_internally_consistent(qrpc_t qrpc)
#else
qrpc_error_t qrpci_make_internally_consistent(qrpc)
  qrpc_t qrpc;
#endif
{
    int i;

    for (i = 0; qrpc->valid_opcodes[i].name != NULL; i++);
    qrpc->max_valid_opcode = i - 1;
    
    qrpc->state = QRPC_ST_EXP_HEADER;

    return(QRPC_SUCCESS);
}


#ifdef __STDC__
qrpc_error_t qrpc_init_tcp_client_info(qrpc_client_info *client, char *host,
				       char *service, int port)
#else
qrpc_error_t qrpc_init_tcp_client_info(client, host, service, port)
  qrpc_client_info *client;
  char *host;
  char *service;
  int port;
#endif /* __STDC__ */
{
    client->host = host;
    client->service = service;
    client->port = htons(port);

    return (QRPC_SUCCESS);
}


#ifdef __STDC__
qrpc_error_t qrpc_init_inetd_server_info(qrpc_server_info *server)
#else
qrpc_error_t qrpc_init_inetd_server_info(server)
  qrpc_server_info *server;
#endif /* __STDC__ */
{
    server->server_type = qst_inetd;

    server->service = NULL;
    server->port = NULL;
    server->backlog = NULL;

    return (QRPC_SUCCESS);
}


#ifdef __STDC__
qrpc_error_t qrpc_init_listen_server_info(qrpc_server_info *server,
					  char *service, int port,
					  int backlog)
#else
qrpc_error_t qrpc_init_listen_server_info(server, service, port, backlog)
  qrpc_server_info *server;
  char *service;
  int port;
  int backlog;
#endif /* __STDC__ */
{
    server->server_type = qst_listen;

    server->service = service;
    server->port = htons(port);
    server->backlog = backlog;

    return (QRPC_SUCCESS);
}


#ifdef __STDC__
qrpc_error_t qrpc_create_client(qrpc_t *qrpcp, qrpc_client_info *client, 
				qrpc_valid_opcode valid_opcodes[], 
				qrpc_version_t minv, qrpc_version_t maxv)
  /*
   * Modifies: 
   *   qrpcp
   * Effects: 
   *   Creates a qrpc object sets *qrpcp to point to it.
   *   The resulting object will set up for full communication through
   *   a TCP connection to "client->host" over the port named by 
   *   "client->service".  If "client->service" is unavailable, the 
   *   port "client->port" will be used instead.  In addition, the 
   *   of the application's protocol that will be used is established.
   */
#else
qrpc_error_t qrpc_create_client(qrpcp, client, valid_opcodes, minv, maxv)
  qrpc_t *qrpcp;		/* pointer to qrpc object to be created */
  qrpc_client_info *client;
  qrpc_valid_opcode valid_opcodes[];
  qrpc_version_t minv;
  qrpc_version_t maxv;
#endif /* __STDC__ */
{
    qrpc_error_t status = QRPC_SUCCESS;
    qrpc_t qrpc = NULL;

    qrpci_global_init();

    if (status = qrpci_create_object(qrpcp))
	return(status);

    qrpc = *qrpcp;

    /* 
     * Initialize error function before there is ever a potential
     * of returning control to the client with a non-null qrpc
     */
    qrpc->default_error_fn = qrpci_client_error;
    qrpc_set_error_fn(qrpc, NULL);

    /* Initialize fields */
    bcopy((char *)client, (char *)&(qrpc->setup.client), 
	  sizeof(qrpc->setup.client));

    qrpc->client_or_server = QRPC_IS_CLIENT;

    qrpc->valid_opcodes = (qrpc_valid_opcode *)valid_opcodes;

    (void) qrpci_make_internally_consistent(qrpc);

    /*
     * Do the network setup and return the result.
     */
    return (qrpci_setup_client_rpc(qrpc, minv, maxv));	
}


#ifdef __STDC__
qrpc_error_t qrpc_create_server(qrpc_t *qrpcp, qrpc_server_info *server, 
				qrpc_valid_opcode valid_opcodes[],
				qrpc_version_t minv,
				qrpc_version_t maxv)
  /*
   * Modifies:
   *   qrpcp
   * Effects:
   *   Creates a new qrpc object and assigns it to *qrpc.
   */
#else
qrpc_error_t qrpc_create_server(qrpcp, server, valid_opcodes, minv, maxv)
  qrpc_t *qrpcp;
  qrpc_server_info *server;
  qrpc_valid_opcode valid_opcodes[];
  qrpc_version_t minv;
  qrpc_version_t maxv;
#endif /* __STDC__ */
{
    qrpc_error_t status = QRPC_SUCCESS;
    qrpc_t qrpc = NULL;

    qrpci_global_init();

    if (status = qrpci_create_object(qrpcp))
	return(status);

    qrpc = *qrpcp;

    /* 
     * Initialize error function before there is ever a potential
     * of returning control to the client with a non-null qrpc
     */
    qrpc->default_error_fn = qrpci_server_error;
    qrpc_set_error_fn(qrpc, NULL);

    /* Initialize fields */
    bcopy((char *)server, (char *)&(qrpc->setup.server), 
	  sizeof(qrpc->setup.server));

    qrpc->client_or_server = QRPC_IS_SERVER;

    qrpc->valid_opcodes = (qrpc_valid_opcode *)valid_opcodes;

    (void) qrpci_make_internally_consistent(qrpc);

    /*
     * Do the network setup and return the result.
     */

    return (qrpci_setup_server_rpc(qrpc, minv, maxv));
}


#ifdef __STDC__
qrpc_error_t qrpc_accept(qrpc_t listenqrpc, qrpc_t *qrpcp)
  /*
   * Effects:
   *   If listenqrpc is a listen-based server qrpc that is 
   *   listening, try to accept.  If succesful, initialize
   *   qrpcp to a qrpc object suitable for normal communication.
   */
#else
qrpc_error_t qrpc_accept(listenqrpc, qrpcp)
  qrpc_t listenqrpc;
  qrpc_t *qrpcp;
#endif /* __STDC__ */
{
    qrpc_error_t status;
    qrpc_t qrpc;
    int addrlen;
    int i;

    if ((listenqrpc->client_or_server != QRPC_IS_SERVER) ||
	(listenqrpc->setup.server.server_type != qst_listen) ||
	(listenqrpc->connected != FALSE)) {
	sprintf(listenqrpc->errmsg, 
		"qrpc_accept must be called on the server with an %s",
		"unconnected\n listening qrpc object as the first argument");
	return(QRPC_ERR_APP);
    }
    
    /*
     * Initialize new qrpc object
     */

    if (status = qrpci_create_object(qrpcp)) {
	sprintf(listenqrpc->errmsg, "Failure creating new qrpc object");
	return (status);
    }
    
    qrpc = *qrpcp;

    /*
     * Make this a copy of the listener, but change the type and 
     * NULL the listen_info field.
     */

    bcopy(listenqrpc, qrpc, sizeof(**qrpcp));
    qrpc->setup.server.server_type = qst_accept;
    qrpc->listen_info = NULL;
    
    /*
     * Accept and complete initialization
     */

    addrlen = sizeof(qrpc->caddr);
    if ((qrpc->out = accept(listenqrpc->listen_info->sock, &(qrpc->caddr),
			    &addrlen)) == -1) {
	sprintf(qrpc->errmsg, "Failure calling accept: %s",
		sys_errlist[errno]);
	return(QRPC_ERR_ACCEPT);
    }

    /* These should be the same for this accepted connection... */
    qrpc->in = qrpc->out;
    
    /*
     * Add this to the list of accepted connections
     */

    /* Scan to the end of the list */
    for (i = 0; listenqrpc->listen_info->accepted[i]; i++);

    listenqrpc->listen_info->accepted[i] = qrpc;

    return (qrpci_complete_server_init(qrpc, 
				       listenqrpc->save_minv,
				       listenqrpc->save_maxv));
}


#ifdef __STDC__
void qrpc_drop(qrpc_t qrpc, char *string)
  /* 
   * This is void because having if it were not, clients who
   * call this routine unconditionally on error could get stuck
   * in an infinite loop if this routine ever returned an error.
   */
#else
void qrpc_drop(qrpc, string)
  qrpc_t qrpc;
  char *string;
#endif /* __STDC__ */
{
    if (qrpc->connected) {
	char errmsg[BUFSIZ + 1];

	bzero(errmsg, sizeof(errmsg));
	if (qrpc->client_or_server == QRPC_IS_CLIENT)
	    strcpy(errmsg, "Client reports error: ");
	else
	    strcpy(errmsg, "Server reports error: ");

	strncat(errmsg, string, BUFSIZ - strlen(errmsg));

	(void) qrpci_send_error(qrpc, errmsg);
	(void) close(qrpc->in);
	if (qrpc->in != qrpc->out)
	    (void) close(qrpc->out);
	qrpc->connected = FALSE;
    }
}


#ifdef __STDC__
void qrpc_destroy(qrpc_t *qrpcp)
  /*
   * Modifies:
   *   qrpcp
   * Effects:
   *   Closes the sockets in *qrpcp, frees the storage it uses, and
   *   sets *qrpcp to zero.
   */
#else
void qrpc_destroy(qrpcp)
  qrpc_t *qrpcp;
#endif /* __STDC__ */
{
    qrpc_t qrpc = *qrpcp;

    if (qrpc->connected) {
	(void) close (qrpc->in);
	if (qrpc->in != qrpc->out)
	    (void) close (qrpc->out);
    }
    
    if (qrpc->listen_info) {
	(void) close (qrpc->listen_info->sock);
	/* 
	 * Free the listen_info structure, but don't touch the
	 * accepted connections.  A server can decide to keep 
	 * all active connections but not accept new ones...
	 */
	free ((char *)qrpc->listen_info);
    }

    free ((char *)qrpc);
    *qrpcp = NULL;	
}
