Overview

Telnet isn't just a raw byte pipe — it carries an in-band negotiation language so the two ends can agree on options like echo, window size, and terminal type. This page explains that language and how the gateway speaks it.

A telnet connection is a stream of ordinary data bytes with one special value woven through it: 0xFF, the IAC (“Interpret As Command”) byte. When a receiver sees IAC it knows the next byte (or few) is a command, not data. Commands let the two ends turn features on and off by mutual agreement — this is option negotiation, defined by RFC 854 and refined by RFC 1143.

The model is deliberately symmetric and conservative: an option is only used if both ends agree, either side may refuse, and a refusal is always final. That is what lets a 1980s terminal and a modern client share one protocol — each enables only what it understands and politely declines the rest. Because a literal 0xFF can occur in real data, it is escaped on the wire as IAC IAC and collapsed back to a single 0xFF by the receiver.

Two places this happens in the gateway: when it acts as a telnet server (your client connects in), and when its outbound telnet gateway proxies you to a remote host. The verbs and options are the same; the policy differs, and both are covered below.

The Four Verbs: WILL / WONT / DO / DONT

Every option has two independent sides — “my side” and “your side.” Two verbs talk about each.

An IAC command of the form IAC <verb> <option> asserts or requests something. WILL / WONT talk about the sender's own side of an option; DO / DONT talk about what the sender wants from the other side:

VerbByteMeansValid replies
WILL251“I will enable this option on my end.”DO (yes, please) or DONT (no)
WONT252“I won't / am disabling this option on my end.”DONT (acknowledged)
DO253“Please enable this option on your end.”WILL (ok) or WONT (refused)
DONT254“Don't enable / disable this option on your end.”WONT (acknowledged)

The two rules that keep negotiation sane:

  • Enabling requires agreement. A WILL is only in effect once answered with DO; a DO only once answered with WILL. Either end may answer with the negative and the option stays off.
  • Disabling is always granted. You can never force an option on a peer: a WONT must be met with DONT, and a DONT with WONT. Refusals are never argued with.
The classic confusion: “who echoes?” is decided by the server sending WILL ECHO (“I will do the echoing”), which the client accepts with DO ECHO. It is not the client telling the server to echo — that asymmetry trips up nearly everyone at first.

IAC Command Bytes

The full command vocabulary. A command is IAC followed by one of these.

Negotiation & subnegotiation
NameDecHexPurpose
IAC2550xFFInterpret As Command — the escape that introduces every command (and, doubled, a literal 0xFF in data).
WILL2510xFBOffer / assert an option on the sender's side.
WONT2520xFCRefuse / disable an option on the sender's side.
DO2530xFDRequest an option on the receiver's side.
DONT2540xFERefuse / disable an option on the receiver's side.
SB2500xFASubnegotiation Begin — start an option's parameter block.
SE2400xF0Subnegotiation End — IAC SE closes the block.
Two-byte function commands

These have no option byte. The gateway consumes them so they never leak to your terminal; AYT gets a visible reply.

NameDecPurpose
NOP241No operation (keep-alive).
DM242Data Mark (the SYNCH sentinel).
BRK243Break.
IP244Interrupt Process.
AO245Abort Output.
AYT246Are You There — the gateway answers with a visible “yes” so a human probing the link gets a reply.
EC247Erase Character.
EL248Erase Line.
GA249Go Ahead — the half-duplex line-turnaround signal (suppressed once SGA is on; consumed if received).

The Options & Their Purpose

Each option is one numbered feature. Here is what every option the gateway handles is for, and how the gateway treats it.

ECHO — option 1 (RFC 857)

Decides which side echoes typed characters back to the screen. When the gateway sends WILL ECHO, it takes responsibility for echoing — so the client should stop local-echoing, and the gateway can suppress echo for password fields and run character-at-a-time apps. As a server the gateway asserts WILL ECHO at connect; as an outbound proxy it accepts a remote host's WILL ECHO with DO ECHO (a BBS that echoes for you).

SGA — Suppress Go-Ahead, option 3 (RFC 858)

Turns off the ancient half-duplex GA “your turn” signal. Suppressing it puts the link into full-duplex, character-at-a-time mode — the modern default. The gateway both offers it (WILL SGA) and asks for it (DO SGA), and never emits a GA itself. Paired with WILL ECHO this is what makes a session feel like a normal terminal.

TTYPE — Terminal Type, option 24 (RFC 1091)

