"""
Dynamic Host Configuration Protocol for IPv4

http://www.networksorcery.com/enp/protocol/dhcp.htm
http://www.networksorcery.com/enp/protocol/bootp/options.htm
"""
from binascii import unhexlify

from construct import *
from ipv4 import IpAddress


dhcp_option = Struct("dhcp_option",
    Enum(Byte("code"),
        Pad = 0,
        Subnet_Mask = 1,
        Time_Offset = 2,
        Router = 3,
        Time_Server = 4,
        Name_Server = 5,
        Domain_Name_Server = 6,
        Log_Server = 7,
        Quote_Server = 8,
        LPR_Server = 9,
        Impress_Server = 10,
        Resource_Location_Server = 11,
        Host_Name = 12,
        Boot_File_Size = 13,
        Merit_Dump_File = 14,
        Domain_Name = 15,
        Swap_Server = 16,
        Root_Path = 17,
        Extensions_Path = 18,
        IP_Forwarding_enabledisable = 19,
        Nonlocal_Source_Routing_enabledisable = 20,
        Policy_Filter = 21,
        Maximum_Datagram_Reassembly_Size = 22,
        Default_IP_TTL = 23,
        Path_MTU_Aging_Timeout = 24,
        Path_MTU_Plateau_Table = 25,
        Interface_MTU = 26,
        All_Subnets_are_Local = 27,
        Broadcast_Address = 28,
        Perform_Mask_Discovery = 29,
        Mask_supplier = 30,
        Perform_router_discovery = 31,
        Router_solicitation_address = 32,
        Static_routing_table = 33,
        Trailer_encapsulation = 34,
        ARP_cache_timeout = 35,
        Ethernet_encapsulation = 36,
        Default_TCP_TTL = 37,
        TCP_keepalive_interval = 38,
        TCP_keepalive_garbage = 39,
        Network_Information_Service_domain = 40,
        Network_Information_Servers = 41,
        NTP_servers = 42,
        Vendor_specific_information = 43,
        NetBIOS_over_TCPIP_name_server = 44,
        NetBIOS_over_TCPIP_Datagram_Distribution_Server = 45,
        NetBIOS_over_TCPIP_Node_Type = 46,
        NetBIOS_over_TCPIP_Scope = 47,
        X_Window_System_Font_Server = 48,
        X_Window_System_Display_Manager = 49,
        Requested_IP_Address = 50,
        IP_address_lease_time = 51,
        Option_overload = 52,
        DHCP_message_type = 53,
        Server_identifier = 54,
        Parameter_request_list = 55,
        Message = 56,
        Maximum_DHCP_message_size = 57,
        Renew_time_value = 58,
        Rebinding_time_value = 59,
        Class_identifier = 60,
        Client_identifier = 61,
        NetWareIP_Domain_Name = 62,
        NetWareIP_information = 63,
        Network_Information_Service_Domain = 64,
        Network_Information_Service_Servers = 65,
        TFTP_server_name = 66,
        Bootfile_name = 67,
        Mobile_IP_Home_Agent = 68,
        Simple_Mail_Transport_Protocol_Server = 69,
        Post_Office_Protocol_Server = 70,
        Network_News_Transport_Protocol_Server = 71,
        Default_World_Wide_Web_Server = 72,
        Default_Finger_Server = 73,
        Default_Internet_Relay_Chat_Server = 74,
        StreetTalk_Server = 75,
        StreetTalk_Directory_Assistance_Server = 76,
        User_Class_Information = 77,
        SLP_Directory_Agent = 78,
        SLP_Service_Scope = 79,
        Rapid_Commit = 80,
        Fully_Qualified_Domain_Name = 81,
        Relay_Agent_Information = 82,
        Internet_Storage_Name_Service = 83,
        NDS_servers = 85,
        NDS_tree_name = 86,
        NDS_context = 87,
        BCMCS_Controller_Domain_Name_list = 88,
        BCMCS_Controller_IPv4_address_list = 89,
        Authentication = 90,
        Client_last_transaction_time = 91,
        Associated_ip = 92,
        Client_System_Architecture_Type = 93,
        Client_Network_Interface_Identifier = 94,
        Lightweight_Directory_Access_Protocol = 95,
        Client_Machine_Identifier = 97,
        Open_Group_User_Authentication = 98,
        Autonomous_System_Number = 109,
        NetInfo_Parent_Server_Address = 112,
        NetInfo_Parent_Server_Tag = 113,
        URL = 114,
        Auto_Configure = 116,
        Name_Service_Search = 117,
        Subnet_Selection = 118,
        DNS_domain_search_list = 119,
        SIP_Servers_DHCP_Option = 120,
        Classless_Static_Route_Option = 121,
        CableLabs_Client_Configuration = 122,
        GeoConf = 123,
    ),
    Switch("value", lambda ctx: ctx.code,
        {
            # codes without any value
            "Pad" : Pass,
        },
        # codes followed by length and value fields
        default = Struct("value",
            Byte("length"),
            Field("data", lambda ctx: ctx.length),
        )
    )
)

dhcp_header = Struct("dhcp_header",
    Enum(Byte("opcode"),
        BootRequest = 1,
        BootReply = 2,
    ),
    Enum(Byte("hardware_type"),
        Ethernet = 1,
        Experimental_Ethernet = 2,
        ProNET_Token_Ring = 4,
        Chaos = 5,
        IEEE_802 = 6,
        ARCNET = 7,
        Hyperchannel = 8,
        Lanstar = 9,
    ),
    Byte("hardware_address_length"),
    Byte("hop_count"),
    UBInt32("transaction_id"),
    UBInt16("elapsed_time"),
    BitStruct("flags",
        Flag("boardcast"),
        Padding(15),
    ),
    IpAddress("client_addr"),
    IpAddress("your_addr"),
    IpAddress("server_addr"),
    IpAddress("relay_addr"),
    Bytes("client_hardware_addr", 16),
    Bytes("server_host_name", 64),
    Bytes("boot_filename", 128),
    # BOOTP/DHCP options
    # "The first four bytes contain the (decimal) values 99, 130, 83 and 99"
    Const(Bytes("magic", 4), b"\x63\x82\x53\x63"),
    Rename("options", OptionalGreedyRange(dhcp_option)),
)


if __name__ == "__main__":
    test = unhexlify(six.b(
        "0101060167c05f5a00000000"
        "0102030405060708090a0b0c"
        "0d0e0f10"
        "DEADBEEFBEEF"
        "000000000000000000000000000000000000000000000000000000"
        "000000000000000000000000000000000000000000000000000000"
        "000000000000000000000000000000000000000000000000000000"
        "000000000000000000000000000000000000000000000000000000"
        "000000000000000000000000000000000000000000000000000000"
        "000000000000000000000000000000000000000000000000000000"
        "000000000000000000000000000000000000000000000000000000"
        "00000000000000000000000000"
        "63825363"
        "3501083d0701DEADBEEFBEEF0c04417375733c084d53465420352e"
        "30370d010f03062c2e2f1f2179f92bfc52210117566c616e333338"
        "382b45746865726e6574312f302f32340206f8f0827348f9ff"
    ))

    print (dhcp_header.parse(test))
