root/branch/UMPA/umpa/protocols/_fields.py @ 4562

Revision 4562, 22.0 kB (checked in by getxsick, 4 years ago)

update pydoc about limitations of IPv6AddrField

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