I don’t know about you, but I love Lego, I really do. Back when I was a kid I actually didn’t have the original Lego set; my father was a fan of more advanced construction toys like Meccano and Erector, where you had to screw parts together with nuts and bolts. However, being a programmer—and I am one—you get to appreciate the beauty of the Lego concept.

Building complex devices or systems by connecting simple blocks together is the essence of engineering, and you will be hard-pressed to find a better example of “complex from simple” than that offered by Lego. All the blocks are compatible, all the blocks connect to other blocks easily, you don’t need any nuts, bolts or tools, and only your imagination is the limit for what you can build. And boy, have you ever tried to break* a Lego brick? I dare you to accomplish this!

When we design software, this has got to be our ultimate goal. We want it to be like Lego. We want it to be built with small building blocks, each one as simple and robust as a Lego brick, each one interchangeable and easily interconnectable with other blocks.

Unfortunately, it is not always possible to achieve this goal, and even when it is, we often find excuses for not going all the way in Lego-izing our projects. After all, our customers (and most employers) usually only care about the end product, not the bricks it is built of.

I will argue, however, that investing time, money, and creative thinking into Lego-izing your software product may reward you with unanticipated product strengths and features. In support of this statement, allow me to present Exhibit A.

Exhibit A

The case in point is our software called IO Ninja. Here is a link to my introductory article covering the origins of Ninja. To make the long story short, once upon a time we were looking for binary-enabled IO terminals and sniffers for a specific set of transports and protocols. After not being satisfied with what we found (check the link above to find out why), we ended up creating our own all-in-one low-level IO debugger. Somewhere along the way we’ve got a crazy idea of making it programmable, so our users could write their own protocol analyzers and testing scripts. This is the preamble to what I want to tell you.

These two design goals—to build an all-in-one product and to make it programmable—have essentially forced a Lego-like architecture upon us. So, this wasn’t even a choice; we couldn’t get away with the usual “the end product is what matters” mantra. In our case, the end product had to look a lot like Lego!

Here is what we ended up with: IO Ninja software consists of two large parts—the binary part and the script part. The binary part provides a framework of Lego bricks: sockets, serial ports, sniffers, buttons, combo boxes and so on. Scripts glue our Lego bricks into specialized IO sessions.

Now comes the interesting part: After creating a Lego-like design, we discovered that IO Ninja naturally has a plethora or interesting, useful, and unexpected features that were not anticipated even by us—the builders of this product!

Ability to Redirect Anything-to-Anything

Users of Unix-like systems are well-aware of the possibility of redirecting the output of one program to the input of another. Session linking in IO Ninja provides similar functionality. The difference is this: instead of unidirectional data cascade of Unix pipes, session linking shorts the TX stream of one session onto the RX stream of another, and vice versa. After this shorting, IO Ninja keeps passing the data back and forth between the two sessions. That turns IO Ninja into a universal redirector.

Let’s say you have a device attached to your PC via a serial port, and you want to be able to connect to this device over TCP. That’s right. Connect to a serial device over TCP.

Launch IO Ninja and start a new serial session. Open and configure your serial port. Next, open a TCP Listener session, choose a TCP port to listen on, and start listening.

Now all is ready for you to reap the fruits of IO Ninja’s Lego-like architecture! Go Menu->Session->Link sessions. Voila! You can now connect to your serial device via TCP (notice the chain links on session tabs):

You can use the same approach for redirecting TCP-to-UDP, UDP-to-SSH, Named Pipe-to-TCP, and so on. As an extra bonus, all passing data will also be logged in the process, turning your setup into a universal man-in-the-middle sniffer.

Searching for Devices Using UDP Broadcasts

A common way of auto-discovering devices on a local network segment is by broadcasting a UDP request packet and collecting replies from participating devices. This is how Tibbo’s software discovers our devices.

With IO Ninja’s modular architecture, we were able to take a Lego brick of UDP socket and plug it into a dedicated session specifically designed to handle one-to-many communications.

To try it, open a UDP Socket session, set the remote address to 255.255.255.255 (or to a subnet broadcast address like 192.168.1.255), then send an echo broadcast packet. As a result, you will see the list of devices connected to your local network segment. Now choose one of these devices, copy-paste its IP address and start communicating with this particular device!

Check out the compass button. When working with the UDP protocol, you often need to reuse the source address from which the most recent packet has arrived (after all, UDP is a connectionless protocol). Of course, nothing prevents you from copying an address from the log and pasting it into the remote address combo box, yet why do something manually when you can automate the task? Press the compass button, and the UDP Socket session will automatically readjust the remote address to the address from which the most recent packet was received.

Sniffing TCP Data Using a TCP Proxy

IO Ninja includes a Network Sniffersession. This is a pcap-based plugin for monitoring network protocols. When running it, IO Ninja behaves much like the Wireshark software. Despite the prevalence of pcap-based software tools, the reality is that the pcap-based approach is not the only way to eavesdrop on network communications. In some situations, using a pcap-based sniffer is simply not possible, and in some situations, there are much better ways to tap into the data stream.

In addition to the traditional pcap-based sniffer, IO Ninja provides the second kind of a TCP sniffer called a TCP Proxy. It is a combination of two TCP sockets—a client one and a server one—passing data between each other.

Here is the benefit of this approach: instead of a packet-based log (with disjoint chunks of data you must mentally piece together), your TCP proxy session provides a clean log of data streams, just like you would see in a TCP connection session. Also, unlike with the pcap-based sniffing, which is only applicable to local networks, TCP proxy sessions allow you to monitor TCP links in all situations, as long as you can position yourself to be the man-in-the-middle.

Here is a screenshot of an FTP session being monitored with the TCP Proxy plugin:

Debugging Named Pipes in Windows

IO Ninja can be used to debug a very common IPC (inter-process communication) method in Windows called named pipes. For example, all Tibbo kernel-mode drivers use this method to communicate with user-mode services and configuration utilities.

Unfortunately, debugging named pipes can be pretty tough due to the lack of named pipe terminals and monitors. Before IO Ninja, you could not even send a packet over a named pipe without having to write a test application! Naturally, we were determined to fix that. IO Ninja can work as both the client- and the server-side terminal for Windows named pipe communications.

To use IO Ninja on the server side (the one that calls the ConnectNamedPipe function), launch the Named Pipe Listener session. This plugin allows you to accept named pipe connections originating from your application, driver, or service, much like the TCP listener plugin accepts incoming TCP connections. After accepting a connection, you can communicate with a client and analyze the log of received commands.

For emulating the other side of named pipe communications, use the Generic File session.

For monitoring named pipe communications you can employ the man-in-the-middle approach described earlier. To do so, start a Named Pipe Listener session, link it to a Generic File session and redirect your named pipe client to IO Ninja. In the future, we are planning to provide a dedicated standalone filesystem filter-based plugin for sniffing Windows named pipes. For the time being, the man-in-the-middle approach will suffice for most debugging scenarios.

Conclusion

Described above are just some of the many cool features stemming from the Lego-like architecture of IO Ninja. Allow me to repeat this: we haven’t even planned to implement some of those features, they simply appeared as a result of having a well-planned modular architecture. Make no mistake, applying this Lego principle to your project is a hard job. It requires a massive investment of time and effort, take my word on that. Still, going down this path is well worth considering. Who knows, maybe you will also discover a few unexpected and useful features magically appearing as a result of your Lego-like approach.