blob: 6151241ff08e3fd4d5b9ad6c66159ba044ced239 [file] [log] [blame]
Russ Cox470549d2012-01-25 15:31:12 -05001// 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.
4
5package ssh
6
7import (
8 "errors"
9 "fmt"
10 "io"
Han-Wen Nienhuys2d394e32013-07-26 14:37:37 -040011 "math/rand"
Russ Cox470549d2012-01-25 15:31:12 -050012 "net"
Han-Wen Nienhuys2d394e32013-07-26 14:37:37 -040013 "strconv"
14 "strings"
Dave Cheneyb333fd12012-04-26 20:37:06 +100015 "sync"
Russ Cox470549d2012-01-25 15:31:12 -050016 "time"
17)
18
Adam Langleyfa50e742014-04-09 13:57:52 -070019// 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.
23func (c *Client) Listen(n, addr string) (net.Listener, error) {
Dave Cheneyb4b42222012-05-01 15:43:58 +100024 laddr, err := net.ResolveTCPAddr(n, addr)
Dave Cheneyb333fd12012-04-26 20:37:06 +100025 if err != nil {
26 return nil, err
27 }
Dave Cheneyb4b42222012-05-01 15:43:58 +100028 return c.ListenTCP(laddr)
Dave Cheneyb333fd12012-04-26 20:37:06 +100029}
30
Han-Wen Nienhuys2d394e32013-07-26 14:37:37 -040031// Automatic port allocation is broken with OpenSSH before 6.0. See
32// also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In
33// particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0,
34// rather than the actual port number. This means you can never open
35// two different listeners with auto allocated ports. We work around
36// this by trying explicit ports until we succeed.
37
38const openSSHPrefix = "OpenSSH_"
39
Han-Wen Nienhuys4dc7c8e2013-07-29 13:09:39 -040040var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano()))
41
Han-Wen Nienhuys2d394e32013-07-26 14:37:37 -040042// isBrokenOpenSSHVersion returns true if the given version string
43// specifies a version of OpenSSH that is known to have a bug in port
44// forwarding.
45func isBrokenOpenSSHVersion(versionStr string) bool {
46 i := strings.Index(versionStr, openSSHPrefix)
47 if i < 0 {
48 return false
49 }
50 i += len(openSSHPrefix)
51 j := i
52 for ; j < len(versionStr); j++ {
53 if versionStr[j] < '0' || versionStr[j] > '9' {
54 break
55 }
56 }
57 version, _ := strconv.Atoi(versionStr[i:j])
58 return version < 6
59}
60
61// autoPortListenWorkaround simulates automatic port allocation by
62// trying random ports repeatedly.
Adam Langleyfa50e742014-04-09 13:57:52 -070063func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) {
Han-Wen Nienhuys2d394e32013-07-26 14:37:37 -040064 var sshListener net.Listener
65 var err error
66 const tries = 10
67 for i := 0; i < tries; i++ {
68 addr := *laddr
Han-Wen Nienhuys4dc7c8e2013-07-29 13:09:39 -040069 addr.Port = 1024 + portRandomizer.Intn(60000)
Han-Wen Nienhuys2d394e32013-07-26 14:37:37 -040070 sshListener, err = c.ListenTCP(&addr)
71 if err == nil {
72 laddr.Port = addr.Port
73 return sshListener, err
74 }
75 }
76 return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err)
77}
78
Han-Wen Nienhuysed40a6c2013-10-16 14:24:28 +110079// RFC 4254 7.1
80type channelForwardMsg struct {
Adam Langleyfa50e742014-04-09 13:57:52 -070081 addr string
82 rport uint32
Han-Wen Nienhuysed40a6c2013-10-16 14:24:28 +110083}
84
Adam Langley4002be22012-12-10 18:12:36 -050085// ListenTCP requests the remote peer open a listening socket
Dave Cheneyb4b42222012-05-01 15:43:58 +100086// on laddr. Incoming connections will be available by calling
87// Accept on the returned net.Listener.
Adam Langleyfa50e742014-04-09 13:57:52 -070088func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
89 if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
Han-Wen Nienhuys2d394e32013-07-26 14:37:37 -040090 return c.autoPortListenWorkaround(laddr)
91 }
92
Dave Cheneyb333fd12012-04-26 20:37:06 +100093 m := channelForwardMsg{
Dave Cheneyb4b42222012-05-01 15:43:58 +100094 laddr.IP.String(),
95 uint32(laddr.Port),
Dave Cheneyb333fd12012-04-26 20:37:06 +100096 }
Dave Cheneyb333fd12012-04-26 20:37:06 +100097 // send message
Adam Langleyfa50e742014-04-09 13:57:52 -070098 ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
Dave Cheneyb4b42222012-05-01 15:43:58 +100099 if err != nil {
Dave Cheneyb333fd12012-04-26 20:37:06 +1000100 return nil, err
101 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700102 if !ok {
103 return nil, errors.New("ssh: tcpip-forward request denied by peer")
104 }
Han-Wen Nienhuys0d8dc3c2013-06-11 22:10:15 -0400105
Han-Wen Nienhuys0d8dc3c2013-06-11 22:10:15 -0400106 // If the original port was 0, then the remote side will
107 // supply a real port number in the response.
Dave Cheneyb4b42222012-05-01 15:43:58 +1000108 if laddr.Port == 0 {
Adam Langleyfa50e742014-04-09 13:57:52 -0700109 var p struct {
110 Port uint32
Dave Cheneyb4b42222012-05-01 15:43:58 +1000111 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700112 if err := Unmarshal(resp, &p); err != nil {
113 return nil, err
114 }
115 laddr.Port = int(p.Port)
Dave Cheneyb4b42222012-05-01 15:43:58 +1000116 }
117
Han-Wen Nienhuys7cbb17f2013-06-18 12:43:42 -0400118 // Register this forward, using the port number we obtained.
Adam Langleyfa50e742014-04-09 13:57:52 -0700119 ch := c.forwards.add(*laddr)
Han-Wen Nienhuys7cbb17f2013-06-18 12:43:42 -0400120
121 return &tcpListener{laddr, c, ch}, nil
Dave Cheneyb333fd12012-04-26 20:37:06 +1000122}
123
Adam Langley4002be22012-12-10 18:12:36 -0500124// forwardList stores a mapping between remote
Dave Cheneyb333fd12012-04-26 20:37:06 +1000125// forward requests and the tcpListeners.
126type forwardList struct {
127 sync.Mutex
128 entries []forwardEntry
129}
130
Adam Langley4002be22012-12-10 18:12:36 -0500131// forwardEntry represents an established mapping of a laddr on a
Dave Cheneyb333fd12012-04-26 20:37:06 +1000132// remote ssh server to a channel connected to a tcpListener.
133type forwardEntry struct {
Han-Wen Nienhuys0d8dc3c2013-06-11 22:10:15 -0400134 laddr net.TCPAddr
Dave Cheneyb333fd12012-04-26 20:37:06 +1000135 c chan forward
136}
137
Han-Wen Nienhuys0d8dc3c2013-06-11 22:10:15 -0400138// forward represents an incoming forwarded tcpip connection. The
139// arguments to add/remove/lookup should be address as specified in
140// the original forward-request.
Dave Cheneyb333fd12012-04-26 20:37:06 +1000141type forward struct {
Adam Langleyfa50e742014-04-09 13:57:52 -0700142 newCh NewChannel // the ssh client channel underlying this forward
Dave Cheneyb333fd12012-04-26 20:37:06 +1000143 raddr *net.TCPAddr // the raddr of the incoming connection
144}
145
Han-Wen Nienhuys0d8dc3c2013-06-11 22:10:15 -0400146func (l *forwardList) add(addr net.TCPAddr) chan forward {
Dave Cheneyb333fd12012-04-26 20:37:06 +1000147 l.Lock()
148 defer l.Unlock()
149 f := forwardEntry{
150 addr,
151 make(chan forward, 1),
152 }
153 l.entries = append(l.entries, f)
154 return f.c
155}
156
Han-Wen Nienhuys72116d52014-09-16 12:34:31 -0700157// See RFC 4254, section 7.2
158type forwardedTCPPayload struct {
159 Addr string
160 Port uint32
161 OriginAddr string
162 OriginPort uint32
163}
164
165// parseTCPAddr parses the originating address from the remote into a *net.TCPAddr.
166func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) {
167 if port == 0 || port > 65535 {
168 return nil, fmt.Errorf("ssh: port number out of range: %d", port)
169 }
170 ip := net.ParseIP(string(addr))
171 if ip == nil {
172 return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr)
173 }
174 return &net.TCPAddr{IP: ip, Port: int(port)}, nil
175}
176
Adam Langleyfa50e742014-04-09 13:57:52 -0700177func (l *forwardList) handleChannels(in <-chan NewChannel) {
178 for ch := range in {
Han-Wen Nienhuys72116d52014-09-16 12:34:31 -0700179 var payload forwardedTCPPayload
180 if err := Unmarshal(ch.ExtraData(), &payload); err != nil {
181 ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error())
Adam Langleyfa50e742014-04-09 13:57:52 -0700182 continue
183 }
184
Han-Wen Nienhuys72116d52014-09-16 12:34:31 -0700185 // RFC 4254 section 7.2 specifies that incoming
186 // addresses should list the address, in string
187 // format. It is implied that this should be an IP
188 // address, as it would be impossible to connect to it
189 // otherwise.
190 laddr, err := parseTCPAddr(payload.Addr, payload.Port)
191 if err != nil {
192 ch.Reject(ConnectionFailed, err.Error())
193 continue
194 }
195 raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort)
196 if err != nil {
197 ch.Reject(ConnectionFailed, err.Error())
Adam Langleyfa50e742014-04-09 13:57:52 -0700198 continue
199 }
200
201 if ok := l.forward(*laddr, *raddr, ch); !ok {
202 // Section 7.2, implementations MUST reject spurious incoming
203 // connections.
204 ch.Reject(Prohibited, "no forward for address")
205 continue
206 }
207 }
208}
209
Han-Wen Nienhuys7f7cbbf2013-07-22 21:50:13 +1000210// remove removes the forward entry, and the channel feeding its
211// listener.
Han-Wen Nienhuys0d8dc3c2013-06-11 22:10:15 -0400212func (l *forwardList) remove(addr net.TCPAddr) {
Dave Cheneyb333fd12012-04-26 20:37:06 +1000213 l.Lock()
214 defer l.Unlock()
215 for i, f := range l.entries {
216 if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port {
217 l.entries = append(l.entries[:i], l.entries[i+1:]...)
Han-Wen Nienhuys7f7cbbf2013-07-22 21:50:13 +1000218 close(f.c)
Dave Cheneyb333fd12012-04-26 20:37:06 +1000219 return
220 }
221 }
222}
223
Han-Wen Nienhuys7f7cbbf2013-07-22 21:50:13 +1000224// closeAll closes and clears all forwards.
225func (l *forwardList) closeAll() {
226 l.Lock()
227 defer l.Unlock()
228 for _, f := range l.entries {
229 close(f.c)
230 }
231 l.entries = nil
232}
233
Adam Langleyfa50e742014-04-09 13:57:52 -0700234func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool {
Dave Cheneyb333fd12012-04-26 20:37:06 +1000235 l.Lock()
236 defer l.Unlock()
237 for _, f := range l.entries {
Adam Langleyfa50e742014-04-09 13:57:52 -0700238 if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port {
239 f.c <- forward{ch, &raddr}
240 return true
Dave Cheneyb333fd12012-04-26 20:37:06 +1000241 }
242 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700243 return false
Dave Cheneyb333fd12012-04-26 20:37:06 +1000244}
245
246type tcpListener struct {
247 laddr *net.TCPAddr
Han-Wen Nienhuys0d8dc3c2013-06-11 22:10:15 -0400248
Adam Langleyfa50e742014-04-09 13:57:52 -0700249 conn *Client
Han-Wen Nienhuys7cbb17f2013-06-18 12:43:42 -0400250 in <-chan forward
Dave Cheneyb333fd12012-04-26 20:37:06 +1000251}
252
253// Accept waits for and returns the next connection to the listener.
254func (l *tcpListener) Accept() (net.Conn, error) {
255 s, ok := <-l.in
256 if !ok {
257 return nil, io.EOF
258 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700259 ch, incoming, err := s.newCh.Accept()
260 if err != nil {
261 return nil, err
262 }
263 go DiscardRequests(incoming)
264
Dave Cheneyb333fd12012-04-26 20:37:06 +1000265 return &tcpChanConn{
Adam Langleyfa50e742014-04-09 13:57:52 -0700266 Channel: ch,
267 laddr: l.laddr,
268 raddr: s.raddr,
Dave Cheneyb333fd12012-04-26 20:37:06 +1000269 }, nil
270}
271
272// Close closes the listener.
273func (l *tcpListener) Close() error {
274 m := channelForwardMsg{
Dave Cheneyb333fd12012-04-26 20:37:06 +1000275 l.laddr.IP.String(),
Han-Wen Nienhuys7cbb17f2013-06-18 12:43:42 -0400276 uint32(l.laddr.Port),
Dave Cheneyb333fd12012-04-26 20:37:06 +1000277 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700278
279 // this also closes the listener.
280 l.conn.forwards.remove(*l.laddr)
281 ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
282 if err == nil && !ok {
283 err = errors.New("ssh: cancel-tcpip-forward failed")
Dave Cheneyb4b42222012-05-01 15:43:58 +1000284 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700285 return err
Dave Cheneyb333fd12012-04-26 20:37:06 +1000286}
287
288// Addr returns the listener's network address.
289func (l *tcpListener) Addr() net.Addr {
290 return l.laddr
291}
292
Russ Cox470549d2012-01-25 15:31:12 -0500293// Dial initiates a connection to the addr from the remote host.
JP Sugarbroada1beccb2013-08-28 17:51:56 -0400294// The resulting connection has a zero LocalAddr() and RemoteAddr().
Adam Langleyfa50e742014-04-09 13:57:52 -0700295func (c *Client) Dial(n, addr string) (net.Conn, error) {
JP Sugarbroada1beccb2013-08-28 17:51:56 -0400296 // Parse the address into host and numeric port.
297 host, portString, err := net.SplitHostPort(addr)
Russ Cox470549d2012-01-25 15:31:12 -0500298 if err != nil {
299 return nil, err
300 }
JP Sugarbroada1beccb2013-08-28 17:51:56 -0400301 port, err := strconv.ParseUint(portString, 10, 16)
302 if err != nil {
303 return nil, err
304 }
305 // Use a zero address for local and remote address.
306 zeroAddr := &net.TCPAddr{
307 IP: net.IPv4zero,
308 Port: 0,
309 }
310 ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port))
311 if err != nil {
312 return nil, err
313 }
314 return &tcpChanConn{
Adam Langleyfa50e742014-04-09 13:57:52 -0700315 Channel: ch,
JP Sugarbroada1beccb2013-08-28 17:51:56 -0400316 laddr: zeroAddr,
317 raddr: zeroAddr,
318 }, nil
Russ Cox470549d2012-01-25 15:31:12 -0500319}
320
321// DialTCP connects to the remote address raddr on the network net,
322// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
323// as the local address for the connection.
Adam Langleyfa50e742014-04-09 13:57:52 -0700324func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) {
Russ Cox470549d2012-01-25 15:31:12 -0500325 if laddr == nil {
326 laddr = &net.TCPAddr{
327 IP: net.IPv4zero,
328 Port: 0,
329 }
330 }
331 ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port)
332 if err != nil {
333 return nil, err
334 }
Dave Cheneyb333fd12012-04-26 20:37:06 +1000335 return &tcpChanConn{
Adam Langleyfa50e742014-04-09 13:57:52 -0700336 Channel: ch,
Russ Cox470549d2012-01-25 15:31:12 -0500337 laddr: laddr,
338 raddr: raddr,
339 }, nil
340}
341
342// RFC 4254 7.2
343type channelOpenDirectMsg struct {
Adam Langleyfa50e742014-04-09 13:57:52 -0700344 raddr string
345 rport uint32
346 laddr string
347 lport uint32
Russ Cox470549d2012-01-25 15:31:12 -0500348}
349
Adam Langleyfa50e742014-04-09 13:57:52 -0700350func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) {
351 msg := channelOpenDirectMsg{
352 raddr: raddr,
353 rport: uint32(rport),
354 laddr: laddr,
355 lport: uint32(lport),
Russ Cox470549d2012-01-25 15:31:12 -0500356 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700357 ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
Eric Garrido83f15032015-08-07 17:38:44 -0400358 if err != nil {
359 return nil, err
360 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700361 go DiscardRequests(in)
362 return ch, err
Russ Cox470549d2012-01-25 15:31:12 -0500363}
364
Dave Cheneyb333fd12012-04-26 20:37:06 +1000365type tcpChan struct {
Adam Langleyfa50e742014-04-09 13:57:52 -0700366 Channel // the backing channel
Russ Cox470549d2012-01-25 15:31:12 -0500367}
368
Adam Langley4002be22012-12-10 18:12:36 -0500369// tcpChanConn fulfills the net.Conn interface without
Dave Cheneyb333fd12012-04-26 20:37:06 +1000370// the tcpChan having to hold laddr or raddr directly.
371type tcpChanConn struct {
Adam Langleyfa50e742014-04-09 13:57:52 -0700372 Channel
Russ Cox470549d2012-01-25 15:31:12 -0500373 laddr, raddr net.Addr
374}
375
376// LocalAddr returns the local network address.
Dave Cheneyb333fd12012-04-26 20:37:06 +1000377func (t *tcpChanConn) LocalAddr() net.Addr {
Russ Cox470549d2012-01-25 15:31:12 -0500378 return t.laddr
379}
380
381// RemoteAddr returns the remote network address.
Dave Cheneyb333fd12012-04-26 20:37:06 +1000382func (t *tcpChanConn) RemoteAddr() net.Addr {
Russ Cox470549d2012-01-25 15:31:12 -0500383 return t.raddr
384}
385
386// SetDeadline sets the read and write deadlines associated
387// with the connection.
Dave Cheneyb333fd12012-04-26 20:37:06 +1000388func (t *tcpChanConn) SetDeadline(deadline time.Time) error {
Russ Cox470549d2012-01-25 15:31:12 -0500389 if err := t.SetReadDeadline(deadline); err != nil {
390 return err
391 }
392 return t.SetWriteDeadline(deadline)
393}
394
395// SetReadDeadline sets the read deadline.
396// A zero value for t means Read will not time out.
397// After the deadline, the error from Read will implement net.Error
398// with Timeout() == true.
Dave Cheneyb333fd12012-04-26 20:37:06 +1000399func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error {
400 return errors.New("ssh: tcpChan: deadline not supported")
Russ Cox470549d2012-01-25 15:31:12 -0500401}
402
403// SetWriteDeadline exists to satisfy the net.Conn interface
404// but is not implemented by this type. It always returns an error.
Dave Cheneyb333fd12012-04-26 20:37:06 +1000405func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error {
406 return errors.New("ssh: tcpChan: deadline not supported")
Russ Cox470549d2012-01-25 15:31:12 -0500407}