Build a local Telnet lab
Observe option negotiation bytes
Make the local server request a Telnet option and decode the client response as protocol bytes instead of printable text.
8 minute lesson
So far the lab server has only received data you typed. Now make it speak the Telnet protocol itself. Add this line immediately after the server accepts a connection:
socket.write(Buffer.from([255, 253, 31]))
The bytes mean IAC DO NAWS: the server asks the client to report window size. Byte 255 is IAC, 253 is DO, and 31 is the option code for Negotiate About Window Size, defined in RFC 1073.
A client that supports the option can answer IAC WILL NAWS and send a NAWS subnegotiation carrying its dimensions.
Restart the server, reconnect with the Telnet client, then inspect the hexadecimal log. You should see something like:
fffb1f
fffa1f00500018fff0
Decode it byte pair by byte pair. A response beginning fffb1f is IAC WILL NAWS: ff is 255, fb is 251 (WILL), 1f is 31 (NAWS). The client accepted.
A later sequence beginning fffa1f is the NAWS subnegotiation. fa is 250, the SB command. The four data bytes that follow are the width and height, two bytes each. In this example 0050 is 80 columns and 0018 is 24 rows. The fff0 at the end is IAC SE, closing the subnegotiation.
Resize your terminal window while connected. A well-behaved client sends a fresh fffa1f sequence with the new dimensions. You are watching live option traffic in your own log.
When the client says no
Client behavior varies. A refusal beginning fffc1f is also a valid result: IAC WONT NAWS. Netcat, for example, will not answer at all, because it is not a Telnet client and treats your three bytes as ordinary data.
Record what happened instead of assuming every client implements the same options. That habit is the whole point of this lab: negotiation is a conversation, and you only know its outcome by reading the actual bytes.
One thing to notice in your log: none of these bytes appeared on the client’s screen. The client consumed them as protocol traffic. Data and commands share the stream, and this is what that looks like in practice.
Lesson completed