telnet.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package cli
  2. import (
  3. "bytes"
  4. "io"
  5. "net"
  6. )
  7. const (
  8. iac = 255 // Interpret As Command
  9. dont = 254
  10. do = 253
  11. wont = 252
  12. will = 251
  13. sb = 250
  14. se = 240
  15. optEcho = 1
  16. optSuppressGoAhead = 3
  17. optTerminalType = 24
  18. optWindowSize = 31
  19. optLinemode = 34
  20. )
  21. // TelnetConn wraps a net.Conn and handles RFC 854 Telnet negotiations
  22. // while presenting a clean stream to terminal.NewTerminal.
  23. type TelnetConn struct {
  24. net.Conn
  25. subnegotiation bool
  26. state byte
  27. optionCommand byte
  28. lastCR bool
  29. }
  30. func NewTelnetConn(conn net.Conn) *TelnetConn {
  31. tc := &TelnetConn{Conn: conn}
  32. tc.initNegotiation()
  33. return tc
  34. }
  35. func (tc *TelnetConn) initNegotiation() {
  36. // Request client to suppress go-ahead and handle character-at-a-time / local echo off
  37. // IAC WILL ECHO, IAC WILL SUPPRESS_GO_AHEAD, IAC DONT LINEMODE
  38. initSeq := []byte{
  39. iac, will, optEcho,
  40. iac, will, optSuppressGoAhead,
  41. iac, dont, optLinemode,
  42. }
  43. _, _ = tc.Conn.Write(initSeq)
  44. }
  45. func (tc *TelnetConn) Read(p []byte) (n int, err error) {
  46. if len(p) == 0 {
  47. return 0, nil
  48. }
  49. buf := make([]byte, len(p))
  50. for {
  51. rawN, readErr := tc.Conn.Read(buf)
  52. var clean bytes.Buffer
  53. for _, b := range buf[:rawN] {
  54. switch tc.state {
  55. case 1: // IAC
  56. tc.state = 0
  57. switch b {
  58. case iac:
  59. if !tc.subnegotiation {
  60. clean.WriteByte(iac)
  61. }
  62. case will, wont, do, dont:
  63. tc.optionCommand = b
  64. tc.state = 2
  65. case sb:
  66. tc.subnegotiation = true
  67. case se:
  68. tc.subnegotiation = false
  69. }
  70. continue
  71. case 2:
  72. tc.handleOption(tc.optionCommand, b)
  73. tc.state = 0
  74. continue
  75. }
  76. if b == iac {
  77. tc.state = 1
  78. continue
  79. }
  80. if tc.subnegotiation {
  81. continue
  82. }
  83. if tc.lastCR {
  84. tc.lastCR = false
  85. if b == '\n' || b == 0 {
  86. continue
  87. }
  88. }
  89. if b == '\r' {
  90. clean.WriteByte('\n')
  91. tc.lastCR = true
  92. } else {
  93. clean.WriteByte(b)
  94. }
  95. }
  96. if clean.Len() > 0 {
  97. return copy(p, clean.Bytes()), readErr
  98. }
  99. if readErr != nil {
  100. return 0, readErr
  101. }
  102. }
  103. }
  104. func (tc *TelnetConn) handleOption(cmd, opt byte) {
  105. // Respond to DO ECHO or DO SUPPRESS_GO_AHEAD with WILL
  106. switch cmd {
  107. case do:
  108. if opt == optEcho || opt == optSuppressGoAhead {
  109. _, _ = tc.Conn.Write([]byte{iac, will, opt})
  110. } else {
  111. _, _ = tc.Conn.Write([]byte{iac, wont, opt})
  112. }
  113. case dont:
  114. _, _ = tc.Conn.Write([]byte{iac, wont, opt})
  115. }
  116. }
  117. func (tc *TelnetConn) Write(p []byte) (n int, err error) {
  118. // Ensure \n is transmitted as \r\n for telnet standard compatibility
  119. var out bytes.Buffer
  120. for _, b := range p {
  121. if b == '\n' {
  122. out.WriteByte('\r')
  123. }
  124. out.WriteByte(b)
  125. }
  126. written, err := tc.Conn.Write(out.Bytes())
  127. if err != nil {
  128. return 0, err
  129. }
  130. if written != out.Len() {
  131. return 0, io.ErrShortWrite
  132. }
  133. return len(p), nil
  134. }