Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
		
							
								
								
									
										633
									
								
								vendor/golang.org/x/crypto/ssh/channel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										633
									
								
								vendor/golang.org/x/crypto/ssh/channel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,633 @@
 | 
			
		||||
// 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 (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	minPacketLength = 9
 | 
			
		||||
	// channelMaxPacket contains the maximum number of bytes that will be
 | 
			
		||||
	// sent in a single packet. As per RFC 4253, section 6.1, 32k is also
 | 
			
		||||
	// the minimum.
 | 
			
		||||
	channelMaxPacket = 1 << 15
 | 
			
		||||
	// We follow OpenSSH here.
 | 
			
		||||
	channelWindowSize = 64 * channelMaxPacket
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewChannel represents an incoming request to a channel. It must either be
 | 
			
		||||
// accepted for use by calling Accept, or rejected by calling Reject.
 | 
			
		||||
type NewChannel interface {
 | 
			
		||||
	// Accept accepts the channel creation request. It returns the Channel
 | 
			
		||||
	// and a Go channel containing SSH requests. The Go channel must be
 | 
			
		||||
	// serviced otherwise the Channel will hang.
 | 
			
		||||
	Accept() (Channel, <-chan *Request, error)
 | 
			
		||||
 | 
			
		||||
	// Reject rejects the channel creation request. After calling
 | 
			
		||||
	// this, no other methods on the Channel may be called.
 | 
			
		||||
	Reject(reason RejectionReason, message string) error
 | 
			
		||||
 | 
			
		||||
	// ChannelType returns the type of the channel, as supplied by the
 | 
			
		||||
	// client.
 | 
			
		||||
	ChannelType() string
 | 
			
		||||
 | 
			
		||||
	// ExtraData returns the arbitrary payload for this channel, as supplied
 | 
			
		||||
	// by the client. This data is specific to the channel type.
 | 
			
		||||
	ExtraData() []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A Channel is an ordered, reliable, flow-controlled, duplex stream
 | 
			
		||||
// that is multiplexed over an SSH connection.
 | 
			
		||||
type Channel interface {
 | 
			
		||||
	// Read reads up to len(data) bytes from the channel.
 | 
			
		||||
	Read(data []byte) (int, error)
 | 
			
		||||
 | 
			
		||||
	// Write writes len(data) bytes to the channel.
 | 
			
		||||
	Write(data []byte) (int, error)
 | 
			
		||||
 | 
			
		||||
	// Close signals end of channel use. No data may be sent after this
 | 
			
		||||
	// call.
 | 
			
		||||
	Close() error
 | 
			
		||||
 | 
			
		||||
	// CloseWrite signals the end of sending in-band
 | 
			
		||||
	// data. Requests may still be sent, and the other side may
 | 
			
		||||
	// still send data
 | 
			
		||||
	CloseWrite() error
 | 
			
		||||
 | 
			
		||||
	// SendRequest sends a channel request.  If wantReply is true,
 | 
			
		||||
	// it will wait for a reply and return the result as a
 | 
			
		||||
	// boolean, otherwise the return value will be false. Channel
 | 
			
		||||
	// requests are out-of-band messages so they may be sent even
 | 
			
		||||
	// if the data stream is closed or blocked by flow control.
 | 
			
		||||
	// If the channel is closed before a reply is returned, io.EOF
 | 
			
		||||
	// is returned.
 | 
			
		||||
	SendRequest(name string, wantReply bool, payload []byte) (bool, error)
 | 
			
		||||
 | 
			
		||||
	// Stderr returns an io.ReadWriter that writes to this channel
 | 
			
		||||
	// with the extended data type set to stderr. Stderr may
 | 
			
		||||
	// safely be read and written from a different goroutine than
 | 
			
		||||
	// Read and Write respectively.
 | 
			
		||||
	Stderr() io.ReadWriter
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Request is a request sent outside of the normal stream of
 | 
			
		||||
// data. Requests can either be specific to an SSH channel, or they
 | 
			
		||||
// can be global.
 | 
			
		||||
type Request struct {
 | 
			
		||||
	Type      string
 | 
			
		||||
	WantReply bool
 | 
			
		||||
	Payload   []byte
 | 
			
		||||
 | 
			
		||||
	ch  *channel
 | 
			
		||||
	mux *mux
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reply sends a response to a request. It must be called for all requests
 | 
			
		||||
// where WantReply is true and is a no-op otherwise. The payload argument is
 | 
			
		||||
// ignored for replies to channel-specific requests.
 | 
			
		||||
func (r *Request) Reply(ok bool, payload []byte) error {
 | 
			
		||||
	if !r.WantReply {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r.ch == nil {
 | 
			
		||||
		return r.mux.ackRequest(ok, payload)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r.ch.ackRequest(ok)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RejectionReason is an enumeration used when rejecting channel creation
 | 
			
		||||
// requests. See RFC 4254, section 5.1.
 | 
			
		||||
type RejectionReason uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	Prohibited RejectionReason = iota + 1
 | 
			
		||||
	ConnectionFailed
 | 
			
		||||
	UnknownChannelType
 | 
			
		||||
	ResourceShortage
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// String converts the rejection reason to human readable form.
 | 
			
		||||
func (r RejectionReason) String() string {
 | 
			
		||||
	switch r {
 | 
			
		||||
	case Prohibited:
 | 
			
		||||
		return "administratively prohibited"
 | 
			
		||||
	case ConnectionFailed:
 | 
			
		||||
		return "connect failed"
 | 
			
		||||
	case UnknownChannelType:
 | 
			
		||||
		return "unknown channel type"
 | 
			
		||||
	case ResourceShortage:
 | 
			
		||||
		return "resource shortage"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("unknown reason %d", int(r))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func min(a uint32, b int) uint32 {
 | 
			
		||||
	if a < uint32(b) {
 | 
			
		||||
		return a
 | 
			
		||||
	}
 | 
			
		||||
	return uint32(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type channelDirection uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	channelInbound channelDirection = iota
 | 
			
		||||
	channelOutbound
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// channel is an implementation of the Channel interface that works
 | 
			
		||||
// with the mux class.
 | 
			
		||||
type channel struct {
 | 
			
		||||
	// R/O after creation
 | 
			
		||||
	chanType          string
 | 
			
		||||
	extraData         []byte
 | 
			
		||||
	localId, remoteId uint32
 | 
			
		||||
 | 
			
		||||
	// maxIncomingPayload and maxRemotePayload are the maximum
 | 
			
		||||
	// payload sizes of normal and extended data packets for
 | 
			
		||||
	// receiving and sending, respectively. The wire packet will
 | 
			
		||||
	// be 9 or 13 bytes larger (excluding encryption overhead).
 | 
			
		||||
	maxIncomingPayload uint32
 | 
			
		||||
	maxRemotePayload   uint32
 | 
			
		||||
 | 
			
		||||
	mux *mux
 | 
			
		||||
 | 
			
		||||
	// decided is set to true if an accept or reject message has been sent
 | 
			
		||||
	// (for outbound channels) or received (for inbound channels).
 | 
			
		||||
	decided bool
 | 
			
		||||
 | 
			
		||||
	// direction contains either channelOutbound, for channels created
 | 
			
		||||
	// locally, or channelInbound, for channels created by the peer.
 | 
			
		||||
	direction channelDirection
 | 
			
		||||
 | 
			
		||||
	// Pending internal channel messages.
 | 
			
		||||
	msg chan interface{}
 | 
			
		||||
 | 
			
		||||
	// Since requests have no ID, there can be only one request
 | 
			
		||||
	// with WantReply=true outstanding.  This lock is held by a
 | 
			
		||||
	// goroutine that has such an outgoing request pending.
 | 
			
		||||
	sentRequestMu sync.Mutex
 | 
			
		||||
 | 
			
		||||
	incomingRequests chan *Request
 | 
			
		||||
 | 
			
		||||
	sentEOF bool
 | 
			
		||||
 | 
			
		||||
	// thread-safe data
 | 
			
		||||
	remoteWin  window
 | 
			
		||||
	pending    *buffer
 | 
			
		||||
	extPending *buffer
 | 
			
		||||
 | 
			
		||||
	// windowMu protects myWindow, the flow-control window.
 | 
			
		||||
	windowMu sync.Mutex
 | 
			
		||||
	myWindow uint32
 | 
			
		||||
 | 
			
		||||
	// writeMu serializes calls to mux.conn.writePacket() and
 | 
			
		||||
	// protects sentClose and packetPool. This mutex must be
 | 
			
		||||
	// different from windowMu, as writePacket can block if there
 | 
			
		||||
	// is a key exchange pending.
 | 
			
		||||
	writeMu   sync.Mutex
 | 
			
		||||
	sentClose bool
 | 
			
		||||
 | 
			
		||||
	// packetPool has a buffer for each extended channel ID to
 | 
			
		||||
	// save allocations during writes.
 | 
			
		||||
	packetPool map[uint32][]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writePacket sends a packet. If the packet is a channel close, it updates
 | 
			
		||||
// sentClose. This method takes the lock c.writeMu.
 | 
			
		||||
func (c *channel) writePacket(packet []byte) error {
 | 
			
		||||
	c.writeMu.Lock()
 | 
			
		||||
	if c.sentClose {
 | 
			
		||||
		c.writeMu.Unlock()
 | 
			
		||||
		return io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	c.sentClose = (packet[0] == msgChannelClose)
 | 
			
		||||
	err := c.mux.conn.writePacket(packet)
 | 
			
		||||
	c.writeMu.Unlock()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) sendMessage(msg interface{}) error {
 | 
			
		||||
	if debugMux {
 | 
			
		||||
		log.Printf("send(%d): %#v", c.mux.chanList.offset, msg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := Marshal(msg)
 | 
			
		||||
	binary.BigEndian.PutUint32(p[1:], c.remoteId)
 | 
			
		||||
	return c.writePacket(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteExtended writes data to a specific extended stream. These streams are
 | 
			
		||||
// used, for example, for stderr.
 | 
			
		||||
func (c *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) {
 | 
			
		||||
	if c.sentEOF {
 | 
			
		||||
		return 0, io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	// 1 byte message type, 4 bytes remoteId, 4 bytes data length
 | 
			
		||||
	opCode := byte(msgChannelData)
 | 
			
		||||
	headerLength := uint32(9)
 | 
			
		||||
	if extendedCode > 0 {
 | 
			
		||||
		headerLength += 4
 | 
			
		||||
		opCode = msgChannelExtendedData
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.writeMu.Lock()
 | 
			
		||||
	packet := c.packetPool[extendedCode]
 | 
			
		||||
	// We don't remove the buffer from packetPool, so
 | 
			
		||||
	// WriteExtended calls from different goroutines will be
 | 
			
		||||
	// flagged as errors by the race detector.
 | 
			
		||||
	c.writeMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	for len(data) > 0 {
 | 
			
		||||
		space := min(c.maxRemotePayload, len(data))
 | 
			
		||||
		if space, err = c.remoteWin.reserve(space); err != nil {
 | 
			
		||||
			return n, err
 | 
			
		||||
		}
 | 
			
		||||
		if want := headerLength + space; uint32(cap(packet)) < want {
 | 
			
		||||
			packet = make([]byte, want)
 | 
			
		||||
		} else {
 | 
			
		||||
			packet = packet[:want]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		todo := data[:space]
 | 
			
		||||
 | 
			
		||||
		packet[0] = opCode
 | 
			
		||||
		binary.BigEndian.PutUint32(packet[1:], c.remoteId)
 | 
			
		||||
		if extendedCode > 0 {
 | 
			
		||||
			binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode))
 | 
			
		||||
		}
 | 
			
		||||
		binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo)))
 | 
			
		||||
		copy(packet[headerLength:], todo)
 | 
			
		||||
		if err = c.writePacket(packet); err != nil {
 | 
			
		||||
			return n, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		n += len(todo)
 | 
			
		||||
		data = data[len(todo):]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.writeMu.Lock()
 | 
			
		||||
	c.packetPool[extendedCode] = packet
 | 
			
		||||
	c.writeMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) handleData(packet []byte) error {
 | 
			
		||||
	headerLen := 9
 | 
			
		||||
	isExtendedData := packet[0] == msgChannelExtendedData
 | 
			
		||||
	if isExtendedData {
 | 
			
		||||
		headerLen = 13
 | 
			
		||||
	}
 | 
			
		||||
	if len(packet) < headerLen {
 | 
			
		||||
		// malformed data packet
 | 
			
		||||
		return parseError(packet[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var extended uint32
 | 
			
		||||
	if isExtendedData {
 | 
			
		||||
		extended = binary.BigEndian.Uint32(packet[5:])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen])
 | 
			
		||||
	if length == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if length > c.maxIncomingPayload {
 | 
			
		||||
		// TODO(hanwen): should send Disconnect?
 | 
			
		||||
		return errors.New("ssh: incoming packet exceeds maximum payload size")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data := packet[headerLen:]
 | 
			
		||||
	if length != uint32(len(data)) {
 | 
			
		||||
		return errors.New("ssh: wrong packet length")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.windowMu.Lock()
 | 
			
		||||
	if c.myWindow < length {
 | 
			
		||||
		c.windowMu.Unlock()
 | 
			
		||||
		// TODO(hanwen): should send Disconnect with reason?
 | 
			
		||||
		return errors.New("ssh: remote side wrote too much")
 | 
			
		||||
	}
 | 
			
		||||
	c.myWindow -= length
 | 
			
		||||
	c.windowMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if extended == 1 {
 | 
			
		||||
		c.extPending.write(data)
 | 
			
		||||
	} else if extended > 0 {
 | 
			
		||||
		// discard other extended data.
 | 
			
		||||
	} else {
 | 
			
		||||
		c.pending.write(data)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) adjustWindow(n uint32) error {
 | 
			
		||||
	c.windowMu.Lock()
 | 
			
		||||
	// Since myWindow is managed on our side, and can never exceed
 | 
			
		||||
	// the initial window setting, we don't worry about overflow.
 | 
			
		||||
	c.myWindow += uint32(n)
 | 
			
		||||
	c.windowMu.Unlock()
 | 
			
		||||
	return c.sendMessage(windowAdjustMsg{
 | 
			
		||||
		AdditionalBytes: uint32(n),
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) {
 | 
			
		||||
	switch extended {
 | 
			
		||||
	case 1:
 | 
			
		||||
		n, err = c.extPending.Read(data)
 | 
			
		||||
	case 0:
 | 
			
		||||
		n, err = c.pending.Read(data)
 | 
			
		||||
	default:
 | 
			
		||||
		return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if n > 0 {
 | 
			
		||||
		err = c.adjustWindow(uint32(n))
 | 
			
		||||
		// sendWindowAdjust can return io.EOF if the remote
 | 
			
		||||
		// peer has closed the connection, however we want to
 | 
			
		||||
		// defer forwarding io.EOF to the caller of Read until
 | 
			
		||||
		// the buffer has been drained.
 | 
			
		||||
		if n > 0 && err == io.EOF {
 | 
			
		||||
			err = nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) close() {
 | 
			
		||||
	c.pending.eof()
 | 
			
		||||
	c.extPending.eof()
 | 
			
		||||
	close(c.msg)
 | 
			
		||||
	close(c.incomingRequests)
 | 
			
		||||
	c.writeMu.Lock()
 | 
			
		||||
	// This is not necessary for a normal channel teardown, but if
 | 
			
		||||
	// there was another error, it is.
 | 
			
		||||
	c.sentClose = true
 | 
			
		||||
	c.writeMu.Unlock()
 | 
			
		||||
	// Unblock writers.
 | 
			
		||||
	c.remoteWin.close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// responseMessageReceived is called when a success or failure message is
 | 
			
		||||
// received on a channel to check that such a message is reasonable for the
 | 
			
		||||
// given channel.
 | 
			
		||||
func (c *channel) responseMessageReceived() error {
 | 
			
		||||
	if c.direction == channelInbound {
 | 
			
		||||
		return errors.New("ssh: channel response message received on inbound channel")
 | 
			
		||||
	}
 | 
			
		||||
	if c.decided {
 | 
			
		||||
		return errors.New("ssh: duplicate response received for channel")
 | 
			
		||||
	}
 | 
			
		||||
	c.decided = true
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) handlePacket(packet []byte) error {
 | 
			
		||||
	switch packet[0] {
 | 
			
		||||
	case msgChannelData, msgChannelExtendedData:
 | 
			
		||||
		return c.handleData(packet)
 | 
			
		||||
	case msgChannelClose:
 | 
			
		||||
		c.sendMessage(channelCloseMsg{PeersId: c.remoteId})
 | 
			
		||||
		c.mux.chanList.remove(c.localId)
 | 
			
		||||
		c.close()
 | 
			
		||||
		return nil
 | 
			
		||||
	case msgChannelEOF:
 | 
			
		||||
		// RFC 4254 is mute on how EOF affects dataExt messages but
 | 
			
		||||
		// it is logical to signal EOF at the same time.
 | 
			
		||||
		c.extPending.eof()
 | 
			
		||||
		c.pending.eof()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decoded, err := decode(packet)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch msg := decoded.(type) {
 | 
			
		||||
	case *channelOpenFailureMsg:
 | 
			
		||||
		if err := c.responseMessageReceived(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		c.mux.chanList.remove(msg.PeersId)
 | 
			
		||||
		c.msg <- msg
 | 
			
		||||
	case *channelOpenConfirmMsg:
 | 
			
		||||
		if err := c.responseMessageReceived(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
 | 
			
		||||
			return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize)
 | 
			
		||||
		}
 | 
			
		||||
		c.remoteId = msg.MyId
 | 
			
		||||
		c.maxRemotePayload = msg.MaxPacketSize
 | 
			
		||||
		c.remoteWin.add(msg.MyWindow)
 | 
			
		||||
		c.msg <- msg
 | 
			
		||||
	case *windowAdjustMsg:
 | 
			
		||||
		if !c.remoteWin.add(msg.AdditionalBytes) {
 | 
			
		||||
			return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes)
 | 
			
		||||
		}
 | 
			
		||||
	case *channelRequestMsg:
 | 
			
		||||
		req := Request{
 | 
			
		||||
			Type:      msg.Request,
 | 
			
		||||
			WantReply: msg.WantReply,
 | 
			
		||||
			Payload:   msg.RequestSpecificData,
 | 
			
		||||
			ch:        c,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.incomingRequests <- &req
 | 
			
		||||
	default:
 | 
			
		||||
		c.msg <- msg
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel {
 | 
			
		||||
	ch := &channel{
 | 
			
		||||
		remoteWin:        window{Cond: newCond()},
 | 
			
		||||
		myWindow:         channelWindowSize,
 | 
			
		||||
		pending:          newBuffer(),
 | 
			
		||||
		extPending:       newBuffer(),
 | 
			
		||||
		direction:        direction,
 | 
			
		||||
		incomingRequests: make(chan *Request, chanSize),
 | 
			
		||||
		msg:              make(chan interface{}, chanSize),
 | 
			
		||||
		chanType:         chanType,
 | 
			
		||||
		extraData:        extraData,
 | 
			
		||||
		mux:              m,
 | 
			
		||||
		packetPool:       make(map[uint32][]byte),
 | 
			
		||||
	}
 | 
			
		||||
	ch.localId = m.chanList.add(ch)
 | 
			
		||||
	return ch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errUndecided = errors.New("ssh: must Accept or Reject channel")
 | 
			
		||||
var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once")
 | 
			
		||||
 | 
			
		||||
type extChannel struct {
 | 
			
		||||
	code uint32
 | 
			
		||||
	ch   *channel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *extChannel) Write(data []byte) (n int, err error) {
 | 
			
		||||
	return e.ch.WriteExtended(data, e.code)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *extChannel) Read(data []byte) (n int, err error) {
 | 
			
		||||
	return e.ch.ReadExtended(data, e.code)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) Accept() (Channel, <-chan *Request, error) {
 | 
			
		||||
	if c.decided {
 | 
			
		||||
		return nil, nil, errDecidedAlready
 | 
			
		||||
	}
 | 
			
		||||
	c.maxIncomingPayload = channelMaxPacket
 | 
			
		||||
	confirm := channelOpenConfirmMsg{
 | 
			
		||||
		PeersId:       c.remoteId,
 | 
			
		||||
		MyId:          c.localId,
 | 
			
		||||
		MyWindow:      c.myWindow,
 | 
			
		||||
		MaxPacketSize: c.maxIncomingPayload,
 | 
			
		||||
	}
 | 
			
		||||
	c.decided = true
 | 
			
		||||
	if err := c.sendMessage(confirm); err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c, c.incomingRequests, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Reject(reason RejectionReason, message string) error {
 | 
			
		||||
	if ch.decided {
 | 
			
		||||
		return errDecidedAlready
 | 
			
		||||
	}
 | 
			
		||||
	reject := channelOpenFailureMsg{
 | 
			
		||||
		PeersId:  ch.remoteId,
 | 
			
		||||
		Reason:   reason,
 | 
			
		||||
		Message:  message,
 | 
			
		||||
		Language: "en",
 | 
			
		||||
	}
 | 
			
		||||
	ch.decided = true
 | 
			
		||||
	return ch.sendMessage(reject)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Read(data []byte) (int, error) {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return 0, errUndecided
 | 
			
		||||
	}
 | 
			
		||||
	return ch.ReadExtended(data, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Write(data []byte) (int, error) {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return 0, errUndecided
 | 
			
		||||
	}
 | 
			
		||||
	return ch.WriteExtended(data, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) CloseWrite() error {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return errUndecided
 | 
			
		||||
	}
 | 
			
		||||
	ch.sentEOF = true
 | 
			
		||||
	return ch.sendMessage(channelEOFMsg{
 | 
			
		||||
		PeersId: ch.remoteId})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Close() error {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return errUndecided
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ch.sendMessage(channelCloseMsg{
 | 
			
		||||
		PeersId: ch.remoteId})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extended returns an io.ReadWriter that sends and receives data on the given,
 | 
			
		||||
// SSH extended stream. Such streams are used, for example, for stderr.
 | 
			
		||||
func (ch *channel) Extended(code uint32) io.ReadWriter {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return &extChannel{code, ch}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Stderr() io.ReadWriter {
 | 
			
		||||
	return ch.Extended(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return false, errUndecided
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if wantReply {
 | 
			
		||||
		ch.sentRequestMu.Lock()
 | 
			
		||||
		defer ch.sentRequestMu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg := channelRequestMsg{
 | 
			
		||||
		PeersId:             ch.remoteId,
 | 
			
		||||
		Request:             name,
 | 
			
		||||
		WantReply:           wantReply,
 | 
			
		||||
		RequestSpecificData: payload,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := ch.sendMessage(msg); err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if wantReply {
 | 
			
		||||
		m, ok := (<-ch.msg)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return false, io.EOF
 | 
			
		||||
		}
 | 
			
		||||
		switch m.(type) {
 | 
			
		||||
		case *channelRequestFailureMsg:
 | 
			
		||||
			return false, nil
 | 
			
		||||
		case *channelRequestSuccessMsg:
 | 
			
		||||
			return true, nil
 | 
			
		||||
		default:
 | 
			
		||||
			return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ackRequest either sends an ack or nack to the channel request.
 | 
			
		||||
func (ch *channel) ackRequest(ok bool) error {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return errUndecided
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var msg interface{}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		msg = channelRequestFailureMsg{
 | 
			
		||||
			PeersId: ch.remoteId,
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		msg = channelRequestSuccessMsg{
 | 
			
		||||
			PeersId: ch.remoteId,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ch.sendMessage(msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) ChannelType() string {
 | 
			
		||||
	return ch.chanType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) ExtraData() []byte {
 | 
			
		||||
	return ch.extraData
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user