-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwriter.go
124 lines (108 loc) · 3.31 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package telnet
import (
"bytes"
"errors"
"io"
"strings"
)
// writer handles escaping data according to the TELNET and TELNETS protocols.
//
// In these protocols, byte value 255 (IAC, "interpret as command") is used for commands.
// TELNET and TELNETS distinguish between 'data' and 'commands'.
//
// writer focuses on escaping 'data', not 'commands'.
// If byte 255 (IAC) appears in the data, it must be escaped by doubling it.
//
// Examples:
//
// Original: []byte{255}
// Escaped: []byte{255, 255}
//
// A more complete example:
//
// Original: []byte{1, 55, 2, 155, 3, 255, 4, 40, 255, 30, 20}
// Escaped: []byte{1, 55, 2, 155, 3, 255, 255, 4, 40, 255, 255, 30, 20}
//
// writer automatically handles this escaping process for you.
type writer struct {
writer io.Writer
}
// newWriter creates a new writer that writes to 'w'.
//
// 'w' will receive the data written to the writer, but escaped according to
// the TELNET and TELNETS protocols. Specifically, byte 255 (IAC) is encoded as 255, 255.
//
// For example, if the following data is written to the writer's Write method:
//
// []byte{1, 55, 2, 155, 3, 255, 4, 40, 255, 30, 20}
//
// then the following data is written to 'w's Write method:
//
// []byte{1, 55, 2, 155, 3, 255, 255, 4, 40, 255, 255, 30, 20}
//
// (Notice that each byte 255 in the original data is doubled.)
//
// The writer handles this escaping process automatically.
func newWriter(w io.Writer) *writer {
return &writer{
writer: w,
}
}
// Write writes the TELNET (and TELNETS) escaped data for of the data in 'data' to the writer io.Writer.
func (w *writer) Write(data []byte) (n int, err error) {
var buffer bytes.Buffer
// Workaround for commands.
if len(data) > 5 && bytes.Equal(data[0:4], commandSignature()) {
numWritten, err := LongWrite(w.writer, data[4:])
return int(numWritten), err
}
for _, value := range data {
if value != IAC {
buffer.WriteByte(value)
continue
}
// Write buffered data first if there's any.
if buffer.Len() > 0 {
numWritten, err := LongWrite(w.writer, buffer.Bytes())
n += int(numWritten)
if err != nil {
return n, err
}
buffer.Reset()
}
// Write escape IAC sequence.
numWritten, err := LongWrite(w.writer, w.escapeIAC())
if err != nil {
return n, err
}
if int(numWritten) != len(w.escapeIAC()) {
return n, errors.New("partial IAC IAC write")
}
n++
}
// Write any remaining buffered data
if buffer.Len() > 0 {
numWritten, err := LongWrite(w.writer, buffer.Bytes())
n += int(numWritten)
if err != nil {
return n, err
}
}
return n, nil
}
func (w *writer) escapeIAC() []byte {
return []byte{IAC, IAC}
}
func WriteLine(writer io.Writer, text ...string) error {
_, err := writer.Write([]byte(strings.Join(text, "")))
return err
}
// WriteCommand is a dirty workaround to write Telnet commands directly to the client. The internal wrapper satisfies
// io.Write, preventing us from including custom logic to handle commands (without risking bodging real data). Instead,
// this submits a signature (IAC x4) the underlying Write function knows to look for, and to treat as a command.
func WriteCommand(writer io.Writer, command byte, option byte, action byte) (n int, err error) {
return writer.Write(append(commandSignature(), command, option, action))
}
func commandSignature() []byte {
return []byte{IAC, IAC, IAC, IAC}
}