#!/usr/bin/python

from optparse import OptionParser
from scapy.all import *
import logging
import random
import time

#################
### Constants ###
#################

SYN_FLAG = 2
ACK_FLAG = 16
EVIL_FLAG = 4
IPV6_EVIL_LBL = 0xbad1e
MAXINT = (2 ** 32) - 1
DNS_RESP_ADDR = '26.245.56.32'
LOG_DIR = '/var/log/spoofer/curr_evil.log'
PCAP_DIR = '/var/log/spoofer/curr_evil.cap'
INT_IF = 'enp0s8'
EVIL_ADDR = '192.168.1.1'
LOG_SIZE = 50 * 1024

#########################
### Boolean Functions ###
#########################

def is_ipv4(data):
    """Checks if the frame contains an IP packet returning a boolean"""
    return data.haslayer(IP) == 1

def is_ipv6(data):
    """Checks if the frame contains an IPv6 packet returning a boolean"""
    return data.haslayer(IPv6) == 1
	
def is_evil_v4(ipv4_pkt):
    """Check if the packet has the evil bit set returning a boolean"""
    return (ipv4_pkt.flags >> 2) == 1

def is_evil_v6(ipv6_pkt):
    """Check if the packet has flow label equal to the IPv6 evil label"""
    return ipv6_pkt.fl == IPV6_EVIL_LBL

def is_evil_ipv4(data):
    """Checks if the frame contains an IP packet witht the evil bit set returning a boolean"""
    return is_ipv4(data) and is_evil_v4(data.payload)

def is_evil_ipv6(data):
    """Checks if the frame contains an IPv6 packet with the flow label set to the IPv6 evil label"""
    return is_ipv6(data) and is_evil_v6(data.payload)

def is_evil_addr(data):
    if data.haslayer(IP):
	return data[IP].dst == EVIL_ADDR

def is_evil(data):
    """Determines if the underlying packet (IP or IPv6) is evil"""
    return is_evil_ipv4(data) or is_evil_ipv6(data) or is_evil_addr(data)
    
def is_tcp_syn(pkt):
    """Checks if the packet has a TCP segment and if it is a SYN packet returning a boolean""" 
    if pkt.haslayer(TCP):
        flags = pkt[TCP].flags
        # checks that they SYN flag is set and not the ACK flag
        return flags & SYN_FLAG == SYN_FLAG and flags & ACK_FLAG == 0
    return False
    
def is_dns_request(pkt):
    """Checks if the packet has a DNS message and is a request returning a boolean"""
    return pkt.haslayer(DNS) and pkt[DNS].qr == 0
    
##########################
### Spoofing Functions ###
##########################
def spoof_ip(pkt):
    """Spoofs IP or IPv6 packet data.

    Spoofs an IP or IPv6 header by examing an IP or IPv6 packet and using the source and destination
    addresses to create a header for a packet from the original destination to the original source.
    """
    if pkt.haslayer(IPv6):
        return IPv6(src = pkt.dst, dst = pkt.src)
    return IP(src = pkt.dst, dst = pkt.src)
    
def spoof_tcp_syn(pkt):
    """Spoofs TCP layer data of an IP packet returning a new IP packet.
    
    Spoofs an IP or IPv6 packet with TCP SYN segment to create a spoofed packet with a TCP 
    SYN ACK segment in order to spoof a 3-way handshake. Swaps the source and destination 
    ports, sets the ack and seq values and sets the ACK flag. Returns an IP packet with
    spoofed IP and TCP headers to complete a 3-way handshake.    
    """
    spoofed_ip_header = spoof_ip(pkt)
    spoofed_tcp_header = TCP(sport = pkt.dport, dport = pkt.sport, ack = pkt.seq + 1, seq = random.randint(0, MAXINT), flags = SYN_FLAG | ACK_FLAG)
    return spoofed_ip_header/spoofed_tcp_header
    
def spoof_dns_request(pkt):
    """Spoof IP or IPv6, UDP and DNS data and returns a new packet
    
    Spoofs a packet with a UDP datagram and DNS message. Swaps the source and destination 
    ports, sets the QR field to 1 and adds a DNSRR to the response. Returns an spoofed IP packet
    with a DNS response.    
    """
    spoofed_ip_header = spoof_ip(pkt)
    spoofed_udp_header = UDP(sport = pkt.dport, dport = pkt.sport)
    spoofed_dns_resp = DNS(id = pkt[DNS].id, qr = 1, qd = pkt[DNS].qd, an=DNSRR(rrname = pkt[DNS].qd.qname, ttl = 10, rdata = DNS_RESP_ADDR))
    return spoofed_ip_header/spoofed_udp_header/spoofed_dns_resp

#########################
### Logging Functions ###
#########################
def write_log(pkt, msg):
    """Writes a log message to the log file at the LOG_DIR"""
    logging.warning('{}. Source: {} Destination: {}'.format(msg, pkt.src, pkt.dst))
    
