Build a local Telnet lab
Start a loopback server
Create a tiny local TCP server that shows exactly which bytes a Telnet client sends without exposing a network service.
To study what a Telnet client actually sends, you need a server you fully control. Not a real telnetd. Just something that accepts a TCP connection and shows you every byte. A few lines of Node.js are enough.
Create server.mjs:
import net from 'node:net'
const server = net.createServer(socket => {
socket.write('Welcome\r\n')
socket.on('data', data => {
console.log(data.toString('hex'))
socket.write(`Received ${data.length} bytes\r\n`)
})
})
server.listen(2323, '127.0.0.1', () => {
console.log('Listening on 127.0.0.1:2323')
})
The hexadecimal logging is the whole point. Printing incoming data as text would hide the bytes we care about most: line endings and Telnet protocol commands, which are not printable characters. In hex, nothing hides.
Run it in one terminal:
node server.mjs
You should see Listening on 127.0.0.1:2323. The process stays in the foreground, waiting. Leave it running. The next lesson connects to it.
Two deliberate choices
Look at that listen call. Binding to 127.0.0.1 keeps the lab on your computer. The server is unreachable from the network, so you can experiment freely without exposing an unauthenticated service to your LAN. If you bound to 0.0.0.0 instead, anyone on your Wi-Fi could connect.
And port 2323 is an unprivileged stand-in for the well-known Telnet port 23. Binding to 23 would need root on Unix-like systems, and there is no reason to run this lab as root.
If the server exits immediately with EADDRINUSE, something already occupies port 2323. Probably a previous run of the same script you forgot about. Find it with lsof -i :2323 and stop it.
What this server is not
This is not a Telnet server. It never sends IAC negotiation and never parses commands. It accepts TCP bytes and echoes statistics back.
That neutrality is useful. Whatever appears in the hex log came from the client, not from us. When you see protocol bytes in there, you know exactly who sent them.
The netcat alternative
If you would rather not write code, netcat can play the same role. Stop the Node server first, because both want port 2323.
Use netcat as a tiny local server in one terminal:
nc -l 127.0.0.1 2323
Connect from another terminal:
telnet 127.0.0.1 2323
Type on both sides. You are seeing a TCP byte stream, not a full remote-login service. Stop both programs, then repeat with tcpdump on the loopback interface. This separates the TCP connection from Telnet option negotiation.
Lesson completed