| # $Id: dhcp.py 23 2006-11-08 15:45:33Z dugsong $ |
| # -*- coding: utf-8 -*- |
| """Dynamic Host Configuration Protocol.""" |
| from __future__ import print_function |
| from __future__ import absolute_import |
| |
| import struct |
| |
| from . import arp |
| from . import dpkt |
| from .compat import compat_ord |
| |
| DHCP_OP_REQUEST = 1 |
| DHCP_OP_REPLY = 2 |
| |
| DHCP_MAGIC = 0x63825363 |
| |
| # DHCP option codes |
| DHCP_OPT_NETMASK = 1 # I: subnet mask |
| DHCP_OPT_TIMEOFFSET = 2 |
| DHCP_OPT_ROUTER = 3 # s: list of router ips |
| DHCP_OPT_TIMESERVER = 4 |
| DHCP_OPT_NAMESERVER = 5 |
| DHCP_OPT_DNS_SVRS = 6 # s: list of DNS servers |
| DHCP_OPT_LOGSERV = 7 |
| DHCP_OPT_COOKIESERV = 8 |
| DHCP_OPT_LPRSERV = 9 |
| DHCP_OPT_IMPSERV = 10 |
| DHCP_OPT_RESSERV = 11 |
| DHCP_OPT_HOSTNAME = 12 # s: client hostname |
| DHCP_OPT_BOOTFILESIZE = 13 |
| DHCP_OPT_DUMPFILE = 14 |
| DHCP_OPT_DOMAIN = 15 # s: domain name |
| DHCP_OPT_SWAPSERV = 16 |
| DHCP_OPT_ROOTPATH = 17 |
| DHCP_OPT_EXTENPATH = 18 |
| DHCP_OPT_IPFORWARD = 19 |
| DHCP_OPT_SRCROUTE = 20 |
| DHCP_OPT_POLICYFILTER = 21 |
| DHCP_OPT_MAXASMSIZE = 22 |
| DHCP_OPT_IPTTL = 23 |
| DHCP_OPT_MTUTIMEOUT = 24 |
| DHCP_OPT_MTUTABLE = 25 |
| DHCP_OPT_MTUSIZE = 26 |
| DHCP_OPT_LOCALSUBNETS = 27 |
| DHCP_OPT_BROADCASTADDR = 28 |
| DHCP_OPT_DOMASKDISCOV = 29 |
| DHCP_OPT_MASKSUPPLY = 30 |
| DHCP_OPT_DOROUTEDISC = 31 |
| DHCP_OPT_ROUTERSOLICIT = 32 |
| DHCP_OPT_STATICROUTE = 33 |
| DHCP_OPT_TRAILERENCAP = 34 |
| DHCP_OPT_ARPTIMEOUT = 35 |
| DHCP_OPT_ETHERENCAP = 36 |
| DHCP_OPT_TCPTTL = 37 |
| DHCP_OPT_TCPKEEPALIVE = 38 |
| DHCP_OPT_TCPALIVEGARBAGE = 39 |
| DHCP_OPT_NISDOMAIN = 40 |
| DHCP_OPT_NISSERVERS = 41 |
| DHCP_OPT_NISTIMESERV = 42 |
| DHCP_OPT_VENDSPECIFIC = 43 |
| DHCP_OPT_NBNS = 44 |
| DHCP_OPT_NBDD = 45 |
| DHCP_OPT_NBTCPIP = 46 |
| DHCP_OPT_NBTCPSCOPE = 47 |
| DHCP_OPT_XFONT = 48 |
| DHCP_OPT_XDISPLAYMGR = 49 |
| DHCP_OPT_REQ_IP = 50 # I: IP address |
| DHCP_OPT_LEASE_SEC = 51 # I: lease seconds |
| DHCP_OPT_OPTIONOVERLOAD = 52 |
| DHCP_OPT_MSGTYPE = 53 # B: message type |
| DHCP_OPT_SERVER_ID = 54 # I: server IP address |
| DHCP_OPT_PARAM_REQ = 55 # s: list of option codes |
| DHCP_OPT_MESSAGE = 56 |
| DHCP_OPT_MAXMSGSIZE = 57 |
| DHCP_OPT_RENEWTIME = 58 |
| DHCP_OPT_REBINDTIME = 59 |
| DHCP_OPT_VENDOR_ID = 60 # s: vendor class id |
| DHCP_OPT_CLIENT_ID = 61 # Bs: idtype, id (idtype 0: FQDN, idtype 1: MAC) |
| DHCP_OPT_NISPLUSDOMAIN = 64 |
| DHCP_OPT_NISPLUSSERVERS = 65 |
| DHCP_OPT_MOBILEIPAGENT = 68 |
| DHCP_OPT_SMTPSERVER = 69 |
| DHCP_OPT_POP3SERVER = 70 |
| DHCP_OPT_NNTPSERVER = 71 |
| DHCP_OPT_WWWSERVER = 72 |
| DHCP_OPT_FINGERSERVER = 73 |
| DHCP_OPT_IRCSERVER = 74 |
| DHCP_OPT_STSERVER = 75 |
| DHCP_OPT_STDASERVER = 76 |
| |
| # DHCP message type values |
| DHCPDISCOVER = 1 |
| DHCPOFFER = 2 |
| DHCPREQUEST = 3 |
| DHCPDECLINE = 4 |
| DHCPACK = 5 |
| DHCPNAK = 6 |
| DHCPRELEASE = 7 |
| DHCPINFORM = 8 |
| |
| |
| class DHCP(dpkt.Packet): |
| """Dynamic Host Configuration Protocol. |
| |
| TODO: Longer class information.... |
| |
| Attributes: |
| __hdr__: Header fields of DHCP. |
| TODO. |
| """ |
| |
| __hdr__ = ( |
| ('op', 'B', DHCP_OP_REQUEST), |
| ('hrd', 'B', arp.ARP_HRD_ETH), # just like ARP.hrd |
| ('hln', 'B', 6), # and ARP.hln |
| ('hops', 'B', 0), |
| ('xid', 'I', 0xdeadbeef), |
| ('secs', 'H', 0), |
| ('flags', 'H', 0), |
| ('ciaddr', 'I', 0), |
| ('yiaddr', 'I', 0), |
| ('siaddr', 'I', 0), |
| ('giaddr', 'I', 0), |
| ('chaddr', '16s', 16 * b'\x00'), |
| ('sname', '64s', 64 * b'\x00'), |
| ('file', '128s', 128 * b'\x00'), |
| ('magic', 'I', DHCP_MAGIC), |
| ) |
| opts = ( |
| (DHCP_OPT_MSGTYPE, chr(DHCPDISCOVER)), |
| (DHCP_OPT_PARAM_REQ, ''.join(map(chr, (DHCP_OPT_REQ_IP, |
| DHCP_OPT_ROUTER, |
| DHCP_OPT_NETMASK, |
| DHCP_OPT_DNS_SVRS)))) |
| ) # list of (type, data) tuples |
| |
| def __len__(self): |
| return self.__hdr_len__ + \ |
| sum([2 + len(o[1]) for o in self.opts]) + 1 + len(self.data) |
| |
| def __bytes__(self): |
| return self.pack_hdr() + self.pack_opts() + bytes(self.data) |
| |
| def pack_opts(self): |
| """Return packed options string.""" |
| if not self.opts: |
| return b'' |
| l = [] |
| for t, data in self.opts: |
| l.append(struct.pack("BB%is"%len(data), t, len(data), data)) |
| l.append(b'\xff') |
| return b''.join(l) |
| |
| def unpack(self, buf): |
| dpkt.Packet.unpack(self, buf) |
| self.chaddr = self.chaddr[:self.hln] |
| buf = self.data |
| l = [] |
| while buf: |
| t = compat_ord(buf[0]) |
| if t == 0xff: |
| buf = buf[1:] |
| break |
| elif t == 0: |
| buf = buf[1:] |
| else: |
| n = compat_ord(buf[1]) |
| l.append((t, buf[2:2 + n])) |
| buf = buf[2 + n:] |
| self.opts = l |
| self.data = buf |
| |
| |
| def test_dhcp(): |
| s = b'\x01\x01\x06\x00\xadS\xc8c\xb8\x87\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02U\x82\xf3\xa6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\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\xfb\x01\x01=\x07\x01\x00\x02U\x82\xf3\xa62\x04\n\x00\x01e\x0c\tGuinevere<\x08MSFT 5.07\n\x01\x0f\x03\x06,./\x1f!+\xff\x00\x00\x00\x00\x00' |
| dhcp = DHCP(s) |
| assert (s == bytes(dhcp)) |
| assert isinstance(dhcp.chaddr, bytes) |
| assert isinstance(dhcp.sname, bytes) |
| assert isinstance(dhcp.file, bytes) |
| |
| # Test default construction |
| dhcp = DHCP() |
| assert isinstance(dhcp.chaddr, bytes) |
| assert isinstance(dhcp.sname, bytes) |
| assert isinstance(dhcp.file, bytes) |
| |
| if __name__ == '__main__': |
| test_dhcp() |
| print('Tests Successful...') |