import sys
import re

from pykd import *

fwpsLayer = typeInfo( "FWPS_BUILTIN_LAYERS_" ).asMap()
fwpsDataType = typeInfo( "FWP_DATA_TYPE_" ).asMap()
fwpDirection = typeInfo( "FWP_DIRECTION_" ).asMap()

def printBlob( blob ):
    bb = loadBytes( blob.data, blob.size )
    str = "\n"

    i = 0
    for b in bb:
        str += " %02x" % b  
        i = ( i + 1 ) % 16
        if i == 0: str += "\n"
    str += "\n"

    return str

def printFwpsValue( value ):
    return { 
        "FWP_UINT8"  : lambda : "%#x" % value.uint8,
        "FWP_UINT16" : lambda : "%#x" % value.uint16,
        "FWP_UINT32" : lambda : "%#x" % value.uint32,
        "FWP_UINT64" : lambda : "%#x" % value.uint64.deref(),
        "FWP_INT8"   : lambda : "%#x" % value.int8,
        "FWP_INT16"  : lambda : "%#x" % value.int16,
        "FWP_INT32"  : lambda : "%#x" % value.int32,
        "FWP_INT64"  : lambda : "%#x" % value.int64.deref(),
        "FWP_BYTE_BLOB_TYPE" : lambda : printBlob( value.byteBlob.deref() ),

    }.get( fwpsDataType[ value.type ], lambda : "---" )()

def wfpFixedValues( addr ):
  
    dprintln( "FWPS_INCOMING_VALUES0:" )

    inFixedValue = typedVar( "FWPS_INCOMING_VALUES0_", addr )

    dprintln( " Layer: " + fwpsLayer[ inFixedValue.layerId ] )
    dprintln( " Value: %d" % inFixedValue.valueCount )

    values = [ x.value for x in typedVarArray( int(inFixedValue.incomingValue), "FWPS_INCOMING_VALUE0_", inFixedValue.valueCount ) ]

    layerName = fwpsLayer[ inFixedValue.layerId ]

    discardRe = re.compile( '_DISCARD' )
    layerName = discardRe.sub( '', layerName, 1 )

    layerRe = re.compile( 'LAYER' )
    fwpsFields = typeInfo( layerRe.sub( 'FIELDS', layerName, 1 ) + '_' ).asMap()

    for i in range( 0, len(values) ): 
        dprintln( "    " + fwpsFields[ i ] )
        dprintln( "      Type: " + fwpsDataType[ values[i].type ] )
        dprintln( "      Value: " +  printFwpsValue( values[i] )  )

def printDiscardReason( discardReason ):
    return ""

def printBlobAsStr( blob ):
    return loadWChars( blob.data, blob.size )

def printFwpsMetaValue( valueIndex, inMetaValues ):

   return {
        0x00000001 : lambda x: printDiscardReason( x.discardMetadata ),
        0x00000002 : lambda x: "%#x" % inMetaValues.flowHandle,
        0x00000004 : lambda x: "%#x" % inMetaValues.ipHeaderSize,
        0x00000008 : lambda x: printBlobAsStr( x.processPath.deref() ),
        0x00000010 : lambda x: "%#lx" % inMetaValues.token,
        0x00000020 : lambda x: "%#lx" % inMetaValues.processId,
        0x00000040 : lambda x: "%#x" % inMetaValues.flags,
        0x00000080 : lambda x: "%#lx" % inMetaValues.reserved,
        0x00000100 : lambda x: "%#x" % inMetaValues.sourceInterfaceIndex,
        0x00000200 : lambda x: "%#x" % inMetaValues.destinationInterfaceIndex,
        0x00000400 : lambda x: "%#x" % inMetaValues.transportHeaderSize,
        0x00000800 : lambda x: "%#x" % inMetaValues.compartmentId,
        0x00001000 : lambda x: "id: %x  offset: %x  length: %x" % ( x.fragmentMetadata.fragmentIdentification, x.fragmentMetadata.fragmentOffset, x.fragmentMetadata.fragmentLength ),
        0x00002000 : lambda x: "%#x" % x.pathMtu,
        0x00004000 : lambda x: "%#lx" % x.completionHandle,
        0x00008000 : lambda x: "%#lx" % x.transportEndpointHandle,
        0x00010000 : lambda x: "Data: %#lx, Length: %#x" % ( x.controlData, x.controlDataLength ),
        0x00020000 : lambda x: "Zone: %d Level: %d" % ( x.remoteScopeId.Zone, x.remoteScopeId.Level ),
        0x00040000 : lambda x: fwpDirection[ x.packetDirection ],
    }.get( valueIndex, lambda x: "" )( inMetaValues )


