go.crypto/ssh: import gosshnew.

See https://groups.google.com/d/msg/Golang-nuts/AoVxQ4bB5XQ/i8kpMxdbVlEJ

R=hanwen
CC=golang-codereviews
https://golang.org/cl/86190043
This commit is contained in:
Adam Langley
2014-04-09 13:57:52 -07:00
parent 8f45c680ce
commit fa50e7408b
53 changed files with 7391 additions and 3777 deletions
+88 -79
View File
@@ -16,10 +16,11 @@ import (
"time"
)
// Listen requests the remote peer open a listening socket
// on addr. Incoming connections will be available by calling
// Accept on the returned net.Listener.
func (c *ClientConn) Listen(n, addr string) (net.Listener, error) {
// Listen requests the remote peer open a listening socket on
// addr. Incoming connections will be available by calling Accept on
// the returned net.Listener. The listener must be serviced, or the
// SSH connection may hang.
func (c *Client) Listen(n, addr string) (net.Listener, error) {
laddr, err := net.ResolveTCPAddr(n, addr)
if err != nil {
return nil, err
@@ -59,7 +60,7 @@ func isBrokenOpenSSHVersion(versionStr string) bool {
// autoPortListenWorkaround simulates automatic port allocation by
// trying random ports repeatedly.
func (c *ClientConn) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) {
func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) {
var sshListener net.Listener
var err error
const tries = 10
@@ -77,44 +78,45 @@ func (c *ClientConn) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener,
// RFC 4254 7.1
type channelForwardMsg struct {
Message string
WantReply bool
raddr string
rport uint32
addr string
rport uint32
}
// ListenTCP requests the remote peer open a listening socket
// on laddr. Incoming connections will be available by calling
// Accept on the returned net.Listener.
func (c *ClientConn) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
if laddr.Port == 0 && isBrokenOpenSSHVersion(c.serverVersion) {
func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
return c.autoPortListenWorkaround(laddr)
}
m := channelForwardMsg{
"tcpip-forward",
true, // sendGlobalRequest waits for a reply
laddr.IP.String(),
uint32(laddr.Port),
}
// send message
resp, err := c.sendGlobalRequest(m)
ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
if err != nil {
return nil, err
}
if !ok {
return nil, errors.New("ssh: tcpip-forward request denied by peer")
}
// If the original port was 0, then the remote side will
// supply a real port number in the response.
if laddr.Port == 0 {
port, _, ok := parseUint32(resp.Data)
if !ok {
return nil, errors.New("unable to parse response")
var p struct {
Port uint32
}
laddr.Port = int(port)
if err := Unmarshal(resp, &p); err != nil {
return nil, err
}
laddr.Port = int(p.Port)
}
// Register this forward, using the port number we obtained.
ch := c.forwardList.add(*laddr)
ch := c.forwards.add(*laddr)
return &tcpListener{laddr, c, ch}, nil
}
@@ -137,7 +139,7 @@ type forwardEntry struct {
// arguments to add/remove/lookup should be address as specified in
// the original forward-request.
type forward struct {
c *clientChan // the ssh client channel underlying this forward
newCh NewChannel // the ssh client channel underlying this forward
raddr *net.TCPAddr // the raddr of the incoming connection
}
@@ -152,6 +154,31 @@ func (l *forwardList) add(addr net.TCPAddr) chan forward {
return f.c
}
func (l *forwardList) handleChannels(in <-chan NewChannel) {
for ch := range in {
laddr, rest, ok := parseTCPAddr(ch.ExtraData())
if !ok {
// invalid request
ch.Reject(ConnectionFailed, "could not parse TCP address")
continue
}
raddr, rest, ok := parseTCPAddr(rest)
if !ok {
// invalid request
ch.Reject(ConnectionFailed, "could not parse TCP address")
continue
}
if ok := l.forward(*laddr, *raddr, ch); !ok {
// Section 7.2, implementations MUST reject spurious incoming
// connections.
ch.Reject(Prohibited, "no forward for address")
continue
}
}
}
// remove removes the forward entry, and the channel feeding its
// listener.
func (l *forwardList) remove(addr net.TCPAddr) {
@@ -176,21 +203,22 @@ func (l *forwardList) closeAll() {
l.entries = nil
}
func (l *forwardList) lookup(addr net.TCPAddr) (chan forward, bool) {
func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool {
l.Lock()
defer l.Unlock()
for _, f := range l.entries {
if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port {
return f.c, true
if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port {
f.c <- forward{ch, &raddr}
return true
}
}
return nil, false
return false
}
type tcpListener struct {
laddr *net.TCPAddr
conn *ClientConn
conn *Client
in <-chan forward
}
@@ -200,30 +228,33 @@ func (l *tcpListener) Accept() (net.Conn, error) {
if !ok {
return nil, io.EOF
}
ch, incoming, err := s.newCh.Accept()
if err != nil {
return nil, err
}
go DiscardRequests(incoming)
return &tcpChanConn{
tcpChan: &tcpChan{
clientChan: s.c,
Reader: s.c.stdout,
Writer: s.c.stdin,
},
laddr: l.laddr,
raddr: s.raddr,
Channel: ch,
laddr: l.laddr,
raddr: s.raddr,
}, nil
}
// Close closes the listener.
func (l *tcpListener) Close() error {
m := channelForwardMsg{
"cancel-tcpip-forward",
true,
l.laddr.IP.String(),
uint32(l.laddr.Port),
}
l.conn.forwardList.remove(*l.laddr)
if _, err := l.conn.sendGlobalRequest(m); err != nil {
return err
// this also closes the listener.
l.conn.forwards.remove(*l.laddr)
ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
if err == nil && !ok {
err = errors.New("ssh: cancel-tcpip-forward failed")
}
return nil
return err
}
// Addr returns the listener's network address.
@@ -233,7 +264,7 @@ func (l *tcpListener) Addr() net.Addr {
// Dial initiates a connection to the addr from the remote host.
// The resulting connection has a zero LocalAddr() and RemoteAddr().
func (c *ClientConn) Dial(n, addr string) (net.Conn, error) {
func (c *Client) Dial(n, addr string) (net.Conn, error) {
// Parse the address into host and numeric port.
host, portString, err := net.SplitHostPort(addr)
if err != nil {
@@ -253,7 +284,7 @@ func (c *ClientConn) Dial(n, addr string) (net.Conn, error) {
return nil, err
}
return &tcpChanConn{
tcpChan: ch,
Channel: ch,
laddr: zeroAddr,
raddr: zeroAddr,
}, nil
@@ -262,7 +293,7 @@ func (c *ClientConn) Dial(n, addr string) (net.Conn, error) {
// 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) {
func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) {
if laddr == nil {
laddr = &net.TCPAddr{
IP: net.IPv4zero,
@@ -274,7 +305,7 @@ func (c *ClientConn) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, err
return nil, err
}
return &tcpChanConn{
tcpChan: ch,
Channel: ch,
laddr: laddr,
raddr: raddr,
}, nil
@@ -282,54 +313,32 @@ func (c *ClientConn) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, err
// RFC 4254 7.2
type channelOpenDirectMsg struct {
ChanType string
PeersId uint32
PeersWindow uint32
MaxPacketSize uint32
raddr string
rport uint32
laddr string
lport uint32
raddr string
rport uint32
laddr string
lport uint32
}
// dial opens a direct-tcpip connection to the remote server. laddr and raddr are passed as
// strings and are expected to be resolvable at the remote end.
func (c *ClientConn) dial(laddr string, lport int, raddr string, rport int) (*tcpChan, error) {
ch := c.newChan(c.transport)
if err := c.transport.writePacket(marshal(msgChannelOpen, channelOpenDirectMsg{
ChanType: "direct-tcpip",
PeersId: ch.localId,
PeersWindow: channelWindowSize,
MaxPacketSize: channelMaxPacketSize,
raddr: raddr,
rport: uint32(rport),
laddr: laddr,
lport: uint32(lport),
})); err != nil {
c.chanList.remove(ch.localId)
return nil, err
func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) {
msg := channelOpenDirectMsg{
raddr: raddr,
rport: uint32(rport),
laddr: laddr,
lport: uint32(lport),
}
if err := ch.waitForChannelOpenResponse(); err != nil {
c.chanList.remove(ch.localId)
return nil, fmt.Errorf("ssh: unable to open direct tcpip connection: %v", err)
}
return &tcpChan{
clientChan: ch,
Reader: ch.stdout,
Writer: ch.stdin,
}, nil
ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
go DiscardRequests(in)
return ch, err
}
type tcpChan struct {
*clientChan // the backing channel
io.Reader
io.Writer
Channel // the backing channel
}
// tcpChanConn fulfills the net.Conn interface without
// the tcpChan having to hold laddr or raddr directly.
type tcpChanConn struct {
*tcpChan
Channel
laddr, raddr net.Addr
}