/*
spamtrap.c - DSBL spamtrap master tester
Copyright (C) 2002 Ian Gulliver

This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <firedns.h>
#include <firestring.h>
#include "testlib.h"

int parallel;
int children = 0;
int verbosity = 0;

void preload_cookie() {
	char env[64];
	char *s;
	s = getcookie();
	if (s == NULL)
		return;
	firestring_snprintf(env,64,"DSBL_COOKIE=%s",s);
	putenv(env);
}

void block_children() {
	pid_t p;
	int status;
	if (parallel == 0)
		return;
	while (children >= parallel) {
		p = wait(&status);
		if (verbosity >= 10) {
			printf("reclaimed child %d\n",p);
			if (WIFEXITED(status))
				printf("exit status %d\n",WEXITSTATUS(status));
			else if (WIFSIGNALED(status))
				printf("caught signal %d\n",WTERMSIG(status));
		}
		if (p < 0)
			exit(100);
		children--;
	}
}

int excluded(const char * const ip) {
	const char *excluded_ip = NULL;

	while ((excluded_ip = firestring_conf_find_next(config,"excluded_ip",excluded_ip)) != NULL) {
		if (strncmp(ip,excluded_ip,strlen(excluded_ip)) == 0)
			return 1;
	}
	return 0;
}

void do_http(const char * const ip) {
	const char *http_port = NULL;
	int i;

	if (verbosity >= 10)
		printf("%s: begin HTTP test\n",ip);

	while ((http_port = firestring_conf_find_next(config,"http_port",http_port)) != NULL) {
		block_children();
		i = fork();
		if (i == 0) {
			execl(BINDIR "/httptest",BINDIR "/httptest",ip,http_port,NULL);
			exit(100);
		}
		if (verbosity >= 10)
			printf("%s: spawn child %d to test HTTP on port %s\n",ip,i,http_port);
		children++;
	}
}

void do_socks(const char * const ip) {
	const char *socks_port = NULL;
	int i;

	if (verbosity >= 10)
		printf("%s: begin SOCKS test\n",ip);

	while ((socks_port = firestring_conf_find_next(config,"socks_port",socks_port)) != NULL) {
		block_children();
		i = fork();
		if (i == 0) {
			execl(BINDIR "/socks4test",BINDIR "/socks4test",ip,socks_port,NULL);
			exit(100);
		}
		if (verbosity >= 10)
			printf("%s: spawn child %d to test SOCKS4 on port %s\n",ip,i,socks_port);
		children++;
		block_children();
		i = fork();
		if (i == 0) {
			execl(BINDIR "/socks5test",BINDIR "/socks5test",ip,socks_port,NULL);
			exit(100);
		}
		if (verbosity >= 10)
			printf("%s: spawn child %d to test SOCKS5 on port %s\n",ip,i,socks_port);
		children++;
	}
}

void do_formmail(const char * const ip) {
	char *tempchr;
	int i;

	if (verbosity >= 10)
		printf("%s: begin FormMail test\n",ip);

	tempchr = firestring_concat("http://",ip,"/cgi-bin/formmail.pl",NULL);
	block_children();
	i = fork();
	if (i == 0) {
		execl(BINDIR "/formmailtest",BINDIR "/formmailtest",tempchr,NULL);
		exit(100);
	}
	if (verbosity >= 10)
		printf("%s: spawn child %d to test FormMail with URL %s\n",ip,i,tempchr);
	children++;
	free(tempchr);

	tempchr = firestring_concat("http://",ip,"/cgi-bin/formmail.cgi",NULL);
	block_children();
	i = fork();
	if (i == 0) {
		execl(BINDIR "/formmailtest",BINDIR "/formmailtest",tempchr,NULL);
		exit(100);
	}
	if (verbosity >= 10)
		printf("%s: spawn child %d to test FormMail with URL %s\n",ip,i,tempchr);
	children++;
	free(tempchr);

	tempchr = firestring_concat("http://",ip,"/cgi-bin/FormMail.pl",NULL);
	block_children();
	i = fork();
	if (i == 0) {
		execl(BINDIR "/formmailtest",BINDIR "/formmailtest",tempchr,NULL);
		exit(100);
	}
	if (verbosity >= 10)
		printf("%s: spawn child %d to test FormMail with URL %s\n",ip,i,tempchr);
	children++;
	free(tempchr);

	tempchr = firestring_concat("http://",ip,"/cgi-bin/FormMail.cgi",NULL);
	block_children();
	i = fork();
	if (i == 0) {
		execl(BINDIR "/formmailtest",BINDIR "/formmailtest",tempchr,NULL);
		exit(100);
	}
	if (verbosity >= 10)
		printf("%s: spawn child %d to test FormMail with URL %s\n",ip,i,tempchr);
	children++;
	free(tempchr);
}

void do_smtp(const char * const ip) {
	int i;

	if (verbosity >= 10)
		printf("%s: begin FormMail test\n",ip);

	block_children();
	i = fork();
	if (i == 0) {
		execl(BINDIR "/relaytest",BINDIR "/relaytest",ip,NULL);
		exit(100);
	}
	if (verbosity >= 10)
		printf("%s: spawn child %d to test SMTP\n",ip,i);
	children++;
}

int main() {
	char buffer[4096];
	int i,j;
	pid_t m;
	struct in_addr *in;
	char *ip;
	const char *statedir,
		*checklist,
		*p;
	struct stat s;
	int o1,o2,o3,o4;
	char *tempchr;
	char ipblock[4][16];

	readconf();

	statedir = firestring_conf_find(config,"statedir");
	if (statedir == NULL) {
		fprintf(stderr,"statedir not set in config.\n");
		exit(100);
	}

	p = firestring_conf_find(config,"parallel");
	if (p == NULL) {
		fprintf(stderr,"parallel not set in config.\n");
		exit(100);
	}

	parallel = atoi(p);
	if (parallel < 0) {
		fprintf(stderr,"invalid parallel setting in config.\n");
		exit(100);
	}

	p = firestring_conf_find(config,"verbosity");
	if (p == NULL) {
		fprintf(stderr,"verbosity not set in config.\n");
		exit(100);
	}

	verbosity = atoi(p);

	preload_cookie();

	while (fgets(buffer,4096,stdin) != NULL) {
		i = strlen(buffer) - 6;
		for (j = 0; j < i; j++) {
			in = firedns_aton4(&buffer[j]);
			if (in == NULL)
				continue;
			ip = firedns_ntoa4(in);
			j += strlen(ip);
			/* ok, we've got an ip
			 * now we check if it's excluded */
			if (excluded(ip) == 1)
			 	continue;
			if (sscanf(ip,"%d.%d.%d.%d",&o1,&o2,&o3,&o4) != 4)
				continue;
			firestring_snprintf(ipblock[0],16,"%d",o1);
			firestring_snprintf(ipblock[1],16,"%d",o2);
			firestring_snprintf(ipblock[2],16,"%d",o3);
			firestring_snprintf(ipblock[3],16,"%d",o4);
			tempchr = firestring_concat(statedir,"/",ipblock[0],"/",ipblock[1],"/",ipblock[2],"/",ipblock[3],NULL);
			if (stat(tempchr,&s) == 0) {
				free(tempchr);
				continue;
			}
			m = fork();
			if (m == 0) {
				execlp("mkdir","mkdir","-p",tempchr,NULL);
				perror("execlp(mkdir)");
				exit(0);
			}
			waitpid(m,NULL,0);
			free(tempchr);
			checklist = firestring_conf_find(config,"checklist");
			while (checklist != NULL) {
				tempchr = firestring_concat(ipblock[3],".",ipblock[2],".",ipblock[1],".",ipblock[0],".",checklist,NULL);
				in = firedns_resolveip4(tempchr);
				if (in != NULL) { /* ip already listed */
					free(tempchr);
					tempchr = firestring_concat(statedir,"/",ipblock[0],"/",ipblock[1],"/",ipblock[2],"/",ipblock[3],NULL);
					rmdir(tempchr);
					free(tempchr);
					goto nextip;
				}
				free(tempchr);
				checklist = firestring_conf_find_next(config,"checklist",checklist);
			}
			/* we now know we haven't tested this host recently, do tests */
			if (verbosity >= 1)
				printf("%s: begin test\n",ip);
			do_smtp(ip);
			do_socks(ip);
			do_http(ip);
			do_formmail(ip);
nextip:
			if (verbosity >= 10)
				printf("%s: end test start\n",ip);
		}
	}
	while (wait(NULL) > 0);
	return 0;
}
