root/branch/UMPA/umit/umpa/protocols/_protocols.py @ 5170

Revision 5170, 13.1 kB (checked in by getxsick, 4 years ago)

restucture namespacing because of silly umit policy.
now you can see 'umit.' all the time and wondering why you have to type
these 5 more characters all the time.
but don't worry, in modern life our editors can do auto-completion
so we are saved!

me, bartosz skowron, don't take any responsibility on futher problems,
heart attacks, annoying, depression of end-users, other developers.

this commit is done because of Umit's policy. i don't agree with this aspect
and please do not associate myself with this idea nor commit.

-getxsick

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (C) 2008-2009 Adriano Monteiro Marques.
5#
6# Author: Bartosz SKOWRON <getxsick at gmail dot com>
7#
8# This library is free software; you can redistribute it and/or modify
9# it under the terms of the GNU Lesser General Public License as published
10# by the Free Software Foundation; either version 2.1 of the License, or
11# (at your option) any later version.
12#
13# This library is distributed in the hope that it will be useful, but
14# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16# License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public License
19# along with this library; if not, write to the Free Software Foundation,
20# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
22"""
23Protocol - super-class for protocols implemenations.
24
25Every protocols have to be subclassed from the Protocol.
26Some methods have to be overridden. Read Protocol's docstrings for more
27information.
28"""
29
30from umit.umpa.protocols._consts import BYTE
31from umit.umpa.protocols._fields import Field, Flags
32from umit.umpa.utils.exceptions import UMPAException, UMPAAttributeException
33from umit.umpa.utils.tools import dict_from_sequence as _dict_from_sequence
34
35class Protocol(object):
36    """
37    Superclass for every protocol's implementations.
38   
39    You have to override following methods:
40     - _pre_raw()
41     - _post_raw()
42    They are used to make some tasks especially with SuperIntField objects.
43    Check IP.py, TCP.py for examples.
44
45    Also, override this __doc__ to get hints in some frontends
46    like the one provided by Umit Project.
47    """
48
49    _ordered_fields = ()
50    layer = None
51    protocol_id = None
52    name = None
53
54    def __init__(self, fields_list, **preset):
55        """
56        Create a new Protocol().
57
58        @type fields_list: C{list}
59        @param fields_list: list of fields B{in correct order}.
60
61        @param preset: predefined values for fields.
62        """
63
64        # pack objects of header's fields to the dict
65        fields = dict(zip(self._ordered_fields, fields_list))
66
67        # because of overwritten __setattr__ we need to assign with __dict__
68        self.__dict__['_fields'] = fields
69        self.__dict__['payload'] = None
70        self.__dict__['__raw_value'] = None
71
72        # setting up passed fields
73        for field in preset:
74            setattr(self, field, preset[field])
75
76        # short-fieldname update
77        for field in fields:
78            self.get_field(field)._shortname = field
79
80    def __getattr__(self, attr):
81        """
82        Return the value of the field.
83       
84        @type attr: C{str}
85        @param attr: name of the field.
86
87        @return: value of the field.
88        """
89       
90        return self.get_field(attr).get()
91
92    def __setattr__(self, attr, value):
93        """
94        Set value of the field.
95
96        @type attr: C{str}
97        @param attr: name of the field.
98
99        @param value: the new value.
100        """
101
102        self.get_field(attr).set(value)
103
104    def __str__(self):
105        """
106        Print in human-readable tree-style a content of the protocol.
107
108        Call print statement for fields.
109
110        @return: the part of the whole tree which accords to the protocol.
111        """
112
113        print "+-< %-27s >" % self.name
114        print "| \\"
115        for field in self.get_fields():
116            print field
117        print "\\-< %-27s >\tcontains %d fields" % (self.name,
118                                                            len(self._fields))
119        return super(Protocol, self).__str__()
120
121    def _is_valid(self, field):
122        """
123        Validate if the filed is allowed.
124
125        @param field: requested field.
126
127        @rtype: C{bool}
128        @return: result of the validation.
129        """
130
131        return field in self._fields
132
133    @classmethod
134    def get_fields_keys(cls):
135        """
136        Yield the ordered sequence of the fields' names.
137
138        This is a generator for ordered names (keys) of header's fields.
139        """
140
141        for field in cls._ordered_fields:
142            yield field
143
144    def get_fields(self):
145        """
146        Yield the ordered sequence of the fields.
147
148        This is a generator for ordered fields.
149        """
150       
151        for field in self._ordered_fields:
152            yield self.get_field(field)
153   
154    def get_field(self, keyname):
155        """
156        Return the field.
157
158        @type keyname: C{str}
159        @param keyname: name of the field
160
161        @rtype: C{Field}
162        @return: requested field.
163        """
164
165        if self._is_valid(keyname):
166            return self._fields[keyname]
167        else:
168            raise UMPAAttributeException(keyname + ' is not allowed')
169
170    def set_fields(self, *args, **kwargs):
171        """
172        Set fields of the protocol.
173
174        There are 2 ways to do that with using tuple or dict-style.
175        For tuple, use sequence as: field_name1, value1, field_name2, value2.
176        For dict, use dict ;) field_name1=value1, field_name2=value2.
177
178        @param args: sequence of field_name and value.
179
180        @param kwargs: field_name=value.
181        """
182       
183        if len(args) % 2:
184            raise UMPAAttributeException('wrong amount of passed arguments')
185        # converting args list to the dict and update our kwargs
186        kwargs.update(_dict_from_sequence(args))
187
188        # first check if all keys are valid to avoid partial-changing
189        for key in kwargs:
190            if not self._is_valid(key):
191                raise UMPAAttributeException(key + ' is not allowed; '
192                                            'nothing has changed')
193
194        for key in kwargs:
195            setattr(self, key, kwargs[key])
196
197    def get_flags(self, name, *args):
198        """
199        Return flags of the field.
200
201        @type name: C{str}
202        @param name: name of the field.
203
204        @param args: names of flags.
205
206        @rtype: C{list}
207        @return: list of flags.
208        """
209
210        flag_field = self.get_field(name)
211        if not isinstance(flag_field, Flags):
212            raise UMPAAttributeException("No Flags instance for " + name)
213
214        if not args:
215            return flag_field.get([])
216        return flag_field.get(*args)
217
218    def set_flags(self, name, *args, **kwargs):
219        """
220        Set flags for flags-field.
221
222        There are 2 ways to do that with using tuple or dict-style.
223        For tuple, use sequence as: flag_name1, value1, flag_name2, value2.
224        For dict, use dict ;) flag_name1=value1, flag_name2=value2.
225
226        @type name: C{str}
227        @param name: name of the field.
228
229        @param args: sequence of field_name and value.
230
231        @param kwargs: field_name=value.
232        """
233
234        if len(args) % 2:
235            raise UMPAAttributeException('wrong amount of passed arguments')
236        # converting args list to the dict and update our kwargs
237        kwargs.update(_dict_from_sequence(args))
238
239        flag_field = self.get_field(name)
240        if isinstance(flag_field, Flags):
241            for flag_name in kwargs:
242                if kwargs[flag_name]:
243                    flag_field.set(flag_name)
244                else:
245                    flag_field.set(False, flag_name)
246        else:
247            raise UMPAAttributeException("No Flags instance for " + name)
248
249    def get_offset(self, field):
250        """
251        Return the offset for the field.
252
253        This offset is for the current protocol, not the packet.
254
255        @type field: C{str} or C{Field}
256        @param field: name of the field
257
258        @rtype: C{int}
259        @return: offset of the field in bits.
260        """
261
262        # checking if argument is a key or instance
263        if isinstance(field, str):
264            field_list = self._ordered_fields
265        elif isinstance(field, Field):
266            field_list = [ f for f in self.get_fields() ]
267        else:
268            raise UMPAException(str(type(field)) + ' unsupported')
269   
270        if field not in field_list:
271            raise UMPAAttributeException(repr(field) + ' is not allowed')
272
273        offset = 0
274        for i, val in enumerate(field_list):
275            if field == val:
276                break
277            offset += self.get_field(self._ordered_fields[i]).bits
278        return offset
279
280    def _pre_raw(self, raw_value, bit, protocol_container, protocol_bits):
281        """
282        Handle with fields before calling fillout() for them.
283
284        Some fields (especially SpecialIntField) need to handle with other
285        fields. Do it here. If they need to handle with other fields B{after}
286        the fillout() (e.g. to calculate a checksum), use _post_raw() instead.
287
288        I{Currently} means that if we're generating raw values for
289        the packet/protocol it's done by some loops. So, currently it's
290        the value in the current stage of that loops.
291
292        @type raw_value: C{int}
293        @param raw_value: currently raw value for the packet.
294
295        @type bit: C{int}
296        @param bit: currently length of the protocol.
297
298        @type protocol_container: C{tuple}
299        @param protocol_container: tuple of protocols included in the packet.
300
301        @type protocol_bits: C{int}
302        @param protocol_bits: currently length of the packet.
303
304        @return: C{raw_value, bit}
305        """
306       
307        raise NotImplementedError("this is abstract class")
308
309    def _raw(self, raw_value, bit, protocol_container, protocol_bits):
310        """
311        Call pre/post handling with other fields and merge results of fillout.
312
313        Call fillout() method for every fields from the protocol.
314        Handle with fields by pre/port methods.
315
316        @type raw_value: C{int}
317        @param raw_value: currently raw value for the packet.
318
319        @type bit: C{int}
320        @param bit: currently length of the protocol.
321
322        @type protocol_container: C{tuple}
323        @param protocol_container: tuple of protocols included in the packet.
324
325        @type protocol_bits: C{int}
326        @param protocol_bits: currently length of the packet.
327
328        @return: C{raw_value, bit}
329        """
330
331        # because of some protocols implementation, there are some tasks before
332        # we call fillout() for fields
333        raw_value, bit = self._pre_raw(raw_value, bit, protocol_container,
334                                                                protocol_bits)
335
336        # so we make a big number with bits of every fields of the protocol
337        for field in reversed(self._ordered_fields):
338            field_bits = self.get_field(field).fillout()
339            raw_value |= field_bits << bit
340            bit += self.get_field(field).bits
341
342        # because of some protocols implementation, there are some tasks after
343        # we call fillout() for fields
344        raw_value, bit = self._post_raw(raw_value, bit, protocol_container,
345                                                                protocol_bits)
346
347        return raw_value, bit
348
349    def _post_raw(self, raw_value, bit, protocol_container, protocol_bits):
350        """
351        Handle with fields after calling fillout() for them.
352
353        Some fields (especially SpecialIntField) need to handle with other
354        fields. First, think to do it in _pre_raw() method. But if they need
355        to handle with other fields B{after} the fillout() (e.g. to calculate
356        a checksum), do it here.
357
358        I{Currently} means that if we're generating raw values for
359        the packet/protocol it's done by some loops. So, currently it's
360        the value in the current stage of that loops.
361
362        @type raw_value: C{int}
363        @param raw_value: currently raw value for the packet.
364
365        @type bit: C{int}
366        @param bit: currently length of the protocol.
367
368        @type protocol_container: C{tuple}
369        @param protocol_container: tuple of protocols included in the packet.
370
371        @type protocol_bits: C{int}
372        @param protocol_bits: currently length of the packet.
373
374        @return: C{raw_value, bit}
375        """
376       
377        raise NotImplementedError("this is abstract class")
378
379    def get_raw(self, protocol_container, protocol_bits):
380        """
381        Return raw bits of the protocol.
382
383        @type protocol_container: C{tuple}
384        @param protocol_container: tuple of protocols included in the packet.
385
386        @type protocol_bits: C{int}
387        @param protocol_bits: currently length of the packet.
388
389        @return: C{raw_value, bit}
390        """
391
392        raw_value = 0
393        bit = 0
394        # The deal: we join all value's fields into one big number
395        # (with taking care about amount of bits).
396        # then we devide the number on byte-chunks
397        # and pack it by struct.pack() function
398        raw_value, bit = self._raw(raw_value, bit, protocol_container,
399                                                                protocol_bits)
400
401        # protocol should return byte-compatible length
402        if bit % BYTE != 0:
403            raise UMPAException('odd number of bits in ' + str(self.name))
404
405        self.__dict__['__raw_value'] = raw_value
406        return raw_value, bit
407
408    def load_raw(self, buffer):
409        """
410        Load raw and update a protocol's fields.
411
412        @return: raw payload
413        """
414
415        raise UMPAException("protocol doesn't support decoding "
416                            "or an abstract class.")
Note: See TracBrowser for help on using the browser.