    /*********************************************************************\
    *  Copyright (c) 1991 by Wen-King Su (wen-king@vlsi.cs.caltech.edu)   *
    *                                                                     *
    *  You may copy or modify this file in any manner you wish, provided  *
    *  that this notice is always included, and that you hold the author  *
    *  harmless for any loss or damage resulting from the installation or *
    *  use of this software.                                              *
    \*********************************************************************/

#include "client_def.h"

extern int errno;

static int myfd;
static struct sockaddr_in server_addr;
static unsigned short myseq = 0;
static unsigned short key;

int client_trace      = 0;
int client_intr_state = 0;
unsigned long target_delay	= MIN_DELAY;	/* expected max delay	 */
unsigned long busy_delay        = MIN_DELAY;	/* busy retransmit timer */
unsigned long idle_delay	= MIN_DELAY;	/* idle retransmit timer */
unsigned long udp_sent_time;

UBUF *client_interact(cmd,pos,l1,p1,l2,p2)
    unsigned cmd, l1, l2;
    unsigned long pos;
    unsigned char *p1, *p2;
{
    struct sockaddr_in from;
    UBUF sbuf;
    static UBUF rbuf;
    unsigned char *s, *t, *d;
    unsigned u, n, sum, mask, mlen;
    int retval, bytes, retry_send, retry_recv;
    unsigned long w_delay;

    sbuf.cmd = cmd;
    sbuf.len = htons(l1);
    sbuf.pos = htonl(pos);

    client_intr_state = 1;

    for(u = l1, d = (unsigned char *) sbuf.buf; u--; *d++ = *p1++);
    for(u = l2				      ; u--; *d++ = *p2++);
    mlen = d - (unsigned char *) &sbuf;

    key = client_get_key();

    for(retry_send = 0; ; retry_send++)
    {
	sbuf.key = key;
	sbuf.seq = (myseq & 0xfffc) | (retry_send & 0x0003);
	sbuf.sum = 0;

	for(t = (unsigned char *) &sbuf, sum = n = mlen; n--; sum += *t++);
	sbuf.sum = sum + (sum >> 8);

	switch(retry_send)	/* adaptive retry delay adjustments */
	{
	    case  0: busy_delay = (target_delay+(busy_delay<<3)-busy_delay)>>3;
		     w_delay = busy_delay;
		     break;

	    case  1: busy_delay = busy_delay + (busy_delay >> 1);
		     w_delay = busy_delay;
		     if(client_trace) write(2,"R",1);
		     break;

	    default:
#ifdef CLIENT_TIMEOUT
		     if (!pos && retry_send >= env_timeout )
		     {
			fprintf(stderr, "\rRemote server not responding.\n");
			exit(1);
		     }
#endif
		     if(idle_delay < 5*60*1000) idle_delay = idle_delay << 1;
		     w_delay = idle_delay;
		     if(client_trace) write(2,"I",1);
		     break;
	}

	if(sendto(myfd,&sbuf,mlen,0,(struct sockaddr *)&server_addr,
                  sizeof(server_addr)) == -1) { perror("sendto"); exit(1); }
	udp_sent_time = time((time_t *) 0);
	mask = 1 << myfd;

	for(retry_recv = 0; ; retry_recv++)
	{
	    if(retry_recv && client_trace) write(2,"E",1);

	    retval = _x_select(&mask, w_delay);

	    if((retval == -1) && (errno == EINTR)) continue;

	    if(retval == 1)    /* an incoming message is waiting */
	    {
		bytes = sizeof(from);
		if((bytes = recvfrom(myfd,(char*)&rbuf,sizeof(rbuf),0,
				     (struct sockaddr *)&from,
                                     &bytes)) < UBUF_HSIZE) continue;

		s = (unsigned char *) &rbuf;
		d = s + bytes;
		u = rbuf.sum; rbuf.sum = 0;
		for(t = s, sum = 0; t < d; sum += *t++);
		sum = (sum + (sum >> 8)) & 0xff;
		if(sum != u) continue;  /* wrong check sum */

		rbuf.len = htons(rbuf.len);
		rbuf.pos = htonl(rbuf.pos);

		if((rbuf.seq & 0xfffc) != myseq) continue;  /* wrong seq # */
		if(rbuf.len+UBUF_HSIZE  > bytes) continue;  /* truncated.  */

		myseq = (myseq + 0x0004) & 0xfffc;  /* seq for next request */
		key   = rbuf.key;		    /* key for next request */

		client_put_key(key);

		if(client_intr_state == 2)
		{
		    if(!key_persists) client_done();
		    exit(1);
		}

		return(&rbuf);

	    } else break;   /* go back to re-transmit buffer again */
	}
    }
}

init_client(host,port,myport)
    char *host;
    int   port;
    int myport;
{
    busy_delay = idle_delay = target_delay;

    if((myfd = _x_udp(&myport)) == -1)
		{ perror("socket open"); exit(1); }

    if(_x_adr(host,port,&server_addr) == -1)
		{ perror("server addr"); exit(1); } 

    client_init_key(server_addr.sin_addr.s_addr,port,getpid());
}

client_done()
{
    (void) client_interact(CC_BYE,0L,0,NULLP,0,NULLP);
}
