Summary

  • RFC 1408 assigned VAR=0 and VALUE=1 to Telnet option 36, but RFC 1571 documented that the BSD reference implementation used the two meanings in reverse. The same byte stream could therefore be parsed under two incompatible dictionaries.
  • RFC 1571 tried to identify a peer from message grammar—illegal markers, ordering, counts and familiar names—but admitted cases in which the evidence remained ambiguous.
  • RFC 1572 kept the RFC-defined mapping and moved it to fresh option 39, NEW-ENVIRON. Even then, a decoded variable was only remote input: the server retained the decision to ignore, replace or apply it.

Zero arrived without its dictionary

RFC 1408 appeared in January 1993 to carry environment information across a Telnet connection. Its option number was 36. Within the subnegotiation, IS was zero, SEND one and INFO two. At the next grammatical level, the document assigned zero to VAR, one to VALUE, two to ESC and three to USERVAR.

Those tiny assignments did a large amount of work. A receiver needed to distinguish a variable name from its value, an undefined variable from a defined but empty one, a well-known name from a user-created name, and a literal control octet from grammar. If the sender and receiver disagreed about zero and one, the message did not merely carry a wrong label. Its structure changed.

The conflict became public a year later. RFC 1571 said RFC 1408's VAR and VALUE definitions were reversed from the BSD Telnet Environment implementation. BSD was not described as an incidental alternative. The memo called it the reference implementation the RFC had been supposed to document and the base for many existing implementations.

That account does not provide a deployment census, and it should not be inflated into one. It establishes the interoperability problem the authors faced: option 36 existed in running software with one interpretation and in a standards-track text with the other. The number alone no longer selected one grammar.

The option carried startup context, not Telnet's own truth

The motivation for ENVIRON was practical. Operating systems had startup information that users wanted to propagate when connecting to another host. Inventing a separate Telnet option for every new item would have made the protocol accumulate special-purpose negotiation. RFC 1408 instead defined a generic carrier.

It named six well-known variables: USER, JOB, ACCT, PRINTER, SYSTEMTYPE and DISPLAY. USERVAR made room for arbitrary name/value pairs while letting software distinguish user-supplied names from the well-known class. The distinction was provenance, not certification. RFC 1408 observed that cautious implementations might distrust both classes equally, and left collisions between a well-known name and a user name implementation-specific.

The grammar also preserved states that a casual key/value export would collapse. A variable with no following VALUE was undefined. A VALUE immediately followed by another type or the end of the subnegotiation meant the variable existed but was empty. An empty environment was a valid answer. These were precise statements about the sender's encoded response, not proof about a person, an account or a future process.

Permission had a direction

No environment information moved by default. The initial state was WONT ENVIRON and DONT ENVIRON. One endpoint used WILL to say it was prepared to send; the other used DO to say it was prepared to receive. Only the DO side could issue SEND, and only the WILL side could answer with IS or announce later changes with INFO.

That separation mattered. IS belonged to the initial request-and-response path. INFO could be generated spontaneously after negotiation to report changes, but it was not the initial exchange. A record of WILL did not prove that a SEND followed. A SEND did not prove that a particular variable existed. An IS frame did not prove that the receiver accepted its contents.

The exchange commonly occurred near connection startup because many operating systems propagated an environment only when creating a process. Timing therefore changed the possible effect. Information decoded after process creation could still be a valid Telnet message while arriving too late for the startup mechanism it was meant to influence.

The receiver kept the future decision

RFC 1408 explicitly refused to turn transport into command. A receiving host did not have to put every received variable into the user's environment. It could ignore an unknown name. It could use USER, ACCT or related information to choose a login context without ever copying the strings into the resulting environment. It could prefer information obtained by a more accurate mechanism.

The document's TERM example made the hierarchy concrete. If a client sent USERVAR TERM=xterm after the Telnet Terminal-Type option had already established the terminal type, the server could ignore the environment variable. For DISPLAY, the specification gave another rule: when the environment variable conflicted with the separate X-Display-Location option, the most recently received information should win.

