LEFT | RIGHT |
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" |
(...skipping 26 matching lines...) Expand all Loading... |
37 | 37 |
38 // Automatic port allocation is broken with OpenSSH before 6.0. See | 38 // Automatic port allocation is broken with OpenSSH before 6.0. See |
39 // also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In | 39 // also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In |
40 // particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0, | 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 | 41 // rather than the actual port number. This means you can never open |
42 // two different listeners with auto allocated ports. We work around | 42 // two different listeners with auto allocated ports. We work around |
43 // this by trying explicit ports until we succeed. | 43 // this by trying explicit ports until we succeed. |
44 | 44 |
45 const openSSHPrefix = "OpenSSH_" | 45 const openSSHPrefix = "OpenSSH_" |
46 | 46 |
47 func autoPortListenBroken(versionStr string) bool { | 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 { |
48 i := strings.Index(versionStr, openSSHPrefix) | 51 i := strings.Index(versionStr, openSSHPrefix) |
49 if i < 0 { | 52 if i < 0 { |
50 return false | 53 return false |
51 } | 54 } |
52 i += len(openSSHPrefix) | 55 i += len(openSSHPrefix) |
53 j := i | 56 j := i |
54 for ; j < len(versionStr); j++ { | 57 for ; j < len(versionStr); j++ { |
55 » » if versionStr[j] < '0' && versionStr[j] > '9' { | 58 » » if versionStr[j] < '0' || versionStr[j] > '9' { |
56 break | 59 break |
57 } | 60 } |
58 } | 61 } |
| 62 fmt.Printf("V %q", versionStr[i:j]) |
59 version, _ := strconv.Atoi(versionStr[i:j]) | 63 version, _ := strconv.Atoi(versionStr[i:j]) |
60 return version < 6 | 64 return version < 6 |
61 } | 65 } |
62 | 66 |
| 67 // autoPortListenWorkaround simulates automatic port allocation by |
| 68 // trying random ports repeatedly. |
63 func (c *ClientConn) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener,
error) { | 69 func (c *ClientConn) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener,
error) { |
64 var sshListener net.Listener | 70 var sshListener net.Listener |
65 var err error | 71 var err error |
66 const tries = 10 | 72 const tries = 10 |
67 for i := 0; i < tries; i++ { | 73 for i := 0; i < tries; i++ { |
68 addr := *laddr | 74 addr := *laddr |
69 addr.Port = 1024 + rand.Intn(60000) | 75 addr.Port = 1024 + rand.Intn(60000) |
70 sshListener, err = c.ListenTCP(&addr) | 76 sshListener, err = c.ListenTCP(&addr) |
71 if err == nil { | 77 if err == nil { |
72 laddr.Port = addr.Port | 78 laddr.Port = addr.Port |
73 return sshListener, err | 79 return sshListener, err |
74 } | 80 } |
75 } | 81 } |
76 return nil, fmt.Errorf("ssh: listen on random port failed after %d tries
: %v", tries, err) | 82 return nil, fmt.Errorf("ssh: listen on random port failed after %d tries
: %v", tries, err) |
77 } | 83 } |
78 | 84 |
79 // ListenTCP requests the remote peer open a listening socket | 85 // ListenTCP requests the remote peer open a listening socket |
80 // on laddr. Incoming connections will be available by calling | 86 // on laddr. Incoming connections will be available by calling |
81 // Accept on the returned net.Listener. | 87 // Accept on the returned net.Listener. |
82 func (c *ClientConn) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { | 88 func (c *ClientConn) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { |
83 » if laddr.Port == 0 && autoPortListenBroken(c.serverVersion) { | 89 » if laddr.Port == 0 && isBrokenOpenSSHVersion(c.serverVersion) { |
84 return c.autoPortListenWorkaround(laddr) | 90 return c.autoPortListenWorkaround(laddr) |
85 } | 91 } |
86 | 92 |
87 m := channelForwardMsg{ | 93 m := channelForwardMsg{ |
88 "tcpip-forward", | 94 "tcpip-forward", |
89 true, // sendGlobalRequest waits for a reply | 95 true, // sendGlobalRequest waits for a reply |
90 laddr.IP.String(), | 96 laddr.IP.String(), |
91 uint32(laddr.Port), | 97 uint32(laddr.Port), |
92 } | 98 } |
93 // send message | 99 // send message |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 // with Timeout() == true. | 341 // with Timeout() == true. |
336 func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { | 342 func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { |
337 return errors.New("ssh: tcpChan: deadline not supported") | 343 return errors.New("ssh: tcpChan: deadline not supported") |
338 } | 344 } |
339 | 345 |
340 // SetWriteDeadline exists to satisfy the net.Conn interface | 346 // SetWriteDeadline exists to satisfy the net.Conn interface |
341 // 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. |
342 func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { | 348 func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { |
343 return errors.New("ssh: tcpChan: deadline not supported") | 349 return errors.New("ssh: tcpChan: deadline not supported") |
344 } | 350 } |
LEFT | RIGHT |