1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
19 // Listen requests the remote peer open a listening socket on
20 // addr. Incoming connections will be available by calling Accept on
21 // the returned net.Listener. The listener must be serviced, or the
22 // SSH connection may hang.
23 // N must be "tcp", "tcp4", "tcp6", or "unix".
24 func (c *Client) Listen(n, addr string) (net.Listener, error) {
26 case "tcp", "tcp4", "tcp6":
27 laddr, err := net.ResolveTCPAddr(n, addr)
31 return c.ListenTCP(laddr)
33 return c.ListenUnix(addr)
35 return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
39 // Automatic port allocation is broken with OpenSSH before 6.0. See
40 // also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In
41 // particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0,
42 // rather than the actual port number. This means you can never open
43 // two different listeners with auto allocated ports. We work around
44 // this by trying explicit ports until we succeed.
46 const openSSHPrefix = "OpenSSH_"
48 var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano()))
50 // isBrokenOpenSSHVersion returns true if the given version string
51 // specifies a version of OpenSSH that is known to have a bug in port
53 func isBrokenOpenSSHVersion(versionStr string) bool {
54 i := strings.Index(versionStr, openSSHPrefix)
58 i += len(openSSHPrefix)
60 for ; j < len(versionStr); j++ {
61 if versionStr[j] < '0' || versionStr[j] > '9' {
65 version, _ := strconv.Atoi(versionStr[i:j])
69 // autoPortListenWorkaround simulates automatic port allocation by
70 // trying random ports repeatedly.
71 func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) {
72 var sshListener net.Listener
75 for i := 0; i < tries; i++ {
77 addr.Port = 1024 + portRandomizer.Intn(60000)
78 sshListener, err = c.ListenTCP(&addr)
80 laddr.Port = addr.Port
81 return sshListener, err
84 return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err)
88 type channelForwardMsg struct {
93 // handleForwards starts goroutines handling forwarded connections.
94 // It's called on first use by (*Client).ListenTCP to not launch
95 // goroutines until needed.
96 func (c *Client) handleForwards() {
97 go c.forwards.handleChannels(c.HandleChannelOpen("forwarded-tcpip"))
98 go c.forwards.handleChannels(c.HandleChannelOpen("forwarded-streamlocal@openssh.com"))
101 // ListenTCP requests the remote peer open a listening socket
102 // on laddr. Incoming connections will be available by calling
103 // Accept on the returned net.Listener.
104 func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
105 c.handleForwardsOnce.Do(c.handleForwards)
106 if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
107 return c.autoPortListenWorkaround(laddr)
110 m := channelForwardMsg{
115 ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
120 return nil, errors.New("ssh: tcpip-forward request denied by peer")
123 // If the original port was 0, then the remote side will
124 // supply a real port number in the response.
129 if err := Unmarshal(resp, &p); err != nil {
132 laddr.Port = int(p.Port)
135 // Register this forward, using the port number we obtained.
136 ch := c.forwards.add(laddr)
138 return &tcpListener{laddr, c, ch}, nil
141 // forwardList stores a mapping between remote
142 // forward requests and the tcpListeners.
143 type forwardList struct {
145 entries []forwardEntry
148 // forwardEntry represents an established mapping of a laddr on a
149 // remote ssh server to a channel connected to a tcpListener.
150 type forwardEntry struct {
155 // forward represents an incoming forwarded tcpip connection. The
156 // arguments to add/remove/lookup should be address as specified in
157 // the original forward-request.
158 type forward struct {
159 newCh NewChannel // the ssh client channel underlying this forward
160 raddr net.Addr // the raddr of the incoming connection
163 func (l *forwardList) add(addr net.Addr) chan forward {
168 c: make(chan forward, 1),
170 l.entries = append(l.entries, f)
174 // See RFC 4254, section 7.2
175 type forwardedTCPPayload struct {
182 // parseTCPAddr parses the originating address from the remote into a *net.TCPAddr.
183 func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) {
184 if port == 0 || port > 65535 {
185 return nil, fmt.Errorf("ssh: port number out of range: %d", port)
187 ip := net.ParseIP(string(addr))
189 return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr)
191 return &net.TCPAddr{IP: ip, Port: int(port)}, nil
194 func (l *forwardList) handleChannels(in <-chan NewChannel) {
201 switch channelType := ch.ChannelType(); channelType {
202 case "forwarded-tcpip":
203 var payload forwardedTCPPayload
204 if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
205 ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error())
209 // RFC 4254 section 7.2 specifies that incoming
210 // addresses should list the address, in string
211 // format. It is implied that this should be an IP
212 // address, as it would be impossible to connect to it
214 laddr, err = parseTCPAddr(payload.Addr, payload.Port)
216 ch.Reject(ConnectionFailed, err.Error())
219 raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort)
221 ch.Reject(ConnectionFailed, err.Error())
225 case "forwarded-streamlocal@openssh.com":
226 var payload forwardedStreamLocalPayload
227 if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
228 ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error())
231 laddr = &net.UnixAddr{
232 Name: payload.SocketPath,
235 raddr = &net.UnixAddr{
240 panic(fmt.Errorf("ssh: unknown channel type %s", channelType))
242 if ok := l.forward(laddr, raddr, ch); !ok {
243 // Section 7.2, implementations MUST reject spurious incoming
245 ch.Reject(Prohibited, "no forward for address")
252 // remove removes the forward entry, and the channel feeding its
254 func (l *forwardList) remove(addr net.Addr) {
257 for i, f := range l.entries {
258 if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() {
259 l.entries = append(l.entries[:i], l.entries[i+1:]...)
266 // closeAll closes and clears all forwards.
267 func (l *forwardList) closeAll() {
270 for _, f := range l.entries {
276 func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool {
279 for _, f := range l.entries {
280 if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() {
281 f.c <- forward{newCh: ch, raddr: raddr}
288 type tcpListener struct {
295 // Accept waits for and returns the next connection to the listener.
296 func (l *tcpListener) Accept() (net.Conn, error) {
301 ch, incoming, err := s.newCh.Accept()
305 go DiscardRequests(incoming)
314 // Close closes the listener.
315 func (l *tcpListener) Close() error {
316 m := channelForwardMsg{
318 uint32(l.laddr.Port),
321 // this also closes the listener.
322 l.conn.forwards.remove(l.laddr)
323 ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
324 if err == nil && !ok {
325 err = errors.New("ssh: cancel-tcpip-forward failed")
330 // Addr returns the listener's network address.
331 func (l *tcpListener) Addr() net.Addr {
335 // Dial initiates a connection to the addr from the remote host.
336 // The resulting connection has a zero LocalAddr() and RemoteAddr().
337 func (c *Client) Dial(n, addr string) (net.Conn, error) {
340 case "tcp", "tcp4", "tcp6":
341 // Parse the address into host and numeric port.
342 host, portString, err := net.SplitHostPort(addr)
346 port, err := strconv.ParseUint(portString, 10, 16)
350 ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port))
354 // Use a zero address for local and remote address.
355 zeroAddr := &net.TCPAddr{
366 ch, err = c.dialStreamLocal(addr)
372 laddr: &net.UnixAddr{
376 raddr: &net.UnixAddr{
382 return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
386 // DialTCP connects to the remote address raddr on the network net,
387 // which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
388 // as the local address for the connection.
389 func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) {
391 laddr = &net.TCPAddr{
396 ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port)
408 type channelOpenDirectMsg struct {
415 func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) {
416 msg := channelOpenDirectMsg{
418 rport: uint32(rport),
420 lport: uint32(lport),
422 ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
426 go DiscardRequests(in)
430 type tcpChan struct {
431 Channel // the backing channel
434 // chanConn fulfills the net.Conn interface without
435 // the tcpChan having to hold laddr or raddr directly.
436 type chanConn struct {
438 laddr, raddr net.Addr
441 // LocalAddr returns the local network address.
442 func (t *chanConn) LocalAddr() net.Addr {
446 // RemoteAddr returns the remote network address.
447 func (t *chanConn) RemoteAddr() net.Addr {
451 // SetDeadline sets the read and write deadlines associated
452 // with the connection.
453 func (t *chanConn) SetDeadline(deadline time.Time) error {
454 if err := t.SetReadDeadline(deadline); err != nil {
457 return t.SetWriteDeadline(deadline)
460 // SetReadDeadline sets the read deadline.
461 // A zero value for t means Read will not time out.
462 // After the deadline, the error from Read will implement net.Error
463 // with Timeout() == true.
464 func (t *chanConn) SetReadDeadline(deadline time.Time) error {
465 // for compatibility with previous version,
466 // the error message contains "tcpChan"
467 return errors.New("ssh: tcpChan: deadline not supported")
470 // SetWriteDeadline exists to satisfy the net.Conn interface
471 // but is not implemented by this type. It always returns an error.
472 func (t *chanConn) SetWriteDeadline(deadline time.Time) error {
473 return errors.New("ssh: tcpChan: deadline not supported")