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

   please read License Agreement

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

#include "proxy.h"
#ifndef MODULEMAINFUNC
#define MODULEMAINFUNC main
#endif

#ifdef child

int MODULEMAINFUNC (int argc, char** argv){

 SOCKET sock = INVALID_SOCKET;
 int i=0;
 SASIZETYPE size;
 pthread_t thread;
 struct clientparam defparam;
 int demon=0;
 struct clientparam * newparam;
 int error = 0;
 int childcount=0;
 struct extparam myconf;
 unsigned char buf[64];
 pthread_mutex_t counter_mutex;




#ifndef UDP
 int new_sock = INVALID_SOCKET;
 struct linger lg;
#else
 fd_set fds;
#endif
#ifdef PORTMAP
#ifdef PORT
#undef PORT
#endif
#define PORT myconf.intport
#endif
#ifdef _WIN32
 WSADATA wd;
 HANDLE h;
 WSAStartup(MAKEWORD( 1, 1 ), &wd);
#else
 signal(SIGPIPE, SIG_IGN);
#endif


 memcpy(&myconf, &conf, sizeof(myconf));
 memset(&defparam, 0, sizeof(struct clientparam));
 defparam.childcount = &childcount;
 defparam.logfunc = myconf.logfunc;
 defparam.authfunc = myconf.authfunc;
 defparam.acl = myconf.acl;
 defparam.pwl = myconf.pwl;
 defparam.logformat = myconf.logformat;
 defparam.service = SERVICE;

 pthread_mutex_init(defparam.counter_mutex = &counter_mutex, NULL);
 for (i=1; i<argc; i++) {
	if(*argv[i]=='-') {
		switch(argv[i][1]) {
		 case 'd': 
			if(!demon)daemonize();
			demon = 1;
			break;
		 case 'l':
			defparam.logfunc = logstdout;
			if(argv[i][2]) {
				if(argv[i][2]=='@'){

#ifndef _WIN32
					openlog(argv[i]+3, LOG_PID, LOG_DAEMON);
					defparam.logfunc = logsyslog;
#endif
				}
				else
					freopen(argv[i] + 2, "a", stdout);
				fseek(stdout, 0L, SEEK_END);
			}
			break;
		 case 'i':
			myconf.intip = getip((unsigned char *)argv[i]+2);
			break;
		 case 'e':
			myconf.extip = getip((unsigned char *)argv[i]+2);
			break;
		 case 'p':
			myconf.intport = atoi(argv[i]+2);
			break;
#ifdef UDP
		case 's':
			myconf.singlepacket = 1;
#endif
		 default:
			error = 1;
			break;
		}
	}
	else break;
 }
#ifndef PORTMAP
 if (error || i!=argc) {
	fprintf(stderr, "Usage: %s [-d] [-l[[@]logfile]] [-i<internal_ip>]"
			" [-e<external_ip>] [-p<port>]\n"
			"\t-d close console (daemonise)\n"
			"\t-l log to stderr\n\n"
			"\tExample: %s -d -i127.0.0.1\n\n"
			"%s", 
			argv[0], argv[0], copyright);
	exit(1);
 }
#else
 if (argc != i+3 || *argv[i]=='-'|| (myconf.intport = atoi(argv[i]))==0 || (defparam.targetport = atoi(argv[i+2]))==0) {
	fprintf(stderr, "Usage: %s [-d] [-l[[@]logfile]] [-i<internal_ip>]"
			" [-e<external_ip>] [-p<port>] <port_to_bind>"
			" <target_hostname> <target_port>\n"
			"\t-d close console (daemonise)\n"
			"\t-l log to stderr\n\n"
			"\tExample: %s -d 127.0.0.1 53 ns.somehost.ru 53\n\n"
			"%s", 
			argv[0], argv[0], copyright);
	exit(1);
 }
 defparam.target = (unsigned char *)argv[i+1];
#endif
#ifndef UDP
 if( (sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
#else
 if( (sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) { 
#endif
	perror("socket()");
	return -2;
 }
 defparam.sinc.sin_addr.s_addr = defparam.intip = myconf.intip;
 htons(PORT);
 if(!myconf.intport)myconf.intport = PORT;
 defparam.sinc.sin_port = defparam.intport = htons(myconf.intport);
 defparam.sins.sin_addr.s_addr = defparam.extip = myconf.extip;
 defparam.sins.sin_port = defparam.extport = htons(myconf.extport);
 defparam.srvsock = sock;
 defparam.remsock = defparam.clisock = defparam.ctrlsock = INVALID_SOCKET;
 defparam.demon = demon;
 defparam.sins.sin_family = defparam.sinc.sin_family = AF_INET;
 size = sizeof(defparam.sinc);
 if(bind(sock, (struct sockaddr*)&defparam.sinc, size)==-1) {
	sprintf((char *)buf, "bind(): %s", strerror(errno));
	(*defparam.logfunc)(&defparam, buf);
	return -2;
 }
#ifndef UDP
 if(listen (sock, myconf.maxchild)==-1) {
	sprintf((char *)buf, "listen(): %s", strerror(errno));
	(*defparam.logfunc)(&defparam, buf);
	return -2;
 }
 lg.l_onoff = 1;
 lg.l_linger = 10;
#else
 FD_ZERO(&fds);
 FD_SET(sock, &fds);
 defparam.srvfds = &fds;
 defparam.clisock = sock;
 defparam.singlepacket = myconf.singlepacket;
#endif

 sprintf((char *)buf, "Accepting connections [%u/%u]", (unsigned)getpid(), (unsigned)pthread_self());
(*defparam.logfunc)(&defparam, buf);
defparam.sinc.sin_addr.s_addr = defparam.sins.sin_addr.s_addr = 0;
defparam.sinc.sin_port = defparam.sins.sin_port = 0;

 for (;;) {
#ifndef UDP
	size = sizeof(defparam.sinc);
	new_sock = accept(sock, (struct sockaddr*)&defparam.sinc, &size);
	if(new_sock == INVALID_SOCKET){
		sprintf((char *)buf, "accept(): %s", strerror(errno));
		(*defparam.logfunc)(&defparam, buf);
		continue;
	}
	setsockopt(new_sock, SOL_SOCKET, SO_LINGER, (unsigned char *)&lg, sizeof(lg));
	setsockopt(new_sock, SOL_SOCKET, SO_OOBINLINE, NULL, 0);
	defparam.clisock = new_sock;
#else
	while(!FD_ISSET(sock, &fds))usleep(SLEEPTIME);
	if (select(sock+1, &fds, NULL, NULL, NULL)!=1){
		sprintf((char *)buf, "select(): %s/%d", strerror(errno), errno);
		(*defparam.logfunc)(&defparam, buf);
		usleep(SLEEPTIME);
		continue;
	}
	FD_CLR(sock, &fds);
#endif
	while(paused ||childcount >= myconf.maxchild)usleep(SLEEPTIME);
	if(! (newparam = malloc (sizeof(defparam)))){
#ifndef UDP
		shutdown(new_sock, SHUT_RDWR);
		close(new_sock);
#endif
defparam.res = 21;
(*defparam.logfunc)(&defparam, (unsigned char *)"Memory Allocation Failed");
		usleep(SLEEPTIME);
		continue;
	};
	memcpy(newparam, &defparam, sizeof(defparam));
#ifdef _WIN32
	h = CreateThread((LPSECURITY_ATTRIBUTES )NULL, 16384, (LPTHREAD_START_ROUTINE)child, (void *) newparam, (DWORD)0, &thread);
	if (h) CloseHandle(h);
#else
	if(pthread_create(&thread, NULL, child, (void *)newparam)){
		sprintf((char *)buf, "pthread_create(): %s", strerror(errno));
		(*defparam.logfunc)(&defparam, buf);
	}
#endif
	pthread_mutex_lock(&counter_mutex);
	childcount++;
	pthread_mutex_unlock(&counter_mutex);
 }
 pthread_mutex_destroy(&counter_mutex);
}

#endif
