Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 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" |
Han-Wen Nienhuys | 2d394e3 | 2013-07-26 14:37:37 -0400 | [diff] [blame] | 11 | "math/rand" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 12 | "net" |
Han-Wen Nienhuys | 2d394e3 | 2013-07-26 14:37:37 -0400 | [diff] [blame] | 13 | "strconv" |
| 14 | "strings" |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 15 | "sync" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 16 | "time" |
| 17 | ) |
| 18 | |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 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) { |
Dave Cheney | b4b4222 | 2012-05-01 15:43:58 +1000 | [diff] [blame] | 24 | laddr, err := net.ResolveTCPAddr(n, addr) |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 25 | if err != nil { |
| 26 | return nil, err |
| 27 | } |
Dave Cheney | b4b4222 | 2012-05-01 15:43:58 +1000 | [diff] [blame] | 28 | return c.ListenTCP(laddr) |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 29 | } |
| 30 | |
Han-Wen Nienhuys | 2d394e3 | 2013-07-26 14:37:37 -0400 | [diff] [blame] | 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 | |
Han-Wen Nienhuys | 4dc7c8e | 2013-07-29 13:09:39 -0400 | [diff] [blame] | 40 | var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano())) |
| 41 | |
Han-Wen Nienhuys | 2d394e3 | 2013-07-26 14:37:37 -0400 | [diff] [blame] | 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. |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 63 | func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) { |
Han-Wen Nienhuys | 2d394e3 | 2013-07-26 14:37:37 -0400 | [diff] [blame] | 64 | var sshListener net.Listener |
| 65 | var err error |
| 66 | const tries = 10 |
| 67 | for i := 0; i < tries; i++ { |
| 68 | addr := *laddr |
Han-Wen Nienhuys | 4dc7c8e | 2013-07-29 13:09:39 -0400 | [diff] [blame] | 69 | addr.Port = 1024 + portRandomizer.Intn(60000) |
Han-Wen Nienhuys | 2d394e3 | 2013-07-26 14:37:37 -0400 | [diff] [blame] | 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 | |
Han-Wen Nienhuys | ed40a6c | 2013-10-16 14:24:28 +1100 | [diff] [blame] | 79 | // RFC 4254 7.1 |
| 80 | type channelForwardMsg struct { |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 81 | addr string |
| 82 | rport uint32 |
Han-Wen Nienhuys | ed40a6c | 2013-10-16 14:24:28 +1100 | [diff] [blame] | 83 | } |
| 84 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 85 | // ListenTCP requests the remote peer open a listening socket |
Dave Cheney | b4b4222 | 2012-05-01 15:43:58 +1000 | [diff] [blame] | 86 | // on laddr. Incoming connections will be available by calling |
| 87 | // Accept on the returned net.Listener. |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 88 | func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { |
| 89 | if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) { |
Han-Wen Nienhuys | 2d394e3 | 2013-07-26 14:37:37 -0400 | [diff] [blame] | 90 | return c.autoPortListenWorkaround(laddr) |
| 91 | } |
| 92 | |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 93 | m := channelForwardMsg{ |
Dave Cheney | b4b4222 | 2012-05-01 15:43:58 +1000 | [diff] [blame] | 94 | laddr.IP.String(), |
| 95 | uint32(laddr.Port), |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 96 | } |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 97 | // send message |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 98 | ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m)) |
Dave Cheney | b4b4222 | 2012-05-01 15:43:58 +1000 | [diff] [blame] | 99 | if err != nil { |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 100 | return nil, err |
| 101 | } |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 102 | if !ok { |
| 103 | return nil, errors.New("ssh: tcpip-forward request denied by peer") |
| 104 | } |
Han-Wen Nienhuys | 0d8dc3c | 2013-06-11 22:10:15 -0400 | [diff] [blame] | 105 | |
Han-Wen Nienhuys | 0d8dc3c | 2013-06-11 22:10:15 -0400 | [diff] [blame] | 106 | // If the original port was 0, then the remote side will |
| 107 | // supply a real port number in the response. |
Dave Cheney | b4b4222 | 2012-05-01 15:43:58 +1000 | [diff] [blame] | 108 | if laddr.Port == 0 { |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 109 | var p struct { |
| 110 | Port uint32 |
Dave Cheney | b4b4222 | 2012-05-01 15:43:58 +1000 | [diff] [blame] | 111 | } |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 112 | if err := Unmarshal(resp, &p); err != nil { |
| 113 | return nil, err |
| 114 | } |
| 115 | laddr.Port = int(p.Port) |
Dave Cheney | b4b4222 | 2012-05-01 15:43:58 +1000 | [diff] [blame] | 116 | } |
| 117 | |
Han-Wen Nienhuys | 7cbb17f | 2013-06-18 12:43:42 -0400 | [diff] [blame] | 118 | // Register this forward, using the port number we obtained. |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 119 | ch := c.forwards.add(*laddr) |
Han-Wen Nienhuys | 7cbb17f | 2013-06-18 12:43:42 -0400 | [diff] [blame] | 120 | |
| 121 | return &tcpListener{laddr, c, ch}, nil |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 122 | } |
| 123 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 124 | // forwardList stores a mapping between remote |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 125 | // forward requests and the tcpListeners. |
| 126 | type forwardList struct { |
| 127 | sync.Mutex |
| 128 | entries []forwardEntry |
| 129 | } |
| 130 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 131 | // forwardEntry represents an established mapping of a laddr on a |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 132 | // remote ssh server to a channel connected to a tcpListener. |
| 133 | type forwardEntry struct { |
Han-Wen Nienhuys | 0d8dc3c | 2013-06-11 22:10:15 -0400 | [diff] [blame] | 134 | laddr net.TCPAddr |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 135 | c chan forward |
| 136 | } |
| 137 | |
Han-Wen Nienhuys | 0d8dc3c | 2013-06-11 22:10:15 -0400 | [diff] [blame] | 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. |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 141 | type forward struct { |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 142 | newCh NewChannel // the ssh client channel underlying this forward |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 143 | raddr *net.TCPAddr // the raddr of the incoming connection |
| 144 | } |
| 145 | |
Han-Wen Nienhuys | 0d8dc3c | 2013-06-11 22:10:15 -0400 | [diff] [blame] | 146 | func (l *forwardList) add(addr net.TCPAddr) chan forward { |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 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 | |
Han-Wen Nienhuys | 72116d5 | 2014-09-16 12:34:31 -0700 | [diff] [blame] | 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 | |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 177 | func (l *forwardList) handleChannels(in <-chan NewChannel) { |
| 178 | for ch := range in { |
Han-Wen Nienhuys | 72116d5 | 2014-09-16 12:34:31 -0700 | [diff] [blame] | 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()) |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 182 | continue |
| 183 | } |
| 184 | |
Han-Wen Nienhuys | 72116d5 | 2014-09-16 12:34:31 -0700 | [diff] [blame] | 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()) |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 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 | |
Han-Wen Nienhuys | 7f7cbbf | 2013-07-22 21:50:13 +1000 | [diff] [blame] | 210 | // remove removes the forward entry, and the channel feeding its |
| 211 | // listener. |
Han-Wen Nienhuys | 0d8dc3c | 2013-06-11 22:10:15 -0400 | [diff] [blame] | 212 | func (l *forwardList) remove(addr net.TCPAddr) { |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 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:]...) |
Han-Wen Nienhuys | 7f7cbbf | 2013-07-22 21:50:13 +1000 | [diff] [blame] | 218 | close(f.c) |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 219 | return |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | |
Han-Wen Nienhuys | 7f7cbbf | 2013-07-22 21:50:13 +1000 | [diff] [blame] | 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 | |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 234 | func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool { |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 235 | l.Lock() |
| 236 | defer l.Unlock() |
| 237 | for _, f := range l.entries { |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 238 | if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port { |
| 239 | f.c <- forward{ch, &raddr} |
| 240 | return true |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 241 | } |
| 242 | } |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 243 | return false |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 244 | } |
| 245 | |
| 246 | type tcpListener struct { |
| 247 | laddr *net.TCPAddr |
Han-Wen Nienhuys | 0d8dc3c | 2013-06-11 22:10:15 -0400 | [diff] [blame] | 248 | |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 249 | conn *Client |
Han-Wen Nienhuys | 7cbb17f | 2013-06-18 12:43:42 -0400 | [diff] [blame] | 250 | in <-chan forward |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 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 | } |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 259 | ch, incoming, err := s.newCh.Accept() |
| 260 | if err != nil { |
| 261 | return nil, err |
| 262 | } |
| 263 | go DiscardRequests(incoming) |
| 264 | |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 265 | return &tcpChanConn{ |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 266 | Channel: ch, |
| 267 | laddr: l.laddr, |
| 268 | raddr: s.raddr, |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 269 | }, nil |
| 270 | } |
| 271 | |
| 272 | // Close closes the listener. |
| 273 | func (l *tcpListener) Close() error { |
| 274 | m := channelForwardMsg{ |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 275 | l.laddr.IP.String(), |
Han-Wen Nienhuys | 7cbb17f | 2013-06-18 12:43:42 -0400 | [diff] [blame] | 276 | uint32(l.laddr.Port), |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 277 | } |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 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") |
Dave Cheney | b4b4222 | 2012-05-01 15:43:58 +1000 | [diff] [blame] | 284 | } |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 285 | return err |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 286 | } |
| 287 | |
| 288 | // Addr returns the listener's network address. |
| 289 | func (l *tcpListener) Addr() net.Addr { |
| 290 | return l.laddr |
| 291 | } |
| 292 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 293 | // Dial initiates a connection to the addr from the remote host. |
JP Sugarbroad | a1beccb | 2013-08-28 17:51:56 -0400 | [diff] [blame] | 294 | // The resulting connection has a zero LocalAddr() and RemoteAddr(). |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 295 | func (c *Client) Dial(n, addr string) (net.Conn, error) { |
JP Sugarbroad | a1beccb | 2013-08-28 17:51:56 -0400 | [diff] [blame] | 296 | // Parse the address into host and numeric port. |
| 297 | host, portString, err := net.SplitHostPort(addr) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 298 | if err != nil { |
| 299 | return nil, err |
| 300 | } |
JP Sugarbroad | a1beccb | 2013-08-28 17:51:56 -0400 | [diff] [blame] | 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{ |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 315 | Channel: ch, |
JP Sugarbroad | a1beccb | 2013-08-28 17:51:56 -0400 | [diff] [blame] | 316 | laddr: zeroAddr, |
| 317 | raddr: zeroAddr, |
| 318 | }, nil |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 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. |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 324 | func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 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 | } |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 335 | return &tcpChanConn{ |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 336 | Channel: ch, |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 337 | laddr: laddr, |
| 338 | raddr: raddr, |
| 339 | }, nil |
| 340 | } |
| 341 | |
| 342 | // RFC 4254 7.2 |
| 343 | type channelOpenDirectMsg struct { |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 344 | raddr string |
| 345 | rport uint32 |
| 346 | laddr string |
| 347 | lport uint32 |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 348 | } |
| 349 | |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 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), |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 356 | } |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 357 | ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg)) |
Eric Garrido | 83f1503 | 2015-08-07 17:38:44 -0400 | [diff] [blame] | 358 | if err != nil { |
| 359 | return nil, err |
| 360 | } |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 361 | go DiscardRequests(in) |
| 362 | return ch, err |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 363 | } |
| 364 | |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 365 | type tcpChan struct { |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 366 | Channel // the backing channel |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 367 | } |
| 368 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 369 | // tcpChanConn fulfills the net.Conn interface without |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 370 | // the tcpChan having to hold laddr or raddr directly. |
| 371 | type tcpChanConn struct { |
Adam Langley | fa50e74 | 2014-04-09 13:57:52 -0700 | [diff] [blame] | 372 | Channel |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 373 | laddr, raddr net.Addr |
| 374 | } |
| 375 | |
| 376 | // LocalAddr returns the local network address. |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 377 | func (t *tcpChanConn) LocalAddr() net.Addr { |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 378 | return t.laddr |
| 379 | } |
| 380 | |
| 381 | // RemoteAddr returns the remote network address. |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 382 | func (t *tcpChanConn) RemoteAddr() net.Addr { |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 383 | return t.raddr |
| 384 | } |
| 385 | |
| 386 | // SetDeadline sets the read and write deadlines associated |
| 387 | // with the connection. |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 388 | func (t *tcpChanConn) SetDeadline(deadline time.Time) error { |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 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. |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 399 | func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { |
| 400 | return errors.New("ssh: tcpChan: deadline not supported") |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 401 | } |
| 402 | |
| 403 | // SetWriteDeadline exists to satisfy the net.Conn interface |
| 404 | // but is not implemented by this type. It always returns an error. |
Dave Cheney | b333fd1 | 2012-04-26 20:37:06 +1000 | [diff] [blame] | 405 | func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { |
| 406 | return errors.New("ssh: tcpChan: deadline not supported") |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 407 | } |