/* 
 *
 * $Id: main.c,v 1.65 2005/10/07 02:30:30 psionic Exp $
 */

#include <sys/types.h>
#include <sys/time.h>

#include "../config.h"

#include <stdlib.h>
#include <unistd.h>

#include <errno.h>
#include <string.h>

#ifdef __FreeBSD__
#include <netinet/in_systm.h>
#endif

#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>

#ifdef HAVE_NET_ETHERNET_H
#include <net/ethernet.h>
#define MACTYPE u_char
#define ETHERCONV(ether) (ether)
#endif

#ifdef HAVE_NETINET_IF_ETHER_H
#include <net/if.h>
#include <netinet/if_ether.h>
#ifndef MACTYPE
#define MACTYPE uchar_t
#endif
#ifndef ETHERCONV
#define ETHERCONV(ether) (&((ether).ether_addr_octet))
#endif
#endif

#include <pthread.h>
#include <pcap.h>
#include <libnet.h>

#include "ugly.h"

static int BIGSERVER;

#ifndef ETHER_ADDR_LEN
#define ETHER_ADDR_LEN 6
#endif

#include "log.h"
#include "hash.h"

#define SERVER_PORT 3434
#define HASHSIZE 512

/* xbox broadcasts to FF:FF:FF:FF:FF:FF */
static const char broadcastmac[ETHER_ADDR_LEN] = { 
	0xFF, 0xFF, 0xFF, 
	0xFF, 0xFF, 0xFF
};

/* multicast mac address is 01:00:5E:XX:XX:XX */
static const char multicastmac[ETHER_ADDR_LEN] = {
	0x01, 0x00, 0x5E,
	0x00, 0x00, 0x00, /* Last 3 octets are unknown and don't matter */
};

struct proxy;

typedef struct xbox {
	MACTYPE macaddr[ETHER_ADDR_LEN];
	struct proxy *proxy; /* The IP address of the proxy this xbox is located at */
	time_t lastseen; /* Last time a packet was seen from this xbox */
} xbox_t;

static hash_t *xboxen = NULL;

/* Container for remote proxies */
typedef struct proxy {
	int ip; /* The ip address of this proxy */
	struct in_addr addr;
	int port; /* The port! */
	int fd; /* File descriptor for the connection to this proxy */
	hash_t *xboxen; /* Table containing known xboxes on this proxy */
} proxy_t;

static hash_t *proxies = NULL;
static fd_set proxysocks;

static char *progname;

static char *pcapdev = NULL;
static char *bindhost = NULL;
static struct in_addr bindaddr;
static char *proxyserver = NULL;

/* Some defaults */
static int use_udp = 1;
static int forwardmulticast = 0;
static int forwardbroadcast = 0;
static int forwardxbox = 1;
static int serverport = SERVER_PORT;
static char *user_filter = NULL;

my_libnet_t *libnet;

void addxbox(MACTYPE *macaddr, proxy_t *ppt);
proxy_t *addproxy(struct sockaddr_in *addr, int sock);
void packet_handler(u_char *args, const struct pcap_pkthdr *head,
					  const u_char *packet);
void remove_proxy(proxy_t *ppt);
void connect_to_proxy();
int recv_from_proxy(proxy_t *ppt);
void distribute_packet(proxy_t *ppt, char *packet, int pktlen);

void usage() {
	debuglog(0, "Usage: %s [-bxm] [-u] [-B <bind ip>] [-s <server>] [-i <dev>] [-d <debuglevel>] [-p <port>] [-h]",
				progname);
	debuglog(0, "-x              forward xbox system link packets");
	debuglog(0, "-b              forward broadcast traffic");
	debuglog(0, "-m              forward multicast packets");
	debuglog(0, "-u              use udp encapsulation instead of tcp (default)");
	debuglog(0, "-B <bind_ip>    specify a specific ip to bind the listen server to");
	debuglog(0, "-s <server>     specify another proxy to send packets to");
	debuglog(0, "-i <dev>        ethernet device to sniff packets on");
	debuglog(0, "-d <level>      specify debug level, (0-1000)");
	debuglog(0, "-p <port>       which port to send data on when talkin to other proxies");
	debuglog(0, "-f <bpf filter> an additional bpf filter string you wish to use");
	debuglog(0, "-h              this message!");
}

