Skip to content

IAC Byte (0xFF) Doubling Not Currently Enforced in Data Transmission Path #2736

Description

@X1AOxiang

Hi, while reviewing Contiki Telnet's Telnet implementation against RFC 854, we noticed one place where the current behavior appears to differ from the specification. We hope this is helpful for improving RFC conformance.

RFC Reference: RFC 854 — TELNET COMMAND STRUCTURE

"All TELNET commands consist of at least a two byte sequence: the "Interpret as Command" (IAC) escape character followed by the code for the command. The commands dealing with option negotiation are three byte sequences, the third byte being the code for the option referenced. This format was chosen so that as more comprehensive use of the "data space" is made -- by negotiations from the basic NVT, of course -- collisions of data bytes with reserved command values will be minimized, all such collisions requiring the inconvenience, and inefficiency, of "escaping" the data bytes into the stream. With the current set-up, only the IAC need be doubled to be sent as data, and the other 255 codes may be passed transparently."

Analysis:
The current implementation transmits data buffers via senddata() (telnet.c:90–103), which calls uip_send(s->text, s->sentlen) directly without inspecting the contents of s->text for bytes with value 0xFF. The upstream entry point, telnet_send() (telnet.c:45–55), accepts an arbitrary (text, len) pair and stores it verbatim into s->text and s->textlen with no transformation. As a result, the entire data transmission pipeline — from telnet_send() through senddata() to uip_send() — does not contain any point where an IAC byte (0xFF) present in the data would be detected or converted to the two-byte escape sequence IAC IAC (0xFF 0xFF). RFC 854 specifies that the IAC byte need be doubled when it appears as data rather than as a command prefix; the current implementation sends it as a single 0xFF, which the receiving Telnet peer would interpret as a command escape rather than a data byte.

Source Code Evidence (telnet.c):

// telnet.c:45-55
unsigned char
telnet_send(struct telnet_state *s, char *text, uint16_t len)
{
  if(s->text != NULL) {
    return 1;
  }
  s->text = text;     // stores buffer without byte inspection
  s->textlen = len;
  s->sentlen = 0;
  return 0;
}

// telnet.c:90-103
static void
senddata(struct telnet_state *s)
{
  if(s->text == NULL) {
    uip_send(s->text, 0);
    return;
  }
  if(s->textlen > uip_mss()) {
    s->sentlen = uip_mss();
  } else {
    s->sentlen = s->textlen;
  }
  uip_send(s->text, s->sentlen);  // sends raw buffer contents to network
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions