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 "encoding/binary"
9 "errors"
10 "fmt"
11 "io"
12 "log"
13 "sync"
14)
15
16const (
17 minPacketLength = 9
18 // channelMaxPacket contains the maximum number of bytes that will be
19 // sent in a single packet. As per RFC 4253, section 6.1, 32k is also
20 // the minimum.
21 channelMaxPacket = 1 << 15
22 // We follow OpenSSH here.
23 channelWindowSize = 64 * channelMaxPacket
24)
25
26// NewChannel represents an incoming request to a channel. It must either be
27// accepted for use by calling Accept, or rejected by calling Reject.
28type NewChannel interface {
29 // Accept accepts the channel creation request. It returns the Channel
30 // and a Go channel containing SSH requests. The Go channel must be
31 // serviced otherwise the Channel will hang.
32 Accept() (Channel, <-chan *Request, error)
33
34 // Reject rejects the channel creation request. After calling
35 // this, no other methods on the Channel may be called.
36 Reject(reason RejectionReason, message string) error
37
38 // ChannelType returns the type of the channel, as supplied by the
39 // client.
40 ChannelType() string
41
42 // ExtraData returns the arbitrary payload for this channel, as supplied
43 // by the client. This data is specific to the channel type.
44 ExtraData() []byte
45}
46
47// A Channel is an ordered, reliable, flow-controlled, duplex stream
48// that is multiplexed over an SSH connection.
49type Channel interface {
50 // Read reads up to len(data) bytes from the channel.
51 Read(data []byte) (int, error)
52
53 // Write writes len(data) bytes to the channel.
54 Write(data []byte) (int, error)
55
56 // Close signals end of channel use. No data may be sent after this
57 // call.
58 Close() error
59
60 // CloseWrite signals the end of sending in-band
61 // data. Requests may still be sent, and the other side may
62 // still send data
63 CloseWrite() error
64
65 // SendRequest sends a channel request. If wantReply is true,
66 // it will wait for a reply and return the result as a
67 // boolean, otherwise the return value will be false. Channel
68 // requests are out-of-band messages so they may be sent even
69 // if the data stream is closed or blocked by flow control.
70 // If the channel is closed before a reply is returned, io.EOF
71 // is returned.
72 SendRequest(name string, wantReply bool, payload []byte) (bool, error)
73
74 // Stderr returns an io.ReadWriter that writes to this channel
75 // with the extended data type set to stderr. Stderr may
76 // safely be read and written from a different goroutine than
77 // Read and Write respectively.
78 Stderr() io.ReadWriter
79}
80
81// Request is a request sent outside of the normal stream of
82// data. Requests can either be specific to an SSH channel, or they
83// can be global.
84type Request struct {
85 Type string
86 WantReply bool
87 Payload []byte
88
89 ch *channel
90 mux *mux
91}
92
93// Reply sends a response to a request. It must be called for all requests
94// where WantReply is true and is a no-op otherwise. The payload argument is
95// ignored for replies to channel-specific requests.
96func (r *Request) Reply(ok bool, payload []byte) error {
97 if !r.WantReply {
98 return nil
99 }
100
101 if r.ch == nil {
102 return r.mux.ackRequest(ok, payload)
103 }
104
105 return r.ch.ackRequest(ok)
106}
107
108// RejectionReason is an enumeration used when rejecting channel creation
109// requests. See RFC 4254, section 5.1.
110type RejectionReason uint32
111
112const (
113 Prohibited RejectionReason = iota + 1
114 ConnectionFailed
115 UnknownChannelType
116 ResourceShortage
117)
118
119// String converts the rejection reason to human readable form.
120func (r RejectionReason) String() string {
121 switch r {
122 case Prohibited:
123 return "administratively prohibited"
124 case ConnectionFailed:
125 return "connect failed"
126 case UnknownChannelType:
127 return "unknown channel type"
128 case ResourceShortage:
129 return "resource shortage"
130 }
131 return fmt.Sprintf("unknown reason %d", int(r))
132}
133
134func min(a uint32, b int) uint32 {
135 if a < uint32(b) {
136 return a
137 }
138 return uint32(b)
139}
140
141type channelDirection uint8
142
143const (
144 channelInbound channelDirection = iota
145 channelOutbound
146)
147
148// channel is an implementation of the Channel interface that works
149// with the mux class.
150type channel struct {
151 // R/O after creation
152 chanType string
153 extraData []byte
154 localId, remoteId uint32
155
156 // maxIncomingPayload and maxRemotePayload are the maximum
157 // payload sizes of normal and extended data packets for
158 // receiving and sending, respectively. The wire packet will
159 // be 9 or 13 bytes larger (excluding encryption overhead).
160 maxIncomingPayload uint32
161 maxRemotePayload uint32
162
163 mux *mux
164
165 // decided is set to true if an accept or reject message has been sent
166 // (for outbound channels) or received (for inbound channels).
167 decided bool
168
169 // direction contains either channelOutbound, for channels created
170 // locally, or channelInbound, for channels created by the peer.
171 direction channelDirection
172
173 // Pending internal channel messages.
174 msg chan interface{}
175
176 // Since requests have no ID, there can be only one request
177 // with WantReply=true outstanding. This lock is held by a
178 // goroutine that has such an outgoing request pending.
179 sentRequestMu sync.Mutex
180
181 incomingRequests chan *Request
182
183 sentEOF bool
184
185 // thread-safe data
186 remoteWin window
187 pending *buffer
188 extPending *buffer
189
190 // windowMu protects myWindow, the flow-control window, and myConsumed,
191 // the number of bytes consumed since we last increased myWindow
192 windowMu sync.Mutex
193 myWindow uint32
194 myConsumed uint32
195
196 // writeMu serializes calls to mux.conn.writePacket() and
197 // protects sentClose and packetPool. This mutex must be
198 // different from windowMu, as writePacket can block if there
199 // is a key exchange pending.
200 writeMu sync.Mutex
201 sentClose bool
202
203 // packetPool has a buffer for each extended channel ID to
204 // save allocations during writes.
205 packetPool map[uint32][]byte
206}
207
208// writePacket sends a packet. If the packet is a channel close, it updates
209// sentClose. This method takes the lock c.writeMu.
210func (ch *channel) writePacket(packet []byte) error {
211 ch.writeMu.Lock()
212 if ch.sentClose {
213 ch.writeMu.Unlock()
214 return io.EOF
215 }
216 ch.sentClose = (packet[0] == msgChannelClose)
217 err := ch.mux.conn.writePacket(packet)
218 ch.writeMu.Unlock()
219 return err
220}
221
222func (ch *channel) sendMessage(msg interface{}) error {
223 if debugMux {
224 log.Printf("send(%d): %#v", ch.mux.chanList.offset, msg)
225 }
226
227 p := Marshal(msg)
228 binary.BigEndian.PutUint32(p[1:], ch.remoteId)
229 return ch.writePacket(p)
230}
231
232// WriteExtended writes data to a specific extended stream. These streams are
233// used, for example, for stderr.
234func (ch *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) {
235 if ch.sentEOF {
236 return 0, io.EOF
237 }
238 // 1 byte message type, 4 bytes remoteId, 4 bytes data length
239 opCode := byte(msgChannelData)
240 headerLength := uint32(9)
241 if extendedCode > 0 {
242 headerLength += 4
243 opCode = msgChannelExtendedData
244 }
245
246 ch.writeMu.Lock()
247 packet := ch.packetPool[extendedCode]
248 // We don't remove the buffer from packetPool, so
249 // WriteExtended calls from different goroutines will be
250 // flagged as errors by the race detector.
251 ch.writeMu.Unlock()
252
253 for len(data) > 0 {
254 space := min(ch.maxRemotePayload, len(data))
255 if space, err = ch.remoteWin.reserve(space); err != nil {
256 return n, err
257 }
258 if want := headerLength + space; uint32(cap(packet)) < want {
259 packet = make([]byte, want)
260 } else {
261 packet = packet[:want]
262 }
263
264 todo := data[:space]
265
266 packet[0] = opCode
267 binary.BigEndian.PutUint32(packet[1:], ch.remoteId)
268 if extendedCode > 0 {
269 binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode))
270 }
271 binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo)))
272 copy(packet[headerLength:], todo)
273 if err = ch.writePacket(packet); err != nil {
274 return n, err
275 }
276
277 n += len(todo)
278 data = data[len(todo):]
279 }
280
281 ch.writeMu.Lock()
282 ch.packetPool[extendedCode] = packet
283 ch.writeMu.Unlock()
284
285 return n, err
286}
287
288func (ch *channel) handleData(packet []byte) error {
289 headerLen := 9
290 isExtendedData := packet[0] == msgChannelExtendedData
291 if isExtendedData {
292 headerLen = 13
293 }
294 if len(packet) < headerLen {
295 // malformed data packet
296 return parseError(packet[0])
297 }
298
299 var extended uint32
300 if isExtendedData {
301 extended = binary.BigEndian.Uint32(packet[5:])
302 }
303
304 length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen])
305 if length == 0 {
306 return nil
307 }
308 if length > ch.maxIncomingPayload {
309 // TODO(hanwen): should send Disconnect?
310 return errors.New("ssh: incoming packet exceeds maximum payload size")
311 }
312
313 data := packet[headerLen:]
314 if length != uint32(len(data)) {
315 return errors.New("ssh: wrong packet length")
316 }
317
318 ch.windowMu.Lock()
319 if ch.myWindow < length {
320 ch.windowMu.Unlock()
321 // TODO(hanwen): should send Disconnect with reason?
322 return errors.New("ssh: remote side wrote too much")
323 }
324 ch.myWindow -= length
325 ch.windowMu.Unlock()
326
327 if extended == 1 {
328 ch.extPending.write(data)
329 } else if extended > 0 {
330 // discard other extended data.
331 } else {
332 ch.pending.write(data)
333 }
334 return nil
335}
336
337func (c *channel) adjustWindow(adj uint32) error {
338 c.windowMu.Lock()
339 // Since myConsumed and myWindow are managed on our side, and can never
340 // exceed the initial window setting, we don't worry about overflow.
341 c.myConsumed += adj
342 var sendAdj uint32
343 if (channelWindowSize-c.myWindow > 3*c.maxIncomingPayload) ||
344 (c.myWindow < channelWindowSize/2) {
345 sendAdj = c.myConsumed
346 c.myConsumed = 0
347 c.myWindow += sendAdj
348 }
349 c.windowMu.Unlock()
350 if sendAdj == 0 {
351 return nil
352 }
353 return c.sendMessage(windowAdjustMsg{
354 AdditionalBytes: sendAdj,
355 })
356}
357
358func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) {
359 switch extended {
360 case 1:
361 n, err = c.extPending.Read(data)
362 case 0:
363 n, err = c.pending.Read(data)
364 default:
365 return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended)
366 }
367
368 if n > 0 {
369 err = c.adjustWindow(uint32(n))
370 // sendWindowAdjust can return io.EOF if the remote
371 // peer has closed the connection, however we want to
372 // defer forwarding io.EOF to the caller of Read until
373 // the buffer has been drained.
374 if n > 0 && err == io.EOF {
375 err = nil
376 }
377 }
378
379 return n, err
380}
381
382func (c *channel) close() {
383 c.pending.eof()
384 c.extPending.eof()
385 close(c.msg)
386 close(c.incomingRequests)
387 c.writeMu.Lock()
388 // This is not necessary for a normal channel teardown, but if
389 // there was another error, it is.
390 c.sentClose = true
391 c.writeMu.Unlock()
392 // Unblock writers.
393 c.remoteWin.close()
394}
395
396// responseMessageReceived is called when a success or failure message is
397// received on a channel to check that such a message is reasonable for the
398// given channel.
399func (ch *channel) responseMessageReceived() error {
400 if ch.direction == channelInbound {
401 return errors.New("ssh: channel response message received on inbound channel")
402 }
403 if ch.decided {
404 return errors.New("ssh: duplicate response received for channel")
405 }
406 ch.decided = true
407 return nil
408}
409
410func (ch *channel) handlePacket(packet []byte) error {
411 switch packet[0] {
412 case msgChannelData, msgChannelExtendedData:
413 return ch.handleData(packet)
414 case msgChannelClose:
415 ch.sendMessage(channelCloseMsg{PeersID: ch.remoteId})
416 ch.mux.chanList.remove(ch.localId)
417 ch.close()
418 return nil
419 case msgChannelEOF:
420 // RFC 4254 is mute on how EOF affects dataExt messages but
421 // it is logical to signal EOF at the same time.
422 ch.extPending.eof()
423 ch.pending.eof()
424 return nil
425 }
426
427 decoded, err := decode(packet)
428 if err != nil {
429 return err
430 }
431
432 switch msg := decoded.(type) {
433 case *channelOpenFailureMsg:
434 if err := ch.responseMessageReceived(); err != nil {
435 return err
436 }
437 ch.mux.chanList.remove(msg.PeersID)
438 ch.msg <- msg
439 case *channelOpenConfirmMsg:
440 if err := ch.responseMessageReceived(); err != nil {
441 return err
442 }
443 if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
444 return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize)
445 }
446 ch.remoteId = msg.MyID
447 ch.maxRemotePayload = msg.MaxPacketSize
448 ch.remoteWin.add(msg.MyWindow)
449 ch.msg <- msg
450 case *windowAdjustMsg:
451 if !ch.remoteWin.add(msg.AdditionalBytes) {
452 return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes)
453 }
454 case *channelRequestMsg:
455 req := Request{
456 Type: msg.Request,
457 WantReply: msg.WantReply,
458 Payload: msg.RequestSpecificData,
459 ch: ch,
460 }
461
462 ch.incomingRequests <- &req
463 default:
464 ch.msg <- msg
465 }
466 return nil
467}
468
469func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel {
470 ch := &channel{
471 remoteWin: window{Cond: newCond()},
472 myWindow: channelWindowSize,
473 pending: newBuffer(),
474 extPending: newBuffer(),
475 direction: direction,
476 incomingRequests: make(chan *Request, chanSize),
477 msg: make(chan interface{}, chanSize),
478 chanType: chanType,
479 extraData: extraData,
480 mux: m,
481 packetPool: make(map[uint32][]byte),
482 }
483 ch.localId = m.chanList.add(ch)
484 return ch
485}
486
487var errUndecided = errors.New("ssh: must Accept or Reject channel")
488var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once")
489
490type extChannel struct {
491 code uint32
492 ch *channel
493}
494
495func (e *extChannel) Write(data []byte) (n int, err error) {
496 return e.ch.WriteExtended(data, e.code)
497}
498
499func (e *extChannel) Read(data []byte) (n int, err error) {
500 return e.ch.ReadExtended(data, e.code)
501}
502
503func (ch *channel) Accept() (Channel, <-chan *Request, error) {
504 if ch.decided {
505 return nil, nil, errDecidedAlready
506 }
507 ch.maxIncomingPayload = channelMaxPacket
508 confirm := channelOpenConfirmMsg{
509 PeersID: ch.remoteId,
510 MyID: ch.localId,
511 MyWindow: ch.myWindow,
512 MaxPacketSize: ch.maxIncomingPayload,
513 }
514 ch.decided = true
515 if err := ch.sendMessage(confirm); err != nil {
516 return nil, nil, err
517 }
518
519 return ch, ch.incomingRequests, nil
520}
521
522func (ch *channel) Reject(reason RejectionReason, message string) error {
523 if ch.decided {
524 return errDecidedAlready
525 }
526 reject := channelOpenFailureMsg{
527 PeersID: ch.remoteId,
528 Reason: reason,
529 Message: message,
530 Language: "en",
531 }
532 ch.decided = true
533 return ch.sendMessage(reject)
534}
535
536func (ch *channel) Read(data []byte) (int, error) {
537 if !ch.decided {
538 return 0, errUndecided
539 }
540 return ch.ReadExtended(data, 0)
541}
542
543func (ch *channel) Write(data []byte) (int, error) {
544 if !ch.decided {
545 return 0, errUndecided
546 }
547 return ch.WriteExtended(data, 0)
548}
549
550func (ch *channel) CloseWrite() error {
551 if !ch.decided {
552 return errUndecided
553 }
554 ch.sentEOF = true
555 return ch.sendMessage(channelEOFMsg{
556 PeersID: ch.remoteId})
557}
558
559func (ch *channel) Close() error {
560 if !ch.decided {
561 return errUndecided
562 }
563
564 return ch.sendMessage(channelCloseMsg{
565 PeersID: ch.remoteId})
566}
567
568// Extended returns an io.ReadWriter that sends and receives data on the given,
569// SSH extended stream. Such streams are used, for example, for stderr.
570func (ch *channel) Extended(code uint32) io.ReadWriter {
571 if !ch.decided {
572 return nil
573 }
574 return &extChannel{code, ch}
575}
576
577func (ch *channel) Stderr() io.ReadWriter {
578 return ch.Extended(1)
579}
580
581func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
582 if !ch.decided {
583 return false, errUndecided
584 }
585
586 if wantReply {
587 ch.sentRequestMu.Lock()
588 defer ch.sentRequestMu.Unlock()
589 }
590
591 msg := channelRequestMsg{
592 PeersID: ch.remoteId,
593 Request: name,
594 WantReply: wantReply,
595 RequestSpecificData: payload,
596 }
597
598 if err := ch.sendMessage(msg); err != nil {
599 return false, err
600 }
601
602 if wantReply {
603 m, ok := (<-ch.msg)
604 if !ok {
605 return false, io.EOF
606 }
607 switch m.(type) {
608 case *channelRequestFailureMsg:
609 return false, nil
610 case *channelRequestSuccessMsg:
611 return true, nil
612 default:
613 return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m)
614 }
615 }
616
617 return false, nil
618}
619
620// ackRequest either sends an ack or nack to the channel request.
621func (ch *channel) ackRequest(ok bool) error {
622 if !ch.decided {
623 return errUndecided
624 }
625
626 var msg interface{}
627 if !ok {
628 msg = channelRequestFailureMsg{
629 PeersID: ch.remoteId,
630 }
631 } else {
632 msg = channelRequestSuccessMsg{
633 PeersID: ch.remoteId,
634 }
635 }
636 return ch.sendMessage(msg)
637}
638
639func (ch *channel) ChannelType() string {
640 return ch.chanType
641}
642
643func (ch *channel) ExtraData() []byte {
644 return ch.extraData
645}