Lets the client tell the server what kind of terminal it is. The gateway sends DO TTYPE; once the client agrees (WILL TTYPE) the gateway asks for the name with a SB TTYPE SEND subnegotiation, and the client answers SB TTYPE IS "<name>". The gateway maps that name to its three classes — PETSCII (Commodore), ANSI, or ASCII/DUMB — so it can skip the manual BACKSPACE detection prompt entirely.

NAWS — Window Size, option 31 (RFC 1073)

“Negotiate About Window Size” — lets the client report its terminal width and height. The gateway sends DO NAWS; the client agrees (WILL NAWS) and sends SB NAWS <width><height> (two 16-bit values), and re-sends it whenever the window is resized. The gateway uses the size for screen layout and forwards live resizes through the outbound gateway in cooperative mode.

STATUS — option 5 (RFC 859)

A way to ask the other end which options it thinks are on. On a DO STATUS the gateway agrees (WILL STATUS); a later SB STATUS SEND triggers a dump of the current option state. Rarely used in practice, but supported.

TIMING-MARK — option 6 (RFC 860)

A one-shot synchronization mark: “tell me when you've processed everything up to here.” On a DO TIMING-MARK the gateway flushes its queued output and then replies WILL TIMING-MARK — that reply is the mark. No persistent state is kept.

Everything else

Any option the gateway doesn't implement is politely refused: an incoming WILL is answered with DONT, and an incoming DO with WONT. Nothing is left un-acknowledged, so a peer never hangs waiting on a reply.

Well-Known Options at a Glance

Where the gateway sits in the wider telnet option landscape. “Refused” means it is acknowledged with WONT/DONT and left off — it does no harm, the feature just isn't used.

OptNameRFCGateway
0BINARY (Transmit-Binary)856Refused — uses IAC-escaping instead (see Data Phase).
1ECHO857Supported
3SGA (Suppress Go-Ahead)858Supported
5STATUS859Supported
6TIMING-MARK860Supported
24TERMINAL-TYPE (TTYPE)1091Supported
31NAWS (Window Size)1073Supported
32TERMINAL-SPEED1079Refused
33TOGGLE-FLOW-CONTROL1372Refused
34LINEMODE1184Refused (the gateway does its own line editing in char-at-a-time mode).
36ENVIRON1408Refused
39NEW-ENVIRON1572Refused (a stray subnegotiation is consumed and ignored).

Subnegotiation (SB … SE)

When an option needs to carry data — a terminal-type string, a window size — that data rides in a subnegotiation block.

A subnegotiation is framed as IAC SB <option> <parameters> IAC SE. Only options that were already enabled by WILL/DO may subnegotiate. The two the gateway uses:

BlockMeaning
IAC SB TTYPE SEND IAC SEServer → client: “send me your terminal type.”
IAC SB TTYPE IS "xterm" IAC SEClient → server: “I am an xterm.”
IAC SB NAWS <w><w><h><h> IAC SEClient → server: width and height, each a 16-bit value (high byte first).
Bounded for safety. A subnegotiation body is capped at 8 KiB. A malicious or broken peer that sends a huge SB with no terminating IAC SE can't exhaust memory — the body is bounded and the session bails rather than buffering without limit. Any literal 0xFF inside a subnegotiation is IAC IAC-escaped like everywhere else.

The NVT & the Data Phase

Negotiation decides the rules; the data phase is where actual bytes flow under them. Both ends pretend to be the same imaginary device — the Network Virtual Terminal.

Telnet's core abstraction is the NVT (Network Virtual Terminal): a canonical, lowest-common-denominator terminal that both ends agree to emulate so neither needs to know the other's real hardware. The NVT is a 7-bit ASCII device with a printer and a keyboard, and it imposes three wire conventions on ordinary data — the same three the gateway's raw I/O layer applies on every byte:

ConventionOn the wireWhy
End of lineCR LF (0x0D 0x0A)The NVT newline is the pair, not a lone LF.
Bare carriage returnCR NUL (0x0D 0x00)A CR not meant to start a new line is followed by NUL so the far end doesn't treat the next byte as a line start. The receiver strips the NUL.
Literal 0xFFIAC IAC (0xFF 0xFF)Since 0xFF is the command escape, a real 0xFF data byte is doubled and collapsed back on receipt.

The gateway implements all three: outbound writes stuff CRCR NUL and escape 0xFFIAC IAC; inbound reads strip the NUL and un-double the IAC. SSH and serial links skip them — SSH has no in-band command byte, and a serial wire isn't an NVT.

Why not BINARY mode?

