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

   please read License Agreement

   $Id: common.c,v 1.4 2003/11/29 10:58:29 vlad Exp $
*/



#include "proxy.h"


char * copyright="(c)2000-2003 3APA3A, Vladimir Dubrovin & Security.Nnov\n"
		 "Documentation and sources: http://www.security.nnov.ru/soft/3proxy\n"
		 "Please read license agreement in \"License\" file.\n"
		 "You can not use this program without accepting license agreement";

int paused = 0;
int demon = 0;
unsigned char **stringtable = NULL;

int timeouts[] = {1, 5, 30, 60, 180, 1800, 15, 60, 0};

struct datatable timeout_table = {
	INTTABLE,
	sizeof(timeouts)/sizeof(int) - 1,
	(void *)timeouts
};

int myinet_ntoa(struct in_addr in, char * buf){
 unsigned u = ntohl(in.s_addr);
 return sprintf(buf, "%u.%u.%u.%u", 
	((u&0xFF000000)>>24), 
	((u&0x00FF0000)>>16),
	((u&0x0000FF00)>>8),
	((u&0x000000FF)));
}

struct extparam conf = {
	0, 100, INADDR_ANY, INADDR_ANY, 0, 0, 0, 
	NULL, NULL, NULL,
	doconnect, lognone, NULL,
	(unsigned char *)"G%y%m%d%H%M%S.%. %p %E %U %C:%c %R:%r %O %I %h %T"
};

char* demanddialprog = NULL;

int dobuf(struct clientparam * param, unsigned char * buf, const unsigned char *s){
	struct tm* tm;
	int i, j;
	unsigned long sec;
	unsigned msec;
	int timezone;
	int len;

#ifdef _WIN32
	struct timeb tb;

	if(param->trafcountfunc)(*param->trafcountfunc)(param);
	ftime(&tb);
	sec = (unsigned long)tb.time;
	msec = (unsigned)tb.millitm;
	timezone = (int) tb.timezone;

#else
	struct timeval tv;
	struct timezone tz;
	gettimeofday(&tv, &tz);

	sec = tv.tv_sec;
	msec = tv.tv_usec / 1000;
	timezone = tz.tz_minuteswest;
#endif

	tm = (*param->logformat == 'G' || *param->logformat == 'g')?
		gmtime((time_t *)&sec) : localtime((time_t *)&sec);
	*buf = 0;
	if(!param) return 0;
	for(i=0, j=1; param->logformat[j] && i < 1000; j++){
		if(param->logformat[j] == '%' && param->logformat[j+1]){
			j++;
			switch(param->logformat[j]){
				case 'y':
				 sprintf((char *)buf+i, "%.2d", tm->tm_year%100);
				 i+=2;
				 break;
				case 'Y':
				 sprintf((char *)buf+i, "%.4d", tm->tm_year+1900);
				 i+=4;
				 break;
				case 'm':
				 sprintf((char *)buf+i, "%.2d", tm->tm_mon+1);
				 i+=2;
				 break;
				case 'd':
				 sprintf((char *)buf+i, "%.2d", tm->tm_mday);
				 i+=2;
				 break;
				case 'H':
				 sprintf((char *)buf+i, "%.2d", tm->tm_hour);
				 i+=2;
				 break;
				case 'M':
				 sprintf((char *)buf+i, "%.2d", tm->tm_min);
				 i+=2;
				 break;
				case 'S':
				 sprintf((char *)buf+i, "%.2d", tm->tm_sec);
				 i+=2;
				 break;
				case 't':
				 sprintf((char *)buf+i, "%.10lu", sec);
				 i+=10;
				 break;
				case '.':
				 sprintf((char *)buf+i, "%.4u", msec);
				 i+=4;
				 break;
				case 'z':
				 sprintf((char *)buf+i, "%+.2d%.2u", (int)(-timezone/60), (unsigned)(timezone%60));
				 i+=5;
				 break;
				case 'U':
				 if(param->username){
					for(len = 0; i<1020 && param->username[len]; len++){
					 buf[i++] = param->username[len];
#ifndef NOODBC
					 if(param->username[len] == '\'')buf[i++] = '\'';
#endif
					}
				 }
				 else {
					buf[i++] = '-';
				 }
				 break;
				case 'N':
				 if(param->service >=0 && param->service < 10) {
					 len = (stringtable)? strlen((char *)stringtable[SERVICES + param->service]) : 0;
					 if(len > 20) len = 20;
					 memcpy(buf+i, (len)?stringtable[SERVICES + param->service]:(unsigned char*)"-", (len)?len:1);
					 i += (len)?len:1;
				 }
				 break;
				case 'E':
				 sprintf((char *)buf+i, "%.03d", param->res);
				 i += 3;
				 break;
				case 'T':
				 if(s){
					for(len = 0; i<1020 && s[len]; len++){
					 buf[i++] = s[len];
#ifndef NOODBC
					 if(s[len] == '\'')buf[i++] = '\'';
#endif
					}
				 }
				 break;
				case 'C':
				 i += myinet_ntoa(param->sinc.sin_addr, buf + i);
				 break;
				case 'R':
				 i += myinet_ntoa(param->sins.sin_addr, buf + i);
				 break;
				case 'p':
				 sprintf((char *)buf+i, "%hu", ntohs(param->intport));
				 i += strlen((char *)buf+i);
				 break;
				case 'c':
				 sprintf((char *)buf+i, "%hu", ntohs(param->sinc.sin_port));
				 i += strlen((char *)buf+i);
				 break;
				case 'r':
				 sprintf((char *)buf+i, "%hu", ntohs(param->sins.sin_port));
				 i += strlen((char *)buf+i);
				 break;
				case 'I':
				 sprintf((char *)buf+i, "%lu", param->statssrv);
				 i += strlen((char *)buf+i);
				 break;
				case 'O':
				 sprintf((char *)buf+i, "%lu", param->statscli);
				 i += strlen((char *)buf+i);
				 break;
				case 'h':
				 sprintf((char *)buf+i, "%d", param->redirected);
				 i += strlen((char *)buf+i);
				 break;
				default:
				 buf[i++] = param->logformat[j];
			}
		}
		else buf[i++] = param->logformat[j];
	}
	buf[i] = 0;
	return i;
}

void lognone(struct clientparam * param, const unsigned char *s) {
	if(param->trafcountfunc)(*param->trafcountfunc)(param);
}


void logstdout(struct clientparam * param, const unsigned char *s) {
	unsigned char buf[1024];

	dobuf(param, buf, s);
	printf("%s\n", buf);
	if(!stringtable)fflush(stdout);
}
#ifndef _WIN32
void logsyslog(struct clientparam * param, const unsigned char *s) {
	unsigned char buf[1024];

	dobuf(param, buf, s);
	syslog(LOG_INFO, "%s", buf);
}
#endif

int doconnect(struct clientparam * param){
 SASIZETYPE size = sizeof(param->sins);
 struct sockaddr_in bindsa;
 if (param->remsock == INVALID_SOCKET) {
	if(param->hostname && !param->sins.sin_addr.s_addr){
		if(!(param->sins.sin_addr.s_addr = getip(param->hostname)))
			return 100;
	}
	if ((param->remsock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {return (11);}
	bindsa.sin_family = AF_INET;
	bindsa.sin_port = param->extport;
	bindsa.sin_addr.s_addr = param->extip;
	if(bind(param->remsock, (struct sockaddr*)&bindsa, sizeof(bindsa))==-1) {
		return 12;
	}
	
	param->sins.sin_family = AF_INET;
	if(param->operation >= 256 || (param->operation & CONNECT)){
		if(connect(param->remsock,(struct sockaddr *)&param->sins,sizeof(param->sins))) {return (13);}
		if(getpeername(param->remsock, (struct sockaddr*)&param->sins, &size)== -1) {return (14);}
	}
	else {
		if(getsockname(param->remsock, (struct sockaddr *)&param->sins, &size)==-1) {return (15);}
	}
 }
 return 0;
}

void freeparam(struct clientparam * param) {
	if(param->res == 2) return;
	pthread_mutex_lock(param->counter_mutex);
	(*param->childcount)--;
	pthread_mutex_unlock(param->counter_mutex);
#ifndef _WIN32
	pthread_detach(pthread_self());
#endif
	if(param->hostname) free(param->hostname);
	if(param->username) free(param->username);
	if(param->password) free(param->password);
	if(param->extusername) free(param->extusername);
	if(param->extpassword) free(param->extpassword);
	if(param->remsock != INVALID_SOCKET) {
		shutdown(param->remsock, SHUT_RDWR);
		close(param->remsock);
	}
	if(param->clisock != INVALID_SOCKET) {
		shutdown(param->clisock, SHUT_RDWR);
		close(param->clisock);
	}
	if(param->ctrlsock != INVALID_SOCKET) {
		shutdown(param->ctrlsock, SHUT_RDWR);
		close(param->ctrlsock);
	}
	free(param);
}

int scanaddr(const unsigned char *s, unsigned long * ip, unsigned long * mask) {
	unsigned d1, d2, d3, d4, m;
	int res;
	if ((res = sscanf((char *)s, "%u.%u.%u.%u/%u", &d1, &d2, &d3, &d4, &m)) < 4) return 0;
	if(mask && res == 4) *mask = 0xFFFFFFFF;
	else if (mask) *mask = htonl(0xFFFFFFFF << (32 - m));
	*ip = htonl ((d1<<24) ^ (d2<<16) ^ (d3<<8) ^ d4);
	return res;
}


RESOLVFUNC resolvfunc = NULL;
#ifndef _WIN32
pthread_mutex_t gethostbyname_mutex;
int ghbn_init = 0;
#endif

unsigned long getip(unsigned char *name){
	unsigned long retval;
	int i;
	int ndots = 0;
	struct hostent *hp;

	if(strlen(name)>255)name[255] = 0;
	for(i=0; name[i]; i++){
		if(name[i] == '.'){
			if(++ndots > 3) break;
			continue;
		}
		if(name[i] <'0' || name[i] >'9') break;
	}
	if(!name[i] && ndots == 3){
		unsigned long ip;
		if(scanaddr(name, &ip, NULL) == 4){
			return ip;
		}
	}
	if(resolvfunc && (retval = (*resolvfunc)(name))) return retval;
#ifndef _WIN32
	if(!ghbn_init){
		pthread_mutex_init(&gethostbyname_mutex, NULL);
		ghbn_init++;
	}
	pthread_mutex_lock(&gethostbyname_mutex);
#endif
	hp=gethostbyname((char *)name);
	if (!hp) {
		if(demanddialprog){
			system(demanddialprog);
		}
		hp=gethostbyname((char *)name);
		if (!hp) {
#ifndef _WIN32
			pthread_mutex_unlock(&gethostbyname_mutex);
#endif
			return 0;
		}
	}
	retval = *(unsigned long *)hp->h_addr;
#ifndef _WIN32
	pthread_mutex_unlock(&gethostbyname_mutex);
#endif
	return retval;
}
