/*-
 * Copyright (c) 2006, Andrea Bittau <a.bittau@cs.ucl.ac.uk>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include <stdio.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/endian.h>
#include <sys/uio.h>
#include <unistd.h>
#include <net/if.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/bpf.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211.h>
#include <openssl/rc4.h>
#include <zlib.h>
#include "w00t.h"

int str2mac(char *mac, char *str)
{
	unsigned int macf[6];
	int i;

	if (sscanf(str, "%x:%x:%x:%x:%x:%x",
		   &macf[0], &macf[1], &macf[2],
		   &macf[3], &macf[4], &macf[5]) != 6)
		return -1;

	for (i = 0; i < 6; i++)
		*mac++ = (char) macf[i];
	
	return 0;
}

void mac2str(char *str, char* m)
{
	unsigned char *mac = m;
	sprintf(str, "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X",
		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

short seqfn(unsigned short seq, unsigned short fn)
{
	unsigned short r = 0;

	assert(fn < 16);

	r = fn;
	r |=  ( (seq % 4096) << IEEE80211_SEQ_SEQ_SHIFT);
	return r;
}

unsigned short seqno(struct ieee80211_frame *wh)
{
	unsigned short *s = (unsigned short*) wh->i_seq;

	return (*s & IEEE80211_SEQ_SEQ_MASK) >> IEEE80211_SEQ_SEQ_SHIFT;
}

int open_bpf(char *dev, int dlt)
{
	int i;
	char buf[64];
	int fd = -1;
	struct ifreq ifr;

	for(i = 0;i < 16; i++) {
		sprintf(buf, "/dev/bpf%d", i);

		fd = open(buf, O_RDWR);
		if(fd == -1) {
			if(errno != EBUSY)
				return -1;
			continue;
		}
		else
			break;
	}

	if(fd == -1)
		return -1;

	strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)-1);
	ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0;

	if(ioctl(fd, BIOCSETIF, &ifr) < 0)
		return -1;

	if (ioctl(fd, BIOCSDLT, &dlt) < 0)
		return -1;

	i = 1;
	if (ioctl(fd, BIOCIMMEDIATE, &i) < 0)
		return -1;

	return fd;
}

int open_tx(char *iface)
{
	return open_bpf(iface, DLT_IEEE802_11_RADIO);
}

int open_rx(char *iface)
{
	return open_bpf(iface, DLT_IEEE802_11_RADIO);
}

int open_rxtx(char *iface, int *rx, int *tx)
{
	*rx = open_bpf(iface, DLT_IEEE802_11_RADIO);
	*tx = *rx;

	return *rx;
}

int inject(int fd, void *buf, int len)
{
	return inject_params(fd, buf, len, NULL);
}

int inject_params(int fd, void *buf, int len,
		  struct ieee80211_bpf_params *params)
{
	static struct ieee80211_bpf_params defaults = {
		.ibp_vers = IEEE80211_BPF_VERSION,
		/* NB: no need to pass series 2-4 rate+try */
		.ibp_len = sizeof(struct ieee80211_bpf_params) - 6,
		.ibp_rate0 = 2,		/* 1 MB/s XXX */
		.ibp_try0 = 1,		/* no retransmits */
		.ibp_flags = IEEE80211_BPF_NOACK,
		.ibp_power = 100,	/* nominal max */
		.ibp_pri = WME_AC_VO,	/* high priority */
	};
	struct iovec iov[2];
	int rc;

	if (params == NULL)
		params = &defaults;
	iov[0].iov_base = params;
	iov[0].iov_len = params->ibp_len;
	iov[1].iov_base = buf;
	iov[1].iov_len = len;

	rc = writev(fd, iov, 2);
	if (rc == -1)
		return rc;

	rc -= iov[0].iov_len; /* XXX could be negative */
	return rc;
}

int sniff(int fd, void *buf, int len)
{
	return read(fd, buf, len);
}

void *get_wifi(void *buf, int *len)
{
#define	BIT(n)	(1<<(n))
	struct bpf_hdr* bpfh = (struct bpf_hdr*) buf;
	struct ieee80211_radiotap_header* rth;
	uint32_t present;
	uint8_t rflags;
	void *ptr;

	/* bpf */
	*len -= bpfh->bh_hdrlen;

	if (bpfh->bh_caplen != *len) {
		assert(bpfh->bh_caplen < *len);
		*len = bpfh->bh_caplen;
	}
	assert(bpfh->bh_caplen == *len);

	/* radiotap */
	rth = (struct ieee80211_radiotap_header*)
	      ((char*)bpfh + bpfh->bh_hdrlen);
	/* XXX cache; drivers won't change this per-packet */
	/* check if FCS/CRC is included in packet */
	present = le32toh(rth->it_present);
	if (present & BIT(IEEE80211_RADIOTAP_FLAGS)) {
		if (present & BIT(IEEE80211_RADIOTAP_TSFT))
			rflags = ((const uint8_t *)rth)[8];
		else
			rflags = ((const uint8_t *)rth)[0];
	} else
		rflags = 0;
	*len -= rth->it_len;

	/* 802.11 CRC */
	if (rflags & IEEE80211_RADIOTAP_F_FCS)
		*len -= IEEE80211_CRC_LEN;

	ptr = (char*)rth + rth->it_len;
	return ptr;
#undef BIT
}

