/* ZPROX 0.3.0
 * Proxy with compression support
 *     by Willy Tarreau
 *
 * zprox       This program was designated to speed up modem connexions by
 *             compressing data. Its main advantage is that you can choose
 *             the compression strength for each one of the two ways of
 *             communication. I made this because HTTP headers don't compress
 *             well, nor does telnet input, which would be slowed down and
 *             even generate network overhead. Of course, by disabling the
 *             compression on each way, it acts like a simple proxy.
 *             Personal advice: use it between two squid proxies, you'll
 *             get about 15 kB/s with a 33600 modem on HTML pages.
 *
 * IMPORTANT:  ZPROX needs libz.a and zlib.h (zlib 1.0.4 or 1.1.2) to compile.
 *             This software is still alpha. It is possible that
 *             something goes wrong. It shouldn't come from zlib which
 *             is known to work at this time.
 *             See zlib home page at http://quest.jpl.nasa.gov/zlib/
 *
 * $Id: zprox.c,v 0.3 1998/06/08 9:44:15 willy Exp $
 *
 * Author:
 *            - Willy Tarreau <tarreau@aemiaif.lip6.fr>
 *
 * Copying-policy: Gnu Public Licence - Freeware
 *
 * ZPROX is under the GPL. You should have received a copy of this licence.
 * If not, please retrieve it from the FSF and read it.
 *
 * The compression algorithms used here are directly taken from
 *
 * ZPROX IS DISTRIBUTED WITH NO EXPRESSED OR IMPLIED SORT OF WARANTY.
 * THE AUTHORS SHOULD NOT BE LIABLE FOR ANY DAMAGE OR LOSS CAUSED TO
 * YOU, YOUR SYSTEM OR ANYBODY.
 *
 *
 */

#include <stdio.h>
#ifndef NO_STDLIB
#include <stdlib.h>
#endif
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <setjmp.h>
#include <sys/socket.h>

/* this is for gcc for win32 for those who want to test ... */
#ifdef WIN32

#ifndef EINPROGRESS
#define EINPROGRESS 10036
#endif

#ifndef SO_ERROR
#define SO_ERROR 0x1007
#endif

#endif

/* user configurable compilation-time parameters */
#define NBDESC		1024
#define BUFSIZE		16384
#define LOCALPORT	3125
#define REMOTEPORT	3128
#define CHNBFREE	4

/* upper limit beyond which we stop receiving. */
#define RHIWAT		(bufsize*7/8)
/* lower limit below which we stop sending.
   Important: to avoid deadlocks, WLOWAT must be lower than
   bufsize-max(minfreeout).
*/
#define WLOWAT		(bufsize/4)

/* Here are some state constants. A socket state is a logical OR of these flags */
#define NOTUSED	0x00
#define	RSHUT	0x01
#define	WSHUT	0x02
#define	RWSHUT	( RSHUT | WSHUT )
#define NOTYET	0x04
#define	CONN	0x08


#define MAX(a,b)  ((a<b)?b:a)
#define THROUGHPUT  64


typedef struct sbuf tbuf;
typedef struct sch tch;
typedef struct sdesc tdesc;


/* buffer description */
struct sbuf {
  unsigned int r, w, l;	/* read ptr, write ptr, data length */
  char data[1];
};

/* let's define a channel structure */
struct sch {
  char end;      /* 1 = "if RB doesn't change, no more bytes will go to WB" */
  int rd, wd;   /* read desc and write desc */
  tbuf *rb, *wb; /* read buffer and write buffer */
  tch *next; /* next one in free list */
};

/* now a descriptor structure */
struct sdesc {
  unsigned int st;	/* socket state */
  int oth;    /* the descriptor on the other end */
  tch	*r,*w;	/* Read and Write channels */
  long long int when;
};

/******************** now the variables ****************/
int nbdesc	= NBDESC;
int nbport      = 1;
int bufsize	= BUFSIZE;

/* used for statistics */
unsigned long int glob_in=0, glob_out=0;

/* the descriptors entry point. desc[0..2]=stdin..stderr, desc[3]...=sockets */
tdesc *desc;
fd_set *rfd, *wfd;
int highdesc;

/* the listening socket */
int listensock;

/* adresse de connexion distante */
char *remotehost=NULL;
int   remoteport=REMOTEPORT;
struct sockaddr_in remote;

int   localport=LOCALPORT;
struct sockaddr_in local;

int chnormfree=16;	/* min of free channels in memory */
int chnbfree=0;		/* actual number of free channels. */
tch *freech;	/* first free channel in memory */

long long int throughput=0;


