]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/golang.org/x/crypto/ssh/channel.go
Transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / golang.org / x / crypto / ssh / channel.go
diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go
deleted file mode 100644 (file)
index 195530e..0000000
+++ /dev/null
@@ -1,633 +0,0 @@
-// 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
-}