-
Notifications
You must be signed in to change notification settings - Fork 6
The Ruckus Object Model
Beware things with object models, but having said that:
Everything in a Ruckus packet is a kind of Ruckus::Parsel. Ruckus::Parsel bears the same relationship to a packet that a DOM node bears to a web page loaded in a browser.
Yes, “Parsel” is a silly name.
All Parsels have a couple basic methods:
- Parsel#out (unfortunately, a.k.a. Parsel#to_s) marshals them into strings so you can blit them onto the network.
- Parsel#in (a.k.a. Parsel#capture) reads the contents of a string into a Parsel, returning the remainder of the string
- Parsel#visit allows you to traverse all the descendant nodes of a Parsel (usually a Structure).
- Parsel#each_matching_selector yields each Parsel in a packet matching a CSS-style selector.
Different kinds of Parsels have different properties. For instance, integers have an :endian property. Strings have a :unicode property. Properties are exposed as instance attributes.
Every Parsel has a :value property, which is how you set and read the actual value of the object. The “value” of Number for instance is going to be a basic Ruby Numeric object you can do math on.
Most things in packets are numbers of some form. Ruckus tries to represent all of them. There’s two ways to slice them up: by properties and by subclasses. First, the various Ruckus::Number properties:
- :width – How many bits wide is the number? Ruckus can handle whatever. A flag in packet is best modeled in Ruckus as a 1-bit-wide number. 17-bit-wide numbers work fine. So do 100 bit numbers.
-
:endian – Either :big (MIPS, Internet) or :little (Microsoft, X86)
-a.k.a. :ass_backwards. - :ascii – True or false: is this a binary number or a text number?
- :radix – If it’s a text number, is it decimal (10, the default), hex (16), or something funkier?
Mix and match, collect all four, or try one of these great flavor combos:
- Le8, Le16, Le32 – or H16 (and so on), Uint16Le (and so on). For no good reason, “Half” and “Halfword” are synonyms for Le16 – little endian integers.
- Be8, Be16 – or N8, N16 – or Uint16Be – or “Short” (16 bits) and “Long” (32 bits) – big-endian integers.
- Byte is an 8 bit number.
- Bit is a 1 bit number.
- Nybble is a 4 bit number.
- Decimal is a 32 bit (by default) decimal text number.
When things in packets aren’t numbers, they’re strings. Also, when you don’t know what the format of something inside a packet you’re reverse-engineering is, it’s just a string.
So that our names don’t collide with Ruby’s when we import the whole Ruckus namespace for convience, strings are Ruckus::Str objects. Ruckus::Str objects are, of course, Parsel descendants. Like Numbers, they have properties:
- :size – What is the absolute size of this string? If you set the size of a Str to 10, and the value to “foo”, it will be padded when you render it.
- :min – What’s the minimum size of the string?
- :max – What’s the maximum size of the string?
- :padding – If the string is padded, what should the padding character be? By default, it’s \x00.
- :pad_to – To what alignment should this string be padded?
- :unicode – Is the string UTF-16-LE Unicode formatted? (We use “Unicode” the way Microsoft does).
- :nul_terminated – Does the string always have a NUL byte at the end?
When things aren’t simply a string or a number, they’re somehow a combination of strings and/or numbers. A Ruckus::Blob is a heterogenous array of Parsels.
When a Ruckus::Blob is rendered with Blob#out, it creates an output string containing the renderings of each of its members, in order. A Ruckus::Blob populated with template Parsels can used with Blob#in to parse a collection of different types, in order.
Obviously, Blobs can contain other Blobs. Blob doesn’t care. Nested Blobs implement tree traversal, like a DOM tree.
Most of the time, when a protocol frame is really a bag of numbers and strings in a particular order, you don’t simply want to put them in a bag; you want named access to each of the fields. Ruckus::Structure is a layer over Ruckus::Blob that gives structure-style named access to each element (now “field”) of a blob.
Structures are most of what you do with Ruckus; they’re described here.