The BINARY option (0, RFC 856) exists to suspend these NVT rules so all 256 byte values pass literally — the “clean 8-bit pipe” you'd want for a file transfer. The gateway deliberately does not negotiate BINARY (it refuses DO BINARY with WONT). Instead it stays in NVT mode and relies on IAC IAC escaping and CR NUL stuffing to carry arbitrary bytes safely. That keeps one consistent code path for every client — including the many retro clients that don't implement BINARY — and it is exactly the transform that XMODEM/YMODEM transfers over telnet must respect (the I toggle on the File Transfer menu). See the XMODEM Protocol Reference for how that interacts with binary file data.


The RFC 1143 Q-Method

How the gateway avoids negotiation loops when both ends change their minds at once.

Naive negotiation has a famous failure mode: if each side answers every request, and both request the same option at the same time, they can bounce WILL/DO back and forth forever. RFC 1143 solves it with a small state machine kept per option, per side. Each side is in one of six states:

StateMeaning
NOOption is off and idle.
YESOption is on and idle.
WANTYESWe asked to turn it on; awaiting the agreement.
WANTNOWe asked to turn it off; awaiting the acknowledgement.
WANTYES / WANTNO (opposite queued)A change-of-mind is queued behind the in-flight request, applied when it resolves.

The practical payoff: the gateway only ever sends a request when the state actually changes, and it treats a peer's spontaneous WILL TTYPE (in answer to its own DO TTYPE) as an acknowledgement rather than a fresh offer to answer again. No loops, no double-acks, and a clean resolution even when a mind-change is already in flight.

A simultaneous request, resolved

Without the state machine, both ends offering at once would echo forever. With it:

both ends decide they want TTYPE on, at the same instant
S: IAC DO TTYPE        gateway: state NO -> WANTYES
C: IAC WILL TTYPE      client's own offer, crossing on the wire
gateway receives WILL TTYPE while in WANTYES:
  -> treats it as the agreement, state -> YES
  -> does NOT emit another DO (which would loop)
S: (silent — option is now on, settled)

The mirror case — a queued change of mind — works the same way: if the gateway is in WANTYES and decides it wants the option off again, that flips it to WANTYES (opposite queued); when the in-flight request resolves, the queued WONT/DONT is sent exactly once. The connection always converges.


Inbound: Gateway as Telnet Server

What the gateway sends the instant your client connects.

On every non-serial connection the gateway opens with a fixed five-command burst, then waits briefly for the replies:

IAC WILL ECHO     "I'll do the echoing"
IAC WILL SGA      "full-duplex, no go-aheads from me"
IAC DO   SGA      "please suppress go-ahead too"
IAC DO   TTYPE    "tell me your terminal type"
IAC DO   NAWS     "tell me your window size"

A cooperating client answers DO ECHO / WILL SGA / DO SGA / WILL TTYPE / WILL NAWS and then sends its SB TTYPE IS and SB NAWS data. If TTYPE identified the terminal, the gateway skips the manual “press BACKSPACE” detection; otherwise it falls back to that prompt. Raw clients that ignore the burst still work — the unanswered options simply stay off.


Outbound: The Telnet Gateway Proxy

When you dial a remote host through the gateway, its negotiation policy depends on the configured mode.

ModeNegotiation behaviour
Reactive (default)Parse IAC both ways but send no proactive offers. Accept a remote WILL ECHO with DO ECHO; refuse every other peer-initiated option (WILL → DONT, DO → WONT); escape outbound 0xFF as IAC IAC. Works with real telnet servers and raw-TCP services on port 23.
CooperativeEverything reactive does, plus proactive WILL TTYPE / WILL NAWS / DO ECHO at connect; answers SB TTYPE SEND with your terminal type (PETSCII / ANSI / DUMB) and DO NAWS with your real window size, forwarding live resizes. Turn this on for modern BBSes.
Raw TCPThe entire IAC layer is disabled — no parsing inbound, no escaping outbound — for destinations that aren't really telnet. (Bytes to your local client are still IAC-escaped so your own telnet client isn't confused.)
Set the mode from the GUI / web Server → More popup or the telnet Configuration → Gateway Configuration menu — see the Gateways section of the User Manual. The same RFC 1143 Q-method drives the cooperative mode's option state.

A Full Session, Start to Finish

Putting it together: everything that happens from TCP connect to disconnect, in order.

