# Script that process stdout from pcf and creates IRI messages
#
# Copyright (C) 2014 Martin Holkovic
#
# 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 3 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, see <http://www.gnu.org/licenses/>.


from parser_functions import *

def decode(input, type):
    dictionary = {
        "linuxCookedCapture": {"0001": "Ethernet", "0304": "loopback"}, # secky mozne hodnoty v /usr/include/net/if_arp.h
        "etherType": {"0800": "IPv4", "86dd": "IPv6", "8863": "PPPoE Discovery", "8864": "PPPoE Session", "0806": "ARP", "8847": "MPLS", "8848": "MPLS"},
        "pppoeDiscovery": {"00": "PPP Session Stage", "07": "PADO, Offer", "09": "PADI, Initiation", "19": "PADR, Request", "65": "PADS, Session-confirmation", "a7": "PADT, Terminate"},
        "pppProtocol": {"0021": "IPv4", "0057": "IPv6", "8021": "IPCP", "8057": "IPv6CP", "c023": "PAP", "c223": "CHAP", "0281": "MPLS", "0283": "MPLS"},
        "papCode": {"01": "Request", "02": "Ack", "03": "Nak"},
        "chapCode": {"01": "Challenge", "02": "Response", "03": "Success", "04": "Failure"},
        "ip(v6)cpCode": {"01": "Configure-Request", "02": "Configure-Ack", "03": "Configure-Nak", "04": "Configure-Reject", "05": "Terminate-Request", "06": "Terminate-Ack", "07": "Code-Reject"},
        "ip(v6)Protocol": {"04": "IPv4", "06": "TCP", "11": "UDP", "29": "IPv6", "2f": "GRE", "33": "AH", "3a": "ICMPv6", "73": "L2TP", "89": "MPLS"},
        "udpPort": {3544: "teredo"},
        "arpOpcode": {"0001": "Request", "0002": "Reply"},
        "linuxCookedCapture8": {"0800": "IPv4", "86dd": "IPv6"}
    }
    try:
        return dictionary[type][input]
    except:
        return "unknown"


def parse_linux_cooked_capture(data):
    replaced = str_to_hex(data[2:4])
    replaced2 = decode(replaced, "linuxCookedCapture")
    if replaced2 == "Ethernet":
        protocol = str_to_hex(data[14:16])
        protocol2 = decode(protocol, "etherType")
        sourceMac = str_to_mac(data[6:12])
        l2 = {"replaced": replaced, "replaced2": replaced2, "protocol": protocol, "protocol2": protocol2, "sourceMAC": sourceMac}
        data = data[(10+str_to_int(data[4:6])):len(data)]
    else:
        return {"replaced": replaced, "replaced2": replaced2}, "unknown", ""
    
    return l2, protocol2, data


def parse_ethernet(data):
    i = 12
    etherType = str_to_hex(data[i:i+2])
    while etherType in ['8100', '9100', '9200', '9300']: # VLAN tag
        i+=4
        etherType = str_to_hex(data[i:i+2])
    
    etherType2 = decode(etherType, "etherType")
    
    destinationMac = str_to_mac(data[0:6])
    sourceMac = str_to_mac(data[6:12])
    l2 = {"etherType": etherType, "etherType2": etherType2, "destinationMAC": destinationMac, "sourceMAC": sourceMac}
    data = data[i+2:len(data)]
    
    return l2, etherType2, data


def parse_arp(data):
    hardwareType = str_to_hex(data[0:2])
    protocolType = str_to_hex(data[2:4])
    opcode = str_to_hex(data[6:8])
    opcode2 = decode(opcode, "arpOpcode")
    
    if hardwareType == "0001" and protocolType == "0800" and opcode2 == "Reply": # MAC address, IPv4 address, Reply
        macAddress = str_to_mac(data[8:14])
        ipAddress = str_to_ip4(data[14:18])
        return {"opcode": opcode, "opcode2": opcode2, "MAC": macAddress, "IPv4": ipAddress}, "finish", ""
        
    return {"opcode": opcode, "opcode2": opcode2}, "finish", ""


