/*
   3APA3A simpliest proxy server
   (c) 2002-2003 by ZARAZA <3APA3A@security.nnov.ru>

   please read License Agreement

   $Id: sockmap.c,v 1.3 2003/09/16 14:55:29 vlad Exp $
*/

#include "proxy.h"

#define CLEARRETURN(code) {res=code; goto RETURNSM;}

#define UDPBUFSIZE 65536
#define TCPBUFSIZE 10240
#define BUFSIZE ((param->service == S_UDPPM)?UDPBUFSIZE:TCPBUFSIZE)

int sockmap(struct clientparam * param, int timeo){
 int i, j, res, sent=0, received=0, trans = 0;
 SASIZETYPE sasize;
 unsigned char *buf = 0;
 fd_set fds;
 struct timeval tv = {0,0};
 int sleeptime;

 if (!(buf = malloc(BUFSIZE))){CLEARRETURN(21);}
 tv.tv_sec = timeo;
 for(; ; ) {
    FD_ZERO(&fds);
/*
    if(!param->singlepacket)FD_SET(param->clisock, &fds);
    FD_SET(param->remsock, &fds);
*/
    if(!param->singlepacket && !param->waitserver)FD_SET(param->clisock, &fds);
    if(!param->waitclient)FD_SET(param->remsock, &fds);
    res = select (param->remsock > param->clisock? param->remsock+1 : param->clisock+1, &fds, NULL, NULL, &tv);
    if(res == 0){CLEARRETURN(97);}
    if(res < 0){CLEARRETURN(91);}
    j = 0;
    if (FD_ISSET(param->remsock, &fds)) {
	sasize = sizeof(struct sockaddr_in);
#ifndef __CYGWIN__
	j = recvfrom(param->remsock, buf, BUFSIZE, 0, (struct sockaddr *)&param->sins, &sasize);
#else
	j = recv(param->remsock, buf, BUFSIZE, 0);
#endif
	if (j > 0) {
		if(param->bandlimfunc) {
			sleeptime = (*param->bandlimfunc)(param, j);
			if(sleeptime) {
				if(sleeptime > timeo * 1000){CLEARRETURN(95);}
				usleep(sleeptime * SLEEPTIME);
			}
		}
		sasize = sizeof(struct sockaddr_in);
		trans = 0;
		do {
#ifndef __CYGWIN__
			res=sendto(param->clisock, buf + trans, j - trans, 0, (struct sockaddr*)&param->sinc, sasize);
#else
			res=send(param->clisock, buf + trans, j - trans, 0);
#endif
			if(res <= 0) {CLEARRETURN(92);}
			trans += res;
		}while(trans < j);
		param->statssrv += j;
		received += j;
		if(param->waitserver && param->waitserver<= received){
			CLEARRETURN(98);
		}
	}
	else if (j==0) {
		shutdown(param->remsock, SHUT_RDWR);
		close(param->remsock);
		param->remsock = INVALID_SOCKET;
		CLEARRETURN(0);
	}
	else if (j < 0 && errno != EAGAIN) {CLEARRETURN(93);}
    }
    else if (res == SOCKET_ERROR) {CLEARRETURN (94);}

    if(param->singlepacket)continue;
    i = 0;
    if (FD_ISSET(param->clisock, &fds)) {
	sasize = sizeof(struct sockaddr_in);
#ifndef __CYGWIN__
	i = recvfrom(param->clisock, buf, BUFSIZE, 0, (struct sockaddr *)&param->sinc, &sasize);
#else
	i = recv(param->clisock, buf, BUFSIZE, 0);
#endif
	if (i > 0) {
		sasize = sizeof(struct sockaddr_in);
		trans = 0;
		do {
#ifndef __CYGWIN__
			res=sendto(param->remsock, buf + trans, i - trans, 0, (struct sockaddr *)&param->sins, sasize);
#else
			res=send(param->remsock, buf + trans, i - trans, 0);
#endif
			if(res <= 0){CLEARRETURN(92);}
			trans += res;
		}while(trans < i);
		param->statscli += i;
		sent += i;
		if(param->waitclient && param->waitclient<= sent) {
			CLEARRETURN(99);
		}
	}
	else if (i==0) {
		shutdown(param->clisock, SHUT_RDWR);
		close(param->clisock);
		param->clisock = INVALID_SOCKET;
		CLEARRETURN(0);
	}
	else if (i <0 && errno != EAGAIN) {CLEARRETURN(93);}
    }
    else if (res == SOCKET_ERROR) {CLEARRETURN(94);}

 }

RETURNSM:
 if(buf)free(buf);
 return res;
}