def write_pcap(pkt):
    """Writes a log message to the log file at the LOG_DIR"""
    if not os.path.isfile(PCAP_DIR):
        f = open(PCAP_DIR, 'w')
        f.close()
    if os.path.getsize(PCAP_DIR) > LOG_SIZE:
        logging.warning('Creating new pcap!')
	format_time = time.strftime('%Y%m%d%H%M%S', time.localtime())
	os.rename(PCAP_DIR, '/var/log/spoofer/evil_{}.pcap'.format(format_time))
    wrpcap(PCAP_DIR, pkt, append= True)
 
######################
### Main Functions ###
######################
    
def spoof_evil(frame, log, pcap, spcap, pcapdir):
    """Spoof evil packets and send a response.
    
    Looks at an evil packet and determines if the packet is a TCP SYN or a DNS Request. If the 
    packet is a TCP SYN, a spoofed TCP SYN ACK is created and the spoofed packet is sent which. 
    If the packet is a DNS Request, a spoofed DNS response is created and the spoofed packet is 
    sent. If log is true, a log message is written to the LOG_DIR for each packet. If pcap is
    true, each packet is written to a pcap file.
    """
    pkt = frame.payload # get the packet
    if pcap: write_pcap(pkt) # write input packet to pcap if true

    if is_tcp_syn(pkt):
        if log: 
	    if is_evil_addr(pkt): write_log(pkt, 'TCP SYN recieved from blackhole address!') # write to the log if log is true
            else:  write_log(pkt, 'Evil TCP Syn recieved.')
        spoofed = spoof_tcp_syn(pkt) # spoof a tcp syn ack packet
        if spcap: write_pcap(spoofed) # write spoofed packet to pcap if spcap is true
        send(spoofed, verbose = False) # send spoofed packet
    elif is_dns_request(pkt):
        if log: write_log(pkt, 'Evil DNS Request recieved. Spoofing address for: {}'.format(pkt[DNS].qd.qname)) # write to the log if log is true
        spoofed = spoof_dns_request(pkt) # create spoofed DNS response
        if spcap: write_pcap(spoofed) # write spoofed packet to pcap if spcap is true
        send(spoofed, verbose = False) # send spoofed packet
    else:
        if is_evil_addr(pkt): write_log(pkt, 'Packet from blackhole address!') 
	else: write_log(pkt, 'Standard Evil Packet Recieved!')

def flip_evil(frame, log, pcap, pcapdir):
    """Turn the evil bit off and send the packet out.

    Flips the evil bit off and sends the packet out of the out interface for an IP or
    IPv6 packet.
    """
    pkt = frame.payload # get packet data
    if log: write_log(pkt, 'Evil Packet recived and allowed through.') # logging
    if pcap: write_pcap(pkt) # pcaps

    if pkt.haslayer(IPv6):
        pkt.fl = 0 # change the flow label to 0
        send(pkt, verbose = False) 
    else:
        pkt.flags &= ~EVIL_FLAG # remove the evil flag
        del pkt.chksum # delete the checksum
        send(pkt, verbose = False)
        
def main(interface = INT_IF, log = False, pcap = False, spcap = False, pcapdir = PCAP_DIR, flip = False):
    """Sniffs traffic on input interface and filters for evil ip packets to spoof.
    
    Uses the built in scapy sniff function to sniff all traffic on the input interface. It filters
    out only traffic that is IP packets with the evil bit set and calls evil spoof on the packets.
    """
    if flip: sniff(iface = interface, prn = lambda frame: flip_evil(frame, log, pcap, pcapdir), lfilter = lambda frame: is_evil(frame)) # sniff and flip evil packets
    else: sniff(iface = interface, prn = lambda frame: spoof_evil(frame, log, pcap, spcap, pcapdir), lfilter = lambda frame: is_evil(frame)) # sniff and spoof
        
if __name__ == "__main__":
    parser = OptionParser()
    parser.add_option('-i', '--interface', dest = 'iface', help = "the interface to sniff traffic on", metavar = 'IFACE', default = INT_IF)
    parser.add_option('-l', '--log', dest = 'log', help = "write logs", default = False, action = 'store_true')
    parser.add_option('-p', '--pcap', dest = 'pcap', help = "write packets to pcap", default = False, action = 'store_true')
    parser.add_option('-f', '--flip', dest = 'flip', help = "flip evil bits and send traffic", default = False, action = 'store_true')
    parser.add_option('--sp', '--spoofpcap', dest = 'spcap', help = "write spoofed packets to pcap", default = False, action = 'store_true')
    parser.add_option('--ld', '--logdir', dest = 'ldir', help = "directory for logs", metavar = 'DIR', default = LOG_DIR)
    parser.add_option('--pd', '--pcapdir', dest = 'pdir', help = "directory for pcaps", metavar = 'DIR', default = PCAP_DIR)
    (options, args) = parser.parse_args()
    logging.basicConfig(filename = options.ldir, format = '%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
    if options.flip: logging.warning('Spoofer started up and is flipping!')
    else: logging.warning('Spoofer started up and is spoofing!')
    main(options.iface, options.log, options.pcap, options.spcap, options.pdir, options.flip)
