| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- package cli
- import (
- "bytes"
- "io"
- "net"
- )
- const (
- iac = 255 // Interpret As Command
- dont = 254
- do = 253
- wont = 252
- will = 251
- sb = 250
- se = 240
- optEcho = 1
- optSuppressGoAhead = 3
- optTerminalType = 24
- optWindowSize = 31
- optLinemode = 34
- )
- // TelnetConn wraps a net.Conn and handles RFC 854 Telnet negotiations
- // while presenting a clean stream to terminal.NewTerminal.
- type TelnetConn struct {
- net.Conn
- subnegotiation bool
- state byte
- optionCommand byte
- lastCR bool
- }
- func NewTelnetConn(conn net.Conn) *TelnetConn {
- tc := &TelnetConn{Conn: conn}
- tc.initNegotiation()
- return tc
- }
- func (tc *TelnetConn) initNegotiation() {
- // Request client to suppress go-ahead and handle character-at-a-time / local echo off
- // IAC WILL ECHO, IAC WILL SUPPRESS_GO_AHEAD, IAC DONT LINEMODE
- initSeq := []byte{
- iac, will, optEcho,
- iac, will, optSuppressGoAhead,
- iac, dont, optLinemode,
- }
- _, _ = tc.Conn.Write(initSeq)
- }
- func (tc *TelnetConn) Read(p []byte) (n int, err error) {
- if len(p) == 0 {
- return 0, nil
- }
- buf := make([]byte, len(p))
- for {
- rawN, readErr := tc.Conn.Read(buf)
- var clean bytes.Buffer
- for _, b := range buf[:rawN] {
- switch tc.state {
- case 1: // IAC
- tc.state = 0
- switch b {
- case iac:
- if !tc.subnegotiation {
- clean.WriteByte(iac)
- }
- case will, wont, do, dont:
- tc.optionCommand = b
- tc.state = 2
- case sb:
- tc.subnegotiation = true
- case se:
- tc.subnegotiation = false
- }
- continue
- case 2:
- tc.handleOption(tc.optionCommand, b)
- tc.state = 0
- continue
- }
- if b == iac {
- tc.state = 1
- continue
- }
- if tc.subnegotiation {
- continue
- }
- if tc.lastCR {
- tc.lastCR = false
- if b == '\n' || b == 0 {
- continue
- }
- }
- if b == '\r' {
- clean.WriteByte('\n')
- tc.lastCR = true
- } else {
- clean.WriteByte(b)
- }
- }
- if clean.Len() > 0 {
- return copy(p, clean.Bytes()), readErr
- }
- if readErr != nil {
- return 0, readErr
- }
- }
- }
- func (tc *TelnetConn) handleOption(cmd, opt byte) {
- // Respond to DO ECHO or DO SUPPRESS_GO_AHEAD with WILL
- switch cmd {
- case do:
- if opt == optEcho || opt == optSuppressGoAhead {
- _, _ = tc.Conn.Write([]byte{iac, will, opt})
- } else {
- _, _ = tc.Conn.Write([]byte{iac, wont, opt})
- }
- case dont:
- _, _ = tc.Conn.Write([]byte{iac, wont, opt})
- }
- }
- func (tc *TelnetConn) Write(p []byte) (n int, err error) {
- // Ensure \n is transmitted as \r\n for telnet standard compatibility
- var out bytes.Buffer
- for _, b := range p {
- if b == '\n' {
- out.WriteByte('\r')
- }
- out.WriteByte(b)
- }
- written, err := tc.Conn.Write(out.Bytes())
- if err != nil {
- return 0, err
- }
- if written != out.Len() {
- return 0, io.ErrShortWrite
- }
- return len(p), nil
- }
|