long long int iterdate;

/* handles stops to terminate normally */
void sighandler(sig)
  int sig;
{
  exit(0);
}


/* disables a one-way or two-way communication on a socket descriptor, and
   tags it as "shutdown" in the descriptors table.
   The mode parameter should be any logical OR based on these values:
   - RSHUT
   - WSHUT
   - RWSHUT
*/
void shutdesc(d, mode)
  int d, mode;
{

  if (mode&RSHUT) {
    /*fprintf(stderr,"Shutdown READ:  %d\n",d);*/
    shutdown(d,0);
  }
  if (mode&WSHUT) {
    /*fprintf(stderr,"Shutdown WRITE: %d\n",d);*/
    shutdown(d,1);
  }
  if (d<nbdesc)
    desc[d].st |= mode;

  /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

}

/* Closes a descriptor and marks it unused in the table.
*/
void closedesc(d)
  int d;
{

  /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

  close(d);
  /*fprintf(stderr,"socket %d closed.\n",d);*/
  if (d<nbdesc)
    desc[d].st=NOTUSED;


  /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

}




/* returns a pointer onto a new freshly initialized channel.
   Warning: these fields remain undefined and must be set by the caller:
   	  rd, wd, mode.
   The zstream still needs to be partially initialized by a deflateInit() or
   inflateInit().
 */
tch *chcreate() {
	tch *ch;

	ch=(tch*)malloc(sizeof(tch));
	ch->end=1;

	/* 2 fresh buffers, as needed.
       There's no need to initialize the data field of the buffer because
	   it will be overwritten before being read.
	 */
	bzero(ch->rb=(tbuf*)malloc(sizeof(tbuf)+bufsize),sizeof(tbuf));
	bzero(ch->wb=(tbuf*)malloc(sizeof(tbuf)+bufsize),sizeof(tbuf));

	ch->next=freech;	/* prepares the chaining of free channels */
	chnbfree++;	/* one new free channel */
	return ch;
}


/*
 returns a pointer onto a free and initialized channel.
 */
tch *challoc() {
	tch *ch;
	if (!freech) {  /* no more free channels */
		freech=chcreate(); /* let's create a new one */
		/*fprintf(stderr,"allocation et creation d'1 canal.\n");*/
	}
	else {
		/*fprintf(stderr,"allocation d'1 canal disponible.\n");*/
	}
	ch=freech;
	freech=ch->next;
	chnbfree--;
	/*fprintf(stderr,"   %d canaux libres.\n",chnbfree);*/
	return ch;
}


/* the channel ch is restored to the list of free channels.
   its buffers remain allocated, but re-initialized. There should be no
   difference between this channel and a new preallocated one.
 */
void chfree(ch)
  tch *ch;
{
	ch->end=1;
	ch->next=freech;
	bzero(ch->rb,sizeof(tbuf));
	bzero(ch->wb,sizeof(tbuf));
	freech=ch;
	chnbfree++;

	/*fprintf(stderr,"1 canal libere. %d libres\n",chnbfree);*/
}

/* kills n free channels from the free list. */
void chkill(n)
  int n;
{
	tch *next;

	/*fprintf(stderr,"destruction de %d canaux.\n",n);*/

	while (n && freech) {
		free(freech->rb);
		free(freech->wb);
		next=freech->next;
		free(freech);
		freech=next;
		n--; chnbfree--;
	}
	/* tries to detect eventual errors */
	if (!freech) {
		if (chnbfree) {
			fprintf(stderr,"INTERNAL ERROR: freech=NULL, chnbfree=%d. CLEARED.\n",chnbfree);
			chnbfree=0;
		}
	}
	else {
		if (chnbfree<=0) {
			fprintf(stderr,"INTERNAL ERROR: freech!=NULL, chnbfree=%d. CLEARED.\n",chnbfree);
			chnbfree=0;
			while (freech)
				freech=freech->next;
		}
	}
}


/* destroys a communication channel, i.e. it shuts down the read on the read
   descriptor, and the write on the write descriptor. It then frees the
   buffers, eventually ZLIB streams, and the channel descriptor itself. Any
   reference to it in the descriptor table is removed (set to NULL), and if
   its associated channel in this connexion has already been destroyed, then
   the socket is released. NOTE That any data is cleared from the buffers and
   lost. It's to the caller to make sure there was nothing important to keep.
*/
void destroy_channel(ch)
  tch *ch;
{
  int d1,d2;

  /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

  /* shut sockets down */
  shutdesc(d1=ch->rd, RSHUT);
  shutdesc(d2=ch->wd, WSHUT);

#if 0
  /* free buffers */
  if (ch->rb) free(ch->rb);
  if (ch->wb) free(ch->wb);

#endif

  /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

#if 0
  free(ch);
#endif

  chfree(ch);

  /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

  desc[d1].r=NULL; desc[d2].w=NULL; /* releases the pointers */
  if (desc[d1].w==NULL) {	/* the other channel was already closed. */
    closedesc(d1);	/* so we can safely close the sockets */
    closedesc(d2);
  }

  /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

}


