Fix the DHCP server (fixes #44)
[invirt/packages/python-pydhcplib.git] / pydhcplib / dhcp_basic_packet.py
1 # Anemon Dhcp
2 # Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
17
18 import operator
19 from struct import unpack
20 from struct import pack
21 from dhcp_constants import *
22
23 # DhcpPacket : base class to encode/decode dhcp packets.
24
25
26 class DhcpBasicPacket:
27     def __init__(self):
28         self.packet_data = [0]*240
29         self.options_data = {}
30         self.packet_data[236:240] = MagicCookie
31
32     def IsDhcpPacket(self):
33         if self.packet_data[236:240] != MagicCookie : return False
34         return True
35
36     # Check if variable is a list with int between 0 and 255
37     def CheckType(self,variable):
38         if type(variable) == list :
39             for each in variable :
40                 if (type(each) != int)  or (each < 0) or (each > 255) :
41                     return False
42             return True
43         else : return False
44         
45
46     def DeleteOption(self,name):
47         # if name is a standard dhcp field
48         # Set field to 0
49         if DhcpFields.has_key(name) :
50             begin = DhcpFields[name][0]
51             end = DhcpFields[name][0]+DhcpFields[name][1]
52             self.packet_data[begin:end] = [0]*DhcpFields[name][1]
53             return True
54
55         # if name is a dhcp option
56         # delete option from self.option_data
57         elif self.options_data.has_key(name) :
58             # forget how to remove a key... try delete
59             self.options_data.__delitem__(name)
60             return True
61
62         return False
63
64     def GetOption(self,name):
65         if DhcpFields.has_key(name) :
66             option_info = DhcpFields[name]
67             return self.packet_data[option_info[0]:option_info[0]+option_info[1]]
68
69         elif self.options_data.has_key(name) :
70             return self.options_data[name]
71
72         return []
73         
74
75     def SetOption(self,name,value):
76
77         # Basic vlue checking :
78         # has value list a correct length
79         
80         # if name is a standard dhcp field
81         if DhcpFields.has_key(name) :
82             if len(value) != DhcpFields[name][1] :
83                 print "Error, bad option length (a): ", name
84                 return False
85             begin = DhcpFields[name][0]
86             end = DhcpFields[name][0]+DhcpFields[name][1]
87             self.packet_data[begin:end] = value
88             return True
89
90         # if name is a dhcp option
91         elif DhcpOptions.has_key(name) :
92
93             # fields_specs : {'option_code',fixed_length,minimum_length,multiple}
94             # if fixed_length == 0 : minimum_length and multiple apply
95             # else : forget minimum_length and multiple 
96             # multiple : length MUST be a multiple of 'multiple'
97             fields_specs = { "ipv4":[4,0,1], "ipv4+":[0,4,4],
98                              "string":[0,0,1], "bool":[1,0,1],
99                              "char":[1,0,1], "16-bits":[2,0,1],
100                              "32-bits":[4,0,1], "identifier":[0,2,1]}
101             
102             specs = fields_specs[DhcpOptionsTypes[DhcpOptions[name]]]
103             length = len(value)
104             if (specs[0]!=0 and specs==length) or (specs[1]<=length and length%specs[2]==0):
105                 self.options_data[name] = value
106                 return True
107             else :
108                 return False
109
110         print "Error, unknown option : ", name, value
111         return False
112
113
114
115     def IsOption(self,name):
116         if self.options_data.has_key(name) : return True
117         elif DhcpFields.has_key(name) : return True
118         else : return False
119
120
121     # Encode Packet and return it
122     def EncodePacket(self):
123         options = []
124         
125         
126         for each in self.options_data.keys() :
127             options.append(DhcpOptions[each])
128             options.append(len(self.options_data[each]))
129             options += self.options_data[each]
130
131         packet = self.packet_data[:240] + options
132         packet.append(255) # add end option
133         pack_fmt = str(len(packet))+"c"
134
135         packet = map(chr,packet)
136         
137         return pack(pack_fmt,*packet)
138
139
140     # Insert packet in the object
141     def DecodePacket(self,data,debug=False):
142         self.packet_data = []
143         self.options_data = {}
144
145         if (not data) : return False
146         # we transform all data to int list
147         unpack_fmt = str(len(data)) + "c"
148         for i in unpack(unpack_fmt,data):
149             self.packet_data.append(ord(i))
150         if ( debug ) : print "Packet length : ",len(self.packet_data)
151
152
153         # Some servers or clients don't place magic cookie immediately
154         # after headers and begin options fields only after magic.
155         # These 4 lines search magic cookie and begin iterator after.
156         iterator = 236
157         end_iterator = len(self.packet_data)
158         while ( self.packet_data[iterator:iterator+4] != MagicCookie and iterator < end_iterator) :
159             iterator += 1
160         iterator += 4
161         
162         # parse extended options
163         if ( debug ) : print "Debug : ", self.packet_data[iterator:-1]
164
165
166         while iterator < end_iterator :
167             if ( debug ) :
168                 print "Debug Option : ", iterator , self.packet_data[iterator]," : ",DhcpOptionsList[self.packet_data[iterator]]
169             if self.packet_data[iterator] == 0 : # pad option
170                 opt_first = iterator+1
171                 iterator += 1
172
173             elif self.packet_data[iterator] == 255 :
174                 self.packet_data = self.packet_data[:240] # base packet length without magic cookie
175                 return
176                 
177             elif DhcpOptionsTypes.has_key(self.packet_data[iterator]) and self.packet_data[iterator]!= 255:
178                 opt_len = self.packet_data[iterator+1]
179                 opt_first = iterator+1
180                 self.options_data[DhcpOptionsList[self.packet_data[iterator]]] = self.packet_data[opt_first+1:opt_len+opt_first+1]
181                 iterator += self.packet_data[opt_first] + 2
182             else :
183                 opt_first = iterator+1
184                 iterator += self.packet_data[opt_first] + 2
185
186         # cut packet_data to remove options
187         
188         self.packet_data = self.packet_data[:240] # base packet length with magic cookie