Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1131)

Side by Side Diff: ssh/tcpip.go

Issue 11921043: code review 11921043: go.crypto/ssh: add workaround for broken port forwarding in (Closed)
Patch Set: diff -r c0932d3462e8 https://code.google.com/p/go.crypto Created 11 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ssh/client.go ('k') | ssh/tcpip_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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])
agl1 2013/07/26 18:38:06 Looks like debugging - will remove before landing.
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
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 }
OLDNEW
« no previous file with comments | « ssh/client.go ('k') | ssh/tcpip_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b