/* destroys the 2 channels attached to this descriptor if they still exist,
   the descriptor which is on the other end, and then force a close on the
   descriptor to ensure it's correctly freed.
*/
void destroy_desc(d)
  int d;
{

  /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

  if (desc[d].r) destroy_channel(desc[d].r);
  if (desc[d].w) destroy_channel(desc[d].w);
  closedesc(d);
  if (desc[d].oth>0)
    closedesc(desc[d].oth);
  /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

}

/* displays a fatal error message and exists.
*/
void fatal(msg)
  char *msg;
{
  fprintf(stderr,"FATAL ERROR -> %s",msg);
  perror(" <- ");
  exit(1);
}

/* this function initializes all the data needed to power the proxy up
*/
void init() {
  struct hostent *he;
  int i;

  signal(SIGINT,sighandler);
  signal(SIGQUIT,sighandler);
  signal(SIGPIPE, SIG_IGN);

  /* initialize the descriptors table */
  desc=(tdesc*)malloc(sizeof(tdesc)*nbdesc);
  rfd=(fd_set*)malloc(sizeof(fd_set)*nbdesc/FD_SETSIZE);
  wfd=(fd_set*)malloc(sizeof(fd_set)*nbdesc/FD_SETSIZE);
  if ((desc==NULL) || (rfd==NULL) || (wfd==NULL)) fatal("malloc() for desc table");

  for (i=0; i<nbdesc; i++)
    desc[i].st=NOTUSED;
  highdesc=0;	/* higher descriptor used +1 = first one not used */


  bzero(&remote,sizeof(struct sockaddr_in));
  remote.sin_family=AF_INET;
  remote.sin_port=htons(remoteport);

  if((he=gethostbyname(remotehost))!=NULL)
    bcopy(he->h_addr, &remote.sin_addr, he->h_length);
  else if((remote.sin_addr.s_addr=inet_addr(remotehost))==-1)
    fatal("gethostbyname()");

  /* let's create the listen socket */


  if ((listensock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
    fatal("socket()");

  i=1;
  if ((setsockopt(listensock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))) == -1)
    fatal("setsockopt()");

  bzero((char *)&local, sizeof(local));
  local.sin_family=AF_INET;
  local.sin_addr.s_addr=htonl(INADDR_ANY);
  local.sin_port=htons(localport);
  if (bind(listensock,(struct sockaddr *)&local,sizeof(local))==-1)
    fatal("bind()");
  if (listen(listensock,5)==-1)
    fatal("listen()");

  /* now there's a server listening on the local port */
}

/* this function transfers data from the read buffer to the write buffer.
   returns 1 if something has been done, 0 else.
*/
int transfer_data(tch *ch) {
  tbuf *r, *w;
  int ldata, freespace, status;
  char altbufi, altbufo;
  int nb, nbtot=0; /* number of bytes to copy at a time */
  long long int delay;

  /* As we work with contiguous blocks of data, we'll sometimes have to
     copy parts of buffers because buffers are linear modulo their size
     and the first byte follows the last one.
     So we'll have to determine what maximum amount of data can be taken from
     the read buffer, and what maximum can be written to the write buffer.

     The stream buffer will be set up to show only the beginning of the source
     block, its size, the beginning of the dest block and its size.
     INFLATE/DEFLATE accomodate very well this way. When using single copy,
	 either we only copy the minimum common block, or we can do a max-3-steps
	 copy, or even a byte-by-byte (slow but far simpler).
     */

  if ((ch->end) && (ch->rb->l==0))
    /* if there's nothing to do or nowhere to store data, we return.
       This assumption is made for the remainder of the function. If
       it had to be removed, then there would be some tests to be
       done on empty/full buffers.
       */
    return 0;

  r=ch->rb; w=ch->wb;

  while ((r->l) && (w->l<bufsize)) {
    /* 1 - source block length */
    if (r->l > 0) {
      if (r->w > r->r)
        ldata = r->w - r->r;
      else
        ldata = bufsize - r->r;
    }
    else break; /* nothing more to be read */
    /* 2 - dest space */
    if (w->r > w->w)
      freespace = bufsize - w->l;
    else if (r!=w) /* if r==w, we know the buffer is empty and not full */
      freespace = bufsize - w->w;
    else break; /* buffer full */

    if (freespace<ldata)
      nb=freespace;
    else
      nb=ldata;

    if (throughput) {
	delay=((long long)nb * 1000000 ) / throughput;
	/*fprintf(stderr,"nb=%ld, w->l=%d\n",(int)nb, w->l);*/
	/*fprintf(stderr,"delay=%lld, when(s)=%lld\n", delay, (desc[ch->wd].when));*/
	desc[ch->wd].when = ((w->l)?(desc[ch->wd].when):iterdate) + delay;
	//desc[ch->wd].when = MAX(iterdate, desc[ch->wd].when) + delay;
	/*fprintf(stderr,"delay=%lld, when(s)=%lld\n", delay, (desc[ch->wd].when));*/
    }
    else
	desc[ch->wd].when=iterdate;

    bcopy(&r->data[r->r], &w->data[w->w], nb);
    r->r=(r->r+nb)%bufsize; r->l-=nb;
    w->w=(w->w+nb)%bufsize; w->l+=nb;
    nbtot+=nb;

  }
  /* resets the 2 pointers to the beginning if the buffer is empty */
  if (!r->l)
    r->r=r->w=0;
  ch->end = 1;
  return (nbtot>0);

  fprintf(stderr,"*****************ligne %d: unreachable\n",__LINE__);
  return 0;
}


/* the select loop itself !
*/
void select_loop() {
  int s;	/* socket descriptor */
  tdesc *d;
  int ss;
  jmp_buf jmpbuf;
  int newhighdesc;	/* used for socket creations */
  int activity;

  struct timeval tv;
  long long int firstdate;


  /* here it is (the select loop) */
  /* WARNING: The longjmp's are very important because once a channel has
     been deleted, we can't rely on some sockets nor on the FD_ISSET
     behavior because some closed sockets may still be activated. So the
     better way is to agree that channel destruction is not that so often
     and so we can go back to the beginning of the loop each time we delete
     one.

     The main problem is that gcc sometimes forgets some local variables
     that are affected before a longjmp if they aren't referenced farther
     in the program.

     Perhaps a future release will be more precise with only FD_SET/FD_CLR
     manipulations.
     */
  newhighdesc=highdesc;

  if (setjmp(jmpbuf));
  while (1) {


    /*fprintf(stderr,"highdesc=%d, Ligne %d\n",highdesc,__LINE__);*/
    highdesc=newhighdesc;
    /*fprintf(stderr,"highdesc=%d, Ligne %d\n",highdesc,__LINE__);*/

    /* first, we'll process internal jobs (cleanup, compression...) */
    do {
	gettimeofday(&tv,NULL);
	iterdate=((long long)tv.tv_sec)*(long long)1000000+(long long)tv.tv_usec;
	
	/* resets the time for the next select */
	firstdate=-1;
	
	activity=0;
	FD_ZERO(rfd); FD_ZERO(wfd);

      /* let's see which descriptor can be read and which one can be written */
      for (s=highdesc-1; s>=0; s--) {
	/*printf("desc=%d, st=0x%02X\n",s,desc[s].st);*/
	if ((d=&desc[s])->st==NOTUSED) {
	  if (s==highdesc-1) highdesc=s; /* automagically updates highdesc */
	  continue;
	}

	/*fprintf(stderr,"Ligne %d\n",__LINE__);*/


	/****************************************************************/
	/* First, we look for deletable channels. The simplest ones simply
	   have a shut down output port, which of course is completely
	   useless.
	   *****************************************************************/
	if ((d->st&WSHUT) && (d->w!=NULL)) {
	  activity=1;
	  destroy_channel(d->w);
	}

	/*fprintf(stderr,"Ligne %d\n",__LINE__);*/

	/* the second type are those whose input is closed without any
	   data left in the buffers nor in any internal mechanism.
	   No new data will never go to the output so we also close.
	   */
	if ((d->st&RSHUT) && (d->r!=NULL)) {
	  if ((d->r->rb->l==0) &&	/* no data in read buffer */
	      (d->r->wb->l==0) &&	/* no data in write buffer */
	      (d->r->end)) { /* data in stream out already flushed */
	    /* kill the channel as it's useless now. */
	    activity=1;
	    destroy_channel(d->r);
	  }
	}


	/*fprintf(stderr,"Ligne %d\n",__LINE__);*/

	if ((d->w!=NULL) && (d->w->rb->l>0) && (d->w->wb->l<bufsize)) {
	  /* some data can be transfered from RB to WB. As we use
	     Hi Water/Lo water method to reduce network overhead,
	     there is a potential risk that data from RB will never be
	     transfered to WB because it would be under the low water
	     mark. Thus, we'll make this transfer by hand now.
	     */
	  /*fprintf(stderr,"ligne %d: r->l=%d, w->l=%d, end=%d\n",
	    __LINE__, d->w->rb->l, d->w->wb->l, d->w->end);*/
	  activity |= transfer_data(d->w);
	  /*fprintf(stderr,"ligne %d: r->l=%d, w->l=%d, end=%d\n",
	    __LINE__, d->w->rb->l, d->w->wb->l, d->w->end);*/
	}


	/*fprintf(stderr,"Ligne %d\n",__LINE__);*/
      }
      /*fprintf(stderr,"activity=%d\n",activity);*/
    } while (activity);

    /* let's see which descriptor can be read and which one can be written */
    for (s=highdesc-1; s>=0; s--) {
      /*printf("desc=%d, st=0x%02X\n",s,desc[s].st);*/
      if ((d=&desc[s])->st==NOTUSED) {
	if (s==highdesc-1) highdesc=s; /* automagically updates highdesc */
	continue;
      }

      /****************************************************************/
      /* Now, let's manage activation on read ports                   */
      /****************************************************************/
      if ((d->r!=NULL) && (!(d->st&RSHUT)) && (d->r->rb->l<=RHIWAT) && !(d->st&NOTYET)) {
	FD_SET(s,rfd);
	/*fprintf(stderr,"%d active en READ\n",s);*/
      }


      /*    fprintf(stderr,"Ligne %d\n",__LINE__);*/

      /* if data persist in a buffer, we set the take the time to send them into account. */
      if ((d->w!=NULL) && (d->w->wb->l > 0)) {
	  /*fprintf(stderr,"d->when=%lld %c date=%lld\n",d->when, (d->when>iterdate)?'>':'<', iterdate);*/
	  /*fprintf(stderr,"s=%d, firstdate avant = %lld ; ",s,firstdate);*/
	  
	  if ((firstdate<0) || (d->when<firstdate))
	      firstdate=d->when;
	  /*fprintf(stderr,"firstdate apres = %lld\n",firstdate);*/
      }

      /****************************************************************/
      /* Ok, activation on write ports                                */
      /****************************************************************/
      if ((d->w!=NULL) &&
	  ((d->st&NOTYET) ||/* connection not active yet */
	   (d->when<=iterdate) &&
	   ((d->w->wb->l>WLOWAT) || /* or write buffer growing quickly */
	    ((d->w->wb->l>0) && /* or a few data in the buffer, and */
	     (d->w->rb->l==0) && /* no data in rbuf */
	     (d->w->end))))) { /* no more data to append to it */
	  FD_SET(s,wfd); /* we activate it */
	  /*fprintf(stderr,"%d active en WRITE\n",s);*/
      }
    }

    /* if there are enough free descriptors to allow new connections, then we do.*/
    if (highdesc<=nbdesc-2) {
      FD_SET(listensock,rfd);
      if (listensock>=highdesc) highdesc=listensock+1;
    }

    /* now, the first part of the job is done ! */

    /*fprintf(stderr,"highdesc=%d, Ligne %d\n",highdesc,__LINE__);*/

    /*	sleep(1);*/
    
    /*
      if the number of free buffer is not equal to that required +/- 1,
      we can adjust it now.
    */
    
    if (chnbfree>chnormfree+1)
	chkill(chnbfree-(chnormfree+1));
    else {
	while (chnbfree<chnormfree)
	    freech=chcreate();
    }

    newhighdesc = highdesc;

    /*fprintf(stderr,"select1(firstdate=%ld)\n",firstdate);*/
    if (!throughput)	/* no limit */
	firstdate=-1;
    /*fprintf(stderr,"select2(firstdate=%ld)\n",firstdate);*/

    if (firstdate >= 0) { /* at least one descriptor needs to send soon */
	/*fprintf(stderr,"firstdate=%lld\n",firstdate);*/
	firstdate -= iterdate;
    	if (firstdate>0) {
	    tv.tv_sec  = firstdate / 1000000;
	    tv.tv_usec = firstdate % 1000000;
	}
	else { /* negative delay: send immediately */ 
	    tv.tv_sec=0;
	    tv.tv_usec=1;
	    firstdate=0;
	}
	/*fprintf(stderr,"tv.tv_sec=%ld, tv.tv_usec=%ld\n",tv.tv_sec, tv.tv_usec);*/
    }

    /* tv.tv_sec=0; tv.tv_usec=100000;*/

    do {
	/*fprintf(stderr,"select(firstdate=%ld)\n",firstdate);*/
	ss=select(highdesc, rfd, wfd, NULL, (firstdate<0)?NULL:&tv);
	/*fprintf(stderr,"select=%d\n",ss);*/
    } while ((ss==-1) && (errno==EINTR));
    
    /*fprintf(stderr,"select=%d\n",ss);*/

    if (ss==-1) {	/* should never appear */
      perror("select()");
      continue;	/* retry the loop */
    }


    /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

    /* since the timeout is NULL, ss must be *at least* 1 */
    if (FD_ISSET(listensock,rfd)) {
      /* we have to accept a new connection */
      struct sockaddr saddr;
      int sal=sizeof(saddr);
      int sockout, lowat;
      struct linger lin;


      /*    fprintf(stderr,"Ligne %d\n",__LINE__);*/

      s=accept(listensock,&saddr,&sal);
      lin.l_onoff=1;
      lin.l_linger=10;
      if ((setsockopt(s,SOL_SOCKET,SO_LINGER,&lin,sizeof(lin))) == -1)
	perror("Warning: setsockopt(SO_LINGER)");
      lowat=8192;
      /*if ((setsockopt(s,SOL_SOCKET,SO_RCVLOWAT,&lowat,sizeof(lowat))) == -1)
	perror("Warning: setsockopt(SO_RCVLOWAT)");*/
      if ((sockout=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
	perror("socket()");
	closedesc(s);
	/*longjmp(jmpbuf,1);*/
	continue;
      }
      lin.l_onoff=1;
      lin.l_linger=10;
      if ((setsockopt(sockout,SOL_SOCKET,SO_LINGER,&lin,sizeof(lin))) == -1)
	perror("Warning: setsockopt(SO_LINGER)");
      if (fcntl(s,F_SETFL,O_NONBLOCK)==-1) {
	perror("Warning: fcntl(SET O_NONBLOCK,s_in)");
      }
      if (fcntl(sockout,F_SETFL,O_NONBLOCK)==-1) {
	perror("Warning: fcntl(SET O_NONBLOCK,s_out)");
      }

      if (ntohs(remote.sin_port) >= (remoteport + nbport))
	  remote.sin_port = htons(remoteport);

      if (connect(sockout,(struct sockaddr*)&remote,sizeof(remote))==-1) {
	if (errno!=EINPROGRESS) {
	  perror("connect()");
	  destroy_desc(s); destroy_desc(sockout);
	  /*longjmp(jmpbuf,1);*/
	  continue;
	}
	desc[sockout].st=NOTYET;
	/*fprintf(stderr,"Connexion asynchrone sur socket %d pour %d\n",sockout,s);*/
      }
      else {
	desc[sockout].st=CONN;
	/*fprintf(stderr,"Connexion immediate sur socket %d pour %d\n",sockout,s);*/
      }

      remote.sin_port = htons(ntohs(remote.sin_port) + 1);

      desc[s].st=CONN;
      desc[s].oth=sockout; desc[sockout].oth=s;
      desc[s].r=desc[sockout].w=challoc();
      desc[s].w=desc[sockout].r=challoc();
      desc[s].r->rd=s; desc[s].r->wd=sockout;
      desc[s].w->rd=sockout; desc[s].w->wd=s;
      desc[s].when=desc[sockout].when=iterdate;

      if (s>=newhighdesc) newhighdesc=s+1;
      if (sockout>=newhighdesc) newhighdesc=sockout+1;

      /*ss--;	/* counts one socket less */
      /* disables these sockets for this turn */
      /*FD_CLR(s,rfd); FD_CLR(sockout,rfd);
	FD_CLR(s,wfd); FD_CLR(sockout,wfd);*/

      /*fprintf(stderr,"highdesc=%d newhighdesc=%d\n",highdesc, newhighdesc);*/
      /*fprintf(stderr,"Creation: sockout=%d, s=%d, rr=%d, rw=%d, wr=%d, ww=%d\n",sockout,s,
	desc[s].r->rb->l,desc[s].r->wb->l,
	desc[s].w->rb->l,desc[s].w->wb->l);*/
      continue;  /* highdesc changed. we can't trust other FD_ISSET() */
      /* FD_CLR(listensock,wfd); FD_CLR(listensock,rfd);*/
    }


    /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

    /* We'll look at the write descriptors first, in order to quickly
       output data to free the place to new ones. */
    for (s=0;s<highdesc;) {
      /* this test allows to skip the empty descriptors block by block */
#if 0
      if (!((long int*)wfd)[s/FD_SETSIZE]) {
	s=(s%FD_SETSIZE)+FD_SETSIZE;
	continue;
      }
#endif
      if (FD_ISSET(s,wfd)) {


	/*fprintf(stderr,"Ligne %d, s=%d, w->r=%d\n",__LINE__, s, desc[s].w->wb->r);*/

	if (desc[s].st&NOTYET) {  /* it's the ack from an async connect */
	  int retval, lretval=sizeof(retval);
#ifndef WIN32
	  getsockopt(s,SOL_SOCKET,SO_ERROR,&retval,&lretval);
#else
	  retval=1;
#endif
	  if (retval) {
	      /*fprintf(stderr,"Socket %d: code retour connect=%d. Fermeture.\n",
	      s,retval);*/
	    FD_CLR(s,wfd); FD_CLR(s,rfd);
	    if (desc[s].oth>0) {
	      FD_CLR(desc[s].oth,wfd); FD_CLR(desc[s].oth,rfd);
	    }

	    destroy_desc(s);
	    /*longjmp(jmpbuf,1);*/
	  } else {
	    desc[s].st&=~NOTYET;
	    desc[s].st|=CONN;
	    /*fprintf(stderr,"Socket %d: connexion etablie.\n",s);*/
	    /*longjmp(jmpbuf,1);*/
	  }
	}
	else { /* it's simply a write OK */
	  int ldata, lw;
	  tbuf *w;
	  /****** A REMPLIR *******************************************/


	  /*fprintf(stderr,"Ligne %d, s=%d, w->r=%d\n",__LINE__, s, desc[s].w->wb->r);*/

	  /* slide some eventual data from the RB to the WB if
	     possible. This way, we'll really write the maximum we can
	     do !
	     */
	  /* fprintf(stderr,"ligne %d: s=%d w=0x%08X w->r=%08X, w->w=%08X, w->l=%08X\n",
	     __LINE__,s, desc[s].w->wb,
	     desc[s].w->wb->r, desc[s].w->wb->w, desc[s].w->wb->l);*/
	  transfer_data(desc[s].w);


	  /*fprintf(stderr,"Ligne %d, s=%d, w->r=%d\n",__LINE__, s, desc[s].w->wb->r);*/

	  w=desc[s].w->wb;
	  if (w->l > 0) {
	    if (w->w > w->r)
	      ldata = w->w - w->r;
	    else
	      ldata = bufsize - w->r;

	    /*fprintf(stderr,"Ligne %d: w->data=0x%08X, w->r=%d, ldata=%d\n",__LINE__, w->data, w->r, ldata);*/

	    /*lw=send(s, w->data+w->r, ldata, 0);*/
	    lw=write(s, w->data+w->r, ldata);

	    /*fprintf(stderr,"write(%d)=%d, ldata=%d\n", s, lw, ldata);*/

	    /*fprintf(stderr,"Ligne %d\n",__LINE__);*/

	    /*fprintf(stderr,"%d/%d bytes ecrits sur %d\n",lw,ldata,s);*/
	    if (lw>0) {
	      w->r = ( w->r + lw ) % bufsize;
	      w->l -= lw;
	    }
	    else
	      shutdesc(s,WSHUT); /* no more writes there */
	  }
	  /****** A REMPLIR *******************************************/
	  /* quite a good optimization: if the buffer is empty
	     (which is often the case after a write), then its
	     pointers are taken back to the beginning so that it
	     is faster to access at once.
	     */
	  if (w->l==0) {
	    w->r=w->w=0;
	  }
	}
      }
      s++;
    }

    /* Now examine the read descriptors */
    for (s=0;s<highdesc;) {
      /* this test allows to skip the empty descriptors block by block */
#if 0
      if (!((long int*)rfd)[s/FD_SETSIZE]) {
	s=(s%FD_SETSIZE)+FD_SETSIZE;
	continue;
      }
#endif
      if (FD_ISSET(s,rfd)) {
	int freespace, lr, i;
	tbuf *r;

	/****** A REMPLIR *******************************************/

	/*fprintf(stderr,"Ligne %d, s=%d, w->r=%d\n",__LINE__, 5,  (desc[5].w)?desc[5].w->wb->r:-1);*/


	/*fprintf(stderr,"ligne %d: r->l=%d\n",__LINE__,desc[s].r->wb->l);*/
	transfer_data(desc[s].r);

	/*fprintf(stderr,"Ligne %d, s=%d, w->r=%d\n",__LINE__, 5,  (desc[5].w)?desc[5].w->wb->r:-1);*/

	r=desc[s].r->rb;

	if (r->r > r->w)
	  freespace = bufsize - r->l;
	else if (r->l<bufsize)
	  freespace = bufsize - r->w;
	else
	  continue; /* no need to read 0 bytes to a full buffer !*/


	/*fprintf(stderr,"Ligne %d, s=%d, r->w=%d w->r=%d\n",__LINE__, s,  r->w, (desc[5].w)?desc[5].w->wb->r:-1);*/

	/*lr=recv(s, r->data + r->w, freespace, 0);*/
	/*		lr=0;
			do {
			i=lr;*/
	lr=read(s, &r->data[r->w], freespace);
	/*			fprintf(stderr,"lr=%d\n",lr);
				} while (lr>0);
				lr=i;*/

	/*fprintf(stderr,"Read(%d)=%d, freespace was %d\n", s, lr, freespace);*/
	/*fprintf(stderr,"ligne %d: s=%d w=0x%08X w->r=%d, w->w=%d, w->l=%d\n",
			__LINE__,s, desc[s].w->wb,
			desc[s].w->wb->r, desc[s].w->wb->w, desc[s].w->wb->l);*/

	/*fprintf(stderr,"%d/%d bytes lus sur %d\n",lr,freespace,s);*/
	if (lr>0) {

	  /*fprintf(stderr,"Ligne %d, s=%d, w->r=%d\n",__LINE__, 5,  (desc[5].w)?desc[5].w->wb->r:-1);*/

	  r->w = ( r->w + lr ) % bufsize;
	  r->l += lr;

	  /*fprintf(stderr,"Ligne %d, s=%d, w->r=%d\n",__LINE__, 5,  (desc[5].w)?desc[5].w->wb->r:-1);*/

	}
	else
	  shutdesc(s,RSHUT); /* no more data to read from here */
	/****** A REMPLIR *******************************************/
      }
      s++;
    }

  }
}

void usage() {
  fprintf(stderr,"MPROX v 0.1 - (C) 990308 Willy Tarreau <willy@meta-x.org>\n" \
	  "Usage:\n" \
	  "       mprox [-h] [-p port] [-b bufsize] -x host [-P port]\n" \
	  "            [-n nbports] [-d nbdesc] [-m memory] [-r rate]\n" \
	  "          -p port defines the listening port (default: %d).\n" \
	  "          -P port specifies the port on the remote host (default: %d).\n" \
	  "          -n nbport activates the round robin for <nbport> remote ports.\n" \
	  "          -b size defines the new buffer size (default: %d).\n" \
	  "          -x host specifies the host on which to connect.\n" \
	  "          -r rate sets the maximum data rate to <rate> bytes by second.\n" \
	  "      Special options:\n"\
	  "          -d nbdesc : sets the maximum number of sockets (default: %d)\n"\
	  "          -m memory : #of permanent channels in memory (default: %d)\n"\
	  "\n",
	  LOCALPORT, REMOTEPORT, BUFSIZE, NBDESC, CHNBFREE);

  exit(1);
}

main(argc,argv)
  int argc;
  char **argv;
{
  int i, c;

  for (i=1; i<argc; i++) {
    if (!strcmp(argv[i],"-h")) {
      usage();
    } else if (!strcmp(argv[i],"-p")) {
      if (argc>++i) {
	localport=atol(argv[i]);
      }
      else usage();
    } else if (!strcmp(argv[i],"-P")) {
      if (argc>++i) {
	remoteport=atol(argv[i]);
      }
      else usage();
    } else if (!strcmp(argv[i],"-x")) {
      if (argc>++i) {
	remotehost=argv[i];
      }
      else usage();
    } else if (!strcmp(argv[i],"-b")) {
      if (argc>++i) {
	bufsize=atoi(argv[i]);
      }
      else usage();
    } else if (!strcmp(argv[i],"-d")) {
      if (argc>++i) {
	nbdesc=atoi(argv[i]);
      }
      else usage();
    } else if (!strcmp(argv[i],"-n")) {
      if (argc>++i) {
	nbport=atoi(argv[i]);
      }
      else usage();
    } else if (!strcmp(argv[i],"-r")) {
      if (argc>++i) {
	throughput=atol(argv[i]);
      }
      else usage();
    } else if (!strcmp(argv[i],"-m")) {
      if (argc>++i) {
	chnormfree=atoi(argv[i]);
      }
      else usage();
    }
	else usage();
  }
  if (!remotehost)
    usage();
  init();
  select_loop();
}