int comparemac(const void *k1, const void *k2) {
	extern loglevel;
	if (loglevel >= 30) {
		char foo[25];
		strcpy(foo,ether_ntoa((struct ether_addr *)k1));
		debuglog(30, "comparemac %s vs %s", foo, ether_ntoa((struct ether_addr *)k2));
	}

	return memcmp(k1, k2, ETHER_ADDR_LEN);
}

int compareip(const void *k1, const void *k2) {
	int a, b;
	a = *(unsigned int *)k1;
	b = *(unsigned int *)k2;

	debuglog(30, "compareip: %08x vs %08x = %d", a, b, (a < b ? -1 : (a > b ? 1 : 0)) );

	return (a < b ? -1 : (a > b ? 1 : 0));
}

hash_val_t haship(const void *key) {
	static unsigned long randbox[] = {
		0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
		0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
		0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
		0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
	};

	const unsigned char *str = key;
	int c = 0;
	hash_val_t acc = 0;

	while (c < sizeof(int)) {
		acc ^= randbox[(*str + acc) & 0xf];
		acc = (acc << 1) | (acc >> 31);
		acc &= 0xffffffffU;
		acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
		acc = (acc << 2) | (acc >> 30);
		acc &= 0xffffffffU;
		c++;
	}
	return acc;
}

hash_val_t hashmac(const void *key) {
	static unsigned long randbox[] = {
		0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
		0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
		0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
		0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
	};

	const unsigned char *str = key;
	int c = 0;
	hash_val_t acc = 0;

	while (c < ETHER_ADDR_LEN) {
		acc ^= randbox[(*str + acc) & 0xf];
		acc = (acc << 1) | (acc >> 31);
		acc &= 0xffffffffU;
		acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
		acc = (acc << 2) | (acc >> 30);
		acc &= 0xffffffffU;
		c++;
	}
	return acc;
}

/* 
 * Packet handling routine.
 */
void packet_handler(u_char *args, const struct pcap_pkthdr *head,
						  const u_char *packet) {
	struct ether_header *eptr;

	eptr = (struct ether_header *) packet;

	/* Try adding this xbox to the list */
	//debuglog(1, "Adding xbox in packet_handler");
	addxbox((MACTYPE *) ETHERCONV(eptr->ether_shost), 0);

	/* If this packet is sent to our local net, ignore it... */
	if (hash_lookup(xboxen, ETHERCONV(eptr->ether_shost)) != NULL) {
		debuglog(15, "Packet!");
		debuglog(3, "From: %s", ether_ntoa((struct ether_addr *)ETHERCONV(eptr->ether_shost)));
		debuglog(3, "To: %s", ether_ntoa((struct ether_addr *)ETHERCONV(eptr->ether_dhost)));
	}

	/*
	 * If this is broadcast or multicast, send it to all known proxies
	 */
	if ((memcmp(ETHERCONV(eptr->ether_dhost), broadcastmac, ETHER_ADDR_LEN) == 0) ||
		 (forwardmulticast && (memcmp(ETHERCONV(eptr->ether_dhost), multicastmac, 3) == 0))) {
		hscan_t hs;
		hnode_t *node;

		debuglog(11, "BROADCAST");
		hash_scan_begin(&hs, proxies);
		while ((node = hash_scan_next(&hs))) {
			proxy_t *p = (proxy_t *)(node->hash_data);

			/* Make sure the source addr of this packet is not getting
			 * sent this packet. Do not send! */
			if ((hash_lookup(p->xboxen, ETHERCONV(eptr->ether_shost)) != NULL))
				continue;

			debuglog(3, "Sending ethernet packet to %s:%d [Length: %d]", inet_ntoa(p->addr), htons(p->port), head->caplen);

			if (use_udp) {
				struct sockaddr_in to;
				to.sin_addr = p->addr;
				//to.sin_port = htons(serverport);
				to.sin_port = p->port;
				to.sin_family = PF_INET;

				sendto(p->fd, packet, head->caplen, 0, (struct sockaddr *)&to, sizeof(struct sockaddr));
			} else {
				/* TCP */
			}
		}
	} else {
		/* This packet is for a specific mac */
		proxy_t *p;
		hnode_t *box;

		box = hash_lookup(xboxen, ETHERCONV(eptr->ether_dhost));
		if (box == NULL) {
			debuglog(3, "Unknown destination %s, hasn't been seen yet.", 
						ether_ntoa((struct ether_addr *)ETHERCONV(eptr->ether_dhost)));
			return;
		}

		debuglog(3, "Found packet destined for %s! (len: %d)", 
					ether_ntoa((struct ether_addr *)ETHERCONV(eptr->ether_dhost)),
					head->caplen);

		p = ((xbox_t *)(box->hash_data))->proxy;

		if (p == NULL) {
			debuglog(3, "Packet is destined for this net, ignore it");
			return;
		}

		debuglog(3, "Proxy is: %s", (p == NULL ? "Local" : inet_ntoa(p->addr)));

		if (use_udp) {
			struct sockaddr_in to;
			to.sin_addr = p->addr;
			//to.sin_port = htons(serverport);
			to.sin_port = p->port;
			to.sin_family = PF_INET;

			sendto(p->fd, packet, head->caplen, 0, (struct sockaddr *)&to, sizeof(struct sockaddr));
		} else {
			/* TCP */
		}
	}
}

