/*
httptest.c - DSBL open HTTP proxy 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 <time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <firedns.h>
#include <firestring.h>
#include "testlib.h"

char headers[4096];

const char *days[] = {
	        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};

const char *months[] = {
	        "Jan", "Feb", "Mar", "Apr", "May",  "Jun", "Jul",  "Aug",  "Sep", "Oct", "Nov", "Dec"
};

char *getline(int fd) {
	static char buffer[4096];
	static char line[4096];
        static int bufpos = 0;
        int i;
        char *tempchr;

        tempchr = memchr(buffer,'\n',bufpos);
        while (tempchr == NULL) {
		i = recv(fd,&buffer[bufpos],4095 - bufpos,0);
		if (i <= 0) /* eof or error, let the parent figure out what to do */
			return NULL;
		bufpos += i;
		tempchr = memchr(buffer,'\n',bufpos);
		if (tempchr == NULL && bufpos == 4095) /* line too long (hostile act) */
			exit(2);
	}
	if (tempchr != NULL) {
		memcpy(line,buffer,tempchr - buffer);
		line[tempchr - buffer] = '\0';
		bufpos -= (tempchr - buffer) + 1;
		memcpy(buffer,&tempchr[1],bufpos);
		return line;
	}
	return NULL;
}

void buildheaders(char *cookie, const char *source, const char *target_user, const char *target_domain) {
	struct tm *timestruct;
	time_t temptime;

	time(&temptime);
	timestruct = gmtime(&temptime);
	firestring_snprintf(headers,4096,"Message-ID: <%s@%s>\r\n"
			"Date: %s, %d %s %d %02d:%02d:%02d +0000\r\n"
			"To: <%s@%s>\r\n"
			"Subject: Open HTTP Proxy Test Message\r\n",cookie,source,days[timestruct->tm_wday],timestruct->tm_mday,months[timestruct->tm_mon],1900 + timestruct->tm_year,timestruct->tm_hour,timestruct->tm_min,timestruct->tm_sec,target_user,target_domain);
}

int sendget(int fd, char *intip, int port, struct in_addr *remoteip, char *cookie, const char *sender, const char *source, const char *target_user, const char *target_domain) {
	char buffer[8192];
	char *tempchr = firedns_ntoa4(remoteip);
	int i;

	i = firestring_snprintf(buffer,8192,"GET http://%s:25/ HTTP/1.1\r\n"
                           "Host: http://%s:25/\r\n"
                           "Connection: close\r\n"
                           "HELO [%s]\r\n"
	                   "MAIL FROM:<%s@%s>\r\n"
                           "RCPT TO:<%s@%s>\r\n"
                           "DATA\r\n"
			   "%s\r\n"
                           "DSBL LISTME: http %s\r\n"
                           "%s\r\n"
                           "Port %d\r\n"
			   "Connect to %s\r\n"
			   "Method GET\r\n"
                           "DSBL END\r\n"
		           ".\r\n"
			   "QUIT\r\n"
			   "\r\n",tempchr,tempchr,intip,sender,source,target_user,target_domain,headers,intip,cookie,port,tempchr);
	buffer[i] = '\0';
	if (send(fd,buffer,i,0) != i)
		return 100;
	
	i = 0;
	while (recv(fd,buffer,8192,0) > 0 && i < 20)
		i++;

	return 0;
}

int sendconnect(int fd, char *intip, int port, struct in_addr *remoteip, char *cookie, const char *sender, const char *source, const char *target_user, const char *target_domain) {
	char buffer[8192];
	char *tempchr = firedns_ntoa4(remoteip);
	char *line;
	int i;

	i = firestring_snprintf(buffer, 8192, "CONNECT %s:25 HTTP/1.0\r\n\r\n", tempchr);
	buffer[i] = '\0';
	if (send(fd, buffer, i, 0) != i)
		return 100;

	/* eat lines until we get the SMTP banner from the target host, unless we reach 20 lines in which case don't bother */
	i = 0;
	while ((line = getline(fd)) != NULL && line[0] != '2' && i < 20) {
		i++;
	}
	if (line == NULL || i == 20)
		return 100;

	i = firestring_snprintf(buffer, 8192, "HELO [%s]\r\n"
	                   "MAIL FROM:<%s@%s>\r\n"
                           "RCPT TO:<%s@%s>\r\n"
                           "DATA\r\n"
			   "%s\r\n"
                           "DSBL LISTME: http %s\r\n"
                           "%s\r\n"
                           "Port %d\r\n"
			   "Connect to %s\r\n"
			   "Method CONNECT\r\n"
                           "DSBL END\r\n"
		           ".\r\n"
			   "QUIT\r\n"
			   "\r\n", intip, sender, source, target_user, target_domain, headers, intip, cookie, port, tempchr);
	buffer[i] = '\0';
	if (send(fd, buffer, i, 0) != i)
		return 100;
	
	i = 0;
	while (recv(fd, buffer, 8192, 0) > 0 && i < 20)
		i++;

	return 0;
}

int sendpost(int fd, char *intip, int port, char *method, struct in_addr *remoteip, char *cookie, const char *sender, const char *source, const char *target_user, const char *target_domain) {
	char http[8192];
	char buffer[8192];
	char *tempchr = firedns_ntoa4(remoteip);
	int i;

	i = firestring_snprintf(buffer,8192,
			   "HELO [%s]\r\n"
	                   "MAIL FROM:<%s@%s>\r\n"
                           "RCPT TO:<%s@%s>\r\n"
                           "DATA\r\n"
			   "%s\r\n"
                           "DSBL LISTME: http %s\r\n"
                           "%s\r\n"
                           "Port %d\r\n"
			   "Connect to %s\r\n"
			   "Method %s\r\n"
                           "DSBL END\r\n"
		           ".\r\n"
			   "QUIT\r\n",intip,sender,source,target_user,target_domain,headers,intip,cookie,port,tempchr,method);

	i = firestring_snprintf(http,8192,"%s http://%s:25/ HTTP/1.1\r\n"
                           "Host: http://%s:25/\r\n"
                           "Connection: close\r\n"
			   "Content-length: %d\r\n"
			   "\r\n"
			   "%s",method,tempchr,tempchr,i,buffer);
	if (send(fd,http,i,0) != i)
		return 100;
	
	i = 0;
	while (recv(fd,buffer,8192,0) > 0 && i < 20)
		i++;

	return 0;
}

int testhttp(char *cookie, char *intip, char *method, struct sockaddr_in *httpaddr, const char *sourceuser, const char *sourcedomain, struct in_addr *remoteip, const char *target_user, const char *target_domain) {
	int i;
	int f;

	f = socket(PF_INET, SOCK_STREAM, 0);
	if (f == -1) {
		perror("socket()");
		exit(100);
	}

	if (connect(f,(struct sockaddr *)httpaddr,sizeof(struct sockaddr_in)) != 0)
		return 2;

	if (strcmp(method,"POST") == 0 || strcmp(method,"PUT") == 0)
		i = sendpost(f, intip, ntohs(httpaddr->sin_port), method, remoteip, cookie, sourceuser, sourcedomain, target_user, target_domain);
	else if (strcmp(method,"CONNECT") == 0)
		i = sendconnect(f, intip, ntohs(httpaddr->sin_port), remoteip, cookie, sourceuser, sourcedomain, target_user, target_domain);
	else
		i = sendget(f, intip, ntohs(httpaddr->sin_port), remoteip, cookie, sourceuser, sourcedomain, target_user, target_domain);

	close(f);
	return i;
}

/* return values:
 *   0 - host accepted some tests, may relay
 *   1 - host accepted no tests, won't relay
 *   2 - host appears to be blocking tester (or may just be seriously broken)
 * 100 - format or internal error
 */
int main(int argc, char **argv) {
	struct in_addr *in;
	struct sockaddr_in http_addr;
	char *cookie;
	int ret = 1;
	char *tempchr;
	char intip[16];
	const char *sender_user,
		*sender_domain,
		*target_user,
		*target_domain;

	setalarm();

	if (argc < 3) {
		fprintf(stderr,"Usage: %s <http ip> <http port>\n",argv[0]);
		exit(100);
	}

	in = firedns_aton4(argv[1]);
	if (in == NULL) {
		fprintf(stderr,"Invalid IP\n");
		exit(100);
	}
	memcpy(&http_addr.sin_addr,in,sizeof(struct in_addr));
	http_addr.sin_family = AF_INET;
	http_addr.sin_port = htons(atoi(argv[2]));
	firestring_strncpy(intip,firedns_ntoa4(in),16);

	readconf();

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

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

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

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

	cookie = getcookie();

	buildheaders(cookie,sender_domain,target_user,target_domain);

	tempchr = firedns_resolvemx(target_domain);
	if (tempchr == NULL) {
		fprintf(stderr,"Target domain has no mail exchanger\n");
		exit(100);
	}
	in = firedns_resolveip4(tempchr);
	if (in == NULL) {
		fprintf(stderr,"Target domain MX has no corresponding A record\n");
		exit(100);
	}

	if (testhttp(cookie,intip,"CONNECT",&http_addr,sender_user,sender_domain,in,target_user,target_domain) == 0)
		ret = 0;

	if (testhttp(cookie,intip,"GET",&http_addr,sender_user,sender_domain,in,target_user,target_domain) == 0)
		ret = 0;

	if (testhttp(cookie,intip,"POST",&http_addr,sender_user,sender_domain,in,target_user,target_domain) == 0)
		ret = 0;

	if (testhttp(cookie,intip,"PUT",&http_addr,sender_user,sender_domain,in,target_user,target_domain) == 0)
		ret = 0;

	in = firedns_aton4("127.0.0.1");

	if (testhttp(cookie,intip,"CONNECT",&http_addr,sender_user,sender_domain,in,target_user,target_domain) == 0)
		ret = 0;

	if (testhttp(cookie,intip,"GET",&http_addr,sender_user,sender_domain,in,target_user,target_domain) == 0)
		ret = 0;

	if (testhttp(cookie,intip,"POST",&http_addr,sender_user,sender_domain,in,target_user,target_domain) == 0)
		ret = 0;

	if (testhttp(cookie,intip,"PUT",&http_addr,sender_user,sender_domain,in,target_user,target_domain) == 0)
		ret = 0;

	exit(ret);
}
