| // 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, |
| peersId: ch.peersId, |
| data: ch.data, |
| }, |
| Writer: &chanWriter{ |
| packetWriter: ch, |
| peersId: ch.peersId, |
| 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") |
| } |