From 7af553ab52cf0c79b906b70b9f4b11b2c926fbe1 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Sun, 13 Nov 2011 21:05:35 -0500 Subject: [PATCH] exp/ssh: add direct-tcpip client support This CL adds experimental support for making proxied net.Conn connections via the remote server. nb. Functional tests exist for this feature but CL 5320053 or similar needs to be committed to support them. R=rsc, agl, n13m3y3r CC=cw, golang-dev, huin https://golang.org/cl/5371081 --- src/pkg/exp/ssh/Makefile | 3 +- src/pkg/exp/ssh/tcpip.go | 146 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/pkg/exp/ssh/tcpip.go diff --git a/src/pkg/exp/ssh/Makefile b/src/pkg/exp/ssh/Makefile index 0db0b6f53f..5c288320fb 100644 --- a/src/pkg/exp/ssh/Makefile +++ b/src/pkg/exp/ssh/Makefile @@ -11,9 +11,10 @@ GOFILES=\ client_auth.go\ common.go\ messages.go\ - transport.go\ server.go\ server_shell.go\ session.go\ + tcpip.go\ + transport.go\ include ../../../Make.pkg diff --git a/src/pkg/exp/ssh/tcpip.go b/src/pkg/exp/ssh/tcpip.go new file mode 100644 index 0000000000..859dedc93b --- /dev/null +++ b/src/pkg/exp/ssh/tcpip.go @@ -0,0 +1,146 @@ +// 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" + "net" +) +// Dial initiates a connection to the addr from the remote host. +// addr is resolved using net.ResolveTCPAddr before connection. +// This could allow an observer to observe the DNS name of the +// remote host. Consider using ssh.DialTCP to avoid this. +func (c *ClientConn) Dial(n, addr string) (net.Conn, error) { + raddr, err := net.ResolveTCPAddr(n, addr) + if err != nil { + return nil, err + } + return c.DialTCP(n, nil, raddr) +} + +// DialTCP connects to the remote address raddr on the network net, +// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used +// as the local address for the connection. +func (c *ClientConn) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { + if laddr == nil { + laddr = &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + } + ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port) + if err != nil { + return nil, err + } + return &tcpchanconn{ + tcpchan: ch, + laddr: laddr, + raddr: raddr, + }, nil +} + +// dial opens a direct-tcpip connection to the remote server. laddr and raddr are passed as +// strings and are expected to be resolveable at the remote end. +func (c *ClientConn) dial(laddr string, lport int, raddr string, rport int) (*tcpchan, error) { + // RFC 4254 7.2 + type channelOpenDirectMsg struct { + ChanType string + PeersId uint32 + PeersWindow uint32 + MaxPacketSize uint32 + raddr string + rport uint32 + laddr string + lport uint32 + } + ch := c.newChan(c.transport) + if err := c.writePacket(marshal(msgChannelOpen, channelOpenDirectMsg{ + ChanType: "direct-tcpip", + PeersId: ch.id, + PeersWindow: 1 << 14, + MaxPacketSize: 1 << 15, // RFC 4253 6.1 + raddr: raddr, + rport: uint32(rport), + laddr: laddr, + lport: uint32(lport), + })); err != nil { + c.chanlist.remove(ch.id) + return nil, err + } + // wait for response + switch msg := (<-ch.msg).(type) { + case *channelOpenConfirmMsg: + ch.peersId = msg.MyId + ch.win <- int(msg.MyWindow) + case *channelOpenFailureMsg: + c.chanlist.remove(ch.id) + return nil, errors.New("ssh: error opening remote TCP connection: " + msg.Message) + default: + c.chanlist.remove(ch.id) + return nil, errors.New("ssh: unexpected packet") + } + return &tcpchan{ + clientChan: ch, + Reader: &chanReader{ + packetWriter: ch, + id: ch.id, + data: ch.data, + }, + Writer: &chanWriter{ + packetWriter: ch, + id: ch.id, + win: ch.win, + }, + }, nil +} + +type tcpchan struct { + *clientChan // the backing channel + io.Reader + io.Writer +} + +// tcpchanconn fulfills the net.Conn interface without +// the tcpchan having to hold laddr or raddr directly. +type tcpchanconn struct { + *tcpchan + laddr, raddr net.Addr +} + +// LocalAddr returns the local network address. +func (t *tcpchanconn) LocalAddr() net.Addr { + return t.laddr +} + +// RemoteAddr returns the remote network address. +func (t *tcpchanconn) RemoteAddr() net.Addr { + return t.raddr +} + +// SetTimeout sets the read and write deadlines associated +// with the connection. +func (t *tcpchanconn) SetTimeout(nsec int64) error { + if err := t.SetReadTimeout(nsec); err != nil { + return err + } + return t.SetWriteTimeout(nsec) +} + +// SetReadTimeout sets the time (in nanoseconds) that +// Read will wait for data before returning an error with Timeout() == true. +// Setting nsec == 0 (the default) disables the deadline. +func (t *tcpchanconn) SetReadTimeout(nsec int64) error { + return errors.New("ssh: tcpchan: timeout not supported") +} + +// SetWriteTimeout sets the time (in nanoseconds) that +// Write will wait to send its data before returning an error with Timeout() == true. +// Setting nsec == 0 (the default) disables the deadline. +// Even if write times out, it may return n > 0, indicating that +// some of the data was successfully written. +func (t *tcpchanconn) SetWriteTimeout(nsec int64) error { + return errors.New("ssh: tcpchan: timeout not supported") +}