Documentation
¶
Index ¶
- Constants
- func GetTelOpt[OptionStruct any, T TypedTelnetOption[OptionStruct]](terminal *Terminal) (T, error)
- type ApcData
- type Charset
- func (c *Charset) BinaryDecode() bool
- func (c *Charset) BinaryEncode() bool
- func (c *Charset) Decode(buffer []byte, incomingText []byte, fallback EncodingState) (consumed int, buffered int, fellback EncodingState, err error)
- func (c *Charset) DecodingName() string
- func (c *Charset) DefaultCharsetName() string
- func (c *Charset) Encode(utf8Text string) ([]byte, error)
- func (c *Charset) EncodingName() string
- func (c *Charset) PromoteDefaultCharset(oldCodePage string, newCodePage string) (bool, error)
- func (c *Charset) SetBinaryDecode(decode bool)
- func (c *Charset) SetBinaryEncode(encode bool)
- func (c *Charset) SetNegotiatedDecodingCharset(codePage string) error
- func (c *Charset) SetNegotiatedEncodingCharset(codePage string) error
- type CharsetUsage
- type Command
- type CommandData
- type ControlCodeData
- type CsiData
- type DcsData
- type EncodingState
- type ErrorHandler
- type EscData
- type EventHook
- type EventHooks
- type EventPublisher
- type Middleware
- type MiddlewareStack
- type OscData
- type PmData
- type PromptCommands
- type PromptData
- type SosData
- type TelOptCode
- type TelOptEvent
- type TelOptEventHandler
- type TelOptLibrary
- type TelOptSide
- type TelOptState
- type TelOptStateChangeEvent
- type TelOptUsage
- type TelnetKeyboard
- func (k *TelnetKeyboard) ClearLock(lockName string)
- func (k *TelnetKeyboard) ClearPromptCommand(flag PromptCommands)
- func (k *TelnetKeyboard) HasActiveLock(lockName string) bool
- func (k *TelnetKeyboard) LineOut(t *Terminal, data TerminalData)
- func (k *TelnetKeyboard) SendPromptHint()
- func (k *TelnetKeyboard) SetLock(lockName string, duration time.Duration)
- func (k *TelnetKeyboard) SetPromptCommand(flag PromptCommands)
- func (k *TelnetKeyboard) WrapWriter(wrap func(io.Writer) (io.Writer, error)) error
- func (k *TelnetKeyboard) WriteCommand(c Command, postSend func() error)
- func (k *TelnetKeyboard) WriteString(str string)
- type TelnetOption
- type TelnetPrinter
- type TelnetScanner
- type Terminal
- func (t *Terminal) Charset() *Charset
- func (t *Terminal) CommandString(c Command) string
- func (t *Terminal) Keyboard() *TelnetKeyboard
- func (t *Terminal) Printer() *TelnetPrinter
- func (t *Terminal) RaiseTelOptEvent(event TelOptEvent)
- func (t *Terminal) RegisterEncounteredErrorHook(encounteredError ErrorHandler)
- func (t *Terminal) RegisterOutboundDataHook(outboundText TerminalDataHandler)
- func (t *Terminal) RegisterPrinterOutputHook(printerOutput TerminalDataHandler)
- func (t *Terminal) RegisterTelOptEventHook(telOptEvent TelOptEventHandler)
- func (t *Terminal) Side() TerminalSide
- func (t *Terminal) WaitForExit() error
- type TerminalConfig
- type TerminalData
- type TerminalDataHandler
- type TerminalDataParser
- type TerminalSide
- type TextData
- type TypedTelnetOption
Constants ¶
const ( // EOR - End Of Record. The real meaning is implementation-specific, but these // days IAC EOR is primarily used as an alternative to IAC GA that can indicate // where a prompt is without all the historical baggage of GA EOR byte = 239 // SE - Subnegotiation End. IAC SE is used to mark the end of a subnegotiation command SE byte = 240 // NOP - No-Op. IAC NOP doesn't indicate anything at all, and this library ignores it. NOP byte = 241 // DATAMARK - not in use by this library DATAMARK byte = 242 // BRK - not in use by this library BRK byte = 243 // IP - Not in use by this library IP byte = 244 // AO - Not in use by this library AO byte = 245 // AYT - If received, an IAC NOP will be sent in response AYT byte = 246 // EC - Not in use by this library EC byte = 247 // EL - Not in use by this library EL byte = 248 // GA - Go Ahead. IAC GA is often used to indicate the end of a prompt line, so // that clients know where to place a cursor. However, it was originally used for // half-duplex terminals to indicate that the user could start typing and there is // a lot of weird baggage around "kludge line mode", so it is usually preferable // not to use this if the remote supports the EOR telopt. GA byte = 249 // SB - Subnegotiation Begin. IAC SB is used to indicate the beginning of a subnegotiation // command. These are telopt-specific commands that have telopt-specific meanings. SB byte = 250 // WILL - IAC WILL is used to indicate that this terminal intends to activate a telopt WILL byte = 251 // WONT - IAC WONT is used to indicate that this terminal refuses to activate a telopt WONT byte = 252 // DO - IAC DO is used to request that the remote terminal activates a telopt DO byte = 253 // DONT - IAC DONT is used to demand that the remote terminal do not activate a telopt DONT byte = 254 // IAC - This opcode indicates the beginning of a new command IAC byte = 255 )
Telnet opcodes
const DefaultKeyboardLock = 5 * time.Second
DefaultKeyboardLock indicates the default time to apply a lock with TelnetKeyboard.SetLock and telopts should use this duration when setting a keyboard lock unless they have a good reason not to.
Variables ¶
This section is empty.
Functions ¶
func GetTelOpt ¶
func GetTelOpt[OptionStruct any, T TypedTelnetOption[OptionStruct]](terminal *Terminal) (T, error)
GetTelOpt retrieves a live telopt from a terminal. It is used like this:
telnet.GetTelOpt[telopts.ECHO](terminal)
The above will return a value of type *telopts.ECHO, or nil if ECHO is not a registered telopt. If there is a telopt of a different type registered under ECHO's code, then the method will return an error.
This can be used to update the local state of a telopt, or respond to TelOptEvents by querying the newly-updated remote state of a telopt.
Types ¶
type ApcData ¶ added in v0.5.0
type ApcData struct {
ansi.ApcSequence
}
func (ApcData) EscapedString ¶ added in v0.5.0
func (o ApcData) EscapedString(terminal TelOptLibrary) string
type Charset ¶
type Charset struct {
// contains filtered or unexported fields
}
Charset represents the full encoding landscape for this terminal. Terminals have both a default charset and separate negotiated charsets for encoding and decoding. On terminal creation, the negotiated charsets are the same as the default charset. Through the CHARSET telopt, a new negotiated charset can be established with the remote. However, according to the RFC, the negotiated charset should only be used when
TRANSMIT-BINARY is active. When it is not active, the default charset should continue
to be used. Not all implementors follow that requirement, though, so CharsetUsage is used to establish when the negotiated charset should be used.
Additionally, the RFC required the default charset to be US-ASCII prior to 2008 and requires it to be UTF-8 since 2008. However, not all peers have been updated to support UTF-8, so it is useful for us to use the default charset to establish whether our peer actually supports UTF-8 and make that information available to the consumer. The consumer can use that information to decide whether to send UTF-8 text to the peer or limit itself to US-ASCII.
Finally, some non-english services written prior to 2008 broke RFC and do not use US-ASCII as their default charset. So in some cases, we will establish a default character set other than US-ASCII to support these services.
Lastly, a fallback character set can be established that will be used during decoding if the correct charset for decoding fails.
func NewCharset ¶
func NewCharset(defaultCodePage string, fallbackCodePage string, usage CharsetUsage) (*Charset, error)
NewCharset creates a new charset with a default charset, an optional fallback charset,
& a CharsetUsage to decide how the negotiated charset will be used if one is negotiated.
func (*Charset) BinaryDecode ¶
BinaryDecode returns a bool indicating whether the printer should use binary mode
func (*Charset) BinaryEncode ¶
BinaryEncode returns a bool indicating whether the keyboard should use binary mode
func (*Charset) Decode ¶
func (c *Charset) Decode(buffer []byte, incomingText []byte, fallback EncodingState) (consumed int, buffered int, fellback EncodingState, err error)
Decode accepts a byte slice that is encoded in the printer's current encoding as well as a destination buffer for decoded bytes. Additionally, it accepts a bool indicating whether the decode process should skip the default/negotiated charset and immediately use the fallback charset.
The method returns how many bytes were consumed from the incoming text, how many bytes were written to the buffer, whether the charset had to move to fallback mode due to decoding failure, and potentially an error.
func (*Charset) DecodingName ¶
DecodingName returns the name of the character set currently used by the printer. This method takes into account the default & negotiated character sets, the CharsetUsage value, and whether the printer is in binary mode
func (*Charset) DefaultCharsetName ¶
DefaultCharsetName returns the name of the default character set
func (*Charset) Encode ¶
Encode accepts a string of UTF-8 text and returns a byte slice that is encoded in the keyboard's current encoding
func (*Charset) EncodingName ¶
EncodingName returns the name of the character set currently used by the keyboard. This method takes into account the default & negotiated character sets, the CharsetUsage value, and whether the keyboard is in binary mode
func (*Charset) PromoteDefaultCharset ¶
PromoteDefaultCharset will change the default character set to the new code page if it is currently set to the old code page. If the default character set is changed, the negotiated character set will also be changed if it's the same as the default character set.
This is primarily used when we get some indication that the remote supports UTF-8 to promote the default charset from US-ASCII to UTF-8. The US-ASCII decoder will always decode UTF-8, but it's useful for the consumer to know whether the remote actually supports UTF-8, in order to decide whether to send things like emojis.
func (*Charset) SetBinaryDecode ¶
SetBinaryDecode is used by the TRANSMIT-BINARY telopt to establish whether the printer should use binary mode
func (*Charset) SetBinaryEncode ¶
SetBinaryEncode is used by the TRANSMIT-BINARY telopt to establish whether the keyboard should use binary mode
func (*Charset) SetNegotiatedDecodingCharset ¶ added in v0.2.0
SetNegotiatedDecodingCHarset modifies the negotiated printer charset to the requested character set.
func (*Charset) SetNegotiatedEncodingCharset ¶ added in v0.2.0
SetNegotiatedEncodingCharset modifies the negotiated keyboard charset to the requested character set
type CharsetUsage ¶
type CharsetUsage byte
CharsetUsage indicates when charsets negotiated via the CHARSET telopt are used. According to RFC, negotiated telopts are only to be used when TRANSMIT-BINARY is active, but many implementations are incorrect. On the other hand, many implementations don't actually do anything, they just advertise that the server can handle UTF-8, so following the RFC doesn't do any harm.
const ( // CharsetUsageBinary indicates that text communications should use a CHARSET-negotiated character set // if the connection is in BINARY mode, and the default character set otherwise CharsetUsageBinary CharsetUsage = iota // CharsetUsageAlways indicates that text communications should always use a CHARSET-negotiated character // set (if any) instead of the default character set CharsetUsageAlways )
type Command ¶
type Command struct {
// OpCode is the code that comes after IAC in this command. Bear in mind that
// subnegotiations, which come in the form of IAC SB <bytes> IAC SE, are represented
// as a single command object with the OpCode of SB. IAC SE is never sent in its
// own command.
OpCode byte
// Option indicates which telopt this command is referring to, if the command has one.
// IAC WILL/WONT/DO/DONT/SB are always followed by a byte indicating a telopt.
Option TelOptCode
// Subnegotiation contains a byte slice containing the bytes, if any, that came
// between IAC SB and IAC SE. For non-SB commands, this slice is empty.
Subnegotiation []byte
}
Command is a struct that indicates some sort of IAC command either received from or sent to the remote. Any possible command can be represented by this struct.
type CommandData ¶ added in v0.3.0
type CommandData struct {
Command
}
CommandData is a type representing a single IAC command received from telnet
func (CommandData) EscapedString ¶ added in v0.3.0
func (o CommandData) EscapedString(terminal TelOptLibrary) string
func (CommandData) String ¶ added in v0.3.0
func (o CommandData) String() string
type ControlCodeData ¶ added in v0.5.0
type ControlCodeData ansi.ControlCode
func (ControlCodeData) EscapedString ¶ added in v0.5.0
func (o ControlCodeData) EscapedString(terminal TelOptLibrary) string
func (ControlCodeData) String ¶ added in v0.5.0
func (o ControlCodeData) String() string
type CsiData ¶ added in v0.5.0
type CsiData struct {
ansi.CsiSequence
}
func (CsiData) EscapedString ¶ added in v0.5.0
func (o CsiData) EscapedString(terminal TelOptLibrary) string
type DcsData ¶ added in v0.5.0
type DcsData struct {
ansi.DcsSequence
}
func (DcsData) EscapedString ¶ added in v0.5.0
func (o DcsData) EscapedString(terminal TelOptLibrary) string
type EncodingState ¶ added in v0.5.0
type EncodingState int
const ( EncodingUnsure EncodingState = iota EncodingInvalid EncodingValid )
type ErrorHandler ¶ added in v0.2.0
ErrorHandler is an event hook type that receives errors
type EscData ¶ added in v0.5.0
type EscData struct {
ansi.EscSequence
}
func (EscData) EscapedString ¶ added in v0.5.0
func (o EscData) EscapedString(terminal TelOptLibrary) string
type EventHooks ¶
type EventHooks struct {
EncounteredError []ErrorHandler
PrinterOutput []TerminalDataHandler
OutboundData []TerminalDataHandler
TelOptEvent []TelOptEventHandler
}
EventHooks is used to pass in a set of pre-registered event hooks to a Terminal when calling NewTerminal. See TerminalConfig for more info.
type EventPublisher ¶
type EventPublisher[U any] struct { // contains filtered or unexported fields }
EventPublisher is a type used to register and fire arbitrary events
func NewPublisher ¶
func NewPublisher[U any, T ~func(terminal *Terminal, data U)](hooks []T) *EventPublisher[U]
NewPublisher creates a new EventPublisher for a particular EventHook. A slice of hooks can be passed in- in which case the hooks will be registered to receive events from the publisher. Otherwise, nil can be passed in.
func (*EventPublisher[U]) Fire ¶
func (e *EventPublisher[U]) Fire(terminal *Terminal, eventData U)
Fire calls the event for all EventHook instances registered to this publisher with the provided parameters
func (*EventPublisher[U]) Register ¶
func (e *EventPublisher[U]) Register(hook EventHook[U])
Register registers a single EventHook to receive events from this publisher.
type Middleware ¶ added in v0.8.0
type Middleware interface {
Handle(terminal *Terminal, data TerminalData, next TerminalDataHandler)
}
type MiddlewareStack ¶ added in v0.8.0
type MiddlewareStack struct {
// contains filtered or unexported fields
}
func NewMiddlewareStack ¶ added in v0.8.0
func NewMiddlewareStack(lineOut TerminalDataHandler, middlewares ...Middleware) *MiddlewareStack
func (*MiddlewareStack) LineIn ¶ added in v0.8.0
func (s *MiddlewareStack) LineIn(t *Terminal, data TerminalData)
func (*MiddlewareStack) PushMiddleware ¶ added in v0.8.0
func (s *MiddlewareStack) PushMiddleware(middleware Middleware)
func (*MiddlewareStack) QueueMiddleware ¶ added in v0.8.0
func (s *MiddlewareStack) QueueMiddleware(middleware Middleware)
func (*MiddlewareStack) RemoveMiddleware ¶ added in v0.8.0
func (s *MiddlewareStack) RemoveMiddleware(middleware Middleware)
type OscData ¶ added in v0.5.0
type OscData struct {
ansi.OscSequence
}
func (OscData) EscapedString ¶ added in v0.5.0
func (o OscData) EscapedString(terminal TelOptLibrary) string
type PmData ¶ added in v0.5.0
type PmData struct {
ansi.PmSequence
}
func (PmData) EscapedString ¶ added in v0.5.0
func (o PmData) EscapedString(terminal TelOptLibrary) string
type PromptCommands ¶
type PromptCommands uint32
PromptCommands is a set of flags indicating which IAC opcodes indicate the end of a prompt line. MUDs like to use GA or EOR to indicate where to place the cursor at the end of a prompt. But GA can be turned off with the SUPPRESS-GO-AHEAD telopt, and EOR has to be turned on with the EOR telopt, so this helps us track where we're at.
const ( // PromptCommandGA refers to the IAC GA command. This command was initially used as part // of telnet's scheme for supporting half-duplex terminals. However, half-duplex terminals // were rapidly phased out after the telnet protocol was introduced and eventually came to be // used for a variety of hacky boutique purposes. For MUDs and BBSs it is often deactivated // via the SUPPRESS-GO-AHEAD telopt in order to activate character mode. It is sometimes used // as a prompt indicator on MUDs PromptCommandGA PromptCommands = 1 << iota // PromptCommandEOR refers to the IAC EOR command. This was introduced as part of the EOR telopt // and can be used for any purpose an application would like to use it for. It is mainly only // used as a prompt indicator on MUDs. PromptCommandEOR )
type PromptData ¶ added in v0.3.0
type PromptData PromptCommands
PromptData is a type representing a hint received from telnet about where the user prompt should be placed in the output stream.
func (PromptData) EscapedString ¶ added in v0.3.0
func (o PromptData) EscapedString(terminal TelOptLibrary) string
func (PromptData) String ¶ added in v0.3.0
func (o PromptData) String() string
type SosData ¶ added in v0.5.0
type SosData struct {
ansi.SosSequence
}
func (SosData) EscapedString ¶ added in v0.5.0
func (o SosData) EscapedString(terminal TelOptLibrary) string
type TelOptCode ¶
type TelOptCode byte
TelOptCode - each telopt has a unique identification number between 0 and 255
type TelOptEvent ¶
type TelOptEvent interface {
// String produces human-readable text describing the event that occurred
String() string
// Option is the specific telopt that experienced an event
Option() TelnetOption
}
TelOptEvent is an interface used for all TelOptEvents issued by anyone, both TelOptStateChangeEvent, which is issued by this terminal, and other events issued by telopts themselves
type TelOptEventHandler ¶ added in v0.2.0
type TelOptEventHandler func(t *Terminal, event TelOptEvent)
TelOptEventHandler is an event hook type that receives arbitrary events raised by telopts with Terminal.RaiseTelOptEvent
type TelOptLibrary ¶ added in v0.2.0
TelOptLibrary is an interface used to abstract Terminal from PrinterOutput for the benefit of anyone who may be using TelnetScanner without Terminal.
Any method that accepts this type will likely want to use *Terminal
type TelOptSide ¶
type TelOptSide byte
TelOptSide is used to distinguish the two "sides" of a telopt. Telopts can be active on either the local side, the remote side, both, or neither. As a result, the current state of a telopt needs to be requested for a particular side of the connection.
const ( TelOptSideUnknown TelOptSide = iota TelOptSideLocal TelOptSideRemote )
func (TelOptSide) String ¶
func (s TelOptSide) String() string
type TelOptState ¶
type TelOptState byte
TelOptState indicates whether the telopt is currently active, inactive, or other
const ( // TelOptUnknown is the zero value for the telopt state value. This is generally interchangeable with // TelOptInactive TelOptUnknown TelOptState = iota // TelOptInactive indicates that the option is not currently active TelOptInactive // TelOptRequested indicates that this client has sent a request to activate the telopt to the other party // but has not yet heard back TelOptRequested // TelOptActive indicates that the option is currently active TelOptActive )
func (TelOptState) String ¶
func (s TelOptState) String() string
type TelOptStateChangeEvent ¶
type TelOptStateChangeEvent struct {
TelnetOption TelnetOption
Side TelOptSide
OldState TelOptState
NewState TelOptState
}
TelOptStateChangeEvent is a TelOptEvent that indicates that a single telopt has changed state on one side of the connection
func (TelOptStateChangeEvent) Option ¶ added in v0.2.0
func (e TelOptStateChangeEvent) Option() TelnetOption
func (TelOptStateChangeEvent) String ¶ added in v0.2.0
func (e TelOptStateChangeEvent) String() string
type TelOptUsage ¶
type TelOptUsage byte
TelOptUsage indicates how a particular TelnetOption is supposed to be used by the terminal. Whether it is permitted to be activated locally or on the remote, and whether we should request activation locally or on the remote when the Terminal launches.
const ( // TelOptAllowRemote - if the remote requests to activate this telopt on their side, // we will permit it TelOptAllowRemote TelOptUsage = 1 << iota // TelOptAllowLocal - if the remote requests that we activate this telopt on our side, // we will comply TelOptAllowLocal )
const ( // TelOptRequestRemote - we will request that the remote activate this telopt during // Terminal startup TelOptRequestRemote TelOptUsage = TelOptAllowRemote | telOptOnlyRequestRemote // TelOptRequestLocal - we will request that the remote allow us to activate this // telopt on our side during Terminal startup TelOptRequestLocal TelOptUsage = TelOptAllowLocal | telOptOnlyRequestLocal )
type TelnetKeyboard ¶
type TelnetKeyboard struct {
// contains filtered or unexported fields
}
TelnetKeyboard is a Terminal subsidiary that is in charge of sending outbound data to the remote peer.
func (*TelnetKeyboard) ClearLock ¶
func (k *TelnetKeyboard) ClearLock(lockName string)
ClearLock will clear a named lock in order to end buffering (assuming there are no other active locks) and immediately write buffered text.
func (*TelnetKeyboard) ClearPromptCommand ¶
func (k *TelnetKeyboard) ClearPromptCommand(flag PromptCommands)
ClearPromptCommand will deactivate a particular prompt command and prevent it from being sent by the keyboard. Prompt commands are IAC GA/IAC EOR, commands that indicate to the remote where to place a prompt
func (*TelnetKeyboard) HasActiveLock ¶
func (k *TelnetKeyboard) HasActiveLock(lockName string) bool
HasActiveLock will indicate whether a named lock is currently active on the keyboard
func (*TelnetKeyboard) LineOut ¶ added in v0.6.0
func (k *TelnetKeyboard) LineOut(t *Terminal, data TerminalData)
func (*TelnetKeyboard) SendPromptHint ¶
func (k *TelnetKeyboard) SendPromptHint()
SendPromptHint will send a IAC GA or IAC EOR if possible, indicating to the remote to place a prompt after the most-recently-sent text.
This command will send an EOR if that telopt is active. Otherwise, it will send a GA if it isn't being suppressed. If it is not valid to send either prompt hint, this method will do nothing.
If one wants to send an IAC GA or IAC EOR command, this method should be used rather than WriteCommand. Commands sent via WriteCommand will not be buffered when the keyboard is under a lock, so prompt hints sent via WriteCommand will arrive before the prompt text when a keyboard lock is active.
func (*TelnetKeyboard) SetLock ¶
func (k *TelnetKeyboard) SetLock(lockName string, duration time.Duration)
SetLock will buffer all text output without sending until the provided lockName is cleared with ClearLock, or until the provided duration expires. This method is primarily used by telopts to handle changes in communication semantics. According to the Telnet RFC, communication semantics should change the moment a side sends a command that requests that they change. Since it is not known at that time whether the remote can receive these semantics, it is recommended that writes are buffered until the remote responds to the request.
func (*TelnetKeyboard) SetPromptCommand ¶
func (k *TelnetKeyboard) SetPromptCommand(flag PromptCommands)
SetPromptCommand will activate a particular prompt command and permit it to be sent by the keyboard. Prompt commands are IAC GA/IAC EOR, commands that indicate to the remote where to place a prompt
func (*TelnetKeyboard) WrapWriter ¶ added in v0.8.0
func (*TelnetKeyboard) WriteCommand ¶
func (k *TelnetKeyboard) WriteCommand(c Command, postSend func() error)
WriteCommand will queue a command to be sent to the remote. A post-send event can be provided, which is useful for cases where the provided command will signal to the remote that the communication semantic is changing in some way. If the postSend method is not nil, it will be executed immediately after writing the command to the output stream, and can be used to change the communication semantic for future writes.
func (*TelnetKeyboard) WriteString ¶
func (k *TelnetKeyboard) WriteString(str string)
WriteString will queue some text to be sent to the remote
type TelnetOption ¶
type TelnetOption interface {
// Code returns the code this option should be registered under. This method is expected to run succesfully
// before Initialize is called.
Code() TelOptCode
// String should return the short name used to refer to this option. This method is expected to run
// successfully before Initialize is called.
String() string
// Usage indicates the way in which this TelOpt is permitted to be used. This method
// is expected to run successfully before Initialize is called.
Usage() TelOptUsage
// Initialize sets the terminal used by this telopt and performs any other necessary
// business before other methods may be called.
Initialize(terminal *Terminal)
// Terminal returns the current terminal. This method must successfully return nil
// before Initialize is called.
Terminal() *Terminal
// LocalState returns the current state of this option locally- receiving a DO command will activate
// it and a DONT command will deactivate it.
LocalState() TelOptState
// RemoteState returns the current state of this option in the remote- receiving a WILL command
// will activate it and a WONT command will deactivate it
RemoteState() TelOptState
// TransitionLocalState is called when the terminal attempts to change this option to a new state
// locally. This is not called when the option is initialized to Inactive at the start of a new
// terminal, and it will not be called if the terminal tries to repeatedly transition this option
// to the same state.
//
// This method returns a simple callback method. If that callback is not nil, then it will
// be executed as soon as the command associated with this state change is written to the
// keyboard stream (or immediately if no command is necessary). This is vital for cases when
// a telopt changes the semantics of outbound communications, since that semantic change needs
// to take place immediately after we send the command indicating that we will be changing things.
TransitionLocalState(newState TelOptState) (func() error, error)
// TransitionRemoteState is calledw hen the terminal attempts to change this option to a new state
// for the remote. This is not called when the option is initialized to Inactive at the start of
// a new terminal, and it will nto be called if the terminal tries to repeatedly transition this
// option to the same state
//
// This method returns a simple callback method. If that callback is not nil, then it will
// be executed as soon as the command associated with this state change is written to the
// keyboard stream (or immediately if no command is necessary). This is vital for cases when
// a telopt changes the semantics of outbound communications, since that semantic change needs
// to take place immediately after we send the command indicating that we will be changing things.
TransitionRemoteState(newState TelOptState) (func() error, error)
// Subnegotiate is called when a subnegotiation request arrives from the remote party. This will only
// be called when the option is active on at least one side of the connection
Subnegotiate(subnegotiation []byte) error
// SubnegotiationString creates a legible string for a subnegotiation request
SubnegotiationString(subnegotiation []byte) (string, error)
}
TelnetOption is an object representing a single telopt within the currently-running terminal. Each terminal has its own telopt object for each telopt it supports.
type TelnetPrinter ¶
type TelnetPrinter struct {
// contains filtered or unexported fields
}
TelnetPrinter is a Terminal subsidiary that parses text sent by the remote peer. This object is largely not used by consumers. It has a few methods that are consumed by telopts, but received text is largely handled through the Terminal itself.
func (*TelnetPrinter) ClearPromptCommand ¶
func (p *TelnetPrinter) ClearPromptCommand(flag PromptCommands)
ClearPromptCommand will deactivate a particular prompt command and cause it to be ignored by the printer. Prompt commands are IAC GA/IAC EOR, commands that indicate to the consumer where to place a prompt
func (*TelnetPrinter) Middlewares ¶ added in v0.8.0
func (p *TelnetPrinter) Middlewares() *MiddlewareStack
func (*TelnetPrinter) SetPromptCommand ¶
func (p *TelnetPrinter) SetPromptCommand(flag PromptCommands)
SetPromptCommand will activate a particular prompt command and permit it to be received by the printer. Prompt commands are IAC GA/IAC EOR, commands that indicate to the consumer where to place a prompt
func (*TelnetPrinter) WrapReader ¶ added in v0.8.0
type TelnetScanner ¶ added in v0.2.0
type TelnetScanner struct {
// contains filtered or unexported fields
}
TelnetScanner is used internally by TelnetPrinter to read sequences from a Reader and output units of received output. It is exported due to the object being potentially useful outside the context of this library's Terminal object. If you intend to use Terminal, there is no need to use or think about this type.
TelnetScanner's Scan method works like an io.Scanner, except that it accepts a context.Context. If the ctx is cancelled or timed out, Scan will return false with with the appropriate error. Otherwise, it will return true until it reaches the input stream's EOF. Like io.Scanner, Scan is a blocking call.
After Scan returns, even if it returns false, Err and Output may have useful return values. Output returns a PrinterOutput object, or nil. PrinterOutput may be one of the PrinterOutput implementations defined in this package (TextOutput, PromptOutput, SequenceOutput, etc.).
PrinterOutput's String method will always return the correct text to print to a VT100 compatible terminal, and EscapedString will always return the correct text to print to a default log in which you'd like to see escape sequences, commands, and control characters.
Otherwise, you can inspect the PrinterOutput objects by using a type switch.
As with Scanner, one should deal with the Output() return value, if any, before dealing with the Err() return value.
func NewTelnetScanner ¶ added in v0.2.0
func NewTelnetScanner(charset *Charset, inputStream io.Reader) *TelnetScanner
NewTelnetScanner creates a new TelnetScanner from a Charset (used to decode bytes from the stream) and an input stream
func (*TelnetScanner) Err ¶ added in v0.2.0
func (s *TelnetScanner) Err() error
Err returns the error, if any, raised by the most recent call to Scan
func (*TelnetScanner) Output ¶ added in v0.2.0
func (s *TelnetScanner) Output() TerminalData
Output returns the PrinterOutput, if any, assembled by the most recent call to Scan
func (*TelnetScanner) Scan ¶ added in v0.2.0
func (s *TelnetScanner) Scan(ctx context.Context) bool
Scan will block until either the provided context is done, or a complete block of data is received from the input stream. "Complete" is subjective, but the TelnetScanner will not output partial ANSI sequences or partial glyphs of text.
Scan returns true if the caller should continue to call Scan to receive additional data. After calling Scan, Err and Output should be called to check for useful data.
func (*TelnetScanner) ScanTelnet ¶ added in v0.2.0
ScanTelnet is a method used as the split method for io.Scanner. It will receive chunks of text or commands as individual tokens.
type Terminal ¶
type Terminal struct {
// contains filtered or unexported fields
}
Terminal is a wrapper around a connection to enable telnet communications over the connection. Telnet's base protocol doesn't distinguish between client and server, so there is only one terminal type for both sides of the connection. A few telopts have different behavior for the client and server side though, so the terminal is aware of which side it is supposed to be.
Telnet functions as a "full duplex" protocol, meaning that it does not operate in a request-response type of semantic that users may be familiar with. Instead, it's best to envision a telnet connection as two asynchronous datastreams- a printer reader that produces text from the remote peer, and a keyboard writer that sends text to the remote peer.
Text from the printer is sent to the consumer of the Terminal via the many event hooks that can be registered for. The PrinterOutput hook produces structured data that is read in from the peer, and output is provided to the printer directly by calling terminal.Keyboard().Send*
Telnet has a mechanism for sending and receiving Command objects. Most of these are related to telopt negotiation, which the Terminal handles on your behalf based on the telopt preferences provided at creation time. In order to receive commands from the other side, it's best to register for TelOptEvent hooks, which provide the results of received commands in a more legible format. Generally, the user should not write commands unless they really, really know what they're doing. If you want to signal a prompt to the remote with IAC GA, use terminal.Keyboard().SendPromptHint().
The user should bear in mind that the terminal runs three (substantive) goroutines: one for the printer, one for the keyboard, and one for the terminal. The terminal loop receives data from both of the other loops and forwards it to registered hooks, which means that blocking calls in hook methods that last long enough will block functioning of the terminal altogether. It is the responsibility of the consumer to move long-running calls to their own concurrency scheme where necessary.
func NewTerminal ¶
NewTerminal initializes a new terminal object from a net.Conn and begins reading from the printer and writing to the keyboard. Telopt negotiation begins with the remote immediately when this method is called.
The terminal will continue until either the passed context is cancelled, or until the connection is closed.
All functioning of this terminal is determined by the properties passed in the TerminalConfig object. See that type for more information.
func NewTerminalFromPipes ¶ added in v0.2.0
func NewTerminalFromPipes(ctx context.Context, reader io.Reader, writer io.Writer, config TerminalConfig) (*Terminal, error)
NewTerminalFromPipes initializes a new terminal from a Reader and Writer instead of a net.Conn. This is useful for testing, or when data is arriving via more circuitous means than a simple connection. This Terminal will continue until BOTH the reader and writer are closed (or the context is cancelled). Only closing one will cause the connection to stall but the terminal will remain active, so that should never be done.
func (*Terminal) Charset ¶
Charset returns the relevant Charset object for the terminal, which stores what charset the terminal uses for encoding & decoding by default, what charset has been negotiated for use with TRANSMIT-BINARY, etc.
func (*Terminal) CommandString ¶
CommandString converts a Command object into a legible stream. This can be useful when logging a received command object
func (*Terminal) Keyboard ¶
func (t *Terminal) Keyboard() *TelnetKeyboard
Keyboard returns the object that is used for sending outbound communications
func (*Terminal) Printer ¶
func (t *Terminal) Printer() *TelnetPrinter
Printer returns the object that is used for receiving inbound communciations
func (*Terminal) RaiseTelOptEvent ¶
func (t *Terminal) RaiseTelOptEvent(event TelOptEvent)
RaiseTelOptEvent is called by telopt implementations, and the Terminal, to inject an event into the terminal event stream. Telopts can use this method to fire arbitrary events that can be interpreted by the consumer. This terminal will use this method to inject TelOptStateChangeEvent when negotiations cause a telopt to change its state. This is good for event-delivery telopts such as GCMP, but it can also be used for things like NAWS to alert the consumer that basic data has been collected from the remote.
func (*Terminal) RegisterEncounteredErrorHook ¶
func (t *Terminal) RegisterEncounteredErrorHook(encounteredError ErrorHandler)
RegisterEncounteredErrorHook will register an event to be called when an error was encountered by the terminal or one of its subsidiaries. Not all errors will be sent via this hook: just errors that are not returned to the user immediately.
If a method call to Terminal or one of its subsidiaries immediately returns an error to the user, it will not be delivered via this hook. If an error ends terminal processing immediately, it will not be delivered via this hook, it will be delivered via WaitForExit.
func (*Terminal) RegisterOutboundDataHook ¶ added in v0.3.0
func (t *Terminal) RegisterOutboundDataHook(outboundText TerminalDataHandler)
RegisterOutboundDataHook will register an event to be called when something has been sent from the keyboard. This is primarily useful for debug logging.
func (*Terminal) RegisterPrinterOutputHook ¶ added in v0.2.0
func (t *Terminal) RegisterPrinterOutputHook(printerOutput TerminalDataHandler)
RegisterPrinterOutputHook will register an event to be called when data is received from the printer.
func (*Terminal) RegisterTelOptEventHook ¶
func (t *Terminal) RegisterTelOptEventHook(telOptEvent TelOptEventHandler)
RegisterTelOptEventHook will register an event to be called when a telopt delivers an event via RaiseTelOptEvent.
func (*Terminal) Side ¶
func (t *Terminal) Side() TerminalSide
Side returns a TerminalSide object indicating whether the terminal represents a client or server
func (*Terminal) WaitForExit ¶
WaitForExit will block until the terminal has ceased operation, either due to the context passed to NewTerminal being cancelled, or due to the underlying data streams closing.
type TerminalConfig ¶
type TerminalConfig struct {
// DefaultCharsetName is the registered IANA name of the character set to use for all communications not
// sent via a negotiated charset (via the CHARSET telopt). RFC 854 (Telnet Protocol) specifies that by
// default, communications take place in ASCII encoding. RFC 5198 specified that since 2008, communications
// should by default take place in UTF-8. However, many active telnet services and a vanishingly small
// number of telnet clients have not been updated to use UTF-8. While UTF-8, as a superset of ASCII,
// will generally function just fine as a communications protocol with ASCII systems, it can be useful
// to make US-ASCII the default character set, allow the remote to negotiate to UTF-8 if they want, and
// use the current character set to determine support for sending things like emojis.
//
// Lastly, in the pre-2008 period, many telnet services were established in languages that could not use
// US-ASCII under any circumstances and used other character sets as the default rather than implementing
// CHARSET appropriately. For these services, launching with an alternative charset such as Big5 can be
// necessary.
//
// The charset specified here will be used initially for all text communications until a different character
// set is negotiated with the CHARSET telopt. If there are non-charset text communications (see CharsetUsage),
// this will be used for them. Text sent in telopt subnegotiations will always use UTF-8 regardless of this
// setting.
//
// If this characters set is US-ASCII and the remote indicates support for UTF-8 via a CHARSET negotiation
// or some other mechanism, the default character set will be promoted to UTF-8.
DefaultCharsetName string
// FallbackCharsetName can be left empty. If populated, it is the registered IANA name for
// a character set that will be used when the normal character decoding fails. If decoding
// a character from the printer results in the unicode replacement character, decoding will
// be retried using this character set. If decoding does not result in a unicode replacement
// character, the fallback character set will continue to be used until the next control code
// (including line break), command, or escape sequence, even if the fallback character set
// starts to fail during that time.
//
// This can be useful when connecting to BBS servers (or certain MUDs that act like them),
// because some use CP437 without any CHARSET negotiation at all. Since all bytes are valid
// CP437 bytes, replacing failed unicode bytes with CP437 bytes will usually detect and decode
// these servers without difficulty, with the minor exception of the small number of sequences
// that result in valid UTF-8 codepoints, such as \xdb\xb1.
FallbackCharsetName string
// CharsetUsage is only relevant if a new characters set has been negotiated via the CHARSET telopt.
// This field indicates when the negotiated character set will be used
// to send and receive text. According to RFC 2066, the charset is only to be used in BINARY mode
// (RFC 856). However, some systems will use it all the time, or only use CHARSET to advertise that the
// server is speaking UTF-8 without actually implementing any encoding functionality. As a result, we offer
// the option to always use the negotiated charset or only use it when BINARY mode is active.
//
// Text sent in telopt subnegotiations will always use UTF-8 regardless of this setting.
CharsetUsage CharsetUsage
// Side indicates whether this terminal is intended to be the client or server. Even though RFC 854
// (Telnet Protocol) does not have the concept of a client or server, just local and remote, some TelOpts,
// such as CHARSET, indicate different behaviors for clients and servers.
Side TerminalSide
// TelOpts indicates which TelOpts the terminal should request from the remote, and which the remote
// should be permitted to request from us.
TelOpts []TelnetOption
// EventHooks is a set of callbacks that the terminal will call when the relevant
// event occurs. You can register additional callbacks after creation with
// Terminal.Register* methods.
EventHooks EventHooks
// PrinterMiddlewares is a set of middlewares that should process output from the printer
// before it is sent to registered hooks
PrinterMiddlewares []Middleware
// KeyboardMiddlewares is a set of middlewares that should process data sent
// to the keyboard before it is sent to the network connection
KeyboardMiddlewares []Middleware
}
type TerminalData ¶ added in v0.3.0
type TerminalData interface {
String() string
EscapedString(terminal TelOptLibrary) string
}
TerminalData is an interface output by Terminal and TelnetScanner to represent a single unit of output from telnet
func NextOutput ¶ added in v0.4.0
func NextOutput[T string | []byte](p *TerminalDataParser, data T) TerminalData
type TerminalDataHandler ¶ added in v0.3.0
type TerminalDataHandler func(t *Terminal, output TerminalData)
TerminalDataHandler is an event hook type that receives text, control codes, escape sequences, and commands from the printer
type TerminalDataParser ¶ added in v0.4.0
type TerminalDataParser struct {
// contains filtered or unexported fields
}
func NewTerminalDataParser ¶ added in v0.4.0
func NewTerminalDataParser() *TerminalDataParser
func (*TerminalDataParser) FireAll ¶ added in v0.4.0
func (p *TerminalDataParser) FireAll(terminal *Terminal, data string, publisher *EventPublisher[TerminalData])
func (*TerminalDataParser) FireSingle ¶ added in v0.4.0
func (p *TerminalDataParser) FireSingle(terminal *Terminal, data string, hook TerminalDataHandler)
func (*TerminalDataParser) Flush ¶ added in v0.4.0
func (p *TerminalDataParser) Flush() TerminalData
type TerminalSide ¶
type TerminalSide byte
TerminalSide indicates whether this terminal represents a client or server. Technically speaking, telnet is a peer-to-peer protocol, more concerned with "local and remote" than "client and server". Some RFCs (mainly CHARSET) have distinct behavior for clients and server, though.
const ( SideUnknown TerminalSide = iota SideClient SideServer )
type TextData ¶ added in v0.3.0
type TextData string
TextData is a type representing printable text that has been received from telnet
func (TextData) EscapedString ¶ added in v0.3.0
func (o TextData) EscapedString(terminal TelOptLibrary) string
type TypedTelnetOption ¶
type TypedTelnetOption[OptionStruct any] interface { *OptionStruct TelnetOption }
TypedTelnetOption - this is used as a bit of a hack for GetTelOpt. It allows the generic semantic for that method to work