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
}
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
Analysis:
The current implementation transmits data buffers via
senddata()(telnet.c:90–103), which callsuip_send(s->text, s->sentlen)directly without inspecting the contents ofs->textfor bytes with value 0xFF. The upstream entry point,telnet_send()(telnet.c:45–55), accepts an arbitrary(text, len)pair and stores it verbatim intos->textands->textlenwith no transformation. As a result, the entire data transmission pipeline — fromtelnet_send()throughsenddata()touip_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):