root/umpa/branches/link-layer-integration/umit/umpa/protocols/_fields.py @ 5746

Revision 5746, 24.0 kB (checked in by kosma, 3 years ago)

fix str to return strings, not print them

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"""
23Generic Field classes.
24
25Protocols' headers contain fields. Each field's objects should be an instance
26of a Field class or of a subclass thereof (especially some generic subclasses
27provided by this module).
28
29Use these fields' classes to create new implementation of any protocols.
30"""
31
32import types
33
34from umit.umpa.utils.exceptions import UMPAException, UMPAAttributeException
35
36class Field(object):
37    """
38    Superclass for any fields.
39
40    Protocols' headers contain there fields.
41
42    To implement new fields, create subclass of this class or any other
43    common classes included in this module.
44
45    IMPORTANT: You should overwrite this __doc__ to get hints in some frontends
46    like the one provided by Umit Project.
47    """
48
49    bits = 0
50    auto = False
51    def __init__(self, name, value=None, bits=None, auto=None):
52        """
53        Create a new Field().
54
55        @type name: C{str}
56        @param name: name of the field.
57
58        @type value: Optional
59        @param value: predefined value of the field.
60
61        @type bits: Optional C{int}
62        @param bits: length of the field.
63
64        @type auto: Optional C{bool}
65        @param auto: information for users if the field can be auto-filling
66        """
67
68        self.name = name
69        if auto is not None:
70            self.auto = auto
71        else:
72            self.auto = self.__class__.auto
73
74        if bits is not None:
75            self.bits = bits
76        if value is None:
77            self._value = None
78        # XXX hack for unitttests, normally Field is only super-class for others
79        elif self.__class__ is Field:
80            self._value = value
81        else:
82            self.set(value)
83
84    def __str__(self):
85        """
86        Print in human-readable tree-style a content of the field.
87
88        @return: the part of the whole tree which accords to the field.
89        """
90
91        if self.auto:
92            return "| +-[ %-25s ]\t%-15s : %s (auto - %s)" % (self.name,
93                        self._shortname, str(self._value), str(self.fillout()))
94        else:
95            return "| +-[ %-25s ]\t%-15s : %s" % (self.name,
96                                            self._shortname, str(self._value))
97
98    def __repr__(self):
99        """
100        Print name of the Field
101        """
102
103        return self.name
104
105    def get(self):
106        """
107        Return the current value of the field.
108
109        Don't generate the value if the is not saved any but auto-filling
110        is possible. In this case, just return None.
111
112        @return: the current value of the field.
113        """
114
115        return self._value
116
117    def set(self, value):
118        """
119        Set a value for the field.
120
121        The new value is validing before assigment.
122        'auto' parameter is unset.
123
124        @param value: new value for the field.
125        """
126
127        if self._is_valid(value):
128            self._value = value
129        else:
130            raise UMPAAttributeException(str(value) + ' is not allowed')
131
132        if self.auto:
133            self.auto = False
134
135    def clear(self):
136        """
137        Clear the current value of the field.
138        """
139
140        self._value = None
141
142    def set_doc(self, text):
143        """
144        Set the pydocs of the field.
145
146        It's important for new subclasses of the Field.
147        Some GUIs use this information in hints etc.
148
149        @type text: C{str}
150        @param text: new pydoc for the field.
151        """
152
153        self.__doc__ = text
154
155    def _is_valid(self, value):
156        """
157        Validate the new value.
158
159        This method is an abstract. You HAVE TO override it.
160
161        @param value: the new value
162
163        @rtype: C{bool}
164        @return: result of the validation.
165        """
166
167        raise NotImplementedError("this is abstract class")
168
169    def _raw_value(self):
170        """
171        Convert the value to the raw mode.
172
173        Raw value's type is a number. It has to be in big-endian order.
174        The bits of the result of this method are inserted into the raw number
175        of the whole protocol.
176
177        This method is an abstract. You HAVE TO override it.
178        You need to implement a conversion of the value here.
179        E.g. for IntField is just return the value. But for some strings-fields
180        you need to convert characters in the specific way.
181
182        @rtype: C{number}
183        @return: raw value of the field.
184        """
185
186        raise NotImplementedError("this is abstract class")
187
188    def _generate_value(self):
189        """
190        Generate value for undefined yet field.
191
192        This is auto-filling feature. If you implement this method, propably
193        you should set the auto attribute to True for the class. It means that
194        user doesn't need to set the value of the field.
195
196        @return: auto-generated value of the field.
197        """
198
199        raise UMPAException("value is not defined or _generate_value()" 
200                                                "method is not implemented.")
201
202    def fillout(self):
203        """
204        Fillout the field.
205
206        Generate the value if undefined and convert the result
207        to the big-endian representation.
208
209        @return: bits of the field for the (generated) value.
210        """
211
212        # we have to clear self._value if it was not defined
213        # because of later usage
214        if self._value is None:
215            self._value = self._generate_value()
216            raw = self._raw_value()
217            self.clear()
218        else:
219            raw = self._raw_value()
220       
221        return raw
222
223class IntField(Field):
224    """
225    Superclass for number-type fields.
226
227    This class implemented _raw_value() and _is_valid() methods.
228    You need to implement _generate_value() method if needed.
229
230    IMPORTANT: You should overwrite this __doc__ to get hints in some frontends
231    like the one provided by Umit Project.
232    """
233
234    def _raw_value(self):
235        """
236        Convert the value to the raw mode.
237
238        Raw value's type is a number. It has to be in big-endian order.
239        The bits of the result of this method are inserted into the raw number
240        of the whole protocol.
241       
242        For IntField there is nothing to convert. Just simple return the value.
243
244        @rtype: C{number}
245        @return: raw value of the field.
246        """
247
248        return self._value
249
250    def _is_valid(self, value):
251        """
252        Validate if the value is not bigger than expected.
253
254        @param value: the new value.
255
256        @rtype: C{bool}
257        @return: result of the validation.
258        """
259
260        if 2**self.bits > value:
261            return True
262        else:
263            return False
264
265class SpecialIntField(IntField):
266    """
267    This class is a specific one and has special meaning.
268   
269    It's a subclass of IntField.
270    Use this class if the field handles with other fields from the protocol
271    or other layers/protocols.
272
273    E.g. Internet Header Length (IHL) field from the IP protocol needs to know
274    some informations about others fields.
275
276    Use _tmp_value attribute then in pre/post raw methods in protocol
277    classes. Just assign to the _tmp_value needed information from other fields
278    and implement _generate_value() method in the related way.
279    Check umit.umpa.protocols.IP module for examples.
280    """
281
282    def __init__(self, *args, **kwargs):
283        """
284        Create a new SpecialIntField().
285
286        Call the super constructor and initiate temporary value.
287        """
288
289        super(SpecialIntField, self).__init__(*args, **kwargs)
290        self.__temp_value = 0
291
292    def get_tmpvalue(self):
293        """
294        Return temporary value.
295
296        @rtype: C{int}
297        @return: temporary value of the field.
298        """
299
300        return self.__temp_value
301
302    def set_tmpvalue(self, value):
303        """
304        Set the temporary value.
305
306        @type value: C{int}
307        @param value: temporary value for special cases
308        """
309
310        self.__temp_value = value
311
312    def clear_tmpvalue(self):
313        """
314        Clear the temporary value.
315        """
316
317        self.__temp_value = 0
318
319    _tmp_value = property(get_tmpvalue, set_tmpvalue, clear_tmpvalue, """
320    The temporary value -- attribute for special cases in pre/post raw methods.
321
322    Use _tmp_value attribute in pre/post raw methods in protocol
323    classes if you need handle with other fields.
324    Assign to the _tmp_value needed information from other fields
325    and implement _generate_value() method in the related way.
326    Check umit.umpa.protocols.IP module for examples.
327
328    @type: C{int}
329    """)
330
331class EnumField(IntField):
332    """
333    This is a specific version of IntField and handles with enumerable fields.
334
335    E.g. SMTP port is 25. To set/get value of port from TCP protocol,
336    use "STMP" instead of "25". Read documentation for get() and set() methods
337    for additional information.
338    """
339
340    enumerable = {}
341
342    def get(self, human=False):
343        """
344        Return the current value of the field.
345
346        @type human: Optional C{bool}
347        @param human: if True, return human-readable value instead of numeric.
348        (Default: False)
349        """
350
351        value = super(EnumField, self).get()
352        if human:
353            for k, val in self.enumerable.items():
354                if val == value:
355                    return k
356        return value
357
358    def set(self, value):
359        """
360        Set the new value of the field.
361
362        Try to use value as a key for a dictionary ("SMTP" e.g.) and set
363        the value returned by the dictionary.
364
365        If value doesn't recognise as a dictionary key, try classic way.
366
367        @type value: C{int} or C{str}
368        @param value: assign new value in both ways (numeric and human).
369        """
370       
371        # we try to use value as a "human" value
372        # if doesn't work, then as a normal one
373        try:
374            super(EnumField, self).set(self.enumerable[value])
375        except KeyError:
376            super(EnumField, self).set(value)
377
378class AddrField(Field):
379    """
380    Superclass for address-type fields.
381
382    Subclasses of this class are related to the different kinds of addresses
383    as IP addresses for example.
384
385    Handle with 2 types of data:
386     1. strings as "127.0.0.1" or "0:0:0:0:0:0:0:1"
387     2. tuples as (127,0,0,1) or (0,0,0,0,0,0,0,1)
388    """
389
390    separator = ""
391    base = 0
392    piece_size = 0
393    pieces_amount = 0
394    bits = 0
395
396    def set(self, value):
397        """
398        Set the new value of the field.
399       
400        @type value: C{str} or C{list} or C{tuple}
401        @param value: new value for the field.
402        """
403
404        # convert list to tuple
405        if isinstance(value, types.ListType):
406            value = tuple(value)
407
408        super(AddrField, self).set(value)
409
410    def _raw_value(self):
411        """
412        Convert the value to the raw mode.
413
414        Raw value's type is a number. It has to be in big-endian order.
415        The bits of the result of this method are inserted into the raw number
416        of the whole protocol.
417
418        @rtype: C{number}
419        @return: raw value of the field.
420        """
421
422        # convert the value to the list if it's str
423        if isinstance(self._value, types.StringType):
424            pieces = self._value.split(self.separator)
425        else:
426            pieces = self._value
427
428        # add every piece of the address to the raw value
429        # with bits-length of them keeping
430        raw = 0
431        for bit in pieces:
432            bit = str(bit)
433            raw += int(bit, self.base)
434            raw <<= self.piece_size
435        raw >>= self.piece_size
436
437        return raw
438
439    def _is_valid(self, value):
440        """
441        Validate the new value.
442
443        Only str or tuple type of the value is allowed.
444
445        @param value: the new value.
446
447        @rtype: C{bool}
448        @return: result of the validation.
449        """
450
451        if isinstance(value, types.StringType):
452            pieces = value.split(self.separator)
453        elif isinstance(value, types.TupleType):
454            pieces = value
455        else:
456            return False
457
458        if len(pieces) != self.pieces_amount:
459            return False
460
461        for i in pieces:
462            i = str(i)
463            try:
464                i_base = int(i, self.base)
465            except ValueError:
466                return False
467            if i_base >= 2**self.piece_size or i_base < 0:
468                return False
469
470        return True
471
472class IPAddrField(AddrField):
473    """
474    Main class for IP-style adresses.
475    """
476    pass
477
478class IPv4AddrField(IPAddrField):
479    """
480    Address in IPv4 style.
481
482    Handle with 2 types of data:
483     1. strings as "127.0.0.1"
484     2. tuples as (127,0,0,1)
485    """
486
487    separator = "."
488    piece_size = 8
489    pieces_amount = 4
490    base = 10
491    bits = 32
492
493#class IPv6AddrField(IPAddrField):
494#    """
495#    Address in IPv6 style.
496#
497#    Handle with 2 types of data:
498#     1. strings as "0:0:0:0:0:0:0:1"
499#     2. tuples as (0,0,0,0,0,0,0,1)
500#
501#    @note: This field is really limited and you can't use address
502#    like 2001:db8::1428:57ab. All groups have to be pass.
503#    This issue should be fixed soon.
504#    """
505#
506#    separator = ":"
507#    piece_size = 16
508#    pieces_amount = 8
509#    base = 16
510#    bits = 128
511
512class MACAddrField(AddrField):
513    """
514    Hardware address in MAC style.
515
516    Handle with 2 types of data:
517     1. strings as "aa:bb:cc:dd:ee:ff"
518     2. tuples as ('aa','bb','cc',11,22,33)
519    """
520
521    separator = ":"
522    piece_size = 8
523    pieces_amount = 6
524    base = 16
525    bits = 48
526
527class PaddingField(SpecialIntField):
528    """
529    This class is for padding cases.
530
531    PaddingField is used to ensure that the header ends on a 32 bit boundary.
532    This is common fields for many protocols.
533    """
534
535    bits = 0
536    auto = True
537
538    def __init__(self, name, word=32, *args, **kwargs):
539        """
540        Create a new PaddingField().
541
542        @type word: C{int}
543        @param word: length of field which need padding (default: 32)
544       
545        Call the super constructor and initiate extra attributes.
546
547        Please note that padding is always done by using zeros (0).
548        """
549
550        self._word = word
551        super(PaddingField, self).__init__(name, 0, *args, **kwargs)
552        self.auto = True    # XXX: super-class overrides it
553                            # (should be fix with #314)
554
555    def fillout(self):
556        """
557        Fillout the field.
558
559        If undefined value, set the correct length of the field and generate
560        a value.
561
562        @return: call _raw_value() method for conversion.
563        """
564
565        if not self.get():
566            self.bits = self._generate_value()
567        else:
568            self.bits = self.get()
569        return self._raw_value()
570   
571    def _is_valid(self, value):
572        """
573        Validate if the value is not bigger than expected.
574
575        @param value: the new value.
576
577        @rtype: C{bool}
578        @return: result of the validation.
579        """
580
581        if isinstance(value, types.IntType) and 0 <= value <= self._word:
582            return True
583        return False
584
585    def _raw_value(self):
586        """
587        Don't convert the value. Return 0.
588
589        Padding B{always} contains bits with 0 assigned.
590
591        @rtype: C{int}
592        @return: 0
593        """
594
595        return 0
596
597    def _generate_value(self):
598        """
599        Generate value for undefined field yet.
600       
601        @return: auto-generated value of the field.
602        """
603
604        return (self._word - (self._tmp_value % self._word)) % self._word
605
606class Flags(Field):
607    """
608    This is special case of field - Flags.
609
610    Most of protocols have a special field with bit-flags.
611    E.g. TCP use them for ACK,SYN and others flags.
612    """
613
614    def __init__(self, name, names, **preset):
615        """
616        Create a new Flags()
617
618        List names need to be in correct order. List contains string names
619        of the bit-flags.
620
621        @type name: C{str}
622        @param name: name of the field.
623       
624        @type names: C{list}
625        @param names: list of bit-flags (C{str} type) B{in correct order}.
626
627        @type preset: C{bool}
628        @param preset: predefined values of bit-flags (defailt: I{0})
629        """
630        super(Flags, self).__init__(name, bits=len(names))
631
632        self._ordered_fields = names
633
634        # initialize of self._value...
635        # call clear() to not duplicate the code
636        self.clear()
637
638        # if **preset exists then we update values
639        for name in preset:
640            if preset[name] is True:
641                self.set(name)
642            else:
643                self.set(False, name)
644
645    def __str__(self):
646        """
647        Print in human-readable tree-style a content of the field.
648
649        Call print statement for bit-flags.
650
651        @return: the part of the whole tree which accords to the field.
652        """
653
654        info = []
655
656        info.append("| +-[ %-25s ]\t%s" % (self.name, self._shortname))
657        info.append("| | \\")
658        info.extend([ str(self._value[bit]) for bit in self._ordered_fields ])
659        info.append("| | /")
660        info.append("| \\-[ %-25s ]\tcontains %d bit flags" %
661                                       (self.name, len(self._ordered_fields)))
662
663        return "\n".join(info)
664
665    def get(self, *names):
666        """
667        Return a number which is n-bits value of bits
668        or a list of passed bits values.
669
670        If no names passed return a numeric value of all bits.
671        To return a list of all bits, pass [] as a first argument.
672
673        @type names: C{str}
674        @param names: names of bit-flags.
675
676        @return: a numeric-value or list of passed bits values.
677        """
678
679        try:
680            result = [ self._value[val].get() for val in names ]
681        except KeyError, msg:
682            raise UMPAAttributeException(msg)
683        except TypeError:
684            if len(names[0]) == 0:
685                result = [ self._value[bit].get() for bit in
686                                                        self._ordered_fields ]
687            else:
688                raise
689
690        if not names:
691            result = 0
692            for bit in self._ordered_fields:
693                result += self._value[bit].get()
694                result <<= 1
695            result >>= 1
696
697        return result
698
699
700    def set(self, value, *args, **kwargs):
701        """
702        Set value of bits.
703
704        This function is pretty complex and handles with many cases.
705       
706        @note: *args overrides value, and **kwargs overrids each.
707       
708        @param value: for C{int}: value of all bits,
709        for C{list}: True for bits from the list,
710        for C{dict}: bitname=value,
711        for C{str}: merging with *args
712        for C{False} or C{True}: bits from *args are set to True/False
713
714        @param args: True for bits from the list
715        @param kwargs: bitname=value
716        """
717
718        # Protocol.__setattr__ call Field.set()
719        # for Flags we have to handle different types
720        # 1) numeric-value 2) lists 3) dicts
721
722        if value in (None, False, True):
723            pass
724        elif isinstance(value, types.IntType):
725            # check if a value exceeds flags's length
726            if value > 2**self.bits - 1:
727                raise UMPAAttributeException('%d is not allowed. %d is '
728                            'a maximum value' % (value, 2**self.bits-1))
729
730            mask = 1
731            for bit in reversed(self._ordered_fields):
732                # cast to list because str is iterable (avoid iter over chars)
733                self._set_bit([bit], value & mask)
734                mask <<= 1
735        elif isinstance(value, (types.ListType, types.TupleType)):
736            value = list(value)
737            value.extend(args)  # to keep an order
738            args = value
739        elif isinstance(value, types.DictType):
740            value.update(kwargs) # to keep an order
741            kwargs = value
742        elif isinstance(value, (types.StringType, types.UnicodeType)):
743            args = list(args)
744            args.insert(0, value)
745        else:
746            raise UMPAAttributeException(value + ' is wrong type.')
747
748        # update bits for *args and **kwargs
749        if value is False:
750            self._set_bit(args, False)
751        else:
752            self._set_bit(args, True)
753        for bit in kwargs:
754            # cast to list because str is iterable (avoid iter over chars)
755            self._set_bit([bit], kwargs[bit])
756
757    def clear(self):
758        """
759        Clear the values of bit-flags.
760
761        Re-create a storing dictionary.
762        """
763
764        # we overwrite an attribute self._value
765        # because we need a list instead of simple var here
766        self._value = {}
767        for flag in self._ordered_fields:
768            self._value[flag] = BitField(flag, False)
769
770    def fillout(self):
771        """
772        Fillout the field.
773
774        Call fillout() methods for every bit-flags.
775        Return concatenated result.
776
777        @return: bits of the bit-flags.
778        """
779
780        raw = 0
781        for bitname in self._ordered_fields:
782            raw += self._value[bitname].fillout()
783            raw <<= 1
784        raw >>= 1
785        return raw
786
787    def _is_valid(self, name):
788        """
789        Validate if the value is not bigger than expected.
790
791        @param name: the name of the bit-flag.
792
793        @rtype: C{bool}
794        @return: result of the validation.
795        """
796
797        return name in self._value
798
799    def _set_bit(self, names, value):
800        """
801        Set the value for a bit.
802
803        Set True or False for the bit-flag.
804        Set the same value for every bit-flags from the list.
805
806        @type names: C{list}
807        @param names: list of names to set the value
808
809        @type value: C{bool}
810        @param value: the logical value.
811        """
812
813        for flag_name in names:
814            if self._is_valid(flag_name):
815                self._value[flag_name].set(value)
816            else:
817                raise UMPAAttributeException(flag_name + ' is not allowed')
818
819class BitField(Field):
820    """
821    This class is used for bit-flags of Flags field.
822
823    Flags is a field which contains several independent bits.
824    Each of the bits is an instance of BitField.
825    """
826
827    bits = 1
828    auto = False
829
830    def __str__(self):
831        """
832        Print in human-readable tree-style a content of the field.
833
834        @return: the part of the whole tree which accords to the field.
835        """
836
837        return "| |  -{ %-23s }\t%d" % (self.name, int(bool(self._value)))
838
839    def get(self):
840        """
841        Return the current value of the field.
842
843        @return: the current value of the field.
844        """
845
846        if self._value is None:
847            return self._value
848        return bool(self._value)
849
850    def fillout(self):
851        """
852        Fillout the field.
853
854        Generate the value if undefined and convert the result
855        to the big-endian representation.
856
857        @return: bits of the field for the (generated) value.
858        """
859
860        if self._value is None:
861            self._value = self._generate_value()
862            raw = self._raw_value()
863            self.clear()
864        else:
865            raw = self._raw_value()
866
867        return raw
868
869    def _is_valid(self, value):
870        """
871        Validate the new value.
872
873        @param value: the new value
874
875        @rtype: C{bool}
876        @return: C{True}, becuase this is a bool type so every value is correct.
877        """
878
879        # always True because it's bool type
880        return True
881
882    def _raw_value(self):
883        """
884        Convert the value to the raw mode.
885
886        In this case simple return 0 or 1.
887
888        @rtype: C{number}
889        @return: raw value of the field.
890        """
891
892        return int(bool(self.get()))
Note: See TracBrowser for help on using the browser.