Timeline

  1. TCP connect. Your client opens a socket to the gateway's telnet port (default 2323).
  2. Opening burst. The gateway immediately sends WILL ECHO, WILL SGA, DO SGA, DO TTYPE, DO NAWS, then pauses briefly for replies.
  3. Client replies. A cooperating client answers (DO ECHO, WILL SGA / DO SGA, WILL TTYPE, WILL NAWS) and may push its SB NAWS size right away. A raw client stays silent — those options simply remain off.
  4. Terminal type. The gateway sends SB TTYPE SEND; the client returns SB TTYPE IS "<name>", which the gateway maps to PETSCII / ANSI / ASCII.
  5. Fallback detection. If TTYPE didn't identify the terminal, the gateway prints “Press BACKSPACE to detect terminal” and classifies by the byte received (0x14 = PETSCII, 0x08/0x7F = ANSI, else ASCII).
  6. Colour prompt. The gateway asks whether to use colour (no default — Y or N).
  7. Authentication. If security_enabled, the login prompt runs (with the shared per-IP lockout).
  8. Interactive use. The main menu and all features run in the data phase as NVT bytes — CR NUL stuffing and IAC IAC escaping applied throughout, echo handled by the gateway.
  9. File transfer (if any). XMODEM/YMODEM honour the IAC / CR-NUL transforms per the I toggle; ZMODEM and Kermit layer their own escaping on top.
  10. Disconnect. On exit (or idle timeout) the gateway closes the session and the TCP socket drops.

Example Exchanges

A few negotiations as they actually appear on the wire (S: = server / gateway, C: = client).

Server takes over echo + full-duplex
S: IAC WILL ECHO      IAC WILL SGA   IAC DO SGA
C: IAC DO   ECHO      IAC DO   SGA   IAC WILL SGA
   -> server echoes; both sides full-duplex
Terminal-type discovery
S: IAC DO TTYPE
C: IAC WILL TTYPE
S: IAC SB TTYPE SEND IAC SE
C: IAC SB TTYPE IS "xterm-256color" IAC SE
   -> detected as ANSI; BACKSPACE prompt skipped
Window size, including a later resize
S: IAC DO NAWS
C: IAC WILL NAWS
C: IAC SB NAWS 0 80 0 24 IAC SE     80 x 24
   ...user resizes the window...
C: IAC SB NAWS 0 132 0 50 IAC SE    now 132 x 50
An option the gateway doesn't support
C: IAC WILL LINEMODE
S: IAC DONT LINEMODE        politely refused, stays off
“Are you there?”
C: IAC AYT
S: [Yes]                    visible reply for a human probing the link

Glossary
TermMeaning
NVTNetwork Virtual Terminal — the canonical imaginary terminal both ends emulate so neither needs to know the other's real hardware.
IACInterpret As Command (0xFF) — the escape byte that introduces every telnet command and, doubled, a literal 0xFF in data.
OptionA numbered, individually negotiable feature (ECHO = 1, SGA = 3, TTYPE = 24, …).
VerbOne of WILL / WONT / DO / DONT — the four ways to assert or request an option.
SubnegotiationAn IAC SB … IAC SE block that carries an enabled option's parameters (a terminal-type string, a window size).
Q-MethodRFC 1143's per-option state machine (NO / YES / WANTYES / WANTNO + queued change) that prevents negotiation loops.
Go-Ahead (GA)The half-duplex “your turn” line-turnaround signal, suppressed once SGA is enabled.
SYNCH / Data MarkAn out-of-band attention mechanism (TCP urgent data + the DM command). Recognised and consumed, not specially acted on here.
CR NULHow a bare carriage return is encoded on an NVT link (0x0D 0x00); the NUL is stripped on receive.

See Also
The Terminals and Gateways sections of the User Manual cover terminal detection and the outbound telnet/SSH proxy. For the file-transfer-over-telnet byte transforms (IAC escaping and CR-NUL stuffing during XMODEM/YMODEM), see the XMODEM Protocol Reference.

References

Deep-dive reference pages for every protocol and interface, plus the character-set tables and ANSI escape-sequence reference.

XMODEM

128-byte blocks; CRC-16 / checksum negotiation.

YMODEM

Block-0 metadata, batch, exact size truncation.

ZMODEM

Streaming; ZDLE, CRC-32, autostart, resume.

Kermit

Send-Init negotiation; F / A / D / Z / B transfer.

Punter

C1 dual checksum, two-phase, GOO/BAD/ACK.

AT Commands

Hayes command set, S-registers, +++ escape.

Telnet (this page)

IAC negotiation, every option, the NVT data phase.

SSH

Server, gateway, host keys, TOFU, auth modes.

Character Code Tables

Hex tables for every encoding: ASCII, ANSI, PETSCII, ATASCII, Baudot/ITA2, ZX Spectrum, TRS-80.

ANSI Escape Sequences

Cursor, colour/SGR, erase, and screen-mode escape codes, with the raw hex bytes.