a geicko-2 based round robin ranking system designed to test c++ battleship submissions
battleship.dunkirk.sh
1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ssh
6
7import (
8 "bufio"
9 "bytes"
10 "errors"
11 "io"
12 "log"
13)
14
15// debugTransport if set, will print packet types as they go over the
16// wire. No message decoding is done, to minimize the impact on timing.
17const debugTransport = false
18
19// packetConn represents a transport that implements packet based
20// operations.
21type packetConn interface {
22 // Encrypt and send a packet of data to the remote peer.
23 writePacket(packet []byte) error
24
25 // Read a packet from the connection. The read is blocking,
26 // i.e. if error is nil, then the returned byte slice is
27 // always non-empty.
28 readPacket() ([]byte, error)
29
30 // Close closes the write-side of the connection.
31 Close() error
32}
33
34// transport is the keyingTransport that implements the SSH packet
35// protocol.
36type transport struct {
37 reader connectionState
38 writer connectionState
39
40 bufReader *bufio.Reader
41 bufWriter *bufio.Writer
42 rand io.Reader
43 isClient bool
44 io.Closer
45
46 strictMode bool
47 initialKEXDone bool
48}
49
50// packetCipher represents a combination of SSH encryption/MAC
51// protocol. A single instance should be used for one direction only.
52type packetCipher interface {
53 // writeCipherPacket encrypts the packet and writes it to w. The
54 // contents of the packet are generally scrambled.
55 writeCipherPacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
56
57 // readCipherPacket reads and decrypts a packet of data. The
58 // returned packet may be overwritten by future calls of
59 // readPacket.
60 readCipherPacket(seqnum uint32, r io.Reader) ([]byte, error)
61}
62
63// connectionState represents one side (read or write) of the
64// connection. This is necessary because each direction has its own
65// keys, and can even have its own algorithms
66type connectionState struct {
67 packetCipher
68 seqNum uint32
69 dir direction
70 pendingKeyChange chan packetCipher
71}
72
73func (t *transport) setStrictMode() error {
74 if t.reader.seqNum != 1 {
75 return errors.New("ssh: sequence number != 1 when strict KEX mode requested")
76 }
77 t.strictMode = true
78 return nil
79}
80
81func (t *transport) setInitialKEXDone() {
82 t.initialKEXDone = true
83}
84
85// prepareKeyChange sets up key material for a keychange. The key changes in
86// both directions are triggered by reading and writing a msgNewKey packet
87// respectively.
88func (t *transport) prepareKeyChange(algs *NegotiatedAlgorithms, kexResult *kexResult) error {
89 ciph, err := newPacketCipher(t.reader.dir, algs.Read, kexResult)
90 if err != nil {
91 return err
92 }
93 t.reader.pendingKeyChange <- ciph
94
95 ciph, err = newPacketCipher(t.writer.dir, algs.Write, kexResult)
96 if err != nil {
97 return err
98 }
99 t.writer.pendingKeyChange <- ciph
100
101 return nil
102}
103
104func (t *transport) printPacket(p []byte, write bool) {
105 if len(p) == 0 {
106 return
107 }
108 who := "server"
109 if t.isClient {
110 who = "client"
111 }
112 what := "read"
113 if write {
114 what = "write"
115 }
116
117 log.Println(what, who, p[0])
118}
119
120// Read and decrypt next packet.
121func (t *transport) readPacket() (p []byte, err error) {
122 for {
123 p, err = t.reader.readPacket(t.bufReader, t.strictMode)
124 if err != nil {
125 break
126 }
127 // in strict mode we pass through DEBUG and IGNORE packets only during the initial KEX
128 if len(p) == 0 || (t.strictMode && !t.initialKEXDone) || (p[0] != msgIgnore && p[0] != msgDebug) {
129 break
130 }
131 }
132 if debugTransport {
133 t.printPacket(p, false)
134 }
135
136 return p, err
137}
138
139func (s *connectionState) readPacket(r *bufio.Reader, strictMode bool) ([]byte, error) {
140 packet, err := s.packetCipher.readCipherPacket(s.seqNum, r)
141 s.seqNum++
142 if err == nil && len(packet) == 0 {
143 err = errors.New("ssh: zero length packet")
144 }
145
146 if len(packet) > 0 {
147 switch packet[0] {
148 case msgNewKeys:
149 select {
150 case cipher := <-s.pendingKeyChange:
151 s.packetCipher = cipher
152 if strictMode {
153 s.seqNum = 0
154 }
155 default:
156 return nil, errors.New("ssh: got bogus newkeys message")
157 }
158
159 case msgDisconnect:
160 // Transform a disconnect message into an
161 // error. Since this is lowest level at which
162 // we interpret message types, doing it here
163 // ensures that we don't have to handle it
164 // elsewhere.
165 var msg disconnectMsg
166 if err := Unmarshal(packet, &msg); err != nil {
167 return nil, err
168 }
169 return nil, &msg
170 }
171 }
172
173 // The packet may point to an internal buffer, so copy the
174 // packet out here.
175 fresh := make([]byte, len(packet))
176 copy(fresh, packet)
177
178 return fresh, err
179}
180
181func (t *transport) writePacket(packet []byte) error {
182 if debugTransport {
183 t.printPacket(packet, true)
184 }
185 return t.writer.writePacket(t.bufWriter, t.rand, packet, t.strictMode)
186}
187
188func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte, strictMode bool) error {
189 changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
190
191 err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet)
192 if err != nil {
193 return err
194 }
195 if err = w.Flush(); err != nil {
196 return err
197 }
198 s.seqNum++
199 if changeKeys {
200 select {
201 case cipher := <-s.pendingKeyChange:
202 s.packetCipher = cipher
203 if strictMode {
204 s.seqNum = 0
205 }
206 default:
207 panic("ssh: no key material for msgNewKeys")
208 }
209 }
210 return err
211}
212
213func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport {
214 t := &transport{
215 bufReader: bufio.NewReader(rwc),
216 bufWriter: bufio.NewWriter(rwc),
217 rand: rand,
218 reader: connectionState{
219 packetCipher: &streamPacketCipher{cipher: noneCipher{}},
220 pendingKeyChange: make(chan packetCipher, 1),
221 },
222 writer: connectionState{
223 packetCipher: &streamPacketCipher{cipher: noneCipher{}},
224 pendingKeyChange: make(chan packetCipher, 1),
225 },
226 Closer: rwc,
227 }
228 t.isClient = isClient
229
230 if isClient {
231 t.reader.dir = serverKeys
232 t.writer.dir = clientKeys
233 } else {
234 t.reader.dir = clientKeys
235 t.writer.dir = serverKeys
236 }
237
238 return t
239}
240
241type direction struct {
242 ivTag []byte
243 keyTag []byte
244 macKeyTag []byte
245}
246
247var (
248 serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}}
249 clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
250)
251
252// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
253// described in RFC 4253, section 6.4. direction should either be serverKeys
254// (to setup server->client keys) or clientKeys (for client->server keys).
255func newPacketCipher(d direction, algs DirectionAlgorithms, kex *kexResult) (packetCipher, error) {
256 cipherMode := cipherModes[algs.Cipher]
257
258 iv := make([]byte, cipherMode.ivSize)
259 key := make([]byte, cipherMode.keySize)
260
261 generateKeyMaterial(iv, d.ivTag, kex)
262 generateKeyMaterial(key, d.keyTag, kex)
263
264 var macKey []byte
265 if !aeadCiphers[algs.Cipher] {
266 macMode := macModes[algs.MAC]
267 macKey = make([]byte, macMode.keySize)
268 generateKeyMaterial(macKey, d.macKeyTag, kex)
269 }
270
271 return cipherModes[algs.Cipher].create(key, iv, macKey, algs)
272}
273
274// generateKeyMaterial fills out with key material generated from tag, K, H
275// and sessionId, as specified in RFC 4253, section 7.2.
276func generateKeyMaterial(out, tag []byte, r *kexResult) {
277 var digestsSoFar []byte
278
279 h := r.Hash.New()
280 for len(out) > 0 {
281 h.Reset()
282 h.Write(r.K)
283 h.Write(r.H)
284
285 if len(digestsSoFar) == 0 {
286 h.Write(tag)
287 h.Write(r.SessionID)
288 } else {
289 h.Write(digestsSoFar)
290 }
291
292 digest := h.Sum(nil)
293 n := copy(out, digest)
294 out = out[n:]
295 if len(out) > 0 {
296 digestsSoFar = append(digestsSoFar, digest...)
297 }
298 }
299}
300
301const packageVersion = "SSH-2.0-Go"
302
303// Sends and receives a version line. The versionLine string should
304// be US ASCII, start with "SSH-2.0-", and should not include a
305// newline. exchangeVersions returns the other side's version line.
306func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) {
307 // Contrary to the RFC, we do not ignore lines that don't
308 // start with "SSH-2.0-" to make the library usable with
309 // nonconforming servers.
310 for _, c := range versionLine {
311 // The spec disallows non US-ASCII chars, and
312 // specifically forbids null chars.
313 if c < 32 {
314 return nil, errors.New("ssh: junk character in version line")
315 }
316 }
317 if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil {
318 return
319 }
320
321 them, err = readVersion(rw)
322 return them, err
323}
324
325// maxVersionStringBytes is the maximum number of bytes that we'll
326// accept as a version string. RFC 4253 section 4.2 limits this at 255
327// chars
328const maxVersionStringBytes = 255
329
330// Read version string as specified by RFC 4253, section 4.2.
331func readVersion(r io.Reader) ([]byte, error) {
332 versionString := make([]byte, 0, 64)
333 var ok bool
334 var buf [1]byte
335
336 for length := 0; length < maxVersionStringBytes; length++ {
337 _, err := io.ReadFull(r, buf[:])
338 if err != nil {
339 return nil, err
340 }
341 // The RFC says that the version should be terminated with \r\n
342 // but several SSH servers actually only send a \n.
343 if buf[0] == '\n' {
344 if !bytes.HasPrefix(versionString, []byte("SSH-")) {
345 // RFC 4253 says we need to ignore all version string lines
346 // except the one containing the SSH version (provided that
347 // all the lines do not exceed 255 bytes in total).
348 versionString = versionString[:0]
349 continue
350 }
351 ok = true
352 break
353 }
354
355 // non ASCII chars are disallowed, but we are lenient,
356 // since Go doesn't use null-terminated strings.
357
358 // The RFC allows a comment after a space, however,
359 // all of it (version and comments) goes into the
360 // session hash.
361 versionString = append(versionString, buf[0])
362 }
363
364 if !ok {
365 return nil, errors.New("ssh: overflow reading version string")
366 }
367
368 // There might be a '\r' on the end which we should remove.
369 if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' {
370 versionString = versionString[:len(versionString)-1]
371 }
372 return versionString, nil
373}