RFC 764
Telnet Protocol specification
RFC 764: TELNET PROTOCOL SPECIFICATION π¦
bestie, this doc is from J. Postel at ISI, dated June 1980. It's also filed as IEN 148 and RFC 764. The header I was given doesn't spell out a category or status, and it doesn't list anything it obsoletes or updates, so I'm not guessing π
Part 1 of 4 β¨
INTRODUCTION π
okay so the whole point of TELNET is to give everyone a standard way to hook up terminal devices and terminal-style programs to each other π it's a fairly general, two-way, eight-bit byte oriented communications facility, so it can carry stuff in both directions. the people who wrote it also imagined it being used for terminal-to-terminal chats ("linking") and for process-to-process stuff (distributed computation) π§
GENERAL CONSIDERATIONS πΈ
a TELNET connection is a Transmission Control Protocol (TCP) connection that carries data with TELNET control information mixed in between. TCP and the way you set up the connection are documented in the ARPA Internet Protocol Handbook π
TELNET is built on three main ideas, and they're all lowkey iconic: first, the "Network Virtual Terminal"; second, negotiated options; and third, a symmetric view of terminals and processes π
- The Network Virtual Terminal (NVT) π
When a TELNET connection is first established, each end is assumed to start and finish at a "Network Virtual Terminal", or NVT. The NVT is an imaginary device that gives everyone one standard, network-wide, in-between way to represent a canonical terminal. So "server" and "user" Hosts* don't have to keep track of each other's terminal quirks π Every Host, user or server, maps its own local device stuff so it looks like it's talking to an NVT over the network, and each side can assume the other one is doing the same mapping. The NVT is trying to hit a sweet spot: not so restricted that Hosts don't get a rich enough vocabulary to map onto their local character sets, and not so inclusive that users with modest terminals get penalized π
*NOTE: The "user" Host is the Host the physical terminal is normally attached to, and the "server" Host is the Host that's normally providing some service. As an alternate view, which works even for terminal-to-terminal or process-to-process communication, the "user" Host is the Host that initiated the communication.
- Negotiated options π₯
This one takes into account that lots of sites will want to offer extra services beyond what an NVT gives, and lots of users have fancy terminals and want elegant services, not bare-minimum ones. Inside the TELNET Protocol, separate from it but structured within it, "options" get sanctioned. Parties can use them with the "DO, DON'T, WILL, WON'T" structure (more on that below) to agree on a fancier, or just different, set of conventions for the connection. Options could change things like the character set, the echo mode, the line width, the page length, and so on β¨
The basic strategy is that either party (or both) starts by requesting that some option take effect. The other party can accept or reject. If it's accepted, the option takes effect right away. If it's rejected, that part of the connection stays the way the NVT says it should be. Honestly, a party may always refuse a request to enable an option, but it must never refuse a request to disable one, since everyone has to be ready to support the NVT π«Ά
The syntax is set up so that if both parties request the same option at the same time, each one sees the other's request as the positive acknowledgment of its own. Main character energy π¦
- Symmetry, and why it can cause chaos π
The symmetry of the negotiation syntax could lead to endless acknowledgment loops, where each party sees the incoming commands as new requests that need answering instead of as acknowledgments. To stop that, these rules apply:
- a. Parties may only request a change in option status. A party may not send out a "request" just to announce what mode it's in.
- b. If a party gets what looks like a request to enter a mode it's already in, the request should not be acknowledged.
- c. Whenever one party sends an option command to a second party, whether as a request or an acknowledgment, and using the option will affect how the data goes from the first party to the second, then the command must be inserted in the data stream at the exact point where it should take effect. (Some time passes between sending a request and getting an acknowledgment, and that acknowledgment might be negative. So a site may want to buffer data after requesting an option until it learns whether the request was accepted or rejected, to hide that "uncertainty period" from the user π)
Option requests are gonna flurry back and forth when a connection is first set up, since everyone's trying to get the best service from the other side π But after that, options can also change the connection's characteristics on the fly to match what's going on locally. For example, the NVT (more on it later) uses a transmission style that's great for "line at a time" apps like BASIC, but not great for "character at a time" apps like NLS. A server might decide to take on the extra processor overhead for a "character at a time" style when it suits the local process, and negotiate for that option. Then, instead of being stuck with that extra load forever, it can switch (negotiate) back to NVT once the more "taut" control isn't needed anymore π
Processes can also cause a nonterminating request loop if they respond to a rejection by just re-requesting the same option. To prevent that, rejected requests shouldn't be repeated until something changes. That might mean the process is running a different program, or the user gave another command, or whatever makes sense for that process and option. A good rule of thumb: a re-request should only happen because of new information from the other end of the connection, or when a human steps in locally π
Option designers shouldn't feel boxed in by the somewhat limited syntax for option negotiation. The simple syntax is meant to make options easy to have, since it's just as easy to say "I don't know about that one." If a particular option needs a richer negotiation structure than "DO, DON'T, WILL, WON'T" can give, the right move is to use those four commands to establish that both parties understand the option. Once that's done, a more exotic syntax can be used freely. For example, a party might send a request to change (establish) the line length. If it's accepted, a different syntax can handle the actual line length negotiation, like a "sub-negotiation" with fields for minimum allowable, maximum allowable, and desired line lengths. The key idea is that the expanded negotiations should never begin until some earlier (standard) negotiation has shown that both parties can parse the expanded syntax π
In summary, WILL XXX is sent by either party to show that the party wants to offer to begin performing option XXX. DO XXX and DON'T XXX are its positive and negative acknowledgments. Similarly, DO XXX is sent to request that the other party (the recipient of the DO) begin performing option XXX, and WILL XXX and WON'T XXX are the positive and negative acknowledgments. Since the NVT is what's left when no options are enabled, the DON'T and WON'T responses are guaranteed to leave the connection in a state both ends can handle. So all Hosts can be totally unaware of options they don't support, and just return a rejection (refuse) to any option request they can't understand π
TELNET is made server-user symmetrical as much as possible, so it covers the user-user (linking) and server-server (cooperating processes) cases easily and naturally. It's hoped, but not absolutely required, that options will help with that goal. Either way, symmetry is openly acknowledged as an operating principle, not an ironclad rule πΈ
There's also a companion document, "TELNET Option Specifications," that you should check for how to set up new options. That document, along with descriptions of every currently defined option, lives in the TELNET section of the ARPA Internet Protocol Handbook π
THE NETWORK VIRTUAL TERMINAL π§
The Network Virtual Terminal (NVT) is a two-way character device. It has a printer and a keyboard. The printer responds to incoming data, and the keyboard produces outgoing data that gets sent over the TELNET connection and, if "echoes" are wanted, to the NVT's printer too. "Echoes" aren't expected to travel over the network (even though options exist for a "remote" echoing mode, no Host is required to implement that option) π«Ά The code set is seven-bit USASCII in an eight-bit field, except where modified here. Any code conversion and timing issues are local problems and don't affect the NVT π
Transmission of Data π
Even though a TELNET connection through the network is fully duplex by nature, the NVT should be viewed as a half-duplex device working in a line-buffered mode. Unless and until options are negotiated otherwise, these default conditions apply to sending data over the TELNET connection:
- As far as local buffer space allows, data should pile up in the Host where it's generated until a complete line is ready to send, or until some locally defined explicit signal to transmit happens. That signal could come from a process or from a human user π
The reason for this rule is that processing network input interrupts is expensive for some Hosts, and the default NVT spec says "echoes" don't go over the network. So it makes sense to buffer some data at its source. Lots of systems do some processing at the end of each input line (even line printers and card punches tend to work this way), so transmission should be triggered at the end of a line. On the other hand, a user or process may sometimes need to send data that doesn't end at a line break. So implementers are cautioned to provide ways of locally signaling that all buffered data should be transmitted immediately β
- okay so, when a process has finished sending data to an NVT printer and has no queued input from the NVT keyboard left to process (aka a process at one end of a TELNET connection can't move forward without input from the other end π₯Ί), it must send the TELNET Go Ahead (
GA) command. π
bestie, this doesn't mean you need to send a GA at the end of every line π ββοΈ server Hosts don't normally need an extra signal on top of end-of-line or other locally-defined characters to start processing. the GA is really there to help a user's local Host run a physically half duplex terminal with a "lockable" keyboard, like the IBM 2741. π let me explain how that kind of terminal works, because it makes the GA make sense.
the terminal-computer connection is always controlled by either the user or the computer. neither one can grab control from the other. instead, whoever has control has to give it up explicitly. on the terminal side, the hardware is built to hand control back every time a "line" ends (aka when the user hits the "New Line" key) π. then the attached (local) computer processes the input, decides whether any output should be generated, and if not, gives control back to the terminal. if output should be generated, the computer keeps control until all of the output has been sent. β¨
now, the network makes this messy. the "local" computer can't decide whether to keep control after seeing an end-of-line signal anymore, because that call can only be made by the "remote" computer doing the actual processing π. so the TELNET GA command is how the "remote" (server) computer tells the "local" (user) computer that it's time to hand control over to the terminal user. it should be sent at those times, and only at those times, when the user should get control of the terminal. π«Ά if you send a GA too early, output can get blocked, because the user will assume the sending system has just paused and won't flip the line around manually π.
the stuff above doesn't apply to the user-to-server direction. in that direction GAs may be sent at any time, but they never have to be. also, if the TELNET connection is being used for process-to-process communication, GAs don't need to be sent in either direction. and for terminal-to-terminal communication, GAs may be required in neither direction, one direction, or both. if a Host plans to support terminal-to-terminal communication, it's suggested that the Host gives the user a way to manually signal that it's time to send a GA over the TELNET connection. however, this isn't a requirement on the implementer of a TELNET process. π
Standard Representation of Control Functions β¨
okay so as the Introduction said, the main goal of TELNET is a standard way for terminal devices and terminal-oriented processes to talk to each other over the network πΈ. early experiences showed that most servers have certain functions, but the ways to invoke those functions are all over the place. for a human who uses lots of different server systems, that's so frustrating π€. so TELNET defines standard representations for five of these functions, described below. these standard representations have standard, but not required, meanings (the exception is that the IP function may be required by other protocols that use TELNET). π¦ a system that doesn't provide a function to its local users doesn't need to provide it to network users, and it may treat the standard representation for that function as a No-operation. on the flip side, a system that does provide the function to local users is obliged to provide the same function to a network user who sends the standard representation for it. no sneaky differences allowed π ββοΈ.
Interrupt Process (IP)
many systems have a function that suspends, interrupts, aborts, or terminates a user's process π. people use it a lot when they think their process is stuck in an endless loop, or when some unwanted process got started by accident. IP is the standard representation for invoking this function. π₯ implementers should know that other protocols that use TELNET may require IP, so it should be implemented if you want to support those protocols.
Abort Output (AO)
many systems have a function that lets a process that's generating output run to completion (or stop at the same point it would have reached if it ran to completion), but without sending that output to the user's terminal π. this function also usually clears any output that's been produced but not yet actually printed or displayed on the user's terminal. AO is the standard representation for invoking this function. for example, a subsystem might take a user's command, send a long text string back to the terminal, and then say it's ready for the next command by sending a "prompt" character (preceded by <CR><LF>) to the terminal π
. if AO arrives while that text string is still being sent, a reasonable implementation would be to suppress the rest of the text string, but still send the prompt character and the <CR><LF> before it. (this is different from what might happen if an IP arrived instead, since IP might suppress the text string and exit the subsystem π.)
systems that provide this function should also know that there may be buffers outside the system (in the network and in the user's "local" Host) that should be cleared. the proper way to clear them is to send the "Synch" signal described below β¨.
Are You There (AYT)
many systems have a function that gives the user some visible (e.g., printable) evidence that the system is still up and running π. a user might invoke this when the system goes "silent" for a long time because of a computation that took longer than they expected, a really heavy system load, etc. AYT is the standard representation for invoking this function. π§
Erase Character (EC)
many systems have a function that deletes the last preceding undeleted character or "print position"* from the stream of data the user is sending π§Ή. it's usually how people fix typing mistakes. EC is the standard representation for invoking this function.
*NOTE: A "print position" may contain several characters that are the result of overstrikes, or of sequences such as <char1> BS <char2>...
Erase Line (EL)
many systems have a function that deletes all of the data in the current "line" of input ποΈ. it's usually used to edit keyboard input. EL is the standard representation for invoking this function. π
The TELNET "Synch" Signal
okay this part is lowkey important π¨. most time-sharing systems have mechanisms that let a terminal user regain control of a "runaway" process. the IP and AO functions above are examples of these. when those systems are used locally, they can see all of the signals the user sends, whether they're normal characters or special "out of band" signals, like the teletype "BREAK" key or the IBM 2741 "ATTN" key. but that's not necessarily true when terminals connect through the network π©. the network's flow control mechanisms can cause one of those signals to get buffered somewhere else, for example in the user's Host.
to fix this problem, TELNET introduces the "Synch" mechanism. a Synch signal is made of a TCP Urgent notification combined with the TELNET command DATA MARK. the Urgent notification isn't subject to the flow control for the TELNET connection, and it's used to trigger special handling of the data stream by the process receiving it. in this mode, the data stream is scanned immediately for "interesting" signals (defined below), and any data in between gets discarded πββοΈ. the TELNET command DATA MARK (DM) is the synchronizing mark in the data stream. it tells the recipient that any special signal has already happened, so it can go back to normal processing. π¦
the Synch is sent with the TCP send operation with the Urgent flag set, and the DM is the last (or only) data octet π¦.
when several Synchs are sent in quick succession, the Urgent notifications may get merged π« . you can't count Urgents, because the number received will be less than or equal to the number sent. in normal mode, a DM is a no-operation. in urgent mode, it signals the end of the urgent processing (this should line up with the end of the Urgent pointer that TCP indicates).
if TCP indicates the end of Urgent data before theDMis found, TELNET should keep the special handling of the data stream going until theDMis found. π¦
"Interesting" signals are defined as: the TELNET standard representations of IP, AO, and AYT (but not EC or EL); the local analogs of these standard representations (if any); all other TELNET commands; and other site-defined signals that can be acted on without delaying the scan of the data stream. β¨
since one effect of the Synch mechanism is that it discards essentially all characters (except TELNET commands) between the sender of the Synch and its recipient, this mechanism is specified as the standard way to clear the data path whenever that's needed π§Ό. for example, if a user at a terminal causes an AO to be sent, the server that receives the AO (if it provides that function at all) should send a Synch back to the user π.
finally, just like the TCP Urgent notification is needed at the TELNET level as an out-of-band signal, other protocols that use TELNET may need a TELNET command that works as an out-of-band signal at a different level π. more parts to come, bestie π«Ά
okay so bestie, the sequence [IP, Synch] is the convention for this signal π¦ Picture this: some other protocol that runs on TELNET defines the string STOP the same way TELNET defines its AO command π Now a user wants the server to process that STOP, but the connection is jammed because the server is busy with other stuff π Here's what the user does:
- Send the TELNET IP character;
- Send the TELNET SYNCH sequence, which is: send the Data Mark (DM) as the only character in a TCP urgent mode send operation;
- Send the character string STOP; and
- Send the other protocol's analog of the TELNET DM, if there is one π
The user (or the process acting for them) must send the TELNET SYNCH sequence from step 2 so the TELNET IP actually makes it to the server's TELNET interpreter π«Ά
The Urgent should wake up the TELNET process, and the IP should wake up the next higher level process. π₯
THE NVT PRINTER AND KEYBOARD
The NVT printer has an unspecified carriage width and page length, and it can make representations of all 95 USASCII graphics (codes 32 through 126) π¨οΈ Out of the 33 USASCII control codes (0 through 31 and 127) and the 128 uncovered codes (128 through 255), these are the ones with a specified meaning for the NVT printer:
NAME CODE MEANING
NULL (NUL) 0 A no operation
Line Feed (LF) 10 Moves the printer to the
next print line, keeping the
same horizontal position
Carriage Return (CR) 13 Moves the printer to the left
margin of the current line.
The following codes shall have defined, but not required, effects on the NVT printer. Neither end of a TELNET connection may assume that the other party will take, or will have taken, any particular action when these are received or sent π
BELL (BEL) 7 Produces an audible or
visible signal (which does
NOT move the print head)
Back Space (BS) 8 Moves the print head one
character position towards
the left margin.
Horizontal Tab (HT) 9 Moves the printer to the
next horizontal tab stop.
It remains unspecified how
either party determines or
establishes where such tab
stops are located.
Vertical Tab (VT) 11 Moves the printer to the
next horizontal tab stop.It
remains unspecified how
either party determines or
establishes where such tab
stops are located.
Form Feed (FF) 12 Moves the printer to the top
of the next page, keeping
the same horizontal position
Every other code does absolutely nothing to the NVT printer π€
Here's the CR drama π The sequence "CR LF", as defined, puts the NVT at the left margin of the next print line (and so does "LF CR"). But plenty of systems and terminals don't treat CR and LF separately, and they have to work pretty hard to fake the effect. (Like, some terminals don't have a CR apart from the LF, but on those it might be possible to fake a CR by backspacing.) So "CR LF" must be treated as one single "new line" character and used whenever the combined action is what you want. "CR NUL" must be used when a carriage return alone is really what you want, and the CR character must be avoided in every other context. This rule gives systems that have to decide between a "new line" function and a multiple-backspace a guarantee that the TELNET stream has a character after the CR that lets them make a rational choice π§
Note that "CR LF" or "CR NUL" is required in both directions (in the default ASCII mode), to preserve the symmetry of the NVT model. Even when it's known in some situations (for example, with remote echo and suppress go ahead options in effect) that the characters aren't going to an actual printer, the protocol still requires that a NUL be inserted after a CR that isn't followed by a LF in the data stream, just for consistency π The flip side: a NUL received after a CR in the data stream (unless option negotiations explicitly say otherwise) should be stripped out before the NVT is applied to the local character set mapping β¨
The NVT keyboard has keys, key combinations, or key sequences for generating all 128 USASCII codes. Even though lots of them do nothing to the NVT printer, the keyboard can still generate them π
On top of that, the NVT keyboard shall be capable of generating the following extra codes. Except where noted, these have defined but not required meanings. Their actual code assignments live in the TELNET Command section, since they're kinda generic and should still be available even when the data stream is read as some other character set π
Synch
This key lets the user clear their data path to the other party. Hitting it sends a DM (see the command section) in the data stream, and a TCP Urgent notification goes along with it. The DM-Urgent pair has the required meaning we talked about earlier π§Ή
Break (BRK)
This one exists because it's a signal outside the USASCII set that lots of systems already give a local meaning to. It's meant to show that the Break Key or the Attention Key got hit. Heads up though: this is there to give a 129th code to systems that need one, not to act as a synonym for the IP standard representation β οΈ
Interrupt Process (IP)
Suspend, interrupt, abort or terminate the process the NVT is connected to. It's also part of the out-of-band signal for other protocols that use TELNET π
Abort Output (AO)
Let the current process (appear to) run to completion, but don't send its output to the user. It also sends a Synch to the user π
Are You There (AYT)
Send back something visible (printable) to the NVT as proof that the AYT arrived π
Erase Character (EC)
The recipient deletes the last preceding undeleted character or "print position" from the data stream π§½
Erase Line (EL)
The recipient deletes characters from the data stream back to, but not including, the last "CR LF" sent over the TELNET connection ποΈ
The vibe behind these "extra" keys (and the printer format effectors) is that they're a natural extension of the mapping that already has to happen from "NVT" into "local" π Just like NVT data byte 104 maps to whatever the local code for "uppercase D" is, the EC character should map to whatever the local "Erase Character" function is. And just like the mapping for 174 is kinda arbitrary in an environment with no "vertical bar" character, the EL character can have a somewhat arbitrary mapping (or none at all) when there's no local "Erase Line" facility. Same deal for format effectors: if the terminal really does have a "Vertical tab", the VT mapping is obvious. Only when the terminal doesn't have a vertical tab should the effect of VT be unpredictable π¦
TELNET COMMAND STRUCTURE
Every TELNET command is at least a two byte sequence: the "Interpret as Command" (IAC) escape character followed by the command's code π The commands for option negotiation are three byte sequences, and the third byte is the code for the option being referenced. This format was picked so that as more serious use of the "data space" happens (through negotiations from the basic NVT, obviously), collisions between data bytes and reserved command values stay minimal. Any collision that does happen needs the annoying, inefficient "escaping" of the data bytes into the stream π© With the current setup, only the IAC needs doubling to be sent as data, and the other 255 codes can pass through transparently π§
The following are the defined TELNET commands. These codes and code sequences only have the meaning shown when they come right after an IAC π
NAME CODE MEANING
SE 240 End of subnegotiation parameters
NOP 241 No operation
Data Mark 242 The data stream portion of a Synch
This should always be accompanied
by a TCP Urgent notification.
Break 243 NVT character BRK
Interrupt Process 244 The function IP
Abort output 245 The function AO
Are You There 246 The function AYT
Erase character 247 The function EC
Erase Line 248 The function EL
Go ahead 249 The GA signal
SB 250 Indicates that what follows is
subnegotiation of the indicated
option
WILL (option code) 251 Indicates the desire to begin
performing, or confirmation that
you are now performing, the
indicated option
WON't (option code) 252 Indicates the refusal to perform,
or continue performing, the
indicated option.
DO (option code) 253 Indicates the request that the
other party perform, or
confirmation that you are expecting
the other party to perform, the
indicated option.
DON'T (option code) 254 Indicates the demand that the
other party stop performing,
or confirmation that you are no
longer expecting the other party
to perform, the indicated option.
IAC 255 Data Byte 255.
CONNECTION ESTABLISHMENT β¨
okay so the TELNET TCP connection gets set up between the user's port U and the server's port L π the server sits there listening on its well known port L waiting for people to knock πͺ
bestie, TCP connections are full duplex and they're identified by the pair of ports, so the server can run tons of connections at the same time π
all using its one port L with a bunch of different user ports U. it's giving multitasking queen energy π
Port Assignment
when this protocol is used for remote user access to service hosts (aka remote terminal access π»), it gets server port 23 (27 octal) π so L=23 β¨ iykyk π«Ά
π¦ where this page comes from
- ietf: rfc764 1980-06
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms