// 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" "fmt" "io" "net" "time" ) // 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 } // RFC 4254 7.2 type channelOpenDirectMsg struct { ChanType string PeersId uint32 PeersWindow uint32 MaxPacketSize 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 resolveable 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.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 } if err := ch.waitForChannelOpenResponse(); err != nil { c.chanlist.remove(ch.id) return nil, fmt.Errorf("ssh: unable to open direct tcpip connection: %v", err) } return &tcpchan{ clientChan: ch, Reader: ch.stdout, Writer: ch.stdin, }, 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 } // SetDeadline sets the read and write deadlines associated // with the connection. func (t *tcpchanconn) SetDeadline(deadline time.Time) error { if err := t.SetReadDeadline(deadline); err != nil { return err } return t.SetWriteDeadline(deadline) } // SetReadDeadline sets the read deadline. // A zero value for t means Read will not time out. // After the deadline, the error from Read will implement net.Error // with Timeout() == true. func (t *tcpchanconn) SetReadDeadline(deadline time.Time) error { return errors.New("ssh: tcpchan: deadline not supported") } // SetWriteDeadline exists to satisfy the net.Conn interface // but is not implemented by this type. It always returns an error. func (t *tcpchanconn) SetWriteDeadline(deadline time.Time) error { return errors.New("ssh: tcpchan: deadline not supported") }