1# $Id: dhcp.py,v 1.1.1.1 2005/10/29 18:20:48 provos Exp $
2
3from dpkt import Packet
4import arp
5
6DHCP_OP_REQUEST = 1
7DHCP_OP_REPLY = 2
8
9DHCP_MAGIC = 0x63825363
10
11# DHCP option codes
12DHCP_OPT_NETMASK = 1	# I: subnet mask
13DHCP_OPT_ROUTER = 3	# s: list of router IPs
14DHCP_OPT_DNS_SVRS = 6	# s: list of DNS servers
15DHCP_OPT_HOSTNAME = 12	# s: client hostname
16DHCP_OPT_DOMAIN = 15	# s: domain name
17DHCP_OPT_REQ_IP = 50	# I: IP address
18DHCP_OPT_LEASE_SEC = 51	# I: lease seconds
19DHCP_OPT_MSGTYPE = 53	# B: message type
20DHCP_OPT_SERVER_ID = 54	# I: server IP address
21DHCP_OPT_PARAM_REQ = 55	# s: list of option codes
22DHCP_OPT_VENDOR_ID = 60	# s: vendor string
23DHCP_OPT_CLIENT_ID = 61	# Bs: idtype, id (idtype 0: FQDN, idtype 1: MAC)
24
25# DHCP message type values
26DHCPDISCOVER = 1
27DHCPOFFER = 2
28DHCPREQUEST = 3
29DHCPDECLINE = 4
30DHCPACK = 5
31DHCPNAK = 6
32DHCPRELEASE = 7
33DHCPINFORM = 8
34
35class DHCP(Packet):
36    """Dynamic Host Configuration Protocol."""
37    __hdr__ = (
38        ('op', 'B', DHCP_OP_REQUEST),
39        ('hrd', 'B', arp.ARP_HRD_ETH),  # just like ARP.hrd
40        ('hln', 'B', 6),		# and ARP.hln
41        ('hops', 'B', 0),
42        ('xid', 'I', 0xdeadbeefL),
43        ('secs', 'H', 0),
44        ('flags', 'H', 0),
45        ('ciaddr', 'I', 0),
46        ('yiaddr', 'I', 0),
47        ('siaddr', 'I', 0),
48        ('giaddr', 'I', 0),
49        ('chaddr', '16s', 16 * '\x00'),
50        ('sname', '64s', 64 * '\x00'),
51        ('file', '128s', 128 * '\x00'),
52        ('magic', 'I', DHCP_MAGIC),
53        )
54    opts = ()	# list of (type, data) tuples
55
56    def __len__(self):
57        return self.__hdr_len__ + \
58               sum([ 2 + len(o[1]) for o in self.opts ]) + len(self.data)
59
60    def __str__(self):
61        return self.pack_hdr() + self.pack_opts() + self.data
62
63    def pack_opts(self):
64        """Return packed options string."""
65        if not self.opts:
66            return ''
67        l = []
68        for t, data in self.opts:
69            l.append('%s%s%s' % (chr(t), chr(len(data)), data))
70        l.append('\xff')
71        return ''.join(l)
72
73    def unpack(self, buf):
74        Packet.unpack(self, buf)
75        self.chaddr = self.chaddr[:self.hln]
76        buf = self.data
77        l = []
78        while buf:
79            t = ord(buf[0])
80            if t == 0xff:
81                buf = buf[1:]
82                break
83            elif t == 0:
84                buf = buf[1:]
85            else:
86                n = ord(buf[1])
87                l.append((t, buf[2:2+n]))
88                buf = buf[2+n:]
89        self.opts = l
90        self.data = buf
91
92if __name__ == '__main__':
93    import unittest
94
95    class DHCPTestCast(unittest.TestCase):
96        def test_DHCP(self):
97            s = '\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'
98            dhcp = DHCP(s)
99            self.failUnless(s == str(dhcp))
100
101    unittest.main()
102
103