Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / golang.org / x / crypto / ssh / tcpip.go
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.
4
5 package ssh
6
7 import (
8         "errors"
9         "fmt"
10         "io"
11         "math/rand"
12         "net"
13         "strconv"
14         "strings"
15         "sync"
16         "time"
17 )
18
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) {
25         switch n {
26         case "tcp", "tcp4", "tcp6":
27                 laddr, err := net.ResolveTCPAddr(n, addr)
28                 if err != nil {
29                         return nil, err
30                 }
31                 return c.ListenTCP(laddr)
32         case "unix":
33                 return c.ListenUnix(addr)
34         default:
35                 return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
36         }
37 }
38
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.
45
46 const openSSHPrefix = "OpenSSH_"
47
48 var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano()))
49
50 // isBrokenOpenSSHVersion returns true if the given version string
51 // specifies a version of OpenSSH that is known to have a bug in port
52 // forwarding.
53 func isBrokenOpenSSHVersion(versionStr string) bool {
54         i := strings.Index(versionStr, openSSHPrefix)
55         if i < 0 {
56                 return false
57         }
58         i += len(openSSHPrefix)
59         j := i
60         for ; j < len(versionStr); j++ {
61                 if versionStr[j] < '0' || versionStr[j] > '9' {
62                         break
63                 }
64         }
65         version, _ := strconv.Atoi(versionStr[i:j])
66         return version < 6
67 }
68
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
73         var err error
74         const tries = 10
75         for i := 0; i < tries; i++ {
76                 addr := *laddr
77                 addr.Port = 1024 + portRandomizer.Intn(60000)
78                 sshListener, err = c.ListenTCP(&addr)
79                 if err == nil {
80                         laddr.Port = addr.Port
81                         return sshListener, err
82                 }
83         }
84         return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err)
85 }
86
87 // RFC 4254 7.1
88 type channelForwardMsg struct {
89         addr  string
90         rport uint32
91 }
92
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"))
99 }
100
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)
108         }
109
110         m := channelForwardMsg{
111                 laddr.IP.String(),
112                 uint32(laddr.Port),
113         }
114         // send message
115         ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
116         if err != nil {
117                 return nil, err
118         }
119         if !ok {
120                 return nil, errors.New("ssh: tcpip-forward request denied by peer")
121         }
122
123         // If the original port was 0, then the remote side will
124         // supply a real port number in the response.
125         if laddr.Port == 0 {
126                 var p struct {
127                         Port uint32
128                 }
129                 if err := Unmarshal(resp, &p); err != nil {
130                         return nil, err
131                 }
132                 laddr.Port = int(p.Port)
133         }
134
135         // Register this forward, using the port number we obtained.
136         ch := c.forwards.add(laddr)
137
138         return &tcpListener{laddr, c, ch}, nil
139 }
140
141 // forwardList stores a mapping between remote
142 // forward requests and the tcpListeners.
143 type forwardList struct {
144         sync.Mutex
145         entries []forwardEntry
146 }
147
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 {
151         laddr net.Addr
152         c     chan forward
153 }
154
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
161 }
162
163 func (l *forwardList) add(addr net.Addr) chan forward {
164         l.Lock()
165         defer l.Unlock()
166         f := forwardEntry{
167                 laddr: addr,
168                 c:     make(chan forward, 1),
169         }
170         l.entries = append(l.entries, f)
171         return f.c
172 }
173
174 // See RFC 4254, section 7.2
175 type forwardedTCPPayload struct {
176         Addr       string
177         Port       uint32
178         OriginAddr string
179         OriginPort uint32
180 }
181
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)
186         }
187         ip := net.ParseIP(string(addr))
188         if ip == nil {
189                 return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr)
190         }
191         return &net.TCPAddr{IP: ip, Port: int(port)}, nil
192 }
193
194 func (l *forwardList) handleChannels(in <-chan NewChannel) {
195         for ch := range in {
196                 var (
197                         laddr net.Addr
198                         raddr net.Addr
199                         err   error
200                 )
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())
206                                 continue
207                         }
208
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
213                         // otherwise.
214                         laddr, err = parseTCPAddr(payload.Addr, payload.Port)
215                         if err != nil {
216                                 ch.Reject(ConnectionFailed, err.Error())
217                                 continue
218                         }
219                         raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort)
220                         if err != nil {
221                                 ch.Reject(ConnectionFailed, err.Error())
222                                 continue
223                         }
224
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())
229                                 continue
230                         }
231                         laddr = &net.UnixAddr{
232                                 Name: payload.SocketPath,
233                                 Net:  "unix",
234                         }
235                         raddr = &net.UnixAddr{
236                                 Name: "@",
237                                 Net:  "unix",
238                         }
239                 default:
240                         panic(fmt.Errorf("ssh: unknown channel type %s", channelType))
241                 }
242                 if ok := l.forward(laddr, raddr, ch); !ok {
243                         // Section 7.2, implementations MUST reject spurious incoming
244                         // connections.
245                         ch.Reject(Prohibited, "no forward for address")
246                         continue
247                 }
248
249         }
250 }
251
252 // remove removes the forward entry, and the channel feeding its
253 // listener.
254 func (l *forwardList) remove(addr net.Addr) {
255         l.Lock()
256         defer l.Unlock()
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:]...)
260                         close(f.c)
261                         return
262                 }
263         }
264 }
265
266 // closeAll closes and clears all forwards.
267 func (l *forwardList) closeAll() {
268         l.Lock()
269         defer l.Unlock()
270         for _, f := range l.entries {
271                 close(f.c)
272         }
273         l.entries = nil
274 }
275
276 func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool {
277         l.Lock()
278         defer l.Unlock()
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}
282                         return true
283                 }
284         }
285         return false
286 }
287
288 type tcpListener struct {
289         laddr *net.TCPAddr
290
291         conn *Client
292         in   <-chan forward
293 }
294
295 // Accept waits for and returns the next connection to the listener.
296 func (l *tcpListener) Accept() (net.Conn, error) {
297         s, ok := <-l.in
298         if !ok {
299                 return nil, io.EOF
300         }
301         ch, incoming, err := s.newCh.Accept()
302         if err != nil {
303                 return nil, err
304         }
305         go DiscardRequests(incoming)
306
307         return &chanConn{
308                 Channel: ch,
309                 laddr:   l.laddr,
310                 raddr:   s.raddr,
311         }, nil
312 }
313
314 // Close closes the listener.
315 func (l *tcpListener) Close() error {
316         m := channelForwardMsg{
317                 l.laddr.IP.String(),
318                 uint32(l.laddr.Port),
319         }
320
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")
326         }
327         return err
328 }
329
330 // Addr returns the listener's network address.
331 func (l *tcpListener) Addr() net.Addr {
332         return l.laddr
333 }
334
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) {
338         var ch Channel
339         switch n {
340         case "tcp", "tcp4", "tcp6":
341                 // Parse the address into host and numeric port.
342                 host, portString, err := net.SplitHostPort(addr)
343                 if err != nil {
344                         return nil, err
345                 }
346                 port, err := strconv.ParseUint(portString, 10, 16)
347                 if err != nil {
348                         return nil, err
349                 }
350                 ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port))
351                 if err != nil {
352                         return nil, err
353                 }
354                 // Use a zero address for local and remote address.
355                 zeroAddr := &net.TCPAddr{
356                         IP:   net.IPv4zero,
357                         Port: 0,
358                 }
359                 return &chanConn{
360                         Channel: ch,
361                         laddr:   zeroAddr,
362                         raddr:   zeroAddr,
363                 }, nil
364         case "unix":
365                 var err error
366                 ch, err = c.dialStreamLocal(addr)
367                 if err != nil {
368                         return nil, err
369                 }
370                 return &chanConn{
371                         Channel: ch,
372                         laddr: &net.UnixAddr{
373                                 Name: "@",
374                                 Net:  "unix",
375                         },
376                         raddr: &net.UnixAddr{
377                                 Name: addr,
378                                 Net:  "unix",
379                         },
380                 }, nil
381         default:
382                 return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
383         }
384 }
385
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) {
390         if laddr == nil {
391                 laddr = &net.TCPAddr{
392                         IP:   net.IPv4zero,
393                         Port: 0,
394                 }
395         }
396         ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port)
397         if err != nil {
398                 return nil, err
399         }
400         return &chanConn{
401                 Channel: ch,
402                 laddr:   laddr,
403                 raddr:   raddr,
404         }, nil
405 }
406
407 // RFC 4254 7.2
408 type channelOpenDirectMsg struct {
409         raddr string
410         rport uint32
411         laddr string
412         lport uint32
413 }
414
415 func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) {
416         msg := channelOpenDirectMsg{
417                 raddr: raddr,
418                 rport: uint32(rport),
419                 laddr: laddr,
420                 lport: uint32(lport),
421         }
422         ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
423         if err != nil {
424                 return nil, err
425         }
426         go DiscardRequests(in)
427         return ch, err
428 }
429
430 type tcpChan struct {
431         Channel // the backing channel
432 }
433
434 // chanConn fulfills the net.Conn interface without
435 // the tcpChan having to hold laddr or raddr directly.
436 type chanConn struct {
437         Channel
438         laddr, raddr net.Addr
439 }
440
441 // LocalAddr returns the local network address.
442 func (t *chanConn) LocalAddr() net.Addr {
443         return t.laddr
444 }
445
446 // RemoteAddr returns the remote network address.
447 func (t *chanConn) RemoteAddr() net.Addr {
448         return t.raddr
449 }
450
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 {
455                 return err
456         }
457         return t.SetWriteDeadline(deadline)
458 }
459
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")
468 }
469
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")
474 }