proxy_t *addproxy(struct sockaddr_in *addr, int sock) {
	proxy_t *newproxy = NULL;
	hnode_t *proxy = NULL;

	debuglog(5, "Data from proxy: %s", inet_ntoa(addr->sin_addr));

	proxy = hash_lookup(proxies, &(addr->sin_addr.s_addr));
	if (proxy == NULL) {
		hscan_t hs;
		hnode_t *node;

		newproxy = malloc(sizeof(proxy_t));
		newproxy->ip = (int) addr->sin_addr.s_addr;
		newproxy->addr = addr->sin_addr;
		newproxy->port = addr->sin_port;
		/* XXX: Is this correct?
		 * If we're using UDP, 'sock' is the server udp socket, so this is correct
		 * If we're using TCP, well... tcp doesn't work properly yet.  */
		newproxy->fd = sock;
		newproxy->xboxen = hash_create(HASHSIZE, comparemac, hashmac);

		debuglog(1, "NEW PROXY FOUND: %s", inet_ntoa(addr->sin_addr));
		hash_alloc_insert(proxies, &(newproxy->ip), newproxy);

		/* Check known proxy connections for data */
		hash_scan_begin(&hs, proxies);
		fprintf(stderr, "KNOWN PROXIES:\n");
		while ((node = hash_scan_next(&hs))) {
			proxy_t *p = (proxy_t *)(node->hash_data);
			struct sockaddr_in i;
			i.sin_addr.s_addr = *(int *)(node->hash_key);
			fprintf(stderr, "PROXY: %s\n", inet_ntoa(p->addr));
		}
		fprintf(stderr, "END\n");


		return newproxy;
	} else {
		proxy_t *p = proxy->hash_data;
		debuglog(15, "Packet from known proxy...");
		if (p->port != addr->sin_port) {
			debuglog(0, "Port changed on client %s (%d -> %d)", inet_ntoa(addr->sin_addr), 
						p->port, addr->sin_port);
			p->port = addr->sin_port;
		}
		return (proxy->hash_data);
	}
}