def parse_pppoe_discovery(data):
    pppoedCode = str_to_hex(data[1:2])
    pppoedCode2 = decode(pppoedCode, "pppoe")
    sessionId = str_to_hex(data[2:4])
    
    header = {"pppoedCode": pppoedCode, "pppoedCode2": pppoedCode2, "sessionId": sessionId}
    return header, "finish", ""


def parse_pppoe_session(data):
    pppoeCode = "00"
    pppoeCode2 = "PPP Session Stage"
    sessionId = str_to_hex(data[2:4])
    protocol = str_to_hex(data[6:8])
    protocol2 = decode(protocol, "pppProtocol")
    payloadLength = str_to_int(data[4:6])
    data = data[8:8+payloadLength]
    
    header = {"pppoeCode": pppoeCode, "pppoeCode2": pppoeCode2, "sessionId": sessionId, "protocol": protocol, "protocol2": protocol2}
    return header, protocol2, data


def parse_pap(data):
    papCode = data[0:1]
    papCode2 = decode(papCode, "papCode")
    identifier = str_to_hex(data[1:2])
    peerIdLength = str_to_int(data[4:5])
    peerId = data[5:5+peerIdLength]
    
    header = {"papCode": papCode, "papCode2": papCode2, "identifier": identifier, "peerId": peerId}
    return header, "finish", ""


def parse_chap(data):
    chapCode = data[0:1]
    chapCode2 = decode(chapCode, "chapCode")
    identifier = str_to_hex(data[1:2])
    length = str_to_int(data[2:4])
    valueSize = str_to_int(data[4:5])
    peerId = data[5+valueSize:length]

    header = {"chapCode": chapCode, "chapCode2": chapCode2, "identifier": identifier, "peerId": peerId}
    return header, "finish", ""


def parse_ipcp(data):
    ipcpCode = data[0:1]
    ipcpCode2 = decode(ipcpCode, "ip(v6)cpCode")
    identifier = str_to_hex(data[1:2])
    length = str_to_int(data[2:4])
    
    ipv4 = None
    offset = 4 # zacatek Options
    while offset < length:
        if str_to_hex(data[offset]) == "03": #parameter IP adresa
            ipv4 = str_to_ip4(data[offset+2:offset+6])
            return {"ipcpCode": ipcpCode, "ipcpCode2": ipcpCode2, "identifier": identifier, "IPv4": ipv4}, "finish", ""
        else:
            offset += str_to_int(data[offset+1])
    
    return {"ipcpCode": ipcpCode, "ipcpCode2": ipcpCode2, "identifier": identifier}, "finish", ""


def parse_ipv6cp(data):
    ipv6cpCode = data[0:1]
    ipv6cpCode2 = decode(ipv6cpCode, "ip(v6)cpCode")
    identifier = str_to_hex(data[1:2])
    length = str_to_int(data[2:4])
    
    ipv6 = None
    offset = 4 # zacatek Options
    while offset < length:
        if str_to_hex(data[offset]) == "01": #parameter IPv6 adresa
            interfaceId = data[offset+2:offset+10]
            ipv6 = ipv6ip_from_interface_identifier(interfaceId)
            return {"ipv6cpCode": ipv6cpCode, "ipv6cpCode2": ipv6cpCode2, "identifier": identifier, "IPv6": ipv6}, "finish", ""
        else:
            offset += str_to_int(data[offset+1])
    
    return {"ipv6cpCode": ipv6cpCode, "ipv6cpCode2": ipv6cpCode2, "identifier": identifier}, "finish", ""


def parse_ipv4(data):
    protocol = str_to_hex(data[9])
    protocol2 = decode(protocol, "ip(v6)Protocol")
    sourceIpv4 = str_to_ip4(data[12:16])
    destinationIpv4 = str_to_ip4(data[16:20])
    
    header = {"protocol": protocol, "protocol2": protocol2, "sourceIPv4": sourceIpv4, "destinationIPv4": destinationIpv4}
    data = data[20:len(data)]
    return header, protocol2, data


