go.crypto/ssh: add support for client side global requests

* Add support for RFC4254 section 4 global requests.
* Improve clientConn.Listen to process responses properly.

R=agl, gustav.paul
CC=golang-dev
https://golang.org/cl/6130050
This commit is contained in:
Dave Cheney
2012-05-01 15:43:58 +10:00
parent 2e60680932
commit b4b42222af
3 changed files with 101 additions and 51 deletions
+60 -21
View File
@@ -23,14 +23,21 @@ type ClientConn struct {
*transport
config *ClientConfig
chanlist // channels associated with this connection
forwardList // forwared tcpip connections from the remote side
forwardList // forwarded tcpip connections from the remote side
globalRequest
}
type globalRequest struct {
sync.Mutex
response chan interface{}
}
// Client returns a new SSH client connection using c as the underlying transport.
func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) {
conn := &ClientConn{
transport: newTransport(c, config.rand()),
config: config,
transport: newTransport(c, config.rand()),
config: config,
globalRequest: globalRequest{response: make(chan interface{}, 1)},
}
if err := conn.handshake(); err != nil {
conn.Close()
@@ -273,6 +280,8 @@ func (c *ClientConn) mainLoop() {
// invalid window update
return
}
case *globalRequestSuccessMsg, *globalRequestFailureMsg:
c.globalRequest.response <- msg
case *disconnectMsg:
return
default:
@@ -286,22 +295,24 @@ func (c *ClientConn) mainLoop() {
func (c *ClientConn) handleChanOpen(msg *channelOpenMsg) {
switch msg.ChanType {
case "forwarded-tcpip":
addr, err := parseAddr(msg.TypeSpecificData)
if err != nil {
laddr, rest, ok := parseTCPAddr(msg.TypeSpecificData)
if !ok {
// invalid request
m := channelOpenFailureMsg{
PeersId: msg.PeersId,
Reason: ConnectionFailed,
Message: fmt.Sprintf("invalid request: %v", err),
Language: "en_US.UTF-8",
}
c.writePacket(marshal(msgChannelOpenFailure, m))
c.sendConnectionFailed(msg.PeersId)
return
}
l, ok := c.forwardList.Lookup(addr)
l, ok := c.forwardList.Lookup(laddr)
if !ok {
fmt.Println("could not find forward list entry for", laddr)
// Section 7.2, implementations MUST reject suprious incoming
// connections.
c.sendConnectionFailed(msg.PeersId)
return
}
raddr, rest, ok := parseTCPAddr(rest)
if !ok {
// invalid request
c.sendConnectionFailed(msg.PeersId)
return
}
ch := c.newChan(c.transport)
@@ -315,7 +326,7 @@ func (c *ClientConn) handleChanOpen(msg *channelOpenMsg) {
MaxPacketSize: 1 << 15, // RFC 4253 6.1
}
c.writePacket(marshal(msgChannelOpenConfirm, m))
l <- forward{ch, addr}
l <- forward{ch, raddr}
default:
// unknown channel type
m := channelOpenFailureMsg{
@@ -328,23 +339,51 @@ func (c *ClientConn) handleChanOpen(msg *channelOpenMsg) {
}
}
// parseAddr parses the originating address from the remote into a *net.TCPAddr.
// sendGlobalRequest sends a global request message as specified
// in RFC4254 section 4. To correctly synchronise messages, a lock
// is held internally until a response is returned.
func (c *ClientConn) sendGlobalRequest(m interface{}) (*globalRequestSuccessMsg, error) {
c.globalRequest.Lock()
defer c.globalRequest.Unlock()
if err := c.writePacket(marshal(msgGlobalRequest, m)); err != nil {
return nil, err
}
r := <-c.globalRequest.response
if r, ok := r.(*globalRequestSuccessMsg); ok {
return r, nil
}
return nil, errors.New("request failed")
}
// sendConnectionFailed rejects an incoming channel identified
// by peersId.
func (c *ClientConn) sendConnectionFailed(peersId uint32) error {
m := channelOpenFailureMsg{
PeersId: peersId,
Reason: ConnectionFailed,
Message: "invalid request",
Language: "en_US.UTF-8",
}
return c.writePacket(marshal(msgChannelOpenFailure, m))
}
// parseTCPAddr parses the originating address from the remote into a *net.TCPAddr.
// RFC 4254 section 7.2 is mute on what to do if parsing fails but the forwardlist
// requires a valid *net.TCPAddr to operate, so we enforce that restriction here.
func parseAddr(b []byte) (*net.TCPAddr, error) {
func parseTCPAddr(b []byte) (*net.TCPAddr, []byte, bool) {
addr, b, ok := parseString(b)
if !ok {
return nil, ParseError{msgChannelOpen}
return nil, b, false
}
port, _, ok := parseUint32(b)
port, b, ok := parseUint32(b)
if !ok {
return nil, ParseError{msgChannelOpen}
return nil, b, false
}
ip := net.ParseIP(string(addr))
if ip == nil {
return nil, ParseError{msgChannelOpen}
return nil, b, false
}
return &net.TCPAddr{ip, int(port)}, nil
return &net.TCPAddr{ip, int(port)}, b, true
}
// Dial connects to the given network address using net.Dial and