For the past couple of weeks, I've been learning a lot of Rust. As is my wont, my usual way of learning a language is to decide to write something in it, and see what comes from doing it. A while ago, I learned that John Carmack still regularly updates his .plan file, which is a text file that was once available through the Remote User Identifier Protocol documented in RCF-1288, popularly known as the finger protocol. The project is named Monologued.

I decided I wanted to write a finger server. The version I've decided to write is very stripped down. It doesn't support user lists by default, it doesn't support host-hopping by default, and it doesn't even support user presence at all, which was the original intent of the protocol. What it does support is returning the user's .plan file. It's fairly paranoid, honoring .noplan over .plan (if the former was present, finger wouldn't even acknowledge that the user requested existed), taking care not to leak usernames, and prioritizing the /etc/finger.conf exclude list over the include list.

I have learned a lot about the finger protocol and it's threadbare implementation during my research. For example, there's a Unix program, who, that will tell you who's currently logged in to a given host. The fingerd server doesn't even run on its own; it's launched by inetd, a helper program for small network utilities not much in use these days, which hooks up finger to the internet via stdin/stdout. fingerd in turn runs the local finger program, which doesn't even do any work-- instead, it figures out who you asked for, runs who to spew the user's identity and presence, and then runs cat on the .plan file if it exists. That seems like a bit of a security nightmare.

My object is to learn Rust, and to learn MIO (Metal I/O, the lowest-level Rust library for networking that's not just wrapping the C network library yourself). Everything will be contained in a single executable. The progression I've planned is straightforward:

  1. Write a simple echo server, as shown in the MIO tutorials.
  2. Separate the Connection object from the Server object.
  3. Write a parser so that instead of returning an echo, we return the command found.
  4. Implement the command found.
  5. Secure the command found with the rules described above.
  6. Implement the rules as a configuration file.
  7. Implement the rules as command-line overrides.

Stretch goals:

  1. Implement a cache with arbitrary Sized<>-based ejection rules for smaller .plan files.
  2. Implement an fstat-based layer to eject .plan files that have expired.
  3. Implement an Inotify-based layer for MIO to eject .plan files that have expired.

Super Stretch goals:

  1. Implement opportunistic encryption (STARTTLS) for the Fingerd protocl.
  2. Modify the configuration so opportunistic encryption can be required.
  3. Write a client to demonstrate opportunistic encryption of RUIP.
  4. Document and submit an RFC for "Using Transport Layer Security (TLS) with the Remote User Identity Protocol (RUIP / Finger)."

Right now I'm working on 4, but I may have to backtrack as I think I've become overly entangled via 2, and may need to think harder about separating the read/write phases.

An acquaintance of mine claims that the last one is an "epic troll," but I don't know; not all of those old protocols are worthless.