void addxbox(MACTYPE *macaddr, proxy_t *ppt) {
	xbox_t *newbox = NULL;
	hnode_t *box = NULL;

	box = hash_lookup(xboxen, macaddr);
	if (box == NULL) {
		hscan_t hs;
		hnode_t *node;

		//fprintf(stderr, "NEW XBOX FOUND: %s / LOCATION: %s\n", 
		//ether_ntoa((struct ether_addr *)macaddr),
		//(ppt == NULL ? "Local" : inet_ntoa(ppt->addr)));


		debuglog(1, "NEW XBOX FOUND: %s", ether_ntoa((struct ether_addr *)macaddr));
		debuglog(1, "\tLocation: %s", (ppt == NULL ? "Local" : inet_ntoa(ppt->addr)));
		newbox = malloc(sizeof(xbox_t));
		memcpy(newbox->macaddr,macaddr,ETHER_ADDR_LEN);
		//fprintf(stderr, "Stored macaddr: %s\n\n", ether_ntoa((struct ether_addr *)(newbox->macaddr)));
		newbox->lastseen = time(NULL);
		newbox->proxy = ppt;
		//hash_alloc_insert(xboxen, macaddr, newbox);
		hash_alloc_insert(xboxen, newbox->macaddr, newbox);
		if (ppt != NULL) 
			hash_alloc_insert(ppt->xboxen, newbox->macaddr, newbox);

		/* Check known proxy connections for data */
		hash_scan_begin(&hs, xboxen);
		fprintf(stderr, "KNOWN XBOXES:\n");
		while ((node = hash_scan_next(&hs))) {
			xbox_t *x = (xbox_t *)(node->hash_data);
			fprintf(stderr, "XBOX: %s\n", ether_ntoa((struct ether_addr *)x->macaddr));
		}
		fprintf(stderr, "END\n");
	}
}

void pcap(void *args) {
	pcap_t *handle;
	struct bpf_program filter;
	char errbuf[PCAP_ERRBUF_SIZE];
	bpf_u_int32	mask, net;
	char *bpf_filter;

	char netstring[17];
	char maskstring[17];

	pcap_lookupnet(pcapdev, &net, &mask, errbuf);

#define GETBYTE(data,i) (*(((unsigned char *)&data) + i))
	sprintf(netstring, "%u.%u.%u.%u", GETBYTE(net, 0), GETBYTE(net,1),
			  GETBYTE(net,2), GETBYTE(net, 3));
	sprintf(maskstring, "%u.%u.%u.%u", GETBYTE(mask, 0), GETBYTE(mask,1),
			  GETBYTE(mask,2), GETBYTE(mask, 3));

	debuglog(0, "Network: %s / %s", netstring, maskstring);

	/* A generous cap length */
	handle = pcap_open_live(pcapdev, 2048, 1, 10, errbuf);

	if (handle == NULL) {
		debuglog(0, "Error trying to open %s: %s", pcapdev, errbuf);
		//return 1;
		pthread_exit(NULL);
	}

	libnet = my_libnet_init(pcapdev, errbuf);
	if (libnet == NULL) {
		debuglog(0, "libnet_init() failed: %s", errbuf);
		pthread_exit(NULL);
	}

	debuglog(2, "Opened %s, listening for xbox traffic", pcapdev);

	bpf_filter = malloc(1024);
	memset(bpf_filter, 0, 1024);

	if (user_filter != NULL) {
		sprintf(bpf_filter, "(%s)", user_filter);
		if (forwardmulticast || forwardbroadcast || forwardxbox)
			sprintf(bpf_filter + strlen(bpf_filter), " and (");
	}

	/* Build the pcap bpf filter string */
	if (forwardmulticast) {
		sprintf(bpf_filter + strlen(bpf_filter), "(src net %s mask %s and (multicast))", netstring, maskstring);
		if (forwardbroadcast || forwardxbox)
			sprintf(bpf_filter + strlen(bpf_filter), " or ");
	}

	/* XXX: Should this match any broadcast? Ether broadcast? or just ip broadcast? */
	if (forwardbroadcast) {
		sprintf(bpf_filter + strlen(bpf_filter), "(ip broadcast)");
		if (forwardxbox) 
			sprintf(bpf_filter + strlen(bpf_filter), " or ");
	}

	if (forwardxbox)
		sprintf(bpf_filter + strlen(bpf_filter), "(host 0.0.0.1)");

	if (user_filter != NULL)
		sprintf(bpf_filter + strlen(bpf_filter), ")");

	debuglog(0, "pcap filter: %s", bpf_filter);

	if (-1 == pcap_compile(handle, &filter, bpf_filter, 1, net)) {
		debuglog(0, "%s: %s", progname, pcap_geterr(handle));
		if (user_filter != NULL) {
			debuglog(0, "%s: You specified an additional packet filter, it probably has an error", 
						progname);
			debuglog(0, "%s: The filter is '%s'", progname, user_filter);
			debuglog(5, "%s: Generated filter: %s", progname, bpf_filter);
		}
		exit(-1);
	}


	pcap_setfilter(handle, &filter);
	pcap_loop(handle, -1, packet_handler, NULL);

	pcap_close(handle);

	pthread_exit(NULL);
}