def wfpMetaValues( addr ):

    dprintln( "FWPS_INCOMING_METADATA_VALUES0:" )

    fwpsMetadataFields = {
        0x00000001 : "FWPS_METADATA_FIELD_DISCARD_REASON",
        0x00000002 : "FWPS_METADATA_FIELD_FLOW_HANDLE",
        0x00000004 : "FWPS_METADATA_FIELD_IP_HEADER_SIZE",
        0x00000008 : "FWPS_METADATA_FIELD_PROCESS_PATH",
        0x00000010 : "FWPS_METADATA_FIELD_TOKEN",
        0x00000020 : "FWPS_METADATA_FIELD_PROCESS_ID",
        0x00000040 : "FWPS_METADATA_FIELD_SYSTEM_FLAGS",
        0x00000080 : "FWPS_METADATA_FIELD_RESERVED",
        0x00000100 : "FWPS_METADATA_FIELD_SOURCE_INTERFACE_INDEX",
        0x00000200 : "FWPS_METADATA_FIELD_DESTINATION_INTERFACE_INDEX",
        0x00000400 : "FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE",
        0x00000800 : "FWPS_METADATA_FIELD_COMPARTMENT_ID",
        0x00001000 : "FWPS_METADATA_FIELD_FRAGMENT_DATA",
        0x00002000 : "FWPS_METADATA_FIELD_PATH_MTU",
        0x00004000 : "FWPS_METADATA_FIELD_COMPLETION_HANDLE",
        0x00008000 : "FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE",
        0x00010000 : "FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA",
        0x00020000 : "FWPS_METADATA_FIELD_REMOTE_SCOPE_ID",
        0x00040000 : "FWPS_METADATA_FIELD_PACKET_DIRECTION",
        0x00080000 : "FWPS_METADATA_FIELD_PACKET_SYSTEM_CRITICAL",
        0x00100000 : "FWPS_METADATA_FIELD_FORWARD_LAYER_OUTBOUND_PASS_THRU",
        0x00200000 : "FWPS_METADATA_FIELD_FORWARD_LAYER_INBOUND_PASS_THRU",
        0x00400000 : "FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED",
        0x00800000 : "FWPS_METADATA_FIELD_TRANSPORT_HEADER_INCLUDE_HEADER",
        0x01000000 : "FWPS_METADATA_FIELD_DESTINATION_PREFIX",
        0x02000000 : "FWPS_METADATA_FIELD_ETHER_FRAME_LENGTH",
        0x04000000 : "FWPS_METADATA_FIELD_PARENT_ENDPOINT_HANDLE",
        0x08000000 : "FWPS_METADATA_FIELD_ICMP_ID_AND_SEQUENCE",
        0x10000000 : "FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_PID",
        0x20000000 : "FWPS_METADATA_FIELD_ORIGINAL_DESTINATION",
        0x40000000 : "FWPS_METADATA_FIELD_REDIRECT_RECORD_HANDLE",
        0x80000000 : "FWPS_METADATA_FIELD_SUB_PROCESS_TAG"
    }

    inMetaValues = typedVar( "FWPS_INCOMING_METADATA_VALUES0_", addr )

    for i in ( 1 << i for i in range( 0, 32) ):
        if inMetaValues.currentMetadataValues & i:
            dprint( "    " )
            dprint( fwpsMetadataFields.get( i, "Unknown filed %#010x" % i ) + ": " )
            dprint( printFwpsMetaValue( i, inMetaValues ) )
            dprintln("")
  

def usage():
    dprintln( "Usage:" )
    dprintln( "!py wfp /fixed addr")
    dprintln( "!py wfp /meta addr" )

def main():

    if not isKernelDebugging():
        dprintln( "This script is for kernel debugging only" )

    if len(sys.argv) < 2:
        usage()
        return 

    if sys.argv[1]=="/fixed":
        wfpFixedValues( expr(sys.argv[2]) )
        return

    if sys.argv[1]=="/meta":
        wfpMetaValues( expr(sys.argv[2]) )
        return

    usage()    
              
if __name__ == "__main__":
    main()