OLD | NEW |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package ssh | 5 package ssh |
6 | 6 |
7 import ( | 7 import ( |
8 "errors" | 8 "errors" |
9 "fmt" | 9 "fmt" |
10 "io" | 10 "io" |
| 11 "math/rand" |
11 "net" | 12 "net" |
| 13 "strconv" |
| 14 "strings" |
12 "sync" | 15 "sync" |
13 "time" | 16 "time" |
14 ) | 17 ) |
15 | 18 |
16 // Listen requests the remote peer open a listening socket | 19 // Listen requests the remote peer open a listening socket |
17 // on addr. Incoming connections will be available by calling | 20 // on addr. Incoming connections will be available by calling |
18 // Accept on the returned net.Listener. | 21 // Accept on the returned net.Listener. |
19 func (c *ClientConn) Listen(n, addr string) (net.Listener, error) { | 22 func (c *ClientConn) Listen(n, addr string) (net.Listener, error) { |
20 laddr, err := net.ResolveTCPAddr(n, addr) | 23 laddr, err := net.ResolveTCPAddr(n, addr) |
21 if err != nil { | 24 if err != nil { |
22 return nil, err | 25 return nil, err |
23 } | 26 } |
24 return c.ListenTCP(laddr) | 27 return c.ListenTCP(laddr) |
25 } | 28 } |
26 | 29 |
27 // RFC 4254 7.1 | 30 // RFC 4254 7.1 |
28 type channelForwardMsg struct { | 31 type channelForwardMsg struct { |
29 Message string | 32 Message string |
30 WantReply bool | 33 WantReply bool |
31 raddr string | 34 raddr string |
32 rport uint32 | 35 rport uint32 |
33 } | 36 } |
34 | 37 |
| 38 // Automatic port allocation is broken with OpenSSH before 6.0. See |
| 39 // also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In |
| 40 // particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0, |
| 41 // rather than the actual port number. This means you can never open |
| 42 // two different listeners with auto allocated ports. We work around |
| 43 // this by trying explicit ports until we succeed. |
| 44 |
| 45 const openSSHPrefix = "OpenSSH_" |
| 46 |
| 47 // isBrokenOpenSSHVersion returns true if the given version string |
| 48 // specifies a version of OpenSSH that is known to have a bug in port |
| 49 // forwarding. |
| 50 func isBrokenOpenSSHVersion(versionStr string) bool { |
| 51 i := strings.Index(versionStr, openSSHPrefix) |
| 52 if i < 0 { |
| 53 return false |
| 54 } |
| 55 i += len(openSSHPrefix) |
| 56 j := i |
| 57 for ; j < len(versionStr); j++ { |
| 58 if versionStr[j] < '0' || versionStr[j] > '9' { |
| 59 break |
| 60 } |
| 61 } |
| 62 fmt.Printf("V %q", versionStr[i:j]) |
| 63 version, _ := strconv.Atoi(versionStr[i:j]) |
| 64 return version < 6 |
| 65 } |
| 66 |
| 67 // autoPortListenWorkaround simulates automatic port allocation by |
| 68 // trying random ports repeatedly. |
| 69 func (c *ClientConn) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener,
error) { |
| 70 var sshListener net.Listener |
| 71 var err error |
| 72 const tries = 10 |
| 73 for i := 0; i < tries; i++ { |
| 74 addr := *laddr |
| 75 addr.Port = 1024 + rand.Intn(60000) |
| 76 sshListener, err = c.ListenTCP(&addr) |
| 77 if err == nil { |
| 78 laddr.Port = addr.Port |
| 79 return sshListener, err |
| 80 } |
| 81 } |
| 82 return nil, fmt.Errorf("ssh: listen on random port failed after %d tries
: %v", tries, err) |
| 83 } |
| 84 |
35 // ListenTCP requests the remote peer open a listening socket | 85 // ListenTCP requests the remote peer open a listening socket |
36 // on laddr. Incoming connections will be available by calling | 86 // on laddr. Incoming connections will be available by calling |
37 // Accept on the returned net.Listener. | 87 // Accept on the returned net.Listener. |
38 func (c *ClientConn) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { | 88 func (c *ClientConn) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { |
| 89 if laddr.Port == 0 && isBrokenOpenSSHVersion(c.serverVersion) { |
| 90 return c.autoPortListenWorkaround(laddr) |
| 91 } |
| 92 |
39 m := channelForwardMsg{ | 93 m := channelForwardMsg{ |
40 "tcpip-forward", | 94 "tcpip-forward", |
41 true, // sendGlobalRequest waits for a reply | 95 true, // sendGlobalRequest waits for a reply |
42 laddr.IP.String(), | 96 laddr.IP.String(), |
43 uint32(laddr.Port), | 97 uint32(laddr.Port), |
44 } | 98 } |
45 // send message | 99 // send message |
46 resp, err := c.sendGlobalRequest(m) | 100 resp, err := c.sendGlobalRequest(m) |
47 if err != nil { | 101 if err != nil { |
48 return nil, err | 102 return nil, err |
49 } | 103 } |
50 | 104 |
51 // If the original port was 0, then the remote side will | 105 // If the original port was 0, then the remote side will |
52 // supply a real port number in the response. | 106 // supply a real port number in the response. |
53 if laddr.Port == 0 { | 107 if laddr.Port == 0 { |
54 port, _, ok := parseUint32(resp.Data) | 108 port, _, ok := parseUint32(resp.Data) |
55 if !ok { | 109 if !ok { |
56 return nil, errors.New("unable to parse response") | 110 return nil, errors.New("unable to parse response") |
57 } | 111 } |
58 laddr.Port = int(port) | 112 laddr.Port = int(port) |
59 } | 113 } |
60 | 114 |
61 // Register this forward, using the port number we obtained. | 115 // Register this forward, using the port number we obtained. |
62 // | |
63 // This does not work on OpenSSH < 6.0, which will send a | |
64 // channelOpenMsg with port number 0, rather than the actual | |
65 // port number. | |
66 ch := c.forwardList.add(*laddr) | 116 ch := c.forwardList.add(*laddr) |
67 | 117 |
68 return &tcpListener{laddr, c, ch}, nil | 118 return &tcpListener{laddr, c, ch}, nil |
69 } | 119 } |
70 | 120 |
71 // forwardList stores a mapping between remote | 121 // forwardList stores a mapping between remote |
72 // forward requests and the tcpListeners. | 122 // forward requests and the tcpListeners. |
73 type forwardList struct { | 123 type forwardList struct { |
74 sync.Mutex | 124 sync.Mutex |
75 entries []forwardEntry | 125 entries []forwardEntry |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
291 // with Timeout() == true. | 341 // with Timeout() == true. |
292 func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { | 342 func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { |
293 return errors.New("ssh: tcpChan: deadline not supported") | 343 return errors.New("ssh: tcpChan: deadline not supported") |
294 } | 344 } |
295 | 345 |
296 // SetWriteDeadline exists to satisfy the net.Conn interface | 346 // SetWriteDeadline exists to satisfy the net.Conn interface |
297 // but is not implemented by this type. It always returns an error. | 347 // but is not implemented by this type. It always returns an error. |
298 func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { | 348 func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { |
299 return errors.New("ssh: tcpChan: deadline not supported") | 349 return errors.New("ssh: tcpChan: deadline not supported") |
300 } | 350 } |
OLD | NEW |