LEFT | RIGHT |
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 Loading... |
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 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 return nil | 222 return nil |
229 } | 223 } |
230 data := packet[sz:] | 224 data := packet[sz:] |
231 if length != uint32(len(data)) { | 225 if length != uint32(len(data)) { |
232 return errors.New("ssh: wrong packet length") | 226 return errors.New("ssh: wrong packet length") |
233 } | 227 } |
234 | 228 |
235 c.mu.Lock() | 229 c.mu.Lock() |
236 if c.myWindow < length { | 230 if c.myWindow < length { |
237 c.mu.Unlock() | 231 c.mu.Unlock() |
| 232 // TODO(hanwen): should send Disconnect with reason? |
238 return errors.New("ssh: remote side wrote too much") | 233 return errors.New("ssh: remote side wrote too much") |
239 } | 234 } |
240 c.myWindow -= length | 235 c.myWindow -= length |
241 c.mu.Unlock() | 236 c.mu.Unlock() |
242 | 237 |
243 if extended == 1 { | 238 if extended == 1 { |
244 c.extPending.write(data) | 239 c.extPending.write(data) |
245 } else if extended > 0 { | 240 } else if extended > 0 { |
246 // discard other extended data. | 241 // discard other extended data. |
247 } else { | 242 } else { |
248 c.pending.write(data) | 243 c.pending.write(data) |
249 } | 244 } |
250 return nil | 245 return nil |
251 } | 246 } |
252 | 247 |
253 func (c *channel) adjustWindow(n uint32) error { | 248 func (c *channel) adjustWindow(n uint32) error { |
254 c.mu.Lock() | 249 c.mu.Lock() |
255 c.myWindow += uint32(n) | 250 c.myWindow += uint32(n) |
256 c.mu.Unlock() | 251 c.mu.Unlock() |
257 return c.sendMessage(msgChannelWindowAdjust, windowAdjustMsg{ | 252 return c.sendMessage(msgChannelWindowAdjust, windowAdjustMsg{ |
258 AdditionalBytes: uint32(n), | 253 AdditionalBytes: uint32(n), |
259 }) | 254 }) |
260 } | 255 } |
261 | 256 |
262 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)
{ |
263 if extended == 1 { | 258 if extended == 1 { |
264 n, err = c.extPending.Read(data) | 259 n, err = c.extPending.Read(data) |
265 } else if extended == 0 { | 260 } else if extended == 0 { |
266 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) |
267 } | 264 } |
268 | 265 |
269 if n > 0 { | 266 if n > 0 { |
270 err = c.adjustWindow(uint32(n)) | 267 err = c.adjustWindow(uint32(n)) |
271 » » // sendWindowAdjust can return io.EOF if the remote peer has | 268 » » // sendWindowAdjust can return io.EOF if the remote |
272 » » // closed the connection, however we want to defer forwarding io
.EOF to the | 269 » » // peer has closed the connection, however we want to |
273 » » // caller of Read until the buffer has been drained. | 270 » » // defer forwarding io.EOF to the caller of Read until |
| 271 » » // the buffer has been drained. |
274 if n > 0 && err == io.EOF { | 272 if n > 0 && err == io.EOF { |
275 err = nil | 273 err = nil |
276 } | 274 } |
277 } | 275 } |
278 | 276 |
279 return n, err | 277 return n, err |
280 } | 278 } |
281 | 279 |
282 func (c *channel) handlePacket(packet []byte) error { | 280 func (c *channel) handlePacket(packet []byte) error { |
| 281 if uint32(len(packet)) > c.maxPacket { |
| 282 // TODO(hanwen): should send Disconnect? |
| 283 return errors.New("ssh: incoming packet exceeds maximum size") |
| 284 } |
| 285 |
283 switch packet[0] { | 286 switch packet[0] { |
284 case msgChannelData, msgChannelExtendedData: | 287 case msgChannelData, msgChannelExtendedData: |
285 return c.handleData(packet) | 288 return c.handleData(packet) |
286 case msgChannelClose: | 289 case msgChannelClose: |
287 // Ack the close. | 290 // Ack the close. |
288 c.sendMessage(msgChannelClose, channelCloseMsg{ | 291 c.sendMessage(msgChannelClose, channelCloseMsg{ |
289 PeersId: c.remoteId}) | 292 PeersId: c.remoteId}) |
290 | 293 |
291 c.pending.eof() | 294 c.pending.eof() |
292 c.extPending.eof() | 295 c.extPending.eof() |
293 close(c.msg) | 296 close(c.msg) |
294 » » close(c.pendingRequests) | 297 » » close(c.incomingRequests) |
295 c.mux.chanList.remove(c.localId) | 298 c.mux.chanList.remove(c.localId) |
296 | 299 |
297 return nil | 300 return nil |
298 case msgChannelEOF: | 301 case msgChannelEOF: |
299 c.pending.eof() | |
300 // RFC 4254 is mute on how EOF affects dataExt messages but | 302 // RFC 4254 is mute on how EOF affects dataExt messages but |
301 // it is logical to signal EOF at the same time. | 303 // it is logical to signal EOF at the same time. |
302 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() |
303 return nil | 310 return nil |
304 } | 311 } |
305 | 312 |
306 decoded, err := decode(packet) | 313 decoded, err := decode(packet) |
307 if err != nil { | 314 if err != nil { |
308 return err | 315 return err |
309 } | 316 } |
310 | 317 |
311 switch msg := decoded.(type) { | 318 switch msg := decoded.(type) { |
312 case *windowAdjustMsg: | 319 case *windowAdjustMsg: |
313 if !c.remoteWin.add(msg.AdditionalBytes) { | 320 if !c.remoteWin.add(msg.AdditionalBytes) { |
314 return fmt.Errorf("invalid window update %d", msg.Additi
onalBytes) | 321 return fmt.Errorf("invalid window update %d", msg.Additi
onalBytes) |
315 } | 322 } |
316 | 323 |
317 case *channelRequestMsg: | 324 case *channelRequestMsg: |
318 req := ChannelRequest{ | 325 req := ChannelRequest{ |
319 Request: msg.Request, | 326 Request: msg.Request, |
320 WantReply: msg.WantReply, | 327 WantReply: msg.WantReply, |
321 Payload: msg.RequestSpecificData, | 328 Payload: msg.RequestSpecificData, |
322 } | 329 } |
323 | 330 |
324 » » c.pendingRequests <- &req | 331 » » c.incomingRequests <- &req |
325 default: | 332 default: |
326 c.msg <- msg | 333 c.msg <- msg |
327 } | 334 } |
328 return nil | 335 return nil |
329 } | 336 } |
330 | 337 |
331 func newChannel(chanType string, extraData []byte) *channel { | 338 func newChannel(chanType string, extraData []byte) *channel { |
332 return &channel{ | 339 return &channel{ |
333 » » remoteWin: window{Cond: newCond()}, | 340 » » remoteWin: window{Cond: newCond()}, |
334 » » myWindow: defaultWindowSize, | 341 » » myWindow: defaultWindowSize, |
335 » » pending: newBuffer(), | 342 » » pending: newBuffer(), |
336 » » extPending: newBuffer(), | 343 » » extPending: newBuffer(), |
337 » » pendingRequests: make(chan *ChannelRequest, 16), | 344 » » incomingRequests: make(chan *ChannelRequest, 16), |
338 » » msg: make(chan interface{}, 16), | 345 » » msg: make(chan interface{}, 16), |
339 » » chanType: chanType, | 346 » » chanType: chanType, |
340 » » extraData: extraData, | 347 » » extraData: extraData, |
341 } | 348 } |
342 } | 349 } |
343 | 350 |
344 var errUndecided = errors.New("ssh: must Accept or Reject channel") | 351 var errUndecided = errors.New("ssh: must Accept or Reject channel") |
345 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") |
346 | 353 |
347 type extChannel channel | 354 type extChannel struct { |
| 355 » code uint32 |
| 356 » ch *channel |
| 357 } |
348 | 358 |
349 func (e *extChannel) Write(data []byte) (n int, err error) { | 359 func (e *extChannel) Write(data []byte) (n int, err error) { |
350 » return ((*channel)(e)).WriteExtended(data, 1) | 360 » return e.ch.WriteExtended(data, e.code) |
351 } | 361 } |
352 func (e *extChannel) Read(data []byte) (n int, err error) { | 362 func (e *extChannel) Read(data []byte) (n int, err error) { |
353 » return ((*channel)(e)).ReadExtended(data, 1) | 363 » return e.ch.ReadExtended(data, e.code) |
354 } | 364 } |
355 | 365 |
356 func (c *channel) Accept() error { | 366 func (c *channel) Accept() error { |
357 if c.decided { | 367 if c.decided { |
358 return errDecidedAlready | 368 return errDecidedAlready |
359 } | 369 } |
360 confirm := channelOpenConfirmMsg{ | 370 confirm := channelOpenConfirmMsg{ |
361 PeersId: c.remoteId, | 371 PeersId: c.remoteId, |
362 MyId: c.localId, | 372 MyId: c.localId, |
363 MyWindow: c.myWindow, | 373 MyWindow: c.myWindow, |
364 MaxPacketSize: c.maxPacket, | 374 MaxPacketSize: c.maxPacket, |
365 } | 375 } |
366 c.decided = true | 376 c.decided = true |
367 » return c.sendMessage(msgChannelOpenConfirm, confirm) | 377 » if err := c.sendMessage(msgChannelOpenConfirm, confirm); err != nil { |
| 378 » » return err |
| 379 » } |
| 380 |
| 381 » return nil |
368 } | 382 } |
369 | 383 |
370 func (ch *channel) Reject(reason RejectionReason, message string) error { | 384 func (ch *channel) Reject(reason RejectionReason, message string) error { |
371 if ch.decided { | 385 if ch.decided { |
372 return errDecidedAlready | 386 return errDecidedAlready |
373 } | 387 } |
374 reject := channelOpenFailureMsg{ | 388 reject := channelOpenFailureMsg{ |
375 PeersId: ch.remoteId, | 389 PeersId: ch.remoteId, |
376 Reason: reason, | 390 Reason: reason, |
377 Message: message, | 391 Message: message, |
378 Language: "en", | 392 Language: "en", |
379 } | 393 } |
380 ch.decided = true | 394 ch.decided = true |
381 return ch.sendMessage(msgChannelOpenFailure, reject) | 395 return ch.sendMessage(msgChannelOpenFailure, reject) |
382 } | 396 } |
383 | 397 |
384 func (ch *channel) Read(data []byte) (int, error) { | 398 func (ch *channel) Read(data []byte) (int, error) { |
385 if !ch.decided { | 399 if !ch.decided { |
386 return 0, errUndecided | 400 return 0, errUndecided |
387 } | 401 } |
388 | 402 |
389 return ch.ReadExtended(data, 0) | 403 return ch.ReadExtended(data, 0) |
390 } | 404 } |
391 | 405 |
392 func (ch *channel) ReceivedRequests() chan *ChannelRequest { | |
393 return ch.pendingRequests | |
394 } | |
395 | |
396 func (ch *channel) Write(data []byte) (int, error) { | 406 func (ch *channel) Write(data []byte) (int, error) { |
397 if !ch.decided { | 407 if !ch.decided { |
398 return 0, errUndecided | 408 return 0, errUndecided |
399 } | 409 } |
400 return ch.WriteExtended(data, 0) | 410 return ch.WriteExtended(data, 0) |
401 } | 411 } |
402 | 412 |
403 func (ch *channel) CloseWrite() error { | 413 func (ch *channel) CloseWrite() error { |
404 if !ch.decided { | 414 if !ch.decided { |
405 return errUndecided | 415 return errUndecided |
406 } | 416 } |
407 ch.sentEOF = true | 417 ch.sentEOF = true |
408 return ch.sendMessage(msgChannelEOF, channelEOFMsg{ | 418 return ch.sendMessage(msgChannelEOF, channelEOFMsg{ |
409 PeersId: ch.remoteId}) | 419 PeersId: ch.remoteId}) |
410 } | 420 } |
411 | 421 |
412 func (ch *channel) Close() error { | 422 func (ch *channel) Close() error { |
413 if !ch.decided { | 423 if !ch.decided { |
414 return errUndecided | 424 return errUndecided |
415 } | 425 } |
416 | 426 |
417 return ch.sendMessage(msgChannelClose, channelCloseMsg{ | 427 return ch.sendMessage(msgChannelClose, channelCloseMsg{ |
418 PeersId: ch.remoteId}) | 428 PeersId: ch.remoteId}) |
419 } | 429 } |
420 | 430 |
421 func (ch *channel) Stderr() io.ReadWriter { | 431 func (ch *channel) Extended(code uint32) io.ReadWriter { |
422 if !ch.decided { | 432 if !ch.decided { |
423 return nil | 433 return nil |
424 } | 434 } |
425 » return (*extChannel)(ch) | 435 » return &extChannel{code, ch} |
426 } | 436 } |
427 | 437 |
428 // SendRequest sends a channel request. If wantReply is set, it will | 438 // SendRequest sends a channel request. If wantReply is set, it will |
429 // wait for a reply and return the result as a boolean. | 439 // wait for a reply and return the result as a boolean. |
430 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) { |
431 if !ch.decided { | 441 if !ch.decided { |
432 return false, errUndecided | 442 return false, errUndecided |
433 } | 443 } |
434 | 444 |
435 if wantReply { | 445 if wantReply { |
436 ch.sentRequestMu.Lock() | 446 ch.sentRequestMu.Lock() |
437 defer ch.sentRequestMu.Unlock() | 447 defer ch.sentRequestMu.Unlock() |
438 } | 448 } |
439 | 449 |
440 msg := channelRequestMsg{ | 450 msg := channelRequestMsg{ |
441 PeersId: ch.remoteId, | 451 PeersId: ch.remoteId, |
442 Request: name, | 452 Request: name, |
443 WantReply: wantReply, | 453 WantReply: wantReply, |
444 RequestSpecificData: payload, | 454 RequestSpecificData: payload, |
445 } | 455 } |
446 | 456 |
447 if err := ch.sendMessage(msgChannelRequest, msg); err != nil { | 457 if err := ch.sendMessage(msgChannelRequest, msg); err != nil { |
448 return false, err | 458 return false, err |
449 } | 459 } |
450 | 460 |
451 if wantReply { | 461 if wantReply { |
452 » » switch (<-ch.msg).(type) { | 462 » » m, ok := (<-ch.msg) |
| 463 » » if !ok { |
| 464 » » » return false, io.EOF |
| 465 » » } |
| 466 » » switch m.(type) { |
453 case *channelRequestFailureMsg: | 467 case *channelRequestFailureMsg: |
454 return false, nil | 468 return false, nil |
455 case *channelRequestSuccessMsg: | 469 case *channelRequestSuccessMsg: |
456 return true, nil | 470 return true, nil |
| 471 default: |
| 472 return false, fmt.Errorf("unexpected response %#v", m) |
457 } | 473 } |
458 } | 474 } |
459 | 475 |
460 return false, nil | 476 return false, nil |
461 } | 477 } |
462 | 478 |
463 // AckRequest either sends an ack or nack to the channel request. | 479 // AckRequest either sends an ack or nack to the channel request. |
464 func (ch *channel) AckRequest(ok bool) error { | 480 func (ch *channel) AckRequest(ok bool) error { |
465 if !ch.decided { | 481 if !ch.decided { |
466 return errUndecided | 482 return errUndecided |
(...skipping 15 matching lines...) Expand all Loading... |
482 return ch.sendMessage(code, msg) | 498 return ch.sendMessage(code, msg) |
483 } | 499 } |
484 | 500 |
485 func (ch *channel) ChannelType() string { | 501 func (ch *channel) ChannelType() string { |
486 return ch.chanType | 502 return ch.chanType |
487 } | 503 } |
488 | 504 |
489 func (ch *channel) ExtraData() []byte { | 505 func (ch *channel) ExtraData() []byte { |
490 return ch.extraData | 506 return ch.extraData |
491 } | 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 } |
LEFT | RIGHT |