Construct (Python library)
- 5 min read
- 3 chapters
Related on Lably
Through Python (programming language), Data structure and Type conversion
Benefits
Using declarative code has many benefits. For example, the same code that can parse can also build (symmetrical), debugging and testing are much simpler (provable to some extent), creating new constructs is easy (wrapping components), and many more. If one is familiar with the C (programming language), one can think of constructs as casting from char * to struct foo * and vice versa, rather than writing code that unpacks the data.
Sign in to keep reading
2 more chapters are free with a Lably account.
Example
The following example show how a TCP/IP protocol stack might be defined using Construct. Some code is omitted for brevity and simplicity. Also note that the following code is just Python code that creates objects.
First, the Ethernet header (layer 2):
<span id="mwHg"></span> <span class="n" id="mwHw">ethernet</span> <span class="o" id="mwIA">=</span> <span class="n" id="mwIQ">Struct</span><span class="p" id="mwIg">(</span> <span class="s2" id="mwIw">"destination"</span> <span class="o" id="mwJA">/</span> <span class="n" id="mwJQ">Bytes</span><span class="p" id="mwJg">(</span><span class="mi" id="mwJw">6</span><span class="p" id="mwKA">),</span> <span class="s2" id="mwKQ">"source"</span> <span class="o" id="mwKg">/</span> <span class="n" id="mwKw">Bytes</span><span class="p" id="mwLA">(</span><span class="mi" id="mwLQ">6</span><span class="p" id="mwLg">),</span> <span class="s2" id="mwLw">"type"</span> <span class="o" id="mwMA">/</span> <span class="n" id="mwMQ">Enum</span><span class="p" id="mwMg">(</span><span class="n" id="mwMw">Int16ub</span><span class="p" id="mwNA">,</span> <span class="n" id="mwNQ">IPv4</span><span class="o" id="mwNg">=</span><span class="mh" id="mwNw">0x0800</span><span class="p" id="mwOA">,</span> <span class="n" id="mwOQ">ARP</span><span class="o" id="mwOg">=</span><span class="mh" id="mwOw">0x0806</span><span class="p" id="mwPA">,</span> <span class="n" id="mwPQ">RARP</span><span class="o" id="mwPg">=</span><span class="mh" id="mwPw">0x8035</span><span class="p" id="mwQA">,</span> <span class="n" id="mwQQ">X25</span><span class="o" id="mwQg">=</span><span class="mh" id="mwQw">0x0805</span><span class="p" id="mwRA">,</span> <span class="n" id="mwRQ">IPX</span><span class="o" id="mwRg">=</span><span class="mh" id="mwRw">0x8137</span><span class="p" id="mwSA">,</span> <span class="n" id="mwSQ">IPv6</span><span class="o" id="mwSg">=</span><span class="mh" id="mwSw">0x86DD</span><span class="p" id="mwTA">,</span> <span class="p" id="mwTQ">),</span> <span class="p" id="mwTg">)</span>
Next, the IP header (layer 3):
<span id="mwUw"></span> <span class="n" id="mwVA">ip</span> <span class="o" id="mwVQ">=</span> <span class="n" id="mwVg">Struct</span><span class="p" id="mwVw">(</span> <span class="s2" id="mwWA">"header"</span> <span class="o" id="mwWQ">/</span> <span class="n" id="mwWg">BitStruct</span><span class="p" id="mwWw">(</span> <span class="s2" id="mwXA">"version"</span> <span class="o" id="mwXQ">/</span> <span class="n" id="mwXg">Const</span><span class="p" id="mwXw">(</span><span class="n" id="mwYA">Nibble</span><span class="p" id="mwYQ">,</span> <span class="mi" id="mwYg">4</span><span class="p" id="mwYw">),</span> <span class="s2" id="mwZA">"header_length"</span> <span class="o" id="mwZQ">/</span> <span class="n" id="mwZg">Nibble</span><span class="p" id="mwZw">,</span> <span class="p" id="mwaA">),</span> <span class="s2" id="mwaQ">"tos"</span> <span class="o" id="mwag">/</span> <span class="n" id="mwaw">BitStruct</span><span class="p" id="mwbA">(</span> <span class="s2" id="mwbQ">"precedence"</span> <span class="o" id="mwbg">/</span> <span class="n" id="mwbw">Bytes</span><span class="p" id="mwcA">(</span><span class="mi" id="mwcQ">3</span><span class="p" id="mwcg">),</span> <span class="s2" id="mwcw">"minimize_delay"</span> <span class="o" id="mwdA">/</span> <span class="n" id="mwdQ">Flag</span><span class="p" id="mwdg">,</span> <span class="s2" id="mwdw">"high_throuput"</span> <span class="o" id="mweA">/</span> <span class="n" id="mweQ">Flag</span><span class="p" id="mweg">,</span> <span class="s2" id="mwew">"high_reliability"</span> <span class="o" id="mwfA">/</span> <span class="n" id="mwfQ">Flag</span><span class="p" id="mwfg">,</span> <span class="s2" id="mwfw">"minimize_cost"</span> <span class="o" id="mwgA">/</span> <span class="n" id="mwgQ">Flag</span><span class="p" id="mwgg">,</span> <span class="n" id="mwgw">Padding</span><span class="p" id="mwhA">(</span><span class="mi" id="mwhQ">1</span><span class="p" id="mwhg">),</span> <span class="p" id="mwhw">),</span> <span class="s2" id="mwiA">"total_length"</span> <span class="o" id="mwiQ">/</span> <span class="n" id="mwig">Int16ub</span><span class="p" id="mwiw">,</span> <span class="c1" id="mwjA"># ...</span> <span class="p" id="mwjQ">)</span>
And finally, the TCP header (layer 4):
<span id="mwkg"></span> <span class="n" id="mwkw">tcp</span> <span class="o" id="mwlA">=</span> <span class="n" id="mwlQ">Struct</span><span class="p" id="mwlg">(</span> <span class="s2" id="mwlw">"source"</span> <span class="o" id="mwmA">/</span> <span class="n" id="mwmQ">Int16ub</span><span class="p" id="mwmg">,</span> <span class="s2" id="mwmw">"destination"</span> <span class="o" id="mwnA">/</span> <span class="n" id="mwnQ">Int16ub</span><span class="p" id="mwng">,</span> <span class="s2" id="mwnw">"seq"</span> <span class="o" id="mwoA">/</span> <span class="n" id="mwoQ">Int32ub</span><span class="p" id="mwog">,</span> <span class="s2" id="mwow">"ack"</span> <span class="o" id="mwpA">/</span> <span class="n" id="mwpQ">Int32ub</span><span class="p" id="mwpg">,</span> <span class="c1" id="mwpw"># ...</span> <span class="p" id="mwqA">)</span>
Now define the hierarchy of the protocol stack. The following code "binds" each pair of adjacent protocols into a separate unit. Each such unit will "select" the proper next layer based on its contained protocol.
<span id="mwrA"></span> <span class="n" id="mwrQ">layer4tcp</span> <span class="o" id="mwrg">=</span> <span class="n" id="mwrw">Struct</span><span class="p" id="mwsA">(</span> <span class="n" id="mwsQ">tcp</span><span class="p" id="mwsg">,</span> <span class="c1" id="mwsw"># ... payload</span> <span class="p" id="mwtA">)</span> <span class="n" id="mwtQ">layer3ip</span> <span class="o" id="mwtg">=</span> <span class="n" id="mwtw">Struct</span><span class="p" id="mwuA">(</span> <span class="n" id="mwuQ">ip</span><span class="p" id="mwug">,</span> <span class="s2" id="mwuw">"next"</span> <span class="o" id="mwvA">/</span> <span class="n" id="mwvQ">Switch</span><span class="p" id="mwvg">(</span><span class="n" id="mwvw">this</span><span class="o" id="mwwA">.</span><span class="n" id="mwwQ">protocol</span><span class="p" id="mwwg">,</span> <span class="p" id="mwww">{</span> <span class="s2" id="mwxA">"TCP"</span> <span class="p" id="mwxQ">:</span> <span class="n" id="mwxg">layer4tcp</span><span class="p" id="mwxw">,</span> <span class="p" id="mwyA">}</span> <span class="p" id="mwyQ">),</span> <span class="p" id="mwyg">)</span> <span class="n" id="mwyw">layer2ethernet</span> <span class="o" id="mwzA">=</span> <span class="n" id="mwzQ">Struct</span><span class="p" id="mwzg">(</span> <span class="n" id="mwzw">ethernet</span><span class="p" id="mw0A">,</span> <span class="s2" id="mw0Q">"next"</span> <span class="o" id="mw0g">/</span> <span class="n" id="mw0w">Switch</span><span class="p" id="mw1A">(</span><span class="n" id="mw1Q">this</span><span class="o" id="mw1g">.</span><span class="n" id="mw1w">type</span><span class="p" id="mw2A">,</span> <span class="p" id="mw2Q">{</span> <span class="s2" id="mw2g">"IP"</span> <span class="p" id="mw2w">:</span> <span class="n" id="mw3A">layer3ip</span><span class="p" id="mw3Q">,</span> <span class="p" id="mw3g">}</span> <span class="p" id="mw3w">),</span> <span class="p" id="mw4A">)</span>At this point, the code can parse captured TCP/IP frames into "packet" objects and build these packet objects back into binary representation.
<span id="mw5A"></span> <span class="n" id="mw5Q">tcpip_stack</span> <span class="o" id="mw5g">=</span> <span class="n" id="mw5w">layer2ethernet</span> <span class="n" id="mw6A">packet</span> <span class="o" id="mw6Q">=</span> <span class="n" id="mw6g">tcpip_stack</span><span class="o" id="mw6w">.</span><span class="n" id="mw7A">parse</span><span class="p" id="mw7Q">(</span><span class="sa" id="mw7g">b</span><span class="s2" id="mw7w">"...raw captured packet..."</span><span class="p" id="mw8A">)</span> <span class="n" id="mw8Q">raw_data</span> <span class="o" id="mw8g">=</span> <span class="n" id="mw8w">tcpip_stack</span><span class="o" id="mw9A">.</span><span class="n" id="mw9Q">build</span><span class="p" id="mw9g">(</span><span class="n" id="mw9w">packet</span><span class="p" id="mw-A">)</span>
Ports and spin-offs
Perl
Data::ParseBinary is a CPAN module that originated as a port of Construct to the Perl programming language. (see its main POD document for its inspiration). Since the initial version, some parts of the original API have been deprecated.
Java
A port to Java is available on GitHub. Examples in Java, the Ethernet header (layer 2):
<span id="mwAQg"></span><span class="w" id="mwAQk"> </span><span class="n" id="mwAQo">Construct</span><span class="w" id="mwAQs"> </span><span class="n" id="mwAQw">ethernet_header</span><span class="w" id="mwAQ0"> </span><span class="o" id="mwAQ4">=</span><span class="w" id="mwAQ8"> </span><span class="n" id="mwARA">Struct</span><span class="p" id="mwARE">(</span><span class="s" id="mwARI">"ethernet_header"</span><span class="p" id="mwARM">,</span> <span class="w" id="mwARQ"> </span><span class="n" id="mwARU">MacAddress</span><span class="p" id="mwARY">(</span><span class="s" id="mwARc">"destination"</span><span class="p" id="mwARg">),</span> <span class="w" id="mwARk"> </span><span class="n" id="mwARo">MacAddress</span><span class="p" id="mwARs">(</span><span class="s" id="mwARw">"source"</span><span class="p" id="mwAR0">),</span> <span class="w" id="mwAR4"> </span><span class="n" id="mwAR8">Enum</span><span class="p" id="mwASA">(</span><span class="n" id="mwASE">UBInt16</span><span class="p" id="mwASI">(</span><span class="s" id="mwASM">"type"</span><span class="p" id="mwASQ">),</span> <span class="w" id="mwASU"> </span><span class="s" id="mwASY">"IPv4"</span><span class="p" id="mwASc">,</span><span class="w" id="mwASg"> </span><span class="mh" id="mwASk">0x0800</span><span class="p" id="mwASo">,</span> <span class="w" id="mwASs"> </span><span class="s" id="mwASw">"ARP"</span><span class="p" id="mwAS0">,</span><span class="w" id="mwAS4"> </span><span class="mh" id="mwAS8">0x0806</span><span class="p" id="mwATA">,</span> <span class="w" id="mwATE"> </span><span class="s" id="mwATI">"RARP"</span><span class="p" id="mwATM">,</span><span class="w" id="mwATQ"> </span><span class="mh" id="mwATU">0x8035</span><span class="p" id="mwATY">,</span> <span class="w" id="mwATc"> </span><span class="s" id="mwATg">"X25"</span><span class="p" id="mwATk">,</span><span class="w" id="mwATo"> </span><span class="mh" id="mwATs">0x0805</span><span class="p" id="mwATw">,</span> <span class="w" id="mwAT0"> </span><span class="s" id="mwAT4">"IPX"</span><span class="p" id="mwAT8">,</span><span class="w" id="mwAUA"> </span><span class="mh" id="mwAUE">0x8137</span><span class="p" id="mwAUI">,</span> <span class="w" id="mwAUM"> </span><span class="s" id="mwAUQ">"IPv6"</span><span class="p" id="mwAUU">,</span><span class="w" id="mwAUY"> </span><span class="mh" id="mwAUc">0x86DD</span><span class="p" id="mwAUg">,</span> <span class="w" id="mwAUk"> </span><span class="s" id="mwAUo">"_default_"</span><span class="p" id="mwAUs">,</span><span class="w" id="mwAUw"> </span><span class="n" id="mwAU0">Pass</span> <span class="w" id="mwAU4"> </span><span class="p" id="mwAU8">));</span>