int send_ack(int fd, char *mac)
{
	static char buf[2+2+6];
	static char *p = 0;
	int rc;

	if (!p) {
		memset(buf, 0, sizeof(buf));
		buf[0] |= IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_ACK;
		p = &buf[4];
	}

	memcpy(p, mac, 6);

	rc = inject(fd, buf, sizeof(buf));
	return rc;
}

int open_tap(char *iface)
{
	char buf[64];

	snprintf(buf, sizeof(buf), "/dev/%s", iface);
	return open(buf, O_RDWR);
}

int set_iface_mac(char *iface, char *mac)
{
	int s, rc;
	struct ifreq ifr;

	s = socket(PF_INET, SOCK_DGRAM, 0);
	if (s == -1)
		return -1;
	
	memset(&ifr, 0, sizeof(ifr));
	strcpy(ifr.ifr_name, iface);

	ifr.ifr_addr.sa_family = AF_LINK;
	ifr.ifr_addr.sa_len = 6;
	memcpy(ifr.ifr_addr.sa_data, mac, 6);

	rc = ioctl(s, SIOCSIFLLADDR, &ifr);

	close(s);

	return rc;
}

int str2wep(char *wep, int *len, char *str)
{
	int klen;

	klen = strlen(str);
	if (klen % 2)
		return -1;
	klen /= 2;

	if (klen != 5 && klen != 13)
		return -1;

	*len = klen;

	while (klen--) {
		unsigned int x;

		if (sscanf(str, "%2x", &x) != 1)
			return -1;
		
		*wep = (unsigned char) x;
		wep++;
		str += 2;
	}

	return 0;
}

int wep_decrypt(struct ieee80211_frame *wh, int len, char *key, int klen)
{
	RC4_KEY k;
	char seed[64];
	char *p = (char*) (wh+1);
	uLong crc = crc32(0L, Z_NULL, 0);
	uLong *pcrc;

	assert(sizeof(seed) >= klen + 3);
	memcpy(seed, p, 3);
	memcpy(&seed[3], key, klen);

	RC4_set_key(&k, klen+3, seed);
	
	len -= sizeof(*wh);
	len -= 4;
	p += 4;
	RC4(&k, len, p, p);

	crc = crc32(crc, p, len - 4);
	pcrc = (uLong*) (p+len-4);

	if (*pcrc == crc)
		return 0;

	return -1;
}

void wep_encrypt(struct ieee80211_frame *wh, int len, char *key, int klen)
{
	RC4_KEY k;
	char seed[64];
	char *p = (char*) (wh+1);
	uLong crc = crc32(0L, Z_NULL, 0);
	uLong *pcrc;

	assert(sizeof(seed) >= klen + 3);
	memcpy(seed, p, 3);
	memcpy(&seed[3], key, klen);

	RC4_set_key(&k, klen+3, seed);
	
	len -= sizeof(*wh);
	p += 4;
	crc = crc32(crc, p, len - 4);
	pcrc = (uLong*) (p+len-4);
	*pcrc = crc;

	RC4(&k, len, p, p);
}

int frame_type(struct ieee80211_frame *wh, int type, int stype)
{       
        if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != type)
                return 0;

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

        return 1;
}

void hexdump(void *b, int len)
{
	unsigned char *p = (unsigned char*) b;

	while (len--)
		printf("%.2X ", *p++);
	printf("\n");	
}

int elapsed(struct timeval *past, struct timeval *now)
{       
        int el;
        
        el = now->tv_sec - past->tv_sec;
        assert(el >= 0);
        if (el == 0) {
                el = now->tv_usec - past->tv_usec;
        } else {
                el = (el - 1)*1000*1000;
                el += 1000*1000-past->tv_usec;
                el += now->tv_usec;
        }
        
        return el;
}

static int is_arp(struct ieee80211_frame *wh, int len)
{       
        /* XXX */
        if (len > (sizeof(*wh) + 4 + 4 + 39))
                return 0;

        return 1;
}

char *known_pt(struct ieee80211_frame *wh, int *len)
{
	static char *known_pt_arp = "\xAA\xAA\x03\x00\x00\x00\x08\x06";
	static char *known_pt_ip = "\xAA\xAA\x03\x00\x00\x00\x08\x00";
	int arp;

	arp = is_arp(wh, *len);
	*len = 8;
	if (arp)
		return known_pt_arp;
	else
		return known_pt_ip;
}
