Package: dhcppython
DHCP PythonVersion 0.1.4A Python implementation of a DHCP client and the tools to manipulate DHCP packets. Includes:A parser of DHCP packets, returning Python objectsSupports for all DHCP options in RFC 2132A rudimentary DHCP clientInstallationpip install dhcppythonRequirementsPython 3.8.0 or higherNOTE: This has been tested on Ubuntu 18.04 and Windows WSL. May or may not work on other platforms.The Packet ParserTwo files contribute to the packet parsing:dhcppython.packetanddhcppython.options. For most operations onlydhcppython.packetwill be required.dhcppython.packetThe main class indhcppython.packetis theDHCPPacket. TheDHCPPacketclass contains multiple constructors for parsing and constructing DHCP packets.Converting a packet in wireformat to a Python objectGiven a DHCP packet inbytesformat (such as what you would get from reading a DHCP packet straight from a socket) a DHCPPacket object can be instantiated by calling thefrom_bytesand supplying the bytes.>>>pkt=dhcppython.packet.DHCPPacket.from_bytes(b'\x01\x01\x06\x00\xea\xbe\xc3\x97\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8cE\x00E\x12\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x01=\x07\x01\x8cE\x00E\x12\t9\x02\x05\xdc<\x0eandroid-dhcp-9\x0c\tGalaxy-S97\n\x01\x03\x06\x0f\x1a\x1c3:;+\xff')>>>pktDHCPPacket(op='BOOTREQUEST',htype='ETHERNET',hlen=6,hops=0,xid=3938370455,secs=1,flags=0,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('0.0.0.0'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='8C:45:00:45:12:09',sname=b'',file=b'',options=OptionList([MessageType(code=53,length=1,data=b'\x01'),ClientIdentifier(code=61,length=7,data=b'\x01\x8cE\x00E\x12\t'),MaxDHCPMessageSize(code=57,length=2,data=b'\x05\xdc'),VendorClassIdentifier(code=60,length=14,data=b'android-dhcp-9'),Hostname(code=12,length=9,data=b'Galaxy-S9'),ParameterRequestList(code=55,length=10,data=b'\x01\x03\x06\x0f\x1a\x1c3:;+'),End(code=255,length=0,data=b'')]))Converting a DHCPPacket object to wireformatGiven a DHCPPacket object you can easily output the corresponding DHCP packet in wireformat by accessing theasbytesattribute of the object.>>>pkt.asbytesb'\x01\x01\x06\x00\xea\xbe\xc3\x97\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8cE\x00E\x12\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x01=\x07\x01\x8cE\x00E\x12\t9\x02\x05\xdc<\x0eandroid-dhcp-9\x0c\tGalaxy-S97\n\x01\x03\x06\x0f\x1a\x1c3:;+\xff'This bytes output is suitable for sending over a socket to a DHCP server.Other Constructors of the DHCPPacket ClassThe default, low level, constructor (not recommended):>>>pkt=dhcppython.packet.DHCPPacket(op="BOOTREQUEST",htype="ETHERNET",hlen=6,hops=0,xid=123456,secs=0,flags=0,ciaddr=ipaddress.IPv4Address(0),yiaddr=ipaddress.IPv4Address(0),siaddr=ipaddress.IPv4Address(0),giaddr=ipaddress.IPv4Address(0),chaddr="DE:AD:BE:EF:C0:DE",sname=b'',file=b'',options=dhcppython.options.OptionList([dhcppython.options.options.short_value_to_object(53,"DHCPDISCOVER")]))>>>pktDHCPPacket(op='BOOTREQUEST',htype='ETHERNET',hlen=6,hops=0,xid=123456,secs=0,flags=0,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('0.0.0.0'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='DE:AD:BE:EF:C0:DE',sname=b'',file=b'',options=OptionList([MessageType(code=53,length=1,data=b'\x01')]))Higher level constructors specific to the four main DHCP message types: DISCOVER, OFFER, REQUEST, ACK:>>>dhcppython.packet.DHCPPacket.Discover('de:ad:be:ef:c0:de')DHCPPacket(op='BOOTREQUEST',htype='ETHERNET',hlen=6,hops=0,xid=4249353806,secs=0,flags=32768,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('0.0.0.0'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='de:ad:be:ef:c0:de',sname=b'',file=b'',options=OptionList([MessageType(code=53,length=1,data=b'\x01')]))>>>dhcppython.packet.DHCPPacket.Offer('de:ad:be:ef:c0:de',seconds=0,tx_id=4249353806,yiaddr=ipaddress.IPv4Address('192.168.56.4'))DHCPPacket(op='BOOTREPLY',htype='ETHERNET',hlen=6,hops=0,xid=4249353806,secs=0,flags=32768,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('192.168.56.4'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='de:ad:be:ef:c0:de',sname=b'',file=b'',options=OptionList([MessageType(code=53,length=1,data=b'\x02')]))>>>dhcppython.packet.DHCPPacket.Request('de:ad:be:ef:c0:de',seconds=0,tx_id=4249353806)DHCPPacket(op='BOOTREQUEST',htype='ETHERNET',hlen=6,hops=0,xid=4249353806,secs=0,flags=32768,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('0.0.0.0'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='de:ad:be:ef:c0:de',sname=b'',file=b'',options=OptionList([MessageType(code=53,length=1,data=b'\x03')]))>>>dhcppython.packet.DHCPPacket.Ack('de:ad:be:ef:c0:de',seconds=0,tx_id=4249353806,yiaddr=ipaddress.IPv4Address('192.168.56.4'))DHCPPacket(op='BOOTREPLY',htype='ETHERNET',hlen=6,hops=0,xid=4249353806,secs=0,flags=32768,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('192.168.56.4'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='de:ad:be:ef:c0:de',sname=b'',file=b'',options=OptionList([MessageType(code=53,length=1,data=b'\x05')]))dhcppython.optionsThis module provides classes for:All DHCP options described in RFC 2132An unknown option class for options not encodedAn abstract Option class that is easily extendable if additional options are requiredA data structure for mananging DHCP options - theOptionListAn higher lever Option factory - theOptionDirectoryA high level API is provided by thedhcppython.options.optionsobject and the Option class:Create an options object from bytes by calling thebytes_to_objectmethod>>>opt=dhcppython.options.options.bytes_to_object(b"\x3d\x07\x01\x8c\x45\x00\x45\x12\x09")>>>optClientIdentifier(code=61,length=7,data=b'\x01\x8cE\x00E\x12\t')Get a human readable dict of an options object value>>>opt.value{'client_identifier':{'hwtype':1,'hwaddr':'8C:45:00:45:12:09'}}Create an options object from its human readable dict of its value:>>>dhcppython.options.options.value_to_object({'client_identifier':{'hwtype':1,'hwaddr':'8C:45:00:45:12:09'}})ClientIdentifier(code=61,length=7,data=b'\x01\x8cE\x00E\x12\t')OR>>>dhcppython.options.options.short_value_to_object(61,{'hwtype':1,'hwaddr':'8C:45:00:45:12:09'})ClientIdentifier(code=61,length=7,data=b'\x01\x8cE\x00E\x12\t')Convert a human readable dict of an option value to the bytes representation>>>dhcppython.options.options.value_to_bytes({'client_identifier':{'hwtype':1,'hwaddr':'8C:45:00:45:12:09'}})b'=\x07\x01\x8cE\x00E\x12\t'Get the bytes representation of an option given its Option object>>>opt=dhcppython.options.ClientIdentifier(code=61,length=7,data=b'\x01\x8cE\x00E\x12\t')>>>opt.asbytesb'=\x07\x01\x8cE\x00E\x12\t'TheOptionListclass provides a very convenient set of methods for managing a list of DHCP options.Create anOptionListinstance from a list ofOptionobjects>>>opt_list=dhcppython.options.OptionList(...[...dhcppython.options.options.short_value_to_object(61,{'hwtype':1,'hwaddr':"8c:45:00:23:45:67"}),...dhcppython.options.options.short_value_to_object(57,1500),...dhcppython.options.options.short_value_to_object(60,"android-dhcp-9"),...dhcppython.options.options.short_value_to_object(12,"Galaxy-S9"),...dhcppython.options.options.short_value_to_object(55,[1,3,6,15,26,28,51,58,59,43])...]...)>>>opt_listOptionList([ClientIdentifier(code=61,length=7,data=b'\x01\x8cE\x00#Eg'),MaxDHCPMessageSize(code=57,length=2,data=b'\x05\xdc'),VendorClassIdentifier(code=60,length=14,data=b'android-dhcp-9'),Hostname(code=12,length=9,data=b'Galaxy-S9'),ParameterRequestList(code=55,length=10,data=b'\x01\x03\x06\x0f\x1a\x1c3:;+')])Retrieve any options in theOptionListby its option code>>>opt_list.by_code(12)Hostname(code=12,length=9,data=b'Galaxy-S9')>>>opt_list.by_code(13)>>>Append (add) any options using theappendmethod>>>opt_list.append(dhcppython.options.options.short_value_to_object(53,"DHCPDISCOVER"))>>>opt_listOptionList([ClientIdentifier(code=61,length=7,data=b'\x01\x8cE\x00#Eg'),MaxDHCPMessageSize(code=57,length=2,data=b'\x05\xdc'),VendorClassIdentifier(code=60,length=14,data=b'android-dhcp-9'),Hostname(code=12,length=9,data=b'Galaxy-S9'),ParameterRequestList(code=55,length=10,data=b'\x01\x03\x06\x0f\x1a\x1c3:;+'),MessageType(code=53,length=1,data=b'\x01')])Protects against duplicate options (duplicate overwrites in place)>>>opt_listOptionList([ClientIdentifier(code=61,length=7,data=b'\x01\x8cE\x00#Eg'),MaxDHCPMessageSize(code=57,length=2,data=b'\x13\x88'),VendorClassIdentifier(code=60,length=14,data=b'android-dhcp-9'),Hostname(code=12,length=9,data=b'Galaxy-S9'),ParameterRequestList(code=55,length=10,data=b'\x01\x03\x06\x0f\x1a\x1c3:;+'),MessageType(code=53,length=1,data=b'\x01')])Allows for iteration like a list>>>foroptinopt_list:...print(opt)...ClientIdentifier(code=61,length=7,data=b'\x01\x8cE\x00#Eg')MaxDHCPMessageSize(code=57,length=2,data=b'\x13\x88')VendorClassIdentifier(code=60,length=14,data=b'android-dhcp-9')Hostname(code=12,length=9,data=b'Galaxy-S9')ParameterRequestList(code=55,length=10,data=b'\x01\x03\x06\x0f\x1a\x1c3:;+')MessageType(code=53,length=1,data=b'\x01')The DHCP ClientA very primitive DHCP client is included in this package in thedhcppython.clientmodule. The client is able to negotiate a lease with a DHCP server and can be configured to use:A given interfaceOption to send broadcast packets or unicast packets to a specific serverSet a relay in the giaddr field"Spoof" MAC addressesSpecify options to send with requestThe high level interface to negotiate a lease is theget_leasemethod of thedhcppython.client.DHCPClientobject. This method goes through the DORA DHCP handshake and returns aLeasenamedtuple which includes all the packets in the :>>>importdhcppython>>>client=dhcppython.client.DHCPClient(interface="enp0s8")>>>lease=client.get_lease(mac_addr="de:ad:be:ef:c0:de",broadcast=True,relay=None,server="255.255.255.255",options_list=None)Leasesuccesful:192.168.56.3--DE:AD:BE:EF:C0:DE--3mselapsed>>>leaseLease(discover=DHCPPacket(op='BOOTREQUEST',htype='ETHERNET',hlen=6,hops=0,xid=2829179566,secs=0,flags=32768,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('0.0.0.0'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='de:ad:be:ef:c0:de',sname=b'',file=b'',options=OptionList([MessageType(code=53,length=1,data=b'\x01')])),offer=DHCPPacket(op='BOOTREPLY',htype='ETHERNET',hlen=6,hops=0,xid=2829179566,secs=0,flags=32768,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('192.168.56.3'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='DE:AD:BE:EF:C0:DE',sname=b'',file=b'',options=OptionList([SubnetMask(code=1,length=4,data=b'\xff\xff\xff\x00'),Router(code=3,length=4,data=b'\n\x97\x01\x01'),DNSServer(code=6,length=4,data=b'\nh\x01\x08'),Hostname(code=12,length=22,data=b'dhcp.-192-168-56-3.com'),DomainName(code=15,length=14,data=b'example.com'),IPAddressLeaseTime(code=51,length=4,data=b'\x00\x01Q\x80'),MessageType(code=53,length=1,data=b'\x02'),ServerIdentifier(code=54,length=4,data=b'\xc0\xa88\x02'),RenewalTime(code=58,length=4,data=b'\x00\x00T`'),RebindingTime(code=59,length=4,data=b'\x00\x00\xa8\xc0'),End(code=255,length=0,data=b'')])),request=DHCPPacket(op='BOOTREQUEST',htype='ETHERNET',hlen=6,hops=0,xid=2829179566,secs=0,flags=32768,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('0.0.0.0'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='de:ad:be:ef:c0:de',sname=b'',file=b'',options=OptionList([MessageType(code=53,length=1,data=b'\x03')])),ack=DHCPPacket(op='BOOTREPLY',htype='ETHERNET',hlen=6,hops=0,xid=2829179566,secs=0,flags=32768,ciaddr=IPv4Address('0.0.0.0'),yiaddr=IPv4Address('192.168.56.3'),siaddr=IPv4Address('0.0.0.0'),giaddr=IPv4Address('0.0.0.0'),chaddr='DE:AD:BE:EF:C0:DE',sname=b'',file=b'',options=OptionList([SubnetMask(code=1,length=4,data=b'\xff\xff\xff\x00'),Router(code=3,length=4,data=b'\n\x97\x01\x01'),DNSServer(code=6,length=4,data=b'\nh\x01\x08'),Hostname(code=12,length=22,data=b'dhcp.-192-168-56-3.com'),DomainName(code=15,length=14,data=b'example.com'),IPAddressLeaseTime(code=51,length=4,data=b'\x00\x01Q\x80'),MessageType(code=53,length=1,data=b'\x05'),ServerIdentifier(code=54,length=4,data=b'\xc0\xa88\x02'),RenewalTime(code=58,length=4,data=b'\x00\x00T`'),RebindingTime(code=59,length=4,data=b'\x00\x00\xa8\xc0'),End(code=255,length=0,data=b'')])),time=0.0032514659978915006,server=('192.168.56.2',67))