A field to represent single bit fields.
API reference: BitField
A packet might be formed by multiple fields that can be single bit fields, numeric fields, etc. Sometimes, byte-aligned fields are also formed by bit fields internally. The purpose of BitField is to provide these single bit fields.
For example, the first byte of the IP header is formed by two nibbles:
| version | hlen |
|---|---|
| 4 bits | 4 bits |
The first nibble, version, can be constructed by the following piece of code:
>>> bf = BitField("version", 4, 15)
>>> print bf
(version = 0x0F)
That is, a 4 bits field with a default, optional, value 15.
A bit field for representing a boolean type.
API reference: Boolean
An abstract class for numeric values (integer or real).
API reference: Value
This is the base class for numeric fields. Internally, it uses Python’s struct module to define the numeric value size and the byte order (little-endian or big-endian).
The following code creates an 32-bit unsigned integer with a little-endian byte ordering.
>>> v32 = Value("value", "<I", 67436735)
>>> print v32
(value = 67436735)
Fortunately, BitPacket already defines most of numeric values that are commonly used.
This module provides classes to define signed and unsigned integers bit fields, from 8-bit to 64-bit.
Multiple signed and unsigned integer classes are available. It is, for example, very easy to create a new 16-bit signed integer bit field:
>>> value = Int16("int16", -1345)
>>> print value
(int16 = -1345)
or a 16-bit unsigned one:
>>> value = UInt16("uint16", 0x8000)
>>> print value
(uint16 = 32768)
Default helper classes use network byte order (big-endian):
| Size | Unsigned | Signed |
|---|---|---|
| 8 | UInt8 | Int8 |
| 16 | UInt16 | Int16 |
| 32 | UInt32 | Int32 |
| 64 | UInt64 | Int64 |
Little-endian helper classes:
| Size | Unsigned | Signed |
|---|---|---|
| 8 | UInt8LE | Int8LE |
| 16 | UInt16LE | Int16LE |
| 32 | UInt32LE | Int32LE |
| 64 | UInt64LE | Int64LE |
Big-endian helper classes:
| Size | Unsigned | Signed |
|---|---|---|
| 8 | UInt8BE | Int8BE |
| 16 | UInt16BE | Int16BE |
| 32 | UInt32BE | Int32BE |
| 64 | UInt64BE | Int64BE |
This module provides classes to define float (32-bit) and double (64-bit) fields.
A float value can be easily created with the Float class:
>>> value = Float("f", 1.967834)
>>> print value
(f = 1.96783399582)
Some times, it is also useful to see the hexadecimal value that forms this float number.
>>> print value.str_hex_value()
0x3FFBE1FC
The same might be applied for doubles:
>>> value = Double("f", 0.0087552)
>>> print value
(f = 0.0087552)
Fields to store a string of characters.
API reference: String
A String field lets you store a string of characters of any size. The length of the string needs to be specified at creation time.
The simplest case is a string with a fixed length. In the next example we create a string field with sixteen characters:
>>> data = String("data", 16)
>>> data.set_value("this is a string")
>>> print data
(data = 0x74686973206973206120737472696E67)
As usual, we can easily get back the original string:
>>> "".join(data.value())
'this is a string'
Note that, above, “print data” returns a human-readable string with hexadecimal values and “data.value()” returns the actual string.
For convenience, there is a Text field that inherits from String and always returns the actual string.
>>> text = Text("text", 16)
>>> text.set_value("this is a string")
>>> print text
(text = this is a string)
So, Text is supposedly to be used with only text while String is to be used with any character.
Instead of a fixed length, we can specify a length function that will tell us what length the string should have. In the following structure we create a Structure with a numeric “length” field and a string of unknown size.
| length | string |
|---|---|
| 1 byte | length |
BitPacket already provides a helper the Data field which contains a length field and a string.
>>> packet = Structure("string")
>>> l = UInt8("length")
>>> s = String("data", lambda root: root["length"])
>>> packet.append(l)
>>> packet.append(s)
If we print the initial contents of the structure we can see that the length is 0 and that we still have an empty string.
>>> print packet
(string =
(length = 0)
(data = ))
We can try to assign a value to our string directly and see what happens:
>>> try:
... s.set_value("this is a string")
... except ValueError as err:
... print "Error: %s" % err
Error: Data length must be 0 (16 given)
A ValueError exception is raised indicating that string length should be 0. This is because the “length” field has not been assigned a value yet.
Finally, we can provide to the structure all the necessary information, for example, in an array:
>>> data = array.array("B", [0x10, 0x74, 0x68, 0x69, 0x73, 0x20,
... 0x69, 0x73, 0x20, 0x61, 0x20, 0x73,
... 0x74, 0x72, 0x69, 0x6E, 0x67])
>>> packet.set_array(data)
>>> print packet
(string =
(length = 16)
(data = 0x74686973206973206120737472696E67))