2 # Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
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.
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.
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
19 from struct import unpack
20 from struct import pack
21 from dhcp_constants import *
23 # DhcpPacket : base class to encode/decode dhcp packets.
26 class DhcpBasicPacket:
28 self.packet_data = [0]*240
29 self.options_data = {}
30 self.packet_data[236:240] = MagicCookie
32 def IsDhcpPacket(self):
33 if self.packet_data[236:240] != MagicCookie : return False
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) :
46 def DeleteOption(self,name):
47 # if name is a standard dhcp field
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]
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)
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]]
69 elif self.options_data.has_key(name) :
70 return self.options_data[name]
75 def SetOption(self,name,value):
77 # Basic vlue checking :
78 # has value list a correct length
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
85 begin = DhcpFields[name][0]
86 end = DhcpFields[name][0]+DhcpFields[name][1]
87 self.packet_data[begin:end] = value
90 # if name is a dhcp option
91 elif DhcpOptions.has_key(name) :
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]}
102 specs = fields_specs[DhcpOptionsTypes[DhcpOptions[name]]]
104 if (specs[0]!=0 and specs==length) or (specs[1]<=length and length%specs[2]==0):
105 self.options_data[name] = value
110 print "Error, unknown option : ", name, value
115 def IsOption(self,name):
116 if self.options_data.has_key(name) : return True
117 elif DhcpFields.has_key(name) : return True
121 # Encode Packet and return it
122 def EncodePacket(self):
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]
131 packet = self.packet_data[:240] + options
132 packet.append(255) # add end option
133 pack_fmt = str(len(packet))+"c"
135 packet = map(chr,packet)
137 return pack(pack_fmt,*packet)
140 # Insert packet in the object
141 def DecodePacket(self,data,debug=False):
142 self.packet_data = []
143 self.options_data = {}
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)
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.
157 end_iterator = len(self.packet_data)
158 while ( self.packet_data[iterator:iterator+4] != MagicCookie and iterator < end_iterator) :
162 # parse extended options
163 if ( debug ) : print "Debug : ", self.packet_data[iterator:-1]
166 while iterator < end_iterator :
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
173 elif self.packet_data[iterator] == 255 :
174 self.packet_data = self.packet_data[:240] # base packet length without magic cookie
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
183 opt_first = iterator+1
184 iterator += self.packet_data[opt_first] + 2
186 # cut packet_data to remove options
188 self.packet_data = self.packet_data[:240] # base packet length with magic cookie