go.crypto/ssh: add support for remote tcpip forwarding

Add support for server (remote) forwarded tcpip channels.
See RFC4254 Section 7.1

R=gustav.paul, jeff, agl, lieqiewang
CC=golang-dev
https://golang.org/cl/6038047
This commit is contained in:
Dave Cheney
2012-04-26 20:37:06 +10:00
parent bcdd6a2fd3
commit b333fd1d05
5 changed files with 270 additions and 26 deletions
+78 -8
View File
@@ -21,8 +21,9 @@ var clientVersion = []byte("SSH-2.0-Go\r\n")
// ClientConn represents the client side of an SSH connection.
type ClientConn struct {
*transport
config *ClientConfig
chanlist
config *ClientConfig
chanlist // channels associated with this connection
forwardList // forwared tcpip connections from the remote side
}
// Client returns a new SSH client connection using c as the underlying transport.
@@ -239,7 +240,7 @@ func (c *ClientConn) mainLoop() {
default:
switch msg := decode(packet).(type) {
case *channelOpenMsg:
c.getChan(msg.PeersId).msg <- msg
c.handleChanOpen(msg)
case *channelOpenConfirmMsg:
c.getChan(msg.PeersId).msg <- msg
case *channelOpenFailureMsg:
@@ -281,6 +282,71 @@ func (c *ClientConn) mainLoop() {
}
}
// Handle channel open messages from the remote side.
func (c *ClientConn) handleChanOpen(msg *channelOpenMsg) {
switch msg.ChanType {
case "forwarded-tcpip":
addr, err := parseAddr(msg.TypeSpecificData)
if err != nil {
// 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))
return
}
l, ok := c.forwardList.Lookup(addr)
if !ok {
// Section 7.2, implementations MUST reject suprious incoming
// connections.
return
}
ch := c.newChan(c.transport)
ch.peersId = msg.PeersId
ch.stdin.win.add(msg.PeersWindow)
m := channelOpenConfirmMsg{
PeersId: ch.peersId,
MyId: ch.id,
MyWindow: 1 << 14,
MaxPacketSize: 1 << 15, // RFC 4253 6.1
}
c.writePacket(marshal(msgChannelOpenConfirm, m))
l <- forward{ch, addr}
default:
// unknown channel type
m := channelOpenFailureMsg{
PeersId: msg.PeersId,
Reason: UnknownChannelType,
Message: fmt.Sprintf("unknown channel type: %v", msg.ChanType),
Language: "en_US.UTF-8",
}
c.writePacket(marshal(msgChannelOpenFailure, m))
}
}
// parseAddr 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) {
addr, b, ok := parseString(b)
if !ok {
return nil, ParseError{msgChannelOpen}
}
port, _, ok := parseUint32(b)
if !ok {
return nil, ParseError{msgChannelOpen}
}
ip := net.ParseIP(string(addr))
if ip == nil {
return nil, ParseError{msgChannelOpen}
}
return &net.TCPAddr{ip, int(port)}, nil
}
// Dial connects to the given network address using net.Dial and
// then initiates a SSH handshake, returning the resulting client connection.
func Dial(network, addr string, config *ClientConfig) (*ClientConn, error) {
@@ -383,6 +449,14 @@ func (c *clientChan) sendClose() error {
}))
}
func (c *clientChan) sendWindowAdj(n int) error {
msg := windowAdjustMsg{
PeersId: c.peersId,
AdditionalBytes: uint32(n),
}
return c.writePacket(marshal(msgChannelWindowAdjust, msg))
}
// Close closes the channel. This does not close the underlying connection.
func (c *clientChan) Close() error {
if !c.weClosed {
@@ -522,11 +596,7 @@ func (r *chanReader) Read(data []byte) (int, error) {
if len(r.buf) > 0 {
n := copy(data, r.buf)
r.buf = r.buf[n:]
msg := windowAdjustMsg{
PeersId: r.clientChan.peersId,
AdditionalBytes: uint32(n),
}
return n, r.clientChan.writePacket(marshal(msgChannelWindowAdjust, msg))
return n, r.clientChan.sendWindowAdj(n)
}
r.buf, ok = <-r.data
if !ok {