You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a supported interactive Telnet CLI that shares the library's transport and optional login path, and fix protocol behavior needed for reliable terminal sessions. This provides a standalone way to reproduce manual and automatic-login connections when troubleshooting library consumers such as Koko.
Changes
Interactive CLI
Add cmd/tclient with manual login by default and explicit --auto-login --user --ask-pass authentication. Support IPv4/IPv6, charset conversion, Enter mapping, negotiated local echo, window updates, and Ctrl+] commands.
Restore terminal settings and preserve original file flags on disconnect, errors, signals, and blocked or broken output pipes, including Go-managed nonblocking input/output. Keep local commands and echo on stderr so redirected stdout contains remote application data.
Keep local commands responsive during blocked network sends with an ordered 256 KiB input queue. Report an error and close the session on overflow; keep redirected input streaming with backpressure.
Send window updates through a serialized worker and refresh dimensions after automatic login, so peer backpressure cannot block quit or disconnect handling. Cancel blocked redirected pipe I/O on Windows as well as Unix.
After stdin EOF, drain final output with a deadline while keeping the write side available for Telnet replies. Provide --close-write for endpoints that require TCP EOF.
Create optional traces exclusively with mode 0600 and a sensitive-data warning. Reuse the CLI from _example/telnet_client.
Protocol handling
Decode receive-side NVT CR-NUL sequences in wire order, including fragmented input and BINARY transitions within one read.
Support SGA in both directions and serialize NAWS updates with negotiation so stale window packets cannot follow option shutdown.
Honor environment-variable requests, ordering, undefined values, and escaping. Export only the configured USER and retain legacy ENVIRON compatibility.
Process state-preserving, no-reply negotiation events without waiting for a blocked application write; retain write serialization for actual option changes and replies.
Compatibility
Client.Write continues to preserve application bytes and escape IAC. Existing automatic-login configuration and LineEnding behavior remain unchanged.
Observable receive change:Client.Read now removes the protocol NUL immediately following CR when receive BINARY mode is disabled. CRLF, standalone NUL, and binary data are preserved.
Add the read-only Client.TerminalMode() snapshot; terminal control remains in the CLI.
The old example now uses the CLI's flags. Replace -ip/-port with positional host [port], and -username/-password with --auto-login --user ... --ask-pass. See usage and migration details.
Validation and scope
Linux and Windows CI passed on Go 1.20.x and stable, including go test -race -timeout=3m ./..., go vet ./..., and both examples.
All 20 Linux PTY/TCP scenarios passed, covering echo, resize during automatic login, input fragmentation, EOF negotiation, final output, terminal restoration, blocked output, hidden Unicode password editing, and trace permissions. Targeted regression tests also cover blocked NAWS and input writes during local quit, EOF deadlines before queued sends complete, ordered data/commands/flush/half-close, bounded queue overflow, original file-flag preservation, and no-reply negotiations during blocked application writes.
Windows native tests cover synchronous and overlapped pipe I/O, cancellation before and during blocked operations, reuse of the original handles, and session drain timeouts. macOS arm64 cross-compilation passed. Interactive terminals currently target Unix; Windows supports redirected sessions and rejects TTY input before changing console settings.
Interaction is character-at-a-time. LINEMODE/local line editing and TCP urgent/SYNCH are outside this change. Koko integration and real vendor-device interoperability have not been tested in this PR.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Peer backpressure can deadlock negotiation reads and resize handling, while uncancellable non-Unix output writes can prevent session shutdown and cleanup.
Adds a supported interactive tclient CLI using the library’s existing transport and optional login flow, alongside improved terminal protocol handling.
Changes:
Decode receive-side NVT data in wire order, expose negotiated terminal modes, and expand environment negotiation.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Synchronous network writes can block local quit handling, and descriptor access can change file flags that cleanup fails to restore.
No open issues 🎉
2 previously missed issues
In code that hasn't changed since last review
Blocked network writes prevent processing local quit commands
internal/cli/input.go:68
If the peer stops reading and a paste fills the TCP send buffer, this synchronous write blocks the only stdin-reading goroutine, so subsequent Ctrl+] quit cannot be processed. Interactive writes have no deadline, and raw-mode Ctrl+C is sent remotely rather than interrupting the client. Keep local-command parsing independent of blocked network writes, with a cancellable send worker and a bounded queue policy that still allows local quit to close the connection.
File.Fd changes descriptor mode and breaks flag restoration
internal/cli/terminal_unix.go:81
For a Go-managed nonblocking pipe such as os.Pipe, File.Fd() switches the descriptor to blocking mode before this flags snapshot, so cleanup restores the wrong flags. The input-side Fd() calls in run.go, input.go, and this file likewise change stdin without restoring its original flags, even if connecting fails. Use non-mutating descriptor access through SyscallConn.Control for both output snapshots and input/terminal operations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add a supported interactive Telnet CLI that shares the library's transport and optional login path, and fix protocol behavior needed for reliable terminal sessions. This provides a standalone way to reproduce manual and automatic-login connections when troubleshooting library consumers such as Koko.
Changes
Interactive CLI
cmd/tclientwith manual login by default and explicit--auto-login --user --ask-passauthentication. Support IPv4/IPv6, charset conversion, Enter mapping, negotiated local echo, window updates, andCtrl+]commands.--close-writefor endpoints that require TCP EOF.0600and a sensitive-data warning. Reuse the CLI from_example/telnet_client.Protocol handling
Compatibility
Client.Writecontinues to preserve application bytes and escape IAC. Existing automatic-login configuration andLineEndingbehavior remain unchanged.Client.Readnow removes the protocol NUL immediately following CR when receive BINARY mode is disabled. CRLF, standalone NUL, and binary data are preserved.Client.TerminalMode()snapshot; terminal control remains in the CLI.-ip/-portwith positionalhost [port], and-username/-passwordwith--auto-login --user ... --ask-pass. See usage and migration details.Validation and scope
Linux and Windows CI passed on Go 1.20.x and stable, including
go test -race -timeout=3m ./...,go vet ./..., and both examples.All 20 Linux PTY/TCP scenarios passed, covering echo, resize during automatic login, input fragmentation, EOF negotiation, final output, terminal restoration, blocked output, hidden Unicode password editing, and trace permissions. Targeted regression tests also cover blocked NAWS and input writes during local quit, EOF deadlines before queued sends complete, ordered data/commands/flush/half-close, bounded queue overflow, original file-flag preservation, and no-reply negotiations during blocked application writes.
Windows native tests cover synchronous and overlapped pipe I/O, cancellation before and during blocked operations, reuse of the original handles, and session drain timeouts. macOS arm64 cross-compilation passed. Interactive terminals currently target Unix; Windows supports redirected sessions and rejects TTY input before changing console settings.
Interaction is character-at-a-time. LINEMODE/local line editing and TCP urgent/SYNCH are outside this change. Koko integration and real vendor-device interoperability have not been tested in this PR.