void proxy(void *args) {
	int server;
	struct sockaddr_in serveraddr;

	FD_ZERO(&proxysocks);

	if (use_udp) {
		server = socket(PF_INET, SOCK_DGRAM, 0);
	} else {
		server = socket(PF_INET, SOCK_STREAM, 0);
	}
	BIGSERVER = server;
	serveraddr.sin_family = PF_INET;
	if (bindhost != NULL) {
		serveraddr.sin_addr = bindaddr;
	} else {
		serveraddr.sin_addr.s_addr = INADDR_ANY;
	}
	serveraddr.sin_port = htons(serverport);

	debuglog(5, "Binding to any on port %d", serverport);
	if (bind(server, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr)) == -1) {
		debuglog(0, "bind() failed: %s", strerror(errno));
		pthread_exit(NULL);
	}

	if (!use_udp) {
		if (listen(server, SOMAXCONN) < 0) {
			debuglog(0, "listen() failed: %s", strerror(errno));
			debuglog(0, "UDP: %d", use_udp);
			pthread_exit(NULL);
		}
	}

	debuglog(1, "Listening on port %d", serverport);

	FD_SET(server, &proxysocks);

	/* If server is non-null, connect to that server and listen for data on it */
	if (proxyserver != NULL) {
		connect_to_proxy();
	}

	for (;;) {
		int fd;
		struct sockaddr_in srcaddr;
		int size = sizeof(struct sockaddr_in); /* sizeof(srcaddr) */
		int sel;
		struct timeval timeout;
		struct fd_set proxycopy;
		timeout.tv_sec = 10;
		timeout.tv_usec = 0;

		/* Some sort of select() thing here */

		proxycopy = proxysocks;

		FD_SET(server, &proxysocks);
		debuglog(150, "select: %08x", proxysocks);
		sel = select(FD_SETSIZE, &proxysocks, NULL, NULL, &timeout);
		if (sel < 0) {
			debuglog(0, "select() failed: %s", strerror(errno));
			pthread_exit(NULL);
		} else if (sel == 0) {
			debuglog(150, "select() timed out waiting for data... trying again");
			proxysocks = proxycopy;
		} else {
			hscan_t hs;
			hnode_t *node;

			debuglog(15, "Data ready from %d descriptors", sel);

			if (use_udp) {
				if (FD_ISSET(server, &proxysocks)) {
					proxy_t p;
					p.fd = server;
					debuglog(13, "Data received on udp");
					if (recv_from_proxy(&p) > 0 )
						FD_SET(server, &proxysocks);
					else
						FD_CLR(server, &proxysocks);
				} else {
					FD_SET(server, &proxysocks);
				}

			} else { /* if using TCP... */
			} 
		} /* else, selected data is available */
	} /* infinite loop */
}