These are local resolution policies. The common protocol made competing inputs legible; it did not appoint the sender as sovereign over the receiver's process. A received USER named the account the client wished to use. It was not authentication. A received PRINTER did not prove that the destination existed, was permitted or printed anything. A decoded DISPLAY did not prove a working X connection or a visible window.

That boundary was also a security boundary. RFC 1408 warned implementers to analyse which variables were safe before login. Allowing an attacker-controlled value to alter the login or authentication program could enable circumvention or compromise. The RFC did not report such an incident. It stated the structural risk: pre-authentication input and local startup authority must not be confused.

RFC 1571 turned grammar into a field detector

Once two interpretations of option 36 existed, a receiver could not repair the past by reading the RFC harder. It needed evidence from the peer's message shape. RFC 1571 offered heuristics.

For a client parsing SEND, the rule began with an impossibility. A conforming request should contain VAR and USERVAR, never VALUE. If the client saw the marker that its dictionary called VALUE, it could infer that the server had reversed zero and one, then reverse its interpretation for the rest of the request and its response. If neither zero nor one appeared, there was no evidence; the client assumed the published definitions.

The server's task was harder because IS and INFO carried names and values. If the first marker was VAR, the server could infer the published order; if it was VALUE, the reversed order. When a message began with USERVAR, both zero and one might legally follow under some parse. The server then looked for consecutive markers, empty markers, relative counts and recognisable well-known names.

This was protocol archaeology performed in real time. The receiver was not discovering an identity. It was classifying which historical dictionary best explained the bytes. Some patterns were decisive only because one parse would violate the grammar. Other patterns were merely probable. If all tests failed, RFC 1571 told the server to assume the published definitions.

The fallback matters as much as the clever tests. A heuristic result was an operational choice under incomplete evidence, not a newly observed fact about the sender. An implementation that records only the decoded variables discards the very history needed to audit that choice: option number, raw subnegotiation, assumed dictionary, triggering heuristic and any remaining ambiguity.

A new number protected the new meaning

RFC 1572 supplied the cleaner future path in January 1994. It retained the RFC-defined VAR=0, VALUE=1 mapping but moved the protocol to Telnet option 39, NEW-ENVIRON. The text explained that assigning a new option number let implementations interoperate without ambiguity.

This is subtler than correcting zero to one or one to zero. Option 36 already carried history. Reissuing its table could not tell a peer whether a given installed implementation followed the document or BSD. Option 39 created a new deterministic common layer: negotiation of the new number selected the new grammar without guessing which old lineage stood behind it.

The current IANA Telnet Options registry preserves both names. Option 36 remains Environment Option with RFC 1408; option 39 is New Environment Option with RFC 1572. The registry records the split. It does not prove that a host implements either option, that a negotiation succeeded or that a variable reached a process.

Nor did the new number erase the need for local judgement. NEW-ENVIRON kept the same directional consent, request/response structure, variable provenance and receiver discretion. The common grammar became unambiguous; the operational future remained local.

The evidence ladder was longer than the packet

The full chain begins with a published assignment. An implementation emits a byte using one dictionary. A peer parses it using another or classifies the peer with a heuristic. The decoder produces a candidate name/value sequence. The receiver records its source and decides whether to ignore, replace or apply it. Login or process creation may consume the selected input. An application may observe the resulting environment. Only separate evidence can show an external action or user-visible result.

No rung inherits the next rung's authority. RFC publication did not rewrite installed code. A parse did not certify the parse. A recognised variable name did not authenticate its sender. Negotiated willingness did not prove transmission. Transmission did not compel application. Process state did not prove execution. A command, print job or display effect required its own record.

Sources

The sources establish the specified grammar, the contemporary report of the BSD conflict, the heuristic compatibility procedure, the new option number and the registry record. They do not establish a named implementation, deployment share, exploit, authenticated login, accepted environment, created process, executed command or present operational risk.