Showing posts with label krumlib. Show all posts
Showing posts with label krumlib. Show all posts

Monday, July 18, 2011

Something You Can See!

This week, I spent more time making pickles than I did writing code. But I did write a little demo that interacts with a Telnet client. I've packaged KrumLib as an executable jar, with the demo as its run target. You can download it here. I'd like confirmation that it works on Windows and Mac, although I don't know why it wouldn't. More importantly, I'd like confirmation that it works with various Telnet clients.

The demo opens port 12345 and listens for a Telnet connection. When it receives one, it prompts the user (via Telnet) for a user name and password. After receiving the user's responses, it prints a diagnostic message and exits. The conversation looks like this:

Enter your name: foo
Enter your password:
Your name is foo and your password is bar


Details I would like confirmed include:
  1. The name is echoed one character at a time (not all at once when the user presses enter) and each character is echoed only once

  2. The password is not echoed at all, except in the diagnostic message

  3. The backspace key works as you'd expect, and deletions made with backspace are reflected in the name and password printed in the diagnostic message

  4. The arrow keys do not move the cursor, and also do not cause escape codes (gibberish) to be printed on the terminal

  5. Each line of text begins on the left column of the screen

If you send feedback, please let me know what Telnet client you're using.

Sunday, July 10, 2011

Automata for the People

Digital communication protocols are built up in layers. In the familiar case of the Web, HTML content is delivered on top of HTTP, which in turn is carried by IP, which is handled by TCP, which rides atop some hardware protocol like Ethernet, cable, or DSL. This protocol lasagne is called a network stack.

I designed my Telnet NVT around the idea that Telnet should be treated like a transport layer, and content should be handled separately in a higher layer. I'm glad I wrote it this way, because when I tried to work with the content I was reading from my NVT, I found it full of control codes for another protocol: VT220 terminal emulation.

VT220 terminals are everywhere. The Linux command-line telnet, the popular cross-platform PuTTY client, and apparently most other "Telnet" clients actually use VT220 control codes for things like the arrow and delete keys. (Even the Linux system console is a VT220 emulator.) In their original forms, the Telnet and VT220 protocols were different ways of accomplishing similar things. But now, it seems, they go hand in hand... and VT220 does most of the work.

To move ahead with my project, I only need to recognize VT220 control codes so I can strip them out and throw them away. I don't even care what control codes I'm removing. My first idea was to use regular expressions to match them. But the catch is that their lengths aren't fixed like Telnet commands, so I would never know if the sequence of characters I'm examining doesn't match because it's not complete yet, or because it's not a VT220 control code. Java's regular expression library doesn't allow me to test for partial matches, so my program could waste a lot of time trying to match character sequences up to some arbitrary length, only to have them turn out to not be VT220 control sequences at all.

Finite automata to the rescue! Automata are the foundation of regular expression matching. By working with automata directly, I'll be able to tell if a sequence of characters is the beginning of one or more matches, and quickly pass it up to the next layer if it isn't. I need to study it a bit more before I make a decision, but I think I'm going to use the BRICS automata library for this task.

Hopefully I won't run into any more surprises like this, and by next week I'll have something you can see.

Monday, July 4, 2011

The KrumLib Cometh

This week, two problems forced me to return to my old habit of digging into the details of how things work. But the problems I encountered are so general, and the readily-available solutions so lacking, that I don't feel the time was wasted. The solutions I produced may be useful to others, so I've decided to publish them separately from the rest of my project. To that end, I've created a new project on SourceForge called KrumLib.

One of the problems I encountered was with BufferedReader, a commonly-used program component for reading text line-by-line. The trouble is that when it's used to read from a network stream, recoverable errors can lead to data loss. (Actually, I hesitate even to call them errors; socket timeouts are a normal part of network programming.) This problem seems likely to affect my protocol library, so I wrote my own version of BufferedReader to avoid it.

The other problem I faced was how to accept connections from Telnet clients. Telnet is an old protocol, predating the Web by about two decades, and virtually every computer has some sort of Telnet client installed. There are also plenty of Java Telnet libraries out there to choose from. But a quick look at several of them showed that most are designed around the assumption that they will be used to write Telnet clients, not Telnet servers.

Telnet is a symmetric protocol, meaning that it's basically the same both directions. Each end has a program component called a Network Virtual Terminal which negotiates options and handles commands from the other end. The two NVTs are practically identical, except that the client side intitiates connections and the server side waits for them. And strictly speaking, how the connection gets established isn't even part of the Telnet protocol. An NVT could just as well remain completely agnostic about who initiated the connection, or even who's on the other end. You ought to be able to just hand it an open connection and set it working. So it seems pretty silly for a Telnet library to be limited to initiating connections, but that's the way everyone writes them.

I probably could have modified one of the existing libraries to listen for connections, but I estimated that it would have taken me at least as long to choose the best one, study it, modify it, and republish my changes as it did to write my own. Of course, that's only true because I had very low expectations for my own. Telnet supports a wide variety of commands and options, and I was only interested in a few of them. I've called my solution BogusNVT because, as the documentation says, it's incomplete and wrong. But it'll do the job.

I'm ready to change my focus to working on the client and server. These two sub-projects present a sort of chicken-and-egg problem, in that I can't test either until the other is written. I expect this to be a bit of a hassle, but the foundation I've laid with the protocol library and KrumLib will make it easier. And any other components I write that are generally useful, or that are used by more than one sub-project, will be added to KrumLib.