Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(745)

Delta Between Two Patch Sets: ssh/channel.go

Issue 14225043: code review 14225043: go.crypto/ssh: reimplement SSH connection protocol modu... (Closed)
Left Patch Set: diff -r 2cd6b3b93cdb https://code.google.com/p/go.crypto Created 10 years, 5 months ago
Right Patch Set: diff -r cd1eea1eb828 https://code.google.com/p/go.crypto Created 10 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | ssh/client.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2011 The Go Authors. All rights reserved. 1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package ssh 5 package ssh
6 6
7 import ( 7 import (
8 "encoding/binary" 8 "encoding/binary"
9 "errors" 9 "errors"
10 "fmt" 10 "fmt"
11 "io" 11 "io"
12 "log" 12 "log"
13 "sync" 13 "sync"
14 ) 14 )
15 15
16 // A Channel is an ordered, reliable, duplex stream that is 16 // A Channel is an ordered, reliable, duplex stream that is
17 // multiplexed over an SSH connection. 17 // multiplexed over an SSH connection.
18 type Channel interface { 18 type Channel interface {
19 // Accept accepts the channel creation request. 19 // Accept accepts the channel creation request.
20 Accept() error 20 Accept() error
21 21
22 // Reject rejects the channel creation request. After calling 22 // Reject rejects the channel creation request. After calling
23 // this, no other methods on the Channel may be called. 23 // this, no other methods on the Channel may be called.
24 Reject(reason RejectionReason, message string) error 24 Reject(reason RejectionReason, message string) error
25 25
26 » // NOSUBMIT - should add ReadExtended/WriteExtended here? 26 » // Read may return a ChannelRequest as an error.
27
28 Read(data []byte) (int, error) 27 Read(data []byte) (int, error)
29 Write(data []byte) (int, error) 28 Write(data []byte) (int, error)
30
31 // Sends EOF to the other side.
32 CloseWrite() error
33 29
34 // Signals end of channel use. No data may be sent after this 30 // Signals end of channel use. No data may be sent after this
35 // call. 31 // call.
36 Close() error 32 Close() error
37 33
38 » // Stderr returns an io.ReadWriter that writes to this channel with the 34 » // Stderr returns an io.Writer that writes to this channel with the
39 // extended data type set to stderr. 35 // extended data type set to stderr.
40 » Stderr() io.ReadWriter 36 » Stderr() io.Writer
41
42 » // SendRequest sends a channel request.
43 » SendRequest(name string, wantReply bool, payload []byte) (bool, error)
44
45 » // ReceivedRequests returns the channel for out-of-band
46 » // requests. For all requests received that have WantReply, a
47 » // call of AckRequest should follow.
48 » ReceivedRequests() chan *ChannelRequest
49 37
50 // AckRequest either sends an ack or nack to the channel 38 // AckRequest either sends an ack or nack to the channel
51 // request. It should only be called if the last 39 // request. It should only be called if the last
52 // ChannelRequest had a WantReply 40 // ChannelRequest had a WantReply
53 AckRequest(ok bool) error 41 AckRequest(ok bool) error
54 42
55 // ChannelType returns the type of the channel, as supplied by the 43 // ChannelType returns the type of the channel, as supplied by the
56 // client. 44 // client.
57 ChannelType() string 45 ChannelType() string
58 46
59 » // ExtraData returns the arbitary payload for this channel, as supplied 47 » // ExtraData returns the arbitrary payload for this channel, as supplied
60 // by the client. This data is specific to the channel type. 48 // by the client. This data is specific to the channel type.
61 ExtraData() []byte 49 ExtraData() []byte
62 } 50 }
63 51
64 // ChannelRequest represents a request sent, outside of the normal 52 // ChannelRequest represents a request sent, outside of the normal
65 // stream of bytes. Requests may either be channel specific, or global 53 // stream of bytes.
66 type ChannelRequest struct { 54 type ChannelRequest struct {
67 Request string 55 Request string
68 WantReply bool 56 WantReply bool
69 Payload []byte 57 Payload []byte
58 }
59
60 func (c ChannelRequest) Error() string {
61 return "ssh: channel request received"
70 } 62 }
71 63
72 // RejectionReason is an enumeration used when rejecting channel creation 64 // RejectionReason is an enumeration used when rejecting channel creation
73 // requests. See RFC 4254, section 5.1. 65 // requests. See RFC 4254, section 5.1.
74 type RejectionReason uint32 66 type RejectionReason uint32
75 67
76 const ( 68 const (
77 Prohibited RejectionReason = iota + 1 69 Prohibited RejectionReason = iota + 1
78 ConnectionFailed 70 ConnectionFailed
79 UnknownChannelType 71 UnknownChannelType
80 ResourceShortage 72 ResourceShortage
81 ) 73 )
82 74
75 // String converts the rejection reason to human readable form.
83 func (r RejectionReason) String() string { 76 func (r RejectionReason) String() string {
84 switch r { 77 switch r {
85 case Prohibited: 78 case Prohibited:
86 » » return "Prohibited" 79 » » return "administratively prohibited"
87 case ConnectionFailed: 80 case ConnectionFailed:
88 » » return "ConnectionFailed" 81 » » return "connect failed"
89 case UnknownChannelType: 82 case UnknownChannelType:
90 » » return "UnknownChannelType" 83 » » return "unknown channel type"
91 case ResourceShortage: 84 case ResourceShortage:
92 » » return "ResourceShortage" 85 » » return "resource shortage"
93 } 86 }
94 return fmt.Sprintf("unknown reason %d", int(r)) 87 return fmt.Sprintf("unknown reason %d", int(r))
95 } 88 }
96 89
97 // channel implements the Channel 90 // channel implements the Channel
98 type channel struct { 91 type channel struct {
99 // R/O after creation 92 // R/O after creation
100 chanType string 93 chanType string
101 extraData []byte 94 extraData []byte
102 localId, remoteId uint32 95 localId, remoteId uint32
103 maxPacket uint32 96 maxPacket uint32
104 mux *mux 97 mux *mux
105 98
106 // If set, we have called Accept or Reject on this channel 99 // If set, we have called Accept or Reject on this channel
107 decided bool 100 decided bool
108 101
109 // Pending internal channel messages. 102 // Pending internal channel messages.
110 msg chan interface{} 103 msg chan interface{}
111 104
112 // Pending user-serviceable messages. 105 // Pending user-serviceable messages.
113 » sentRequestMu sync.Mutex 106 » sentRequestMu sync.Mutex
114 » pendingRequests chan *ChannelRequest 107
108 » incomingRequests chan *ChannelRequest
115 109
116 sentEOF bool 110 sentEOF bool
117 111
118 // thread-safe data 112 // thread-safe data
119 remoteWin window 113 remoteWin window
120 pending *buffer 114 pending *buffer
121 extPending *buffer 115 extPending *buffer
122 116
123 // Protects all of the below. 117 // Protects all of the below.
124 mu sync.Mutex 118 mu sync.Mutex
(...skipping 13 matching lines...) Expand all
138 if uint32(len(packet)) > c.maxPacket { 132 if uint32(len(packet)) > c.maxPacket {
139 return fmt.Errorf("ssh: cannot write %d bytes, maxPacket is %d b ytes", len(packet), c.maxPacket) 133 return fmt.Errorf("ssh: cannot write %d bytes, maxPacket is %d b ytes", len(packet), c.maxPacket)
140 } 134 }
141 135
142 c.mu.Lock() 136 c.mu.Lock()
143 if c.sentClose { 137 if c.sentClose {
144 c.mu.Unlock() 138 c.mu.Unlock()
145 return io.EOF 139 return io.EOF
146 } 140 }
147 c.sentClose = (packet[0] == msgChannelClose) 141 c.sentClose = (packet[0] == msgChannelClose)
148 » err := c.mux.writePacket(packet) 142 » err := c.mux.conn.writePacket(packet)
149 c.mu.Unlock() 143 c.mu.Unlock()
150 return err 144 return err
151 } 145 }
152 146
153 func (c *channel) sendMessage(code byte, msg interface{}) error { 147 func (c *channel) sendMessage(code byte, msg interface{}) error {
154 if debug { 148 if debug {
155 log.Printf("send %d: %#v", c.mux.chanList.offset, msg) 149 log.Printf("send %d: %#v", c.mux.chanList.offset, msg)
156 } 150 }
157 151
158 p := marshal(code, msg) 152 p := marshal(code, msg)
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
258 return c.sendMessage(msgChannelWindowAdjust, windowAdjustMsg{ 252 return c.sendMessage(msgChannelWindowAdjust, windowAdjustMsg{
259 AdditionalBytes: uint32(n), 253 AdditionalBytes: uint32(n),
260 }) 254 })
261 } 255 }
262 256
263 func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) { 257 func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) {
264 if extended == 1 { 258 if extended == 1 {
265 n, err = c.extPending.Read(data) 259 n, err = c.extPending.Read(data)
266 } else if extended == 0 { 260 } else if extended == 0 {
267 n, err = c.pending.Read(data) 261 n, err = c.pending.Read(data)
262 } else {
263 return 0, fmt.Errorf("ssh: extended code %d implemented", extend ed)
268 } 264 }
269 265
270 if n > 0 { 266 if n > 0 {
271 err = c.adjustWindow(uint32(n)) 267 err = c.adjustWindow(uint32(n))
272 // sendWindowAdjust can return io.EOF if the remote 268 // sendWindowAdjust can return io.EOF if the remote
273 // peer has closed the connection, however we want to 269 // peer has closed the connection, however we want to
274 // defer forwarding io.EOF to the caller of Read until 270 // defer forwarding io.EOF to the caller of Read until
275 // the buffer has been drained. 271 // the buffer has been drained.
276 if n > 0 && err == io.EOF { 272 if n > 0 && err == io.EOF {
277 err = nil 273 err = nil
(...skipping 13 matching lines...) Expand all
291 case msgChannelData, msgChannelExtendedData: 287 case msgChannelData, msgChannelExtendedData:
292 return c.handleData(packet) 288 return c.handleData(packet)
293 case msgChannelClose: 289 case msgChannelClose:
294 // Ack the close. 290 // Ack the close.
295 c.sendMessage(msgChannelClose, channelCloseMsg{ 291 c.sendMessage(msgChannelClose, channelCloseMsg{
296 PeersId: c.remoteId}) 292 PeersId: c.remoteId})
297 293
298 c.pending.eof() 294 c.pending.eof()
299 c.extPending.eof() 295 c.extPending.eof()
300 close(c.msg) 296 close(c.msg)
301 » » close(c.pendingRequests) 297 » » close(c.incomingRequests)
302 c.mux.chanList.remove(c.localId) 298 c.mux.chanList.remove(c.localId)
303 299
304 return nil 300 return nil
305 case msgChannelEOF: 301 case msgChannelEOF:
306 c.pending.eof()
307 // RFC 4254 is mute on how EOF affects dataExt messages but 302 // RFC 4254 is mute on how EOF affects dataExt messages but
308 // it is logical to signal EOF at the same time. 303 // it is logical to signal EOF at the same time.
309 c.extPending.eof() 304 c.extPending.eof()
305
306 // For ServerConn, ChannelRequests are actually output
307 // as Read error. This means that no requests can be
308 // processed after EOF is sent, which is a bug
309 c.pending.eof()
310 return nil 310 return nil
311 } 311 }
312 312
313 decoded, err := decode(packet) 313 decoded, err := decode(packet)
314 if err != nil { 314 if err != nil {
315 return err 315 return err
316 } 316 }
317 317
318 switch msg := decoded.(type) { 318 switch msg := decoded.(type) {
319 case *windowAdjustMsg: 319 case *windowAdjustMsg:
320 if !c.remoteWin.add(msg.AdditionalBytes) { 320 if !c.remoteWin.add(msg.AdditionalBytes) {
321 return fmt.Errorf("invalid window update %d", msg.Additi onalBytes) 321 return fmt.Errorf("invalid window update %d", msg.Additi onalBytes)
322 } 322 }
323 323
324 case *channelRequestMsg: 324 case *channelRequestMsg:
325 req := ChannelRequest{ 325 req := ChannelRequest{
326 Request: msg.Request, 326 Request: msg.Request,
327 WantReply: msg.WantReply, 327 WantReply: msg.WantReply,
328 Payload: msg.RequestSpecificData, 328 Payload: msg.RequestSpecificData,
329 } 329 }
330 330
331 » » c.pendingRequests <- &req 331 » » c.incomingRequests <- &req
332 default: 332 default:
333 c.msg <- msg 333 c.msg <- msg
334 } 334 }
335 return nil 335 return nil
336 } 336 }
337 337
338 func newChannel(chanType string, extraData []byte) *channel { 338 func newChannel(chanType string, extraData []byte) *channel {
339 return &channel{ 339 return &channel{
340 » » remoteWin: window{Cond: newCond()}, 340 » » remoteWin: window{Cond: newCond()},
341 » » myWindow: defaultWindowSize, 341 » » myWindow: defaultWindowSize,
342 » » pending: newBuffer(), 342 » » pending: newBuffer(),
343 » » extPending: newBuffer(), 343 » » extPending: newBuffer(),
344 » » pendingRequests: make(chan *ChannelRequest, 16), 344 » » incomingRequests: make(chan *ChannelRequest, 16),
345 » » msg: make(chan interface{}, 16), 345 » » msg: make(chan interface{}, 16),
346 » » chanType: chanType, 346 » » chanType: chanType,
347 » » extraData: extraData, 347 » » extraData: extraData,
348 } 348 }
349 } 349 }
350 350
351 var errUndecided = errors.New("ssh: must Accept or Reject channel") 351 var errUndecided = errors.New("ssh: must Accept or Reject channel")
352 var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once") 352 var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once")
353 353
354 type extChannel channel 354 type extChannel struct {
355 » code uint32
356 » ch *channel
357 }
355 358
356 func (e *extChannel) Write(data []byte) (n int, err error) { 359 func (e *extChannel) Write(data []byte) (n int, err error) {
357 » return ((*channel)(e)).WriteExtended(data, 1) 360 » return e.ch.WriteExtended(data, e.code)
358 } 361 }
359 func (e *extChannel) Read(data []byte) (n int, err error) { 362 func (e *extChannel) Read(data []byte) (n int, err error) {
360 » return ((*channel)(e)).ReadExtended(data, 1) 363 » return e.ch.ReadExtended(data, e.code)
361 } 364 }
362 365
363 func (c *channel) Accept() error { 366 func (c *channel) Accept() error {
364 if c.decided { 367 if c.decided {
365 return errDecidedAlready 368 return errDecidedAlready
366 } 369 }
367 confirm := channelOpenConfirmMsg{ 370 confirm := channelOpenConfirmMsg{
368 PeersId: c.remoteId, 371 PeersId: c.remoteId,
369 MyId: c.localId, 372 MyId: c.localId,
370 MyWindow: c.myWindow, 373 MyWindow: c.myWindow,
371 MaxPacketSize: c.maxPacket, 374 MaxPacketSize: c.maxPacket,
372 } 375 }
373 c.decided = true 376 c.decided = true
374 » return c.sendMessage(msgChannelOpenConfirm, confirm) 377 » if err := c.sendMessage(msgChannelOpenConfirm, confirm); err != nil {
378 » » return err
379 » }
380
381 » return nil
375 } 382 }
376 383
377 func (ch *channel) Reject(reason RejectionReason, message string) error { 384 func (ch *channel) Reject(reason RejectionReason, message string) error {
378 if ch.decided { 385 if ch.decided {
379 return errDecidedAlready 386 return errDecidedAlready
380 } 387 }
381 reject := channelOpenFailureMsg{ 388 reject := channelOpenFailureMsg{
382 PeersId: ch.remoteId, 389 PeersId: ch.remoteId,
383 Reason: reason, 390 Reason: reason,
384 Message: message, 391 Message: message,
385 Language: "en", 392 Language: "en",
386 } 393 }
387 ch.decided = true 394 ch.decided = true
388 return ch.sendMessage(msgChannelOpenFailure, reject) 395 return ch.sendMessage(msgChannelOpenFailure, reject)
389 } 396 }
390 397
391 func (ch *channel) Read(data []byte) (int, error) { 398 func (ch *channel) Read(data []byte) (int, error) {
392 if !ch.decided { 399 if !ch.decided {
393 return 0, errUndecided 400 return 0, errUndecided
394 } 401 }
395 402
396 return ch.ReadExtended(data, 0) 403 return ch.ReadExtended(data, 0)
397 } 404 }
398 405
399 func (ch *channel) ReceivedRequests() chan *ChannelRequest {
400 return ch.pendingRequests
401 }
402
403 func (ch *channel) Write(data []byte) (int, error) { 406 func (ch *channel) Write(data []byte) (int, error) {
404 if !ch.decided { 407 if !ch.decided {
405 return 0, errUndecided 408 return 0, errUndecided
406 } 409 }
407 return ch.WriteExtended(data, 0) 410 return ch.WriteExtended(data, 0)
408 } 411 }
409 412
410 func (ch *channel) CloseWrite() error { 413 func (ch *channel) CloseWrite() error {
411 if !ch.decided { 414 if !ch.decided {
412 return errUndecided 415 return errUndecided
413 } 416 }
414 ch.sentEOF = true 417 ch.sentEOF = true
415 return ch.sendMessage(msgChannelEOF, channelEOFMsg{ 418 return ch.sendMessage(msgChannelEOF, channelEOFMsg{
416 PeersId: ch.remoteId}) 419 PeersId: ch.remoteId})
417 } 420 }
418 421
419 func (ch *channel) Close() error { 422 func (ch *channel) Close() error {
420 if !ch.decided { 423 if !ch.decided {
421 return errUndecided 424 return errUndecided
422 } 425 }
423 426
424 return ch.sendMessage(msgChannelClose, channelCloseMsg{ 427 return ch.sendMessage(msgChannelClose, channelCloseMsg{
425 PeersId: ch.remoteId}) 428 PeersId: ch.remoteId})
426 } 429 }
427 430
428 func (ch *channel) Stderr() io.ReadWriter { 431 func (ch *channel) Extended(code uint32) io.ReadWriter {
429 if !ch.decided { 432 if !ch.decided {
430 return nil 433 return nil
431 } 434 }
432 » return (*extChannel)(ch) 435 » return &extChannel{code, ch}
433 } 436 }
434 437
435 // SendRequest sends a channel request. If wantReply is set, it will 438 // SendRequest sends a channel request. If wantReply is set, it will
436 // wait for a reply and return the result as a boolean. 439 // wait for a reply and return the result as a boolean.
437 func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (boo l, error) { 440 func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (boo l, error) {
438 if !ch.decided { 441 if !ch.decided {
439 return false, errUndecided 442 return false, errUndecided
440 } 443 }
441 444
442 if wantReply { 445 if wantReply {
443 ch.sentRequestMu.Lock() 446 ch.sentRequestMu.Lock()
444 defer ch.sentRequestMu.Unlock() 447 defer ch.sentRequestMu.Unlock()
445 } 448 }
446 449
447 msg := channelRequestMsg{ 450 msg := channelRequestMsg{
448 PeersId: ch.remoteId, 451 PeersId: ch.remoteId,
449 Request: name, 452 Request: name,
450 WantReply: wantReply, 453 WantReply: wantReply,
451 RequestSpecificData: payload, 454 RequestSpecificData: payload,
452 } 455 }
453 456
454 if err := ch.sendMessage(msgChannelRequest, msg); err != nil { 457 if err := ch.sendMessage(msgChannelRequest, msg); err != nil {
455 return false, err 458 return false, err
456 } 459 }
457 460
458 if wantReply { 461 if wantReply {
459 » » switch (<-ch.msg).(type) { 462 » » m, ok := (<-ch.msg)
463 » » if !ok {
464 » » » return false, io.EOF
465 » » }
466 » » switch m.(type) {
460 case *channelRequestFailureMsg: 467 case *channelRequestFailureMsg:
461 return false, nil 468 return false, nil
462 case *channelRequestSuccessMsg: 469 case *channelRequestSuccessMsg:
463 return true, nil 470 return true, nil
471 default:
472 return false, fmt.Errorf("unexpected response %#v", m)
464 } 473 }
465 } 474 }
466 475
467 return false, nil 476 return false, nil
468 } 477 }
469 478
470 // AckRequest either sends an ack or nack to the channel request. 479 // AckRequest either sends an ack or nack to the channel request.
471 func (ch *channel) AckRequest(ok bool) error { 480 func (ch *channel) AckRequest(ok bool) error {
472 if !ch.decided { 481 if !ch.decided {
473 return errUndecided 482 return errUndecided
(...skipping 15 matching lines...) Expand all
489 return ch.sendMessage(code, msg) 498 return ch.sendMessage(code, msg)
490 } 499 }
491 500
492 func (ch *channel) ChannelType() string { 501 func (ch *channel) ChannelType() string {
493 return ch.chanType 502 return ch.chanType
494 } 503 }
495 504
496 func (ch *channel) ExtraData() []byte { 505 func (ch *channel) ExtraData() []byte {
497 return ch.extraData 506 return ch.extraData
498 } 507 }
508
509 // compatChannel is a hack to implement Channel's
510 // handing of channel requests.
511 type compatChannel struct {
512 *channel
513 }
514
515 func newCompatChannel(ch *channel) *compatChannel {
516 c := &compatChannel{ch}
517 go c.loop()
518 return c
519 }
520
521 func (c *compatChannel) loop() {
522 for r := range c.channel.incomingRequests {
523 c.channel.pending.addRequest(r)
524 }
525 }
526
527 func (c *compatChannel) Stderr() io.Writer {
528 return c.Extended(1)
529 }
LEFTRIGHT
« no previous file | ssh/client.go » ('j') | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b