]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/golang.org/x/crypto/ssh/tcpip.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / 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 func (c *Client) Listen(n, addr string) (net.Listener, error) {
24 laddr, err := net.ResolveTCPAddr(n, addr)
25 if err != nil {
26 return nil, err
27 }
28 return c.ListenTCP(laddr)
29 }
30
31 // 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
38 const openSSHPrefix = "OpenSSH_"
39
40 var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano()))
41
42 // 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.
45 func 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.
63 func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) {
64 var sshListener net.Listener
65 var err error
66 const tries = 10
67 for i := 0; i < tries; i++ {
68 addr := *laddr
69 addr.Port = 1024 + portRandomizer.Intn(60000)
70 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
79 // RFC 4254 7.1
80 type channelForwardMsg struct {
81 addr string
82 rport uint32
83 }
84
85 // ListenTCP requests the remote peer open a listening socket
86 // on laddr. Incoming connections will be available by calling
87 // Accept on the returned net.Listener.
88 func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
89 if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
90 return c.autoPortListenWorkaround(laddr)
91 }
92
93 m := channelForwardMsg{
94 laddr.IP.String(),
95 uint32(laddr.Port),
96 }
97 // send message
98 ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
99 if err != nil {
100 return nil, err
101 }
102 if !ok {
103 return nil, errors.New("ssh: tcpip-forward request denied by peer")
104 }
105
106 // If the original port was 0, then the remote side will
107 // supply a real port number in the response.
108 if laddr.Port == 0 {
109 var p struct {
110 Port uint32
111 }
112 if err := Unmarshal(resp, &p); err != nil {
113 return nil, err
114 }
115 laddr.Port = int(p.Port)
116 }
117
118 // Register this forward, using the port number we obtained.
119 ch := c.forwards.add(*laddr)
120
121 return &tcpListener{laddr, c, ch}, nil
122 }
123
124 // forwardList stores a mapping between remote
125 // forward requests and the tcpListeners.
126 type forwardList struct {
127 sync.Mutex
128 entries []forwardEntry
129 }
130
131 // forwardEntry represents an established mapping of a laddr on a
132 // remote ssh server to a channel connected to a tcpListener.
133 type forwardEntry struct {
134 laddr net.TCPAddr
135 c chan forward
136 }
137
138 // 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.
141 type forward struct {
142 newCh NewChannel // the ssh client channel underlying this forward
143 raddr *net.TCPAddr // the raddr of the incoming connection
144 }
145
146 func (l *forwardList) add(addr net.TCPAddr) chan forward {
147 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
157 // See RFC 4254, section 7.2
158 type 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.
166 func 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
177 func (l *forwardList) handleChannels(in <-chan NewChannel) {
178 for ch := range in {
179 var payload forwardedTCPPayload
180 if err := Unmarshal(ch.ExtraData(), &payload); err != nil {
181 ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error())
182 continue
183 }
184
185 // 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())
198 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
210 // remove removes the forward entry, and the channel feeding its
211 // listener.
212 func (l *forwardList) remove(addr net.TCPAddr) {
213 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:]...)
218 close(f.c)
219 return
220 }
221 }
222 }
223
224 // closeAll closes and clears all forwards.
225 func (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
234 func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool {
235 l.Lock()
236 defer l.Unlock()
237 for _, f := range l.entries {
238 if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port {
239 f.c <- forward{ch, &raddr}
240 return true
241 }
242 }
243 return false
244 }
245
246 type tcpListener struct {
247 laddr *net.TCPAddr
248
249 conn *Client
250 in <-chan forward
251 }
252
253 // Accept waits for and returns the next connection to the listener.
254 func (l *tcpListener) Accept() (net.Conn, error) {
255 s, ok := <-l.in
256 if !ok {
257 return nil, io.EOF
258 }
259 ch, incoming, err := s.newCh.Accept()
260 if err != nil {
261 return nil, err
262 }
263 go DiscardRequests(incoming)
264
265 return &tcpChanConn{
266 Channel: ch,
267 laddr: l.laddr,
268 raddr: s.raddr,
269 }, nil
270 }
271
272 // Close closes the listener.
273 func (l *tcpListener) Close() error {
274 m := channelForwardMsg{
275 l.laddr.IP.String(),
276 uint32(l.laddr.Port),
277 }
278
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")
284 }
285 return err
286 }
287
288 // Addr returns the listener's network address.
289 func (l *tcpListener) Addr() net.Addr {
290 return l.laddr
291 }
292
293 // Dial initiates a connection to the addr from the remote host.
294 // The resulting connection has a zero LocalAddr() and RemoteAddr().
295 func (c *Client) Dial(n, addr string) (net.Conn, error) {
296 // Parse the address into host and numeric port.
297 host, portString, err := net.SplitHostPort(addr)
298 if err != nil {
299 return nil, err
300 }
301 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{
315 Channel: ch,
316 laddr: zeroAddr,
317 raddr: zeroAddr,
318 }, nil
319 }
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.
324 func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) {
325 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 }
335 return &tcpChanConn{
336 Channel: ch,
337 laddr: laddr,
338 raddr: raddr,
339 }, nil
340 }
341
342 // RFC 4254 7.2
343 type channelOpenDirectMsg struct {
344 raddr string
345 rport uint32
346 laddr string
347 lport uint32
348 }
349
350 func (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),
356 }
357 ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
358 if err != nil {
359 return nil, err
360 }
361 go DiscardRequests(in)
362 return ch, err
363 }
364
365 type tcpChan struct {
366 Channel // the backing channel
367 }
368
369 // tcpChanConn fulfills the net.Conn interface without
370 // the tcpChan having to hold laddr or raddr directly.
371 type tcpChanConn struct {
372 Channel
373 laddr, raddr net.Addr
374 }
375
376 // LocalAddr returns the local network address.
377 func (t *tcpChanConn) LocalAddr() net.Addr {
378 return t.laddr
379 }
380
381 // RemoteAddr returns the remote network address.
382 func (t *tcpChanConn) RemoteAddr() net.Addr {
383 return t.raddr
384 }
385
386 // SetDeadline sets the read and write deadlines associated
387 // with the connection.
388 func (t *tcpChanConn) SetDeadline(deadline time.Time) error {
389 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.
399 func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error {
400 return errors.New("ssh: tcpChan: deadline not supported")
401 }
402
403 // SetWriteDeadline exists to satisfy the net.Conn interface
404 // but is not implemented by this type. It always returns an error.
405 func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error {
406 return errors.New("ssh: tcpChan: deadline not supported")
407 }