void connect_to_proxy() {
	int sock;
	struct sockaddr_in destaddr;
	struct hostent *hostdata;
	struct in_addr in;

	//proxy_t *newproxy;

	if (use_udp) {
		if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
			debuglog(0, "socket() failed: %s", strerror(errno));
			pthread_exit(NULL);
		}
	} else {
		if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
			debuglog(0, "socket() failed: %s", strerror(errno));
			pthread_exit(NULL);
		}
	}

	/* XXX: Should we bind the socket here to port 3434? */

	if ((hostdata = gethostbyname(proxyserver))== NULL) {
		debuglog(0, "gethostbyname() failed: %s", strerror(errno));
		pthread_exit(NULL);
	}

	/* Grab the IP this hostname is, this is an ugly oneliner */
	memcpy(&(in.s_addr),hostdata->h_addr_list[0],hostdata->h_length);

	debuglog(1, "%s is %s (%s)", proxyserver, hostdata->h_name, inet_ntoa(in));
	//inet_ntoa((struct in_addr *)(hostdata->h_addr_list[0])));

	destaddr.sin_family = PF_INET;
	destaddr.sin_addr = in;
	destaddr.sin_port = htons(serverport);

	if (use_udp) {
		debuglog(30, "Sending UDP hello packet to %s", proxyserver);
		if (sendto(BIGSERVER, NULL, 0, 0, (struct sockaddr *)&destaddr, sizeof(struct sockaddr)) < 0) {
			debuglog(0, "sendto() failed [while saying hi]: %s", strerror(errno));
			pthread_exit(NULL);
		}
	} else { /* TCP */
	} 


	/* After we establish a connection with the proxy server host,
	 * we should completely forget about this relationship 
	 * (us being the client). Now, add it to the list of
	 * known proxies and move along.
	 */
	addproxy(&destaddr, BIGSERVER);
	FD_SET(sock, &proxysocks);
}

int recv_from_proxy(proxy_t *ppt) {
	int bytes = 0;
	int pktlen = 0;
	int total;

	char *packet;

	if (use_udp) {
		bytes = 4;
		pktlen = 8192;
	} else {
		bytes = recv(ppt->fd, &pktlen, 4, 0);
		debuglog(100, "Bytes read: [%d] %d", bytes, pktlen);

		/* if bytes read is 0, then the connection was closed. */
		if (bytes < 0) {
			debuglog(0, "recv_from_proxy - recv() (1) failed: %s", strerror(errno));
			return bytes;
		} else if (bytes == 0) {
			remove_proxy(ppt);
			return 0;
		}
	}

	packet = malloc(pktlen);

	if (use_udp) {
		struct sockaddr_in from;
		int len = sizeof(struct sockaddr);
		bytes = recvfrom(ppt->fd, packet, pktlen, 0, (struct sockaddr *)&from, &len);
		pktlen = bytes; /* XXX: Is this a good idea? */

		ppt = addproxy(&from, ppt->fd);
	} else {
		bytes = recv(ppt->fd, packet, pktlen, 0);
	}

	if (bytes < 0) {
		if (use_udp)
			debuglog(0, "recv_from_proxy - recvfrom() (2) failed: %s", strerror(errno));
		else
			debuglog(0, "recv_from_proxy - recv() (2) failed: %s", strerror(errno));
		return bytes;
	} if (bytes == 0) {
		if (!use_udp) 
			remove_proxy(ppt);
		return 0;
	}

	//debuglog(1, "Adding xbox in  recv_from_proxy");
	addxbox(ETHERCONV(((struct ether_header *)packet)->ether_shost), ppt);

	debuglog(1, "Packet received from %s. Length: %d vs %d (%s)", 
				inet_ntoa(ppt->addr), bytes, pktlen,
				(bytes == pktlen ? "OK" : "FRAGMENTED"));
	if (bytes < pktlen) {
		total = bytes;
		while (total < pktlen) {
			bytes = recv(ppt->fd, packet + total, pktlen - total, 0);
			debuglog(1, "REASSEMBLY PROGRESS - %s. Length: %d vs %d", inet_ntoa(ppt->addr), total, pktlen);
			total += bytes;
		}
	}

	distribute_packet(ppt, packet, bytes);

	free(packet);

	return bytes;
}

void distribute_packet(proxy_t *ppt, char *packet, int pktlen) {
	struct ether_header *eptr;
	u_short ether_type;
	int bytes = 0;

	eptr = (struct ether_header *)packet;
	ether_type = ntohs(eptr->ether_type);

	debuglog(30, "----- REMOTE PACKET From: %s", 
				ether_ntoa((struct ether_addr *)ETHERCONV(eptr->ether_shost)));
	debuglog(30, "----- REMOTE PACKET To: %s",
				ether_ntoa((struct ether_addr *)ETHERCONV(eptr->ether_dhost)));

	bytes = my_libnet_write_link_layer(libnet,pcapdev,packet,pktlen);

	if (bytes < 0) {
		debuglog(0, "FATAL ERROR WRITING RAW PACKET TO DEVICE");
	} else {
		debuglog(4, "Packet dumped on local net (%s), bytes vs length = %d vs %d", 
					pcapdev, bytes, pktlen);
	}

	if (get_log_level() > 10)
		hexdump(packet, pktlen);
}

