2012-01-26 00:31:12 +04:00
|
|
|
// Copyright 2011 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package ssh
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
2012-02-20 19:42:43 +04:00
|
|
|
// extendedDataTypeCode identifies an OpenSSL extended data type. See RFC 4254,
|
|
|
|
// section 5.2.
|
|
|
|
type extendedDataTypeCode uint32
|
|
|
|
|
|
|
|
// extendedDataStderr is the extended data type that is used for stderr.
|
|
|
|
const extendedDataStderr extendedDataTypeCode = 1
|
|
|
|
|
2012-01-26 00:31:12 +04:00
|
|
|
// A Channel is an ordered, reliable, duplex stream that is multiplexed over an
|
2012-02-20 19:42:43 +04:00
|
|
|
// SSH connection. Channel.Read can return a ChannelRequest as an error.
|
2012-01-26 00:31:12 +04:00
|
|
|
type Channel interface {
|
|
|
|
// Accept accepts the channel creation request.
|
|
|
|
Accept() error
|
|
|
|
// Reject rejects the channel creation request. After calling this, no
|
|
|
|
// other methods on the Channel may be called. If they are then the
|
|
|
|
// peer is likely to signal a protocol error and drop the connection.
|
|
|
|
Reject(reason RejectionReason, message string) error
|
|
|
|
|
|
|
|
// Read may return a ChannelRequest as an error.
|
|
|
|
Read(data []byte) (int, error)
|
|
|
|
Write(data []byte) (int, error)
|
|
|
|
Close() error
|
|
|
|
|
2012-02-20 19:42:43 +04:00
|
|
|
// Stderr returns an io.Writer that writes to this channel with the
|
|
|
|
// extended data type set to stderr.
|
|
|
|
Stderr() io.Writer
|
|
|
|
|
2012-01-26 00:31:12 +04:00
|
|
|
// AckRequest either sends an ack or nack to the channel request.
|
|
|
|
AckRequest(ok bool) error
|
|
|
|
|
|
|
|
// ChannelType returns the type of the channel, as supplied by the
|
|
|
|
// client.
|
|
|
|
ChannelType() string
|
|
|
|
// ExtraData returns the arbitary payload for this channel, as supplied
|
|
|
|
// by the client. This data is specific to the channel type.
|
|
|
|
ExtraData() []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChannelRequest represents a request sent on a channel, outside of the normal
|
|
|
|
// stream of bytes. It may result from calling Read on a Channel.
|
|
|
|
type ChannelRequest struct {
|
|
|
|
Request string
|
|
|
|
WantReply bool
|
|
|
|
Payload []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c ChannelRequest) Error() string {
|
2012-04-20 23:17:42 +04:00
|
|
|
return "ssh: channel request received"
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// RejectionReason is an enumeration used when rejecting channel creation
|
|
|
|
// requests. See RFC 4254, section 5.1.
|
2012-05-18 03:11:31 +04:00
|
|
|
type RejectionReason uint32
|
2012-01-26 00:31:12 +04:00
|
|
|
|
|
|
|
const (
|
|
|
|
Prohibited RejectionReason = iota + 1
|
|
|
|
ConnectionFailed
|
|
|
|
UnknownChannelType
|
|
|
|
ResourceShortage
|
|
|
|
)
|
|
|
|
|
2012-05-09 02:20:05 +04:00
|
|
|
type channel struct {
|
|
|
|
conn // the underlying transport
|
|
|
|
localId, remoteId uint32
|
2012-05-10 23:56:44 +04:00
|
|
|
remoteWin window
|
2012-08-09 04:22:00 +04:00
|
|
|
maxPacketSize uint32
|
2012-05-09 02:20:05 +04:00
|
|
|
|
|
|
|
theyClosed bool // indicates the close msg has been received from the remote side
|
|
|
|
weClosed bool // incidates the close msg has been sent from our side
|
|
|
|
theySentEOF bool // used by serverChan
|
|
|
|
dead bool // used by ServerChan to force close
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *channel) sendWindowAdj(n int) error {
|
|
|
|
msg := windowAdjustMsg{
|
|
|
|
PeersId: c.remoteId,
|
|
|
|
AdditionalBytes: uint32(n),
|
|
|
|
}
|
|
|
|
return c.writePacket(marshal(msgChannelWindowAdjust, msg))
|
|
|
|
}
|
|
|
|
|
|
|
|
// sendClose signals the intent to close the channel.
|
|
|
|
func (c *channel) sendClose() error {
|
|
|
|
return c.writePacket(marshal(msgChannelClose, channelCloseMsg{
|
|
|
|
PeersId: c.remoteId,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
// sendEOF sends EOF to the server. RFC 4254 Section 5.3
|
|
|
|
func (c *channel) sendEOF() error {
|
|
|
|
return c.writePacket(marshal(msgChannelEOF, channelEOFMsg{
|
|
|
|
PeersId: c.remoteId,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *channel) sendChannelOpenFailure(reason RejectionReason, message string) error {
|
|
|
|
reject := channelOpenFailureMsg{
|
|
|
|
PeersId: c.remoteId,
|
|
|
|
Reason: reason,
|
|
|
|
Message: message,
|
|
|
|
Language: "en",
|
|
|
|
}
|
|
|
|
return c.writePacket(marshal(msgChannelOpenFailure, reject))
|
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
type serverChan struct {
|
2012-05-09 02:20:05 +04:00
|
|
|
channel
|
2012-01-26 00:31:12 +04:00
|
|
|
// immutable once created
|
|
|
|
chanType string
|
|
|
|
extraData []byte
|
|
|
|
|
2012-08-09 04:22:00 +04:00
|
|
|
serverConn *ServerConn
|
|
|
|
myWindow uint32
|
|
|
|
err error
|
2012-01-26 00:31:12 +04:00
|
|
|
|
|
|
|
pendingRequests []ChannelRequest
|
|
|
|
pendingData []byte
|
|
|
|
head, length int
|
|
|
|
|
|
|
|
// This lock is inferior to serverConn.lock
|
|
|
|
cond *sync.Cond
|
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) Accept() error {
|
2012-01-26 00:31:12 +04:00
|
|
|
c.serverConn.lock.Lock()
|
|
|
|
defer c.serverConn.lock.Unlock()
|
|
|
|
|
|
|
|
if c.serverConn.err != nil {
|
|
|
|
return c.serverConn.err
|
|
|
|
}
|
|
|
|
|
|
|
|
confirm := channelOpenConfirmMsg{
|
2012-05-05 01:59:48 +04:00
|
|
|
PeersId: c.remoteId,
|
|
|
|
MyId: c.localId,
|
2012-01-26 00:31:12 +04:00
|
|
|
MyWindow: c.myWindow,
|
|
|
|
MaxPacketSize: c.maxPacketSize,
|
|
|
|
}
|
2012-05-09 02:20:05 +04:00
|
|
|
return c.writePacket(marshal(msgChannelOpenConfirm, confirm))
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) Reject(reason RejectionReason, message string) error {
|
2012-01-26 00:31:12 +04:00
|
|
|
c.serverConn.lock.Lock()
|
|
|
|
defer c.serverConn.lock.Unlock()
|
|
|
|
|
|
|
|
if c.serverConn.err != nil {
|
|
|
|
return c.serverConn.err
|
|
|
|
}
|
|
|
|
|
2012-05-09 02:20:05 +04:00
|
|
|
return c.sendChannelOpenFailure(reason, message)
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) handlePacket(packet interface{}) {
|
2012-05-09 02:20:05 +04:00
|
|
|
c.cond.L.Lock()
|
|
|
|
defer c.cond.L.Unlock()
|
2012-01-26 00:31:12 +04:00
|
|
|
|
|
|
|
switch packet := packet.(type) {
|
|
|
|
case *channelRequestMsg:
|
|
|
|
req := ChannelRequest{
|
|
|
|
Request: packet.Request,
|
|
|
|
WantReply: packet.WantReply,
|
|
|
|
Payload: packet.RequestSpecificData,
|
|
|
|
}
|
|
|
|
|
|
|
|
c.pendingRequests = append(c.pendingRequests, req)
|
|
|
|
c.cond.Signal()
|
|
|
|
case *channelCloseMsg:
|
|
|
|
c.theyClosed = true
|
|
|
|
c.cond.Signal()
|
|
|
|
case *channelEOFMsg:
|
|
|
|
c.theySentEOF = true
|
|
|
|
c.cond.Signal()
|
2012-03-26 20:44:23 +04:00
|
|
|
case *windowAdjustMsg:
|
2012-05-10 23:56:44 +04:00
|
|
|
if !c.remoteWin.add(packet.AdditionalBytes) {
|
|
|
|
panic("illegal window update")
|
|
|
|
}
|
2012-01-26 00:31:12 +04:00
|
|
|
default:
|
|
|
|
panic("unknown packet type")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) handleData(data []byte) {
|
2012-05-09 02:20:05 +04:00
|
|
|
c.cond.L.Lock()
|
|
|
|
defer c.cond.L.Unlock()
|
2012-01-26 00:31:12 +04:00
|
|
|
|
|
|
|
// The other side should never send us more than our window.
|
|
|
|
if len(data)+c.length > len(c.pendingData) {
|
|
|
|
// TODO(agl): we should tear down the channel with a protocol
|
|
|
|
// error.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.myWindow -= uint32(len(data))
|
|
|
|
for i := 0; i < 2; i++ {
|
|
|
|
tail := c.head + c.length
|
2012-04-19 19:16:57 +04:00
|
|
|
if tail >= len(c.pendingData) {
|
2012-01-26 00:31:12 +04:00
|
|
|
tail -= len(c.pendingData)
|
|
|
|
}
|
|
|
|
n := copy(c.pendingData[tail:], data)
|
|
|
|
data = data[n:]
|
|
|
|
c.length += n
|
|
|
|
}
|
|
|
|
|
|
|
|
c.cond.Signal()
|
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) Stderr() io.Writer {
|
2012-02-20 19:42:43 +04:00
|
|
|
return extendedDataChannel{c: c, t: extendedDataStderr}
|
|
|
|
}
|
|
|
|
|
|
|
|
// extendedDataChannel is an io.Writer that writes any data to c as extended
|
|
|
|
// data of the given type.
|
|
|
|
type extendedDataChannel struct {
|
|
|
|
t extendedDataTypeCode
|
2012-05-05 01:59:48 +04:00
|
|
|
c *serverChan
|
2012-02-20 19:42:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (edc extendedDataChannel) Write(data []byte) (n int, err error) {
|
|
|
|
c := edc.c
|
|
|
|
for len(data) > 0 {
|
|
|
|
var space uint32
|
|
|
|
if space, err = c.getWindowSpace(uint32(len(data))); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
todo := data
|
|
|
|
if uint32(len(todo)) > space {
|
|
|
|
todo = todo[:space]
|
|
|
|
}
|
|
|
|
|
|
|
|
packet := make([]byte, 1+4+4+4+len(todo))
|
|
|
|
packet[0] = msgChannelExtendedData
|
2012-05-05 01:59:48 +04:00
|
|
|
marshalUint32(packet[1:], c.remoteId)
|
2012-02-20 19:42:43 +04:00
|
|
|
marshalUint32(packet[5:], uint32(edc.t))
|
|
|
|
marshalUint32(packet[9:], uint32(len(todo)))
|
|
|
|
copy(packet[13:], todo)
|
|
|
|
|
2012-05-09 02:20:05 +04:00
|
|
|
if err = c.writePacket(packet); err != nil {
|
2012-02-20 19:42:43 +04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
n += len(todo)
|
|
|
|
data = data[len(todo):]
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) Read(data []byte) (n int, err error) {
|
2012-04-26 20:05:35 +04:00
|
|
|
n, err, windowAdjustment := c.read(data)
|
|
|
|
|
|
|
|
if windowAdjustment > 0 {
|
|
|
|
packet := marshal(msgChannelWindowAdjust, windowAdjustMsg{
|
2012-05-05 01:59:48 +04:00
|
|
|
PeersId: c.remoteId,
|
2012-04-26 20:05:35 +04:00
|
|
|
AdditionalBytes: windowAdjustment,
|
|
|
|
})
|
2012-05-09 02:20:05 +04:00
|
|
|
err = c.writePacket(packet)
|
2012-04-26 20:05:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) read(data []byte) (n int, err error, windowAdjustment uint32) {
|
2012-05-09 02:20:05 +04:00
|
|
|
c.cond.L.Lock()
|
|
|
|
defer c.cond.L.Unlock()
|
2012-01-26 00:31:12 +04:00
|
|
|
|
|
|
|
if c.err != nil {
|
2012-04-26 20:05:35 +04:00
|
|
|
return 0, c.err, 0
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
if c.theySentEOF || c.theyClosed || c.dead {
|
2012-04-26 20:05:35 +04:00
|
|
|
return 0, io.EOF, 0
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(c.pendingRequests) > 0 {
|
|
|
|
req := c.pendingRequests[0]
|
|
|
|
if len(c.pendingRequests) == 1 {
|
|
|
|
c.pendingRequests = nil
|
|
|
|
} else {
|
|
|
|
oldPendingRequests := c.pendingRequests
|
|
|
|
c.pendingRequests = make([]ChannelRequest, len(oldPendingRequests)-1)
|
|
|
|
copy(c.pendingRequests, oldPendingRequests[1:])
|
|
|
|
}
|
|
|
|
|
2012-04-26 20:05:35 +04:00
|
|
|
return 0, req, 0
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.length > 0 {
|
2012-04-20 23:17:42 +04:00
|
|
|
tail := min(c.head+c.length, len(c.pendingData))
|
2012-01-26 00:31:12 +04:00
|
|
|
n = copy(data, c.pendingData[c.head:tail])
|
|
|
|
c.head += n
|
|
|
|
c.length -= n
|
|
|
|
if c.head == len(c.pendingData) {
|
|
|
|
c.head = 0
|
|
|
|
}
|
2012-04-19 19:16:57 +04:00
|
|
|
|
2012-04-26 20:05:35 +04:00
|
|
|
windowAdjustment = uint32(len(c.pendingData)-c.length) - c.myWindow
|
|
|
|
if windowAdjustment < uint32(len(c.pendingData)/2) {
|
|
|
|
windowAdjustment = 0
|
2012-04-19 19:16:57 +04:00
|
|
|
}
|
2012-04-26 20:05:35 +04:00
|
|
|
c.myWindow += windowAdjustment
|
2012-04-19 19:16:57 +04:00
|
|
|
|
2012-01-26 00:31:12 +04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.cond.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
panic("unreachable")
|
|
|
|
}
|
|
|
|
|
2012-02-18 22:49:50 +04:00
|
|
|
// getWindowSpace takes, at most, max bytes of space from the peer's window. It
|
|
|
|
// returns the number of bytes actually reserved.
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) getWindowSpace(max uint32) (uint32, error) {
|
2012-05-10 23:56:44 +04:00
|
|
|
var err error
|
|
|
|
// TODO(dfc) This lock and check of c.weClosed is necessary because unlike
|
|
|
|
// clientChan, c.weClosed is observed by more than one goroutine.
|
2012-05-09 02:20:05 +04:00
|
|
|
c.cond.L.Lock()
|
2012-05-10 23:56:44 +04:00
|
|
|
if c.dead || c.weClosed {
|
|
|
|
err = io.EOF
|
2012-02-18 22:49:50 +04:00
|
|
|
}
|
2012-05-10 23:56:44 +04:00
|
|
|
c.cond.L.Unlock()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
2012-02-18 22:49:50 +04:00
|
|
|
}
|
2012-05-10 23:56:44 +04:00
|
|
|
return c.remoteWin.reserve(max), nil
|
2012-02-18 22:49:50 +04:00
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) Write(data []byte) (n int, err error) {
|
2012-02-18 22:49:50 +04:00
|
|
|
for len(data) > 0 {
|
|
|
|
var space uint32
|
|
|
|
if space, err = c.getWindowSpace(uint32(len(data))); err != nil {
|
|
|
|
return 0, err
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
todo := data
|
2012-02-18 22:49:50 +04:00
|
|
|
if uint32(len(todo)) > space {
|
|
|
|
todo = todo[:space]
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
packet := make([]byte, 1+4+4+len(todo))
|
|
|
|
packet[0] = msgChannelData
|
2012-05-05 01:59:48 +04:00
|
|
|
marshalUint32(packet[1:], c.remoteId)
|
2012-02-20 19:42:43 +04:00
|
|
|
marshalUint32(packet[5:], uint32(len(todo)))
|
2012-01-26 00:31:12 +04:00
|
|
|
copy(packet[9:], todo)
|
|
|
|
|
2012-05-09 02:20:05 +04:00
|
|
|
if err = c.writePacket(packet); err != nil {
|
2012-01-26 00:31:12 +04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
n += len(todo)
|
|
|
|
data = data[len(todo):]
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) Close() error {
|
2012-01-26 00:31:12 +04:00
|
|
|
c.serverConn.lock.Lock()
|
|
|
|
defer c.serverConn.lock.Unlock()
|
|
|
|
|
|
|
|
if c.serverConn.err != nil {
|
|
|
|
return c.serverConn.err
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.weClosed {
|
|
|
|
return errors.New("ssh: channel already closed")
|
|
|
|
}
|
|
|
|
c.weClosed = true
|
|
|
|
|
2012-05-09 02:20:05 +04:00
|
|
|
return c.sendClose()
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) AckRequest(ok bool) error {
|
2012-01-26 00:31:12 +04:00
|
|
|
c.serverConn.lock.Lock()
|
|
|
|
defer c.serverConn.lock.Unlock()
|
|
|
|
|
|
|
|
if c.serverConn.err != nil {
|
|
|
|
return c.serverConn.err
|
|
|
|
}
|
|
|
|
|
2012-04-20 23:17:42 +04:00
|
|
|
if !ok {
|
2012-01-26 00:31:12 +04:00
|
|
|
ack := channelRequestFailureMsg{
|
2012-05-05 01:59:48 +04:00
|
|
|
PeersId: c.remoteId,
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
2012-05-09 02:20:05 +04:00
|
|
|
return c.writePacket(marshal(msgChannelFailure, ack))
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
2012-04-20 23:17:42 +04:00
|
|
|
|
|
|
|
ack := channelRequestSuccessMsg{
|
2012-05-05 01:59:48 +04:00
|
|
|
PeersId: c.remoteId,
|
2012-04-20 23:17:42 +04:00
|
|
|
}
|
2012-05-09 02:20:05 +04:00
|
|
|
return c.writePacket(marshal(msgChannelSuccess, ack))
|
2012-01-26 00:31:12 +04:00
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) ChannelType() string {
|
2012-01-26 00:31:12 +04:00
|
|
|
return c.chanType
|
|
|
|
}
|
|
|
|
|
2012-05-05 01:59:48 +04:00
|
|
|
func (c *serverChan) ExtraData() []byte {
|
2012-01-26 00:31:12 +04:00
|
|
|
return c.extraData
|
|
|
|
}
|
2012-08-11 06:18:54 +04:00
|
|
|
|
|
|
|
// A clientChan represents a single RFC 4254 channel multiplexed
|
|
|
|
// over a SSH connection.
|
|
|
|
type clientChan struct {
|
|
|
|
channel
|
|
|
|
stdin *chanWriter
|
|
|
|
stdout *chanReader
|
|
|
|
stderr *chanReader
|
|
|
|
msg chan interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// newClientChan returns a partially constructed *clientChan
|
|
|
|
// using the local id provided. To be usable clientChan.remoteId
|
|
|
|
// needs to be assigned once known.
|
|
|
|
func newClientChan(cc conn, id uint32) *clientChan {
|
|
|
|
c := &clientChan{
|
|
|
|
channel: channel{
|
|
|
|
conn: cc,
|
|
|
|
localId: id,
|
|
|
|
remoteWin: window{Cond: newCond()},
|
|
|
|
},
|
|
|
|
msg: make(chan interface{}, 16),
|
|
|
|
}
|
|
|
|
c.stdin = &chanWriter{
|
|
|
|
channel: &c.channel,
|
|
|
|
}
|
|
|
|
c.stdout = &chanReader{
|
|
|
|
channel: &c.channel,
|
|
|
|
buffer: newBuffer(),
|
|
|
|
}
|
|
|
|
c.stderr = &chanReader{
|
|
|
|
channel: &c.channel,
|
|
|
|
buffer: newBuffer(),
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// waitForChannelOpenResponse, if successful, fills out
|
|
|
|
// the remoteId and records any initial window advertisement.
|
|
|
|
func (c *clientChan) waitForChannelOpenResponse() error {
|
|
|
|
switch msg := (<-c.msg).(type) {
|
|
|
|
case *channelOpenConfirmMsg:
|
|
|
|
// fixup remoteId field
|
|
|
|
c.remoteId = msg.MyId
|
|
|
|
// TODO(dfc) asset this is < 2^31.
|
|
|
|
c.maxPacketSize = msg.MaxPacketSize
|
|
|
|
c.remoteWin.add(msg.MyWindow)
|
|
|
|
return nil
|
|
|
|
case *channelOpenFailureMsg:
|
|
|
|
return errors.New(safeString(msg.Message))
|
|
|
|
}
|
|
|
|
return errors.New("ssh: unexpected packet")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes the channel. This does not close the underlying connection.
|
|
|
|
func (c *clientChan) Close() error {
|
|
|
|
if !c.weClosed {
|
|
|
|
c.weClosed = true
|
|
|
|
return c.sendClose()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// A chanWriter represents the stdin of a remote process.
|
|
|
|
type chanWriter struct {
|
|
|
|
*channel
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write writes data to the remote process's standard input.
|
|
|
|
func (w *chanWriter) Write(data []byte) (written int, err error) {
|
|
|
|
for len(data) > 0 {
|
|
|
|
// never send more data than maxPacketSize even if
|
|
|
|
// there is sufficent window.
|
|
|
|
n := min(int(w.maxPacketSize), len(data))
|
|
|
|
n = int(w.remoteWin.reserve(uint32(n)))
|
|
|
|
remoteId := w.remoteId
|
|
|
|
packet := []byte{
|
|
|
|
msgChannelData,
|
|
|
|
byte(remoteId >> 24), byte(remoteId >> 16), byte(remoteId >> 8), byte(remoteId),
|
|
|
|
byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n),
|
|
|
|
}
|
|
|
|
if err = w.writePacket(append(packet, data[:n]...)); err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
data = data[n:]
|
|
|
|
written += n
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func min(a, b int) int {
|
|
|
|
if a < b {
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *chanWriter) Close() error {
|
|
|
|
return w.sendEOF()
|
|
|
|
}
|
|
|
|
|
|
|
|
// A chanReader represents stdout or stderr of a remote process.
|
|
|
|
type chanReader struct {
|
|
|
|
*channel // the channel backing this reader
|
|
|
|
*buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read reads data from the remote process's stdout or stderr.
|
|
|
|
func (r *chanReader) Read(buf []byte) (int, error) {
|
|
|
|
n, err := r.buffer.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return n, r.sendWindowAdj(n)
|
|
|
|
}
|