-
Notifications
You must be signed in to change notification settings - Fork 3
/
transport.go
150 lines (129 loc) · 3.68 KB
/
transport.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package ssh1
import (
"bufio"
"io"
"log"
)
// debugTransport if set, will print packet types as they go over the
// wire. No message decoding is done, to minimize the impact on timing.
const debugTransport = false
// packetConn represents a transport that implements packet based
// operations.
type packetConn interface {
// Encrypt and send a packet of data to the remote peer.
writePacket(packetType byte, packet []byte) error
// Read a packet from the connection. The read is blocking,
// i.e. if error is nil, then the returned byte slice is
// always non-empty.
readPacket() (byte, []byte, error)
// Close closes the write-side of the connection.
Close() error
}
// transport implements the SSH packet protocol.
type transport struct {
reader connectionState
writer connectionState
bufReader *bufio.Reader
bufWriter *bufio.Writer
rand io.Reader
io.Closer
}
// packetCipher represents a combination of SSH encryption protocol.
// A single instance should be used for one direction only.
type packetCipher interface {
// writeCipherPacket encrypts the packet and writes it to w. The
// contents of the packet are generally scrambled.
writeCipherPacket(w io.Writer, rand io.Reader, packetType byte, packet []byte) error
// readCipherPacket reads and decrypts a packet of data. The
// returned packet may be overwritten by future calls of
// readPacket.
readCipherPacket(r io.Reader) (byte, []byte, error)
}
// connectionState represents one side (read or write) of the
// connection.
type connectionState struct {
packetCipher
}
func (t *transport) printPacket(pt byte, write bool) {
if pt == 0 {
return
}
who := "server"
what := "read"
if write {
who = "client"
what = "write"
}
log.Println(what, who, pt)
}
// Read and decrypt next packet.
func (t *transport) readPacket() (pt byte, p []byte, err error) {
for {
pt, p, err = t.reader.readPacket(t.bufReader)
if err != nil {
break
}
if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) {
break
}
}
if debugTransport {
t.printPacket(pt, false)
}
return pt, p, err
}
func (s *connectionState) readPacket(r *bufio.Reader) (byte, []byte, error) {
packetType, packet, err := s.packetCipher.readCipherPacket(r)
if err != nil {
return packetTypeForError, nil, err
}
switch packetType {
case msgDisconnect:
// Transform a disconnect message into an
// error. Since this is lowest level at which
// we interpret message types, doing it here
// ensures that we don't have to handle it
// elsewhere.
var msg disconnectMsg
if err := Unmarshal(packetType, packet, &msg); err != nil {
return packetTypeForError, nil, err
}
return packetTypeForError, nil, &msg
}
// The packet may point to an internal buffer, so copy the
// packet out here.
fresh := make([]byte, len(packet))
copy(fresh, packet)
return packetType, fresh, nil
}
func (t *transport) writePacket(packetType byte, packet []byte) error {
if debugTransport {
t.printPacket(packetType, true)
}
return t.writer.writePacket(t.bufWriter, t.rand, packetType, packet)
}
func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packetType byte, packet []byte) error {
err := s.packetCipher.writeCipherPacket(w, rand, packetType, packet)
if err != nil {
return err
}
if err = w.Flush(); err != nil {
return err
}
return err
}
func newTransport(rwc io.ReadWriteCloser, rand io.Reader) *transport {
t := &transport{
bufReader: bufio.NewReader(rwc),
bufWriter: bufio.NewWriter(rwc),
rand: rand,
reader: connectionState{
packetCipher: &streamPacketCipher{cipher: noneCipher{}},
},
writer: connectionState{
packetCipher: &streamPacketCipher{cipher: noneCipher{}},
},
Closer: rwc,
}
return t
}