void remove_proxy(proxy_t *ppt) {
	hscan_t hs;
	hnode_t *node;

	debuglog(1, "Removing proxy %s", inet_ntoa(ppt->addr));

	hash_scan_begin(&hs, proxies);
	while ((node = hash_scan_next(&hs))) {
		proxy_t *p = (proxy_t *)(node->hash_data);
		if (ppt->ip == p->ip) {
			hash_delete(proxies, node);
			break;
		}
	}

}

int main(int argc, char **argv) {
	char errbuf[PCAP_ERRBUF_SIZE];
	//int *pthread_return;
	char ch;
	pthread_t pcapthread, proxythread;

	progname = argv[0];

	/* Initialization and Defaults */
	xboxen = hash_create(HASHSIZE, comparemac, hashmac);
	proxies = hash_create(HASHSIZE, compareip, haship);
	set_log_level(0);


	/* Argument Processing */
	while ((ch = getopt(argc, argv, "B:bxmus:i:d:h?p:f:")) != -1) {
		switch (ch) {
			case 'B':
				bindhost = malloc(strlen(optarg) + 1);
				//strlcpy(bindhost,optarg,strlen(optarg) + 1);
				strcpy(bindhost,optarg);

				if (inet_aton(bindhost, &bindaddr) == 0) {
					bindhost = NULL;
					debuglog(0, "-B flag: Invalid IP address '%s'", bindhost);
				}
			case 'b':
				debuglog(10, "-b flag, enabling broadcast forwarding");
				forwardbroadcast = 1;
				break;
			case 'd':
				set_log_level(atoi(optarg));
				break;
			case 'f':
				user_filter = malloc(strlen(optarg));
				strcpy(user_filter, optarg);
				break;
			case 'i':
				pcapdev = malloc(strlen(optarg) + 1);
				memset(pcapdev,0,strlen(optarg) + 1);
				//strlcpy(pcapdev,optarg,strlen(optarg) + 1);
				strncpy(pcapdev,optarg,strlen(optarg));
				debuglog(10, "-i flag, setting device to %s", pcapdev);
				break;
			case 'm':
				debuglog(10, "-m flag, enabling multicast forwarding");
				forwardmulticast = 1;
				break;
			case 'p':
				serverport = atoi(optarg);
				debuglog(10, "-p flag, setting proxy port to %d", serverport);
				break;
			case 's':
				proxyserver = malloc(strlen(optarg) + 1);
				memset(proxyserver,0,strlen(optarg) + 1);
				//strlcpy(proxyserver,optarg,strlen(optarg) + 1);
				strncpy(proxyserver,optarg,strlen(optarg));
				debuglog(10, "-s flag, setting proxy server to %s", proxyserver);
				break;
			case 'u':
				debuglog(10, "-u flag, enabling udp");
				use_udp = 1;
				break;
			case 'x':
				debuglog(10, "-x flag, enabling xbox system link forwarding");
				forwardxbox = 1;
				break;
			case 'h':
			case '?':
			default:
				usage();
				exit(1);
				break;
		}
	}

	argc -= optind;
	argv += optind;

	if (pcapdev == NULL) 
		pcapdev = pcap_lookupdev(errbuf);

	if (pcapdev == NULL) {
		debuglog(0, "No device specified or unable to find a device to open.");
		debuglog(0, "Error message: %s", errbuf);
		return 1;
	}

	pthread_create(&pcapthread, NULL, (void*)pcap, NULL);
	pthread_create(&proxythread, NULL, (void*)proxy, NULL);

	pthread_join(pcapthread, NULL);
	debuglog(0, "pcap thread exited unexpectedly");
	exit(1);
	pthread_join(proxythread, NULL);

	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1