def parse_ipv6(data):
    sourceIpv6 = str_to_ip6(data[8:24])
    destinationIpv6 = str_to_ip6(data[24:40])
    
    nextHeader = str_to_hex(data[6])
    offset = 40
    
    while nextHeader in ['00', '2b', '2c', '32', '33', '3b', '3c', '87']: # next header hlavicky pro smerovace po ceste
        nextHeader = str_to_hex(data[offset])
        headerLength = 8 + str_to_int(data[offset+1])*4
        offset += headerLength
    
    nextHeader2 = decode(nextHeader, "ip(v6)Protocol")
    header = {"nextHeader": nextHeader, "nextHeader2": nextHeader2, "sourceIPv6": sourceIpv6, "destinationIPv6": destinationIpv6}
    data = data[offset:len(data)]
    return header, nextHeader2, data


def parse_mpls(data):
    offset = 2
    while True: # najdem poslednu MPLS hlavicku
        thirdByte = str_to_int(data[offset]) # posledny bit v 3 bajte urcuje ci sa jedna o poslednu MPLS hlavicku
        if thirdByte%2 == 1: # ked je bit == 1, jedna sa o poslednu hlavicku
            break
        offset += 4
    offset += 2 # MPLS hlavicka ma este 1B
    
    firstByte = str_to_int(data[offset])
    firstNibble = firstByte >> 4
    
    pseudoWire = False
    # za MPLS hlavickami sa este mozu nachadzat 4B pridanych dal protokolem MPLS
    if firstNibble == 0 or firstNibble == 1: # PW MPLS Control Word || PW Associated Channel Header
        offset += 4
        pseudoWire = True
    
    firstByte = str_to_int(data[offset])
    firstNibble = firstByte >> 4
    
    if pseudoWire == True:
        encapsuled = "Ethernet"
    if firstNibble == 4:
        encapsuled = "IPv4"
    elif firstNibble == 6:
        encapsuled = "IPv6"
    else: # asi zbytecne
        encapsuled = "Ethernet"
        
    data = data[offset:len(data)]
    return {}, encapsuled, data


def parse_gre(data):
    protocolType = str_to_hex(data[2:4])
    protocolType2 = decode(protocolType, "etherType")
    
    header = {"protocolType": protocolType, "protocolType2": protocolType2}
    data = data[4:len(data)]
    return header, protocolType2, data


def parse_l2tp(data):
    protocolType = str_to_hex(data[0:4])
    protocolType2 = decode(protocolType, "etherType")
    
    header = {"protocolType": protocolType, "protocolType2": protocolType2}
    data = data[4:len(data)]
    return header, protocolType2, data


def parse_udp(data):
    sourcePort = str_to_int(data[0:2])
    sourcePort2 = decode(sourcePort, "udpPort")
    destinationPort = str_to_int(data[2:4])
    destinationPort2 = decode(destinationPort, "udpPort")
    
    header = {"sourcePort": sourcePort, "sourcePort2": sourcePort2, "destinationPort": destinationPort, "destinationPort2": destinationPort2}
    data = data[8:len(data)]
    return header, destinationPort2, data


def parse_tcp(data):
    sourcePort = str_to_int(data[0:2])
    sourcePort2 = decode(sourcePort, "tcpPort")
    destinationPort = str_to_int(data[2:4])
    destinationPort2 = decode(destinationPort, "tcpPort")
    flags = str_to_int(data[13:14])
    seqNum = str_to_int(data[4:8])
    ackNum = str_to_int(data[8:12])
    offset = str_to_int(data[12:13]) / 16
    headerLength = offset * 4
    length = len(data) - offset
    
    header = {"sourcePort": sourcePort, "sourcePort2": sourcePort2, "destinationPort": destinationPort, "destinationPort2": destinationPort2, "seqNum": seqNum, "ackNum": ackNum, "flags": flags, "length": length}
    data = data[headerLength:len(data)]
    
    return header, destinationPort2, data


def parse_teredo(data):
    offset = 0
    
    if str_to_hex(data[offset:offset+2]) == "0001": # Authentication
        offset += 13
    
    if str_to_hex(data[offset:offset+2]) == "0000": # Origin indication
        sourcePort = int_to_hex(str_to_int(data[0:2]) | 0xFFFF)
        sourcePort2 = decode(sourcePort, "udpPort")
        destinationPort = int_to_hex(str_to_int(data[2:4]) | 0xFFFFFFFF)
        destinationPort2 = decode(destinationPort, "udpPort")
        header = {"sourcePort": sourcePort, "sourcePort2": sourcePort2, "destinationPort": destinationPort, "destinationPort2": destinationPort2}
        offset += 8
    else:
        header = {}
    
    data = data[offset:len(data)]
    return header, "IPv6", data


def parse_ah(data):
    protocol = str_to_hex(data[0:1])
    protocol2 = decode(protocol, "ip(v6)Protocol")
    length = str_to_int(data[1:2])
    
    header = {"protocol": protocol, "protocol2": protocol2}
    data = data[8+length*4:len(data)]
    return header, protocol2, data


def parse(data):
    sourceMac = None
    destinationMac = None
    sourceIp = None
    destinationIp = None
    sourcePort = None
    destinationPort = None
    transportProtocol = None
    header = False
    
    if str_to_hex(data[12:14]) == "0000":
        actual = "Linux cooked capture"
    else:
        actual = "Ethernet"
    last = None
    
    while(actual != "unknown" and actual != "finish"):
        last = actual
        #print actual
        if actual == "Linux cooked capture":
            header, actual, data = parse_linux_cooked_capture(data)
            if sourceMac == None: # prva L2 hlavicka
                sourceMac = header["sourceMAC"]
        elif actual == "Ethernet":
            header, actual, data = parse_ethernet(data)
            if sourceMac == None: # prva L2 hlavicka
                sourceMac = header["sourceMAC"]
                destinationMac = header["destinationMAC"]
        elif actual == "ARP":
            header, actual, data = parse_arp(data)
        elif actual == "MPLS":
            header, actual, data = parse_mpls(data)
        elif actual == "PPPoE Discovery":
            header, actual, data = parse_pppoe_discovery(data)
        elif actual == "PPPoE Session":
            header, actual, data = parse_pppoe_session(data)
        elif actual == "PAP":
            header, actual, data = parse_pap(data)
        elif actual == "CHAP":
            header, actual, data = parse_chap(data)
        elif actual == "IPCP":
            header, actual, data = parse_ipcp(data)
        elif actual == "IPv6CP":
            header, actual, data = parse_ipv6cp(data)
        elif actual == "IPv4":
            header, actual, data = parse_ipv4(data)
            sourceIp = header["sourceIPv4"]
            destinationIp = header["destinationIPv4"]
            if destinationIp == "127.0.0.1":
				return "looback", None, None, None
        elif actual == "IPv6":
            header, actual, data = parse_ipv6(data)
            sourceIp = header["sourceIPv6"]
            destinationIp = header["destinationIPv6"]
            if destinationIp == "::":
				return "looback", None, None, None
        elif actual == "GRE":
            header, actual, data = parse_gre(data)
        elif actual == "L2TP":
            header, actual, data = parse_l2tp(data)
        elif actual == "UDP":
            header, actual, data = parse_udp(data)
            sourcePort = header["sourcePort"]
            destinationPort = header["destinationPort"]
            transportProtocol = "UDP"
        elif actual == "TCP":
            header, actual, data = parse_tcp(data)
            sourcePort = header["sourcePort"]
            destinationPort = header["destinationPort"]
            transportProtocol = "TCP"
        elif actual == "teredo":
            header, actual, data = parse_teredo(data)
        else:
            actual = "unknown"
        #print header
    
    if data != "":
        data = str_to_hex(data)
    
    return last, {"srcMac": sourceMac, "dstMac": destinationMac, "srcIp": sourceIp, "dstIp": destinationIp, "srcPort": sourcePort, "dstPort": destinationPort, "transportProtocol": transportProtocol}, header, data
    
