urbit/outside/anachronism
Elliot Glaysher e6f6315691 Revert "Merge pull request #949 from eglaysher/meson-build-clean"
This reverts commit 9f795b535f, reversing
changes made to d5c7b3b932.
2018-03-19 15:13:19 -07:00
..
doc Revert "Merge pull request #949 from eglaysher/meson-build-clean" 2018-03-19 15:13:19 -07:00
include/anachronism Revert "Merge pull request #949 from eglaysher/meson-build-clean" 2018-03-19 15:13:19 -07:00
src Revert "Merge pull request #949 from eglaysher/meson-build-clean" 2018-03-19 15:13:19 -07:00
.gitignore Revert "Merge pull request #949 from eglaysher/meson-build-clean" 2018-03-19 15:13:19 -07:00
LICENSE Revert "Merge pull request #949 from eglaysher/meson-build-clean" 2018-03-19 15:13:19 -07:00
Makefile Revert "Merge pull request #949 from eglaysher/meson-build-clean" 2018-03-19 15:13:19 -07:00
README.md Revert "Merge pull request #949 from eglaysher/meson-build-clean" 2018-03-19 15:13:19 -07:00

Anachronism

Anachronism is a fully-compliant implementation of the Telnet protocol. Fallen out of favor in this day and age, most people only know it as a command-line tool for debugging HTTP. Today, Telnet is most commonly used in the realm of MUDs, though there are still a few other niches filled by Telnet.

Anachronism offers a simple API for translating between streams of data and events, and is completely network-agnostic. Anachronism also offers channels, an abstraction layer which treats Telnet as a data multiplexer. Channels make it extremely easy to build reusable modules for Telnet sub-protocols such as MCCP (MUD Client Compression Protocol), which can be written once and plugged into any application that wants to include support.

Installation

While Anachronism has no dependencies and is theoretically cross-platform, I've only written a Makefile for Linux. Help would be appreciated for making this work across more platforms.

make
sudo make install

This will install Anachronism's shared and static libraries to /usr/local/lib, and its header files to /usr/local/include/anachronism/. You may also need to run ldconfig to make Anachronism available to your project's compiler/linker.

Usage

The anachronism/nvt.h header can be consulted for more complete documentation.

Basic usage

The core type exposed by Anachronism is the telnet_nvt, which represents the Telnet RFC's "Network Virtual Terminal". An NVT is created using telnet_nvt_new(). When creating an NVT, you must provide it with a set of callbacks to send events to, and an optional void* to store as the event handler's context. You can use telnet_recv() to process incoming data, and the telnet_send_*() set of functions to emit outgoing data.

#include <stdio.h>
#include <anachronism/nvt.h>

void on_event(telnet_nvt* nvt, telnet_event* event)
{
  switch (event->type)
  {
    // A data event (normal text received)
    case TELNET_EV_DATA:
    {
      telnet_data_event* ev = (telnet_data_event*)event;
      printf("[IN]: %.*s\n", ev->length, ev->data);
      break;
    }
    
    // Outgoing data emitted by the NVT
    case TELNET_EV_SEND:
    {
      telnet_send_event* ev = (telnet_send_event*)event;
      printf("[OUT]: %.*s\n", ev->length, ev->data);
      break;
    }
  }
}

int main()
{
  // Create an NVT
  telnet_nvt* nvt = telnet_nvt_new(NULL, &on_event, NULL, NULL);
  
  // Process some incoming data
  const char* data = "foo bar baz";
  telnet_receive(nvt, (const telnet_byte*)data, strlen(data), NULL);
  
  // Free the NVT
  telnet_nvt_free(nvt);
  return 0;
}

Telopts

Anachronism provides an easy-to-use interface to Telnet's "telopt" functionality via the telnet_telopt_*() set of functions. As telopts are negotiated and utilized, events are sent to the telopt callback provided to telnet_nvt_new().

#include <stdio.h>
#include <anachronism/nvt.h>

void on_event(telnet_nvt* nvt, telnet_event* event)
{
  switch (event->type)
  {
    // Outgoing data emitted by the NVT
    case TELNET_EV_SEND:
    {
      telnet_send_event* ev = (telnet_send_event*)event;
      printf("[OUT]: %.*s\n", ev->length, ev->data);
      break;
    }
  }
}

void on_telopt_event(telnet_nvt* nvt, telnet_byte telopt, telnet_telopt_event* event)
{
  // telopt is the telopt this event was triggered for
  
  switch (event->type)
  {
    case TELNET_EV_TELOPT_TOGGLE:
      telnet_telopt_toggle_event* ev = (telnet_telopt_toggle_event*)event;
      // ev->where is TELNET_TELOPT_LOCAL or TELNET_TELOPT_REMOTE,
      //     corresponding to Telnet's WILL/WONT and DO/DONT commands.
      // ev->status is TELNET_TELOPT_ON or TELNET_TELOPT_OFF.
      break;
    case TELNET_EV_TELOPT_FOCUS:
      telnet_telopt_focus_event* ev = (telnet_telopt_focus_event*)event;
      // ev->focus is 1 or 0 depending on if a subnegotiation packet has
      //     begun or ended.
      break;
    case TELNET_EV_TELOPT_DATA:
      telnet_telopt_data_event* ev = (telnet_telopt_data_event*)event;
      // ev->data is a pointer to the received data.
      // ev->length is the length of the data buffer.
      break;
  }
}

int main()
{
  // Create an NVT
  telnet_nvt* nvt = telnet_nvt_new(NULL, &on_event, &on_telopt_event, NULL);
  
  // Ask to enable a telopt locally (a WILL command)
  telnet_request_enable(nvt, 230, TELNET_LOCAL);
  
  // Process some incoming data
  const char* data = "\xFF\xFD\xE6" // IAC DO 230  (turn channel on)
                     "\xFF\xFA\xE6" // IAC SB 230  (switch to channel)
                     "foo bar baz"                 (send data)
                     "\xFF\xF0";    // IAC SE      (switch to main)
  telnet_receive(nvt, (const telnet_byte*)data, strlen(data), NULL);
  
  // Free the NVT
  telnet_nvt_free(nvt);
  return 0;
}

Interrupting

TODO: Explain how to interrupt the parser.

Alternatives

  • libtelnet, by Elanthis
    It incorporates a number of (rather MUD-specific) protocols by default, though its API is quite different.

Credits

Someone from #startups on Freenode IRC suggested the name (I'm sure as a joke). If you read this, remind me who you are so I can credit you properly!