123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713 |
- package telnet
- // import "bytes"
- import "io"
- import "strings"
- import "fmt"
- import "compress/zlib"
- import "github.com/beoran/woe/monolog"
- // This Telnet module implements a subset of the Telnet protocol.
- // Telnet states
- type TelnetState int
- const (
- data_state TelnetState = iota
- iac_state = iota
- will_state = iota
- wont_state = iota
- do_state = iota
- dont_state = iota
- sb_state = iota
- sb_data_state = iota
- sb_data_iac_state = iota
- )
- // Telnet event type constants
- type EventType int
- const (
- TELNET_DATA_EVENT EventType = iota
- TELNET_NAWS_EVENT EventType = iota
- TELNET_TTYPE_EVENT EventType = iota
- TELNET_SUBNEGOTIATE_EVENT EventType = iota
- TELNET_IAC_EVENT EventType = iota
- TELNET_COMPRESS_EVENT EventType = iota
- TELNET_ENVIRONMENT_EVENT EventType = iota
- TELNET_MSSP_EVENT EventType = iota
- TELNET_ZMP_EVENT EventType = iota
- TELNET_WILL_EVENT EventType = iota
- TELNET_WONT_EVENT EventType = iota
- TELNET_DO_EVENT EventType = iota
- TELNET_DONT_EVENT EventType = iota
- TELNET_ERROR_EVENT EventType = iota
- TELNET_UNKNOWN_EVENT EventType = iota
- TELNET_NIL_EVENT EventType = iota
- )
- // Telnet event types
- type Event interface {
- Type() EventType
- }
- type DataEvent struct {
- Data []byte
- }
- func (me DataEvent) Type() EventType { return TELNET_DATA_EVENT }
- type NAWSEvent struct {
- W int
- H int
- }
- func (me NAWSEvent) Type() EventType { return TELNET_NAWS_EVENT }
- type TTypeEvent struct {
- Telopt byte
- Name string
- }
- func (me TTypeEvent) Type() EventType { return TELNET_TTYPE_EVENT }
- type SubnegotiateEvent struct {
- Telopt byte
- Buffer []byte
- }
- func (me SubnegotiateEvent) Type() EventType { return TELNET_SUBNEGOTIATE_EVENT }
- type IACEvent struct {
- Telopt byte
- }
- func (me IACEvent) Type() EventType { return TELNET_IAC_EVENT }
- type CompressEvent struct {
- Compress bool
- }
- func (me CompressEvent) Type() EventType { return TELNET_COMPRESS_EVENT }
- // Storage for environment values
- type Environment struct {
- Type byte
- Value string
- }
- type EnvironmentEvent struct {
- Telopt byte
- Vars []Environment
- }
- func (me EnvironmentEvent) Type() EventType { return TELNET_ENVIRONMENT_EVENT }
- type MSSPEvent struct {
- Telopt byte
- Vars map[string]string
- }
- func (me MSSPEvent) Type() EventType { return TELNET_MSSP_EVENT }
- type ZMPEvent struct {
- Vars []string
- }
- func (me ZMPEvent) Type() EventType { return TELNET_ZMP_EVENT }
- type WillEvent struct {
- Telopt byte
- }
- func (me WillEvent) Type() EventType { return TELNET_WILL_EVENT }
- type WontEvent struct {
- Telopt byte
- }
- func (me WontEvent) Type() EventType { return TELNET_WONT_EVENT }
- type DoEvent struct {
- Telopt byte
- }
- func (me DoEvent) Type() EventType { return TELNET_DO_EVENT }
- type DontEvent struct {
- Telopt byte
- }
- func (me DontEvent) Type() EventType { return TELNET_DONT_EVENT }
- // For protocol errors.
- type ErrorEvent struct {
- error string
- }
- func (me ErrorEvent) Type() EventType { return TELNET_ERROR_EVENT }
- // Returns the numerical event type of an event. Useful for direct comparison.
- func EventTypeOf(event Event) EventType {
- if event == nil {
- return TELNET_NIL_EVENT
- }
- return event.Type()
- }
- // Returns true if the event is of the given type, or false if not
- func IsEventType(event Event, typ EventType) bool {
- return EventTypeOf(event) == typ
- }
- type EventChannel chan (Event)
- type Telopt struct {
- telopt byte
- us byte
- him byte
- }
- type Telnet struct {
- Events EventChannel
- ToClient chan ([]byte)
- telopts map[byte]Telopt
- state TelnetState
- compress bool
- zwriter zlib.Writer
- zreader io.ReadCloser
- buffer []byte
- sb_telopt byte
- }
- func New() (telnet *Telnet) {
- events := make(EventChannel, 64)
- toclient := make(chan ([]byte), 64)
- telopts := make(map[byte]Telopt)
- state := data_state
- compress := false
- var zwriter zlib.Writer
- var zreader io.ReadCloser
- var buffer []byte = nil
- sb_telopt := byte(0)
- telnet = &Telnet{events, toclient, telopts, state, compress, zwriter, zreader, buffer, sb_telopt}
- return telnet
- }
- // Starts compresssion
- func (me *Telnet) StartCompression() {
- // XXX implement compression.
- // var zwbuf bytes.Buffer
- // me.zwriter = zlib.NewWriter(&zwbuf);
- }
- // Closes the telnet connection, send last compressed data if needed.
- func (me *Telnet) Close() {
- if me.compress {
- me.zwriter.Close()
- me.zreader.Close()
- }
- }
- // Filters raw text, only compressing it if needed.
- func (me *Telnet) SendRaw(in []byte) {
- // XXX Handle compression here later
- me.ToClient <- in
- }
- // Filters text, escaping IAC bytes.
- func (me *Telnet) SendEscaped(in []byte) {
- buffer := make([]byte, len(in)*2, len(in)*2)
- outdex := 0
- /* Double IAC characters to escape them. */
- for index := 0; index < len(in); index++ {
- now := in[index]
- if now == TELNET_IAC {
- buffer[outdex] = TELNET_IAC
- outdex++
- }
- buffer[outdex] = now
- outdex++
- }
- me.SendRaw(buffer)
- }
- // Send negotiation bytes
- func (me *Telnet) SendNegotiate(cmd byte, telopt byte) {
- buffer := make([]byte, 3)
- buffer[0] = TELNET_IAC
- buffer[1] = cmd
- buffer[2] = telopt
- me.SendRaw(buffer)
- }
- func (me *Telnet) SendEvent(event Event) {
- me.Events <- event
- }
- // Parse a subnegotiation buffer for a naws event
- func (me *Telnet) SubnegotiateNAWS(buffer []byte) {
- // Some clients, like Gnome-Mud can't even get this right. Grrr!
- if buffer == nil || len(buffer) != 4 {
- monolog.Warning("Bad NAWS negotiation: #{buffer}")
- return
- }
- var w int = (int(buffer[0]) << 8) + int(buffer[1])
- var h int = (int(buffer[2]) << 8) + int(buffer[3])
- me.SendEvent(&NAWSEvent{w, h})
- }
- // process an ENVIRON/NEW-ENVIRON subnegotiation buffer
- func (me *Telnet) SubnegotiateEnviron(buffer []byte) {
- var vars []Environment
- var cmd []byte
- fb := buffer[0]
- // First byte must be a valid command
- if fb != TELNET_ENVIRON_SEND && fb != TELNET_ENVIRON_IS && fb != TELNET_ENVIRON_INFO {
- monolog.Warning("telopt environment subneg command not valid")
- }
- cmd = append(cmd, fb)
- if len(buffer) == 1 {
- me.SendEvent(&EnvironmentEvent{fb, vars})
- return
- }
- // Second byte must be VAR or USERVAR, if present
- sb := buffer[1]
- if sb != TELNET_ENVIRON_VAR && fb != TELNET_ENVIRON_USERVAR {
- monolog.Warning("telopt environment subneg missing variable type")
- return
- }
- // ensure last byte is not an escape byte (makes parsing later easier)
- lb := buffer[len(buffer)-1]
- if lb == TELNET_ENVIRON_ESC {
- monolog.Warning("telopt environment subneg ends with ESC")
- return
- }
- /* XXX : not implemented yet
- var variable * Environment = nil
- index := 1
- escape := false
- for index := 1 ; index < len(buffer) ; index++ {
- c := buffer[index]
- switch c {
- case TELNET_ENVIRON_VAR:
- fallthrough
- case TELNET_ENVIRON_VALUE:
- fallthrough
- case TELNET_ENVIRON_USERVAR:
- if escape {
- escape = false
- variable.Value = append(variable.Value, c)
- } else if (variable != nil) {
- vars = append(vars, variable)
- variable = new(Environment)
- variable.Type = c
- } else {
- variable = new(Environment)
- variable.Type = c
- }
- case TELNET_ENVIRON_ESC:
- escape = true
- default:
- variable.Value = append(variable.Value, c)
- }
- }
- // Finally send event
- me.SendEvent(&EnvironmentEvent{fb, vars})
- */
- }
- const (
- MSTATE_NONE = 0
- MSTATE_VAR = 1
- MSTATE_VAL = 2
- )
- // process an MSSP subnegotiation buffer
- func (me *Telnet) SubnegotiateMSSP(buffer []byte) {
- if len(buffer) < 1 {
- return
- }
- fb := buffer[0]
- // first byte must be a valid command
- if fb != TELNET_MSSP_VAR {
- monolog.Warning("telopt MSSP subneg data not valid")
- return
- }
- variables := make(map[string]string)
- var variable []byte
- var value []byte
- mstate := MSTATE_NONE
- for index := 0; index < len(buffer); index++ {
- c := buffer[index]
- switch c {
- case TELNET_MSSP_VAR:
- mstate = MSTATE_VAR
- if mstate == MSTATE_VAR {
- variables[string(variable)] = string(value)
- variable = nil
- value = nil
- }
- case TELNET_MSSP_VAL:
- mstate = MSTATE_VAL
- default:
- if mstate == MSTATE_VAL {
- variable = append(variable, c)
- } else {
- value = append(value, c)
- }
- }
- }
- me.SendEvent(&MSSPEvent{fb, variables})
- }
- // Parse ZMP command subnegotiation buffers
- func (me *Telnet) SubnegotiateZMP(buffer []byte) {
- var vars []string
- var variable []byte
- var b byte
- for index := 0; index < len(buffer); index++ {
- b = buffer[index]
- if b == 0 {
- vars = append(vars, string(variable))
- variable = nil
- } else {
- variable = append(variable, b)
- }
- }
- me.SendEvent(&ZMPEvent{vars})
- }
- // parse TERMINAL-TYPE command subnegotiation buffers
- func (me *Telnet) SubnegotiateTType(buffer []byte) {
- // make sure request is not empty
- if len(buffer) == 0 {
- monolog.Warning("Incomplete TERMINAL-TYPE request")
- return
- }
- fb := buffer[0]
- if fb != TELNET_TTYPE_IS && fb != TELNET_TTYPE_SEND {
- monolog.Warning("TERMINAL-TYPE request has invalid type %d (%v)", fb, buffer)
- return
- }
- term := string(buffer[1:])
- me.SendEvent(&TTypeEvent{fb, term})
- }
- // process a subnegotiation buffer; returns true if the current buffer
- // must be aborted and reprocessed due to COMPRESS2 being activated
- func (me *Telnet) DoSubnegotiate(buffer []byte) bool {
- switch me.sb_telopt {
- case TELNET_TELOPT_COMPRESS2:
- // received COMPRESS2 begin marker, setup our zlib box and
- // start handling the compressed stream if it's not already.
- me.compress = true
- me.SendEvent(&CompressEvent{me.compress})
- return true
- // specially handled subnegotiation telopt types
- case TELNET_TELOPT_TTYPE:
- me.SubnegotiateTType(buffer)
- case TELNET_TELOPT_ENVIRON:
- me.SubnegotiateEnviron(buffer)
- case TELNET_TELOPT_NEW_ENVIRON:
- me.SubnegotiateEnviron(buffer)
- case TELNET_TELOPT_MSSP:
- me.SubnegotiateMSSP(buffer)
- case TELNET_TELOPT_NAWS:
- me.SubnegotiateNAWS(buffer)
- case TELNET_TELOPT_ZMP:
- me.SubnegotiateZMP(buffer)
- default:
- // Send catch all subnegotiation event
- me.SendEvent(&SubnegotiateEvent{me.sb_telopt, buffer})
- }
- return false
- }
- func (me *Telnet) DoNegotiate(state TelnetState, telopt byte) bool {
- switch me.state {
- case will_state:
- me.SendEvent(&WillEvent{telopt})
- case wont_state:
- me.SendEvent(&WontEvent{telopt})
- case do_state:
- me.SendEvent(&DoEvent{telopt})
- case dont_state:
- me.SendEvent(&DontEvent{telopt})
- default:
- monolog.Warning("State not vvalid in telnet negotiation.")
- }
- me.state = data_state
- return false
- }
- // Send the current buffer as a DataEvent if it's not empty
- // Also empties the buffer if it wasn't emmpty
- func (me *Telnet) maybeSendDataEventAndEmptyBuffer() {
- if (me.buffer != nil) && (len(me.buffer) > 0) {
- me.SendEvent(&DataEvent{me.buffer})
- me.buffer = nil
- }
- }
- // Append a byte to the data buffer
- func (me *Telnet) appendByte(bin byte) {
- monolog.Log("TELNET", "Appending to telnet buffer: %d %d", len(me.buffer), cap(me.buffer))
- me.buffer = append(me.buffer, bin)
- }
- // Process a byte in the data state
- func (me *Telnet) dataStateProcessByte(bin byte) bool {
- if bin == TELNET_IAC {
- // receive buffered bytes as data and go to IAC state if it's notempty
- me.maybeSendDataEventAndEmptyBuffer()
- me.state = iac_state
- } else {
- me.appendByte(bin)
- }
- return false
- }
- // Process a byte in the IAC state
- func (me *Telnet) iacStateProcessByte(bin byte) bool {
- switch bin {
- // subnegotiation
- case TELNET_SB:
- me.state = sb_state
- // negotiation commands
- case TELNET_WILL:
- me.state = will_state
- case TELNET_WONT:
- me.state = wont_state
- case TELNET_DO:
- me.state = do_state
- case TELNET_DONT:
- me.state = dont_state
- // IAC escaping
- case TELNET_IAC:
- me.appendByte(TELNET_IAC)
- me.maybeSendDataEventAndEmptyBuffer()
- me.state = data_state
- // some other command
- default:
- me.SendEvent(IACEvent{bin})
- me.state = data_state
- }
- return false
- }
- // Process a byte in the subnegotiation data state
- func (me *Telnet) sbdataStateProcessByte(bin byte) bool {
- // IAC command in subnegotiation -- either IAC SE or IAC IAC
- if bin == TELNET_IAC {
- me.state = sb_data_iac_state
- } else if me.sb_telopt == TELNET_TELOPT_COMPRESS && bin == TELNET_WILL {
- // MCCPv1 defined an invalid subnegotiation sequence (IAC SB 85 WILL SE)
- // to start compression. Catch and discard this case, only support
- // MMCPv2.
- me.state = data_state
- } else {
- me.appendByte(bin)
- }
- return false
- }
- // Process a byte in the IAC received when processing subnegotiation data state
- func (me *Telnet) sbdataiacStateProcessByte(bin byte) bool {
- switch bin {
- //end subnegotiation
- case TELNET_SE:
- me.state = data_state
- // process subnegotiation
- compress := me.DoSubnegotiate(me.buffer)
- // if compression was negotiated, the rest of the stream is compressed
- // and processing it requires decompressing it. Return true to signal
- // this.
- me.buffer = nil
- if compress {
- return true
- }
- // escaped IAC byte
- case TELNET_IAC:
- // push IAC into buffer
- me.appendByte(bin)
- me.state = sb_data_state
- // something else -- protocol error. attempt to process
- // content in subnegotiation buffer, then evaluate the
- // given command as an IAC code.
- default:
- monolog.Warning("Unexpected byte after IAC inside SB: %d", bin)
- me.state = iac_state
- // subnegotiate with the buffer anyway, even though it's an error
- compress := me.DoSubnegotiate(me.buffer)
- // if compression was negotiated, the rest of the stream is compressed
- // and processing it requires decompressing it. Return true to signal
- // this.
- me.buffer = nil
- if compress {
- return true
- }
- }
- return false
- }
- // Process a single byte received from the client
- func (me *Telnet) ProcessByte(bin byte) bool {
- monolog.Log("TELNET", "ProcessByte %d %d", bin, me.state)
- switch me.state {
- // regular data
- case data_state:
- return me.dataStateProcessByte(bin)
- // IAC received before
- case iac_state:
- return me.iacStateProcessByte(bin)
- case will_state, wont_state, do_state, dont_state:
- return me.DoNegotiate(me.state, bin)
- // subnegotiation started, determine option to subnegotiate
- case sb_state:
- me.sb_telopt = bin
- me.state = sb_data_state
- // subnegotiation data, buffer bytes until the end request
- case sb_data_state:
- return me.sbdataStateProcessByte(bin)
- // IAC received inside a subnegotiation
- case sb_data_iac_state:
- return me.sbdataiacStateProcessByte(bin)
- default:
- // programing error, shouldn't happen
- panic("Error in telnet state machine!")
- }
- // return false to signal compression needn't start
- return false
- }
- // Process multiple bytes received from the client
- func (me *Telnet) ProcessBytes(bytes []byte) {
- for index := 0; index < len(bytes); {
- bin := bytes[index]
- compress := me.ProcessByte(bin)
- if compress {
- // paper over this for a while...
- // new_bytes = Zlib.inflate(arr.pack('c*')) rescue nil
- // if new_bytes
- //arr = new_bytes.bytes.to_a
- }
- index++
- }
- me.maybeSendDataEventAndEmptyBuffer()
- }
- // Call this when the server receives data from the client
- func (me *Telnet) TelnetReceive(data []byte) {
- // the COMPRESS2 protocol seems to be half-duplex in that only
- // the server's data stream is compressed (unless maybe if the client
- // is asked to also compress with a DO command ?)
- me.ProcessBytes(data)
- }
- // Send a bytes array (raw) to the client
- func (me *Telnet) TelnetSendBytes(bytes ...byte) {
- me.SendRaw(bytes)
- }
- // Send an iac command
- func (me *Telnet) TelnetSendIac(cmd byte) {
- me.TelnetSendBytes(TELNET_IAC, cmd)
- }
- // Send negotiation. Currently rfc1143 is not implemented, so beware of
- // server client loops. The simplest way to avoid those is to never answer any
- // client requests, only send server requests.
- func (me *Telnet) TelnetSendNegotiate(cmd byte, telopt byte) {
- me.TelnetSendBytes(TELNET_IAC, cmd, telopt)
- }
- // Send non-command data (escapes IAC bytes)
- func (me *Telnet) TelnetSend(buffer []byte) {
- me.SendEscaped(buffer)
- }
- // send subnegotiation header
- func (me *Telnet) TelnetBeginSubnegotiation(telopt byte) {
- me.TelnetSendBytes(TELNET_IAC, TELNET_SB, telopt)
- }
- // send subnegotiation ending
- func (me *Telnet) TelnetEndSubnegotiation() {
- me.TelnetSendBytes(TELNET_IAC, TELNET_SE)
- }
- // Send complete subnegotiation
- func (me *Telnet) TelnetSubnegotiation(telopt byte, buffer []byte) {
- me.TelnetBeginSubnegotiation(telopt)
- if buffer != nil {
- me.TelnetSend(buffer)
- }
- me.TelnetEndSubnegotiation()
- }
- // Ask client to start accepting compress2 compression
- func (me *Telnet) TelnetBeginCompress2() {
- me.TelnetSendBytes(TELNET_IAC, TELNET_SB, TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE)
- me.compress = true
- }
- // Send formatted data to the client
- func (me *Telnet) TelnetRawPrintf(format string, args ...interface{}) {
- buf := fmt.Sprintf(format, args...)
- me.TelnetSend([]byte(buf))
- }
- const CRLF = "\r\n"
- const CRNUL = "\r\000"
- // send formatted data with \r and \n translation in addition to IAC IAC
- // escaping
- func (me *Telnet) TelnetPrintf(format string, args ...interface{}) {
- buf := fmt.Sprintf(format, args...)
- buf = strings.Replace(buf, "\r", CRNUL, -1)
- buf = strings.Replace(buf, "\n", CRLF, -1)
- me.TelnetSend([]byte(buf))
- }
- // NEW-ENVIRON subnegotation
- func (me *Telnet) TelnetNewenviron(cmd []byte) {
- me.TelnetSubnegotiation(TELNET_TELOPT_NEW_ENVIRON, cmd)
- }
- // send TERMINAL-TYPE SEND command
- func (me *Telnet) TelnetTTypeSend() {
- me.TelnetSendBytes(TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_SEND, TELNET_IAC, TELNET_SE)
- }
- // send TERMINAL-TYPE IS command
- func (me *Telnet) TelnetTTypeIS(ttype string) {
- me.TelnetSendBytes(TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_IS)
- me.TelnetSend([]byte(ttype))
- }
- // send MSSP data
- func (me *Telnet) TelnetSendMSSP(mssp map[string]string) {
- var buf []byte
- for key, val := range mssp {
- buf = append(buf, TELNET_MSSP_VAR)
- buf = append(buf, key...)
- buf = append(buf, TELNET_MSSP_VAL)
- buf = append(buf, val...)
- }
- me.TelnetSubnegotiation(TELNET_TELOPT_MSSP, buf)
- }
|