/* pkgrewr - program to modify network package payload
 *
 * don't forget to set appropriate iptables rules, e.g.:
 * iptables -A INPUT -p tcp --dport 1337 -j QUEUE
 * ---
 * Author: Philipp Winter
 * Contact: pwr@7C0.org
 *
 * $Id: pkgrewr.c 3 2009-01-01 16:45:57Z pwr $
 */

#include <libnet.h>
#include <libipq.h>
#include <linux/netfilter.h>

#define PKG_BUF_SIZE	0xffff
#define TIME_DIGITS	7

struct ipq_handle *handle;
static int reinject = 0;

const char *before = NULL;
const char *after = NULL;

u_int8_t *find_substr( const char *haystack, const char *needle, unsigned int len ) {

	int i = 0;
	int j = 0;
	int needle_len = strlen(needle);

	for (i = 0; i < len; i++) {
		if (haystack[i] == needle[0]) {
			for (j = 1; needle[j]; j++) {
				if (haystack[i+j] != needle[j]) {
					break;
				}
			}
			if (j == needle_len) {
				return ((u_int8_t *) haystack+i);
			}
		}
	}
	return NULL;
}

void print_payload( u_int8_t *payload, int len ) {

	int i = 0;

	printf("<payload>\n");
	for (i = 0; i < len; i++) {
		printf("%X ", *(payload+i));
	}
	printf("\n</payload>\n");
}

void handle_pkg( ipq_packet_msg_t *pkg ) {

	u_int8_t *ptr = NULL;
	int len = 0;
	int i = 0;

	reinject = 0;

	/* check if string exists in payload */
	ptr = find_substr((const char *) pkg->payload, before, pkg->data_len);
	if (ptr == NULL) {
		return;
	}

	len = strlen(before);
	/* set pointer to beginning of time */
	for (i = 0; i < len; i++) {
		*(ptr+i) = *(after+i);
	}
	printf("[+] changed \"%s\" to \"%s\"\n", before, after);
	reinject = 1;

#ifdef DEBUG
	print_payload(pkg->payload, pkg->data_len);
#endif
}

void fetch_packets( struct ipq_handle *handle ) {

	u_int8_t pkg_buf[PKG_BUF_SIZE];
	ipq_packet_msg_t *pkg = NULL;

	printf("[+] starting to fetch packets...\n");
	while (1) {
		memset(pkg_buf, 0, PKG_BUF_SIZE);
		if (ipq_read(handle, pkg_buf, PKG_BUF_SIZE, 0) == -1) {
			fprintf(stderr, "ipq_read() failed\n");
		}

		/* determine whether the received message is a packet or error */
		switch (ipq_message_type(pkg_buf)) {
			case IPQM_PACKET:
				pkg = ipq_get_packet(pkg_buf);
				handle_pkg(pkg);
				break;
			case NLMSG_ERROR :
				/* TODO - use ipq_get_msgerr */
				break;
		}

		/* if the package payload has been modified, it must be reinjected */
		if (reinject) {
			libnet_do_checksum(NULL, pkg->payload, IPPROTO_TCP, pkg->data_len-LIBNET_IPV4_H);
			ipq_set_verdict(handle, pkg->packet_id, NF_ACCEPT, pkg->data_len, pkg->payload);
		} else {
			ipq_set_verdict(handle, pkg->packet_id, NF_ACCEPT, 0, NULL);
		}
	}
}

void catch_sigint( int signum ) {

	printf("\n[+] caught SIGINT - exiting now\n");
	ipq_destroy_handle(handle);
	exit(0);
}

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

	if (argc != 3) {
		fprintf(stderr, "usage: %s <string_before> <string_after>\n", argv[0]);
		return 1;
	} else {
		before = argv[1];
		after = argv[2];
	}

	if (strlen(argv[1]) != strlen(argv[2])) {
		fprintf(stderr, "the two given strings should have the same length\n");
		return 2;
	}

	/* install signal handler for SIGINT */
	signal(SIGINT, catch_sigint);

	/* disable buffering for stdout */
	setvbuf(stdout, NULL, _IONBF, 0);

	printf("[+] checking for root privileges...   ");
	if (geteuid() != 0) {
		fprintf(stderr, "failed (UID = %d)!\n", geteuid());
		return 3;
	}
	printf("ok\n");

	printf("[+] creating ipqueue handle...   ");
	if ((handle = ipq_create_handle(0, PF_INET)) == NULL) {
		fprintf(stderr, "ipq_create_handle() failed!\n");
		return 4;
	}
	printf("ok\n");

	printf("[+] setting ipqueue mode...   ");
	if ((ipq_set_mode(handle, IPQ_COPY_PACKET, PKG_BUF_SIZE)) == -1) {
		ipq_destroy_handle(handle);
		fprintf(stderr, "ipq_set_mode() failed!\n");
		return 5;
	}
	printf("ok\n");

	fetch_packets(handle);

	return 0;
}

