LEFT | RIGHT |
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 "bytes" | 8 "bytes" |
9 "crypto/rand" | 9 "crypto/rand" |
10 "errors" | 10 "errors" |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 type cachedPubKey struct { | 88 type cachedPubKey struct { |
89 user, algo string | 89 user, algo string |
90 pubKey []byte | 90 pubKey []byte |
91 result bool | 91 result bool |
92 } | 92 } |
93 | 93 |
94 const maxCachedPubKeys = 16 | 94 const maxCachedPubKeys = 16 |
95 | 95 |
96 // A ServerConn represents an incoming connection. | 96 // A ServerConn represents an incoming connection. |
97 type ServerConn struct { | 97 type ServerConn struct { |
98 » *transport | 98 » transport *transport |
99 » config *ServerConfig | 99 » config *ServerConfig |
100 | 100 |
101 // cachedPubKeys contains the cache results of tests for public keys. | 101 // cachedPubKeys contains the cache results of tests for public keys. |
102 // Since SSH clients will query whether a public key is acceptable | 102 // Since SSH clients will query whether a public key is acceptable |
103 // before attempting to authenticate with it, we end up with duplicate | 103 // before attempting to authenticate with it, we end up with duplicate |
104 // queries for public key validity. | 104 // queries for public key validity. |
105 cachedPubKeys []cachedPubKey | 105 cachedPubKeys []cachedPubKey |
106 | 106 |
107 // User holds the successfully authenticated user name. | 107 // User holds the successfully authenticated user name. |
108 // It is empty if no authentication is used. It is populated before | 108 // It is empty if no authentication is used. It is populated before |
109 // any authentication callback is called and not assigned to after that. | 109 // any authentication callback is called and not assigned to after that. |
(...skipping 12 matching lines...) Expand all Loading... |
122 mux *mux | 122 mux *mux |
123 } | 123 } |
124 | 124 |
125 // Server returns a new SSH server connection | 125 // Server returns a new SSH server connection |
126 // using c as the underlying transport. | 126 // using c as the underlying transport. |
127 func Server(c net.Conn, config *ServerConfig) *ServerConn { | 127 func Server(c net.Conn, config *ServerConfig) *ServerConn { |
128 tr := newTransport(c, config.rand(), false /* not client */) | 128 tr := newTransport(c, config.rand(), false /* not client */) |
129 return &ServerConn{ | 129 return &ServerConn{ |
130 transport: tr, | 130 transport: tr, |
131 config: config, | 131 config: config, |
132 mux: newMux(tr), | |
133 } | 132 } |
134 } | 133 } |
135 | 134 |
136 // signAndMarshal signs the data with the appropriate algorithm, | 135 // signAndMarshal signs the data with the appropriate algorithm, |
137 // and serializes the result in SSH wire format. | 136 // and serializes the result in SSH wire format. |
138 func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) { | 137 func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) { |
139 sig, err := k.Sign(rand, data) | 138 sig, err := k.Sign(rand, data) |
140 if err != nil { | 139 if err != nil { |
141 return nil, err | 140 return nil, err |
142 } | 141 } |
143 | 142 |
144 return serializeSignature(k.PublicKey().PrivateKeyAlgo(), sig), nil | 143 return serializeSignature(k.PublicKey().PrivateKeyAlgo(), sig), nil |
145 } | 144 } |
| 145 |
| 146 // Close closes the connection. |
| 147 func (s *ServerConn) Close() error { return s.transport.Close() } |
| 148 |
| 149 // LocalAddr returns the local network address. |
| 150 func (c *ServerConn) LocalAddr() net.Addr { return c.transport.LocalAddr() } |
| 151 |
| 152 // RemoteAddr returns the remote network address. |
| 153 func (c *ServerConn) RemoteAddr() net.Addr { return c.transport.RemoteAddr() } |
146 | 154 |
147 // Handshake performs an SSH transport and client authentication on the given Se
rverConn. | 155 // Handshake performs an SSH transport and client authentication on the given Se
rverConn. |
148 func (s *ServerConn) Handshake() error { | 156 func (s *ServerConn) Handshake() error { |
149 var err error | 157 var err error |
150 s.serverVersion = []byte(packageVersion) | 158 s.serverVersion = []byte(packageVersion) |
151 s.ClientVersion, err = exchangeVersions(s.transport.Conn, s.serverVersio
n) | 159 s.ClientVersion, err = exchangeVersions(s.transport.Conn, s.serverVersio
n) |
152 if err != nil { | 160 if err != nil { |
153 return err | 161 return err |
154 } | 162 } |
155 if err := s.clientInitHandshake(nil, nil); err != nil { | 163 if err := s.clientInitHandshake(nil, nil); err != nil { |
156 return err | 164 return err |
157 } | 165 } |
158 | 166 |
159 var packet []byte | 167 var packet []byte |
160 » if packet, err = s.readPacket(); err != nil { | 168 » if packet, err = s.transport.readPacket(); err != nil { |
161 return err | 169 return err |
162 } | 170 } |
163 var serviceRequest serviceRequestMsg | 171 var serviceRequest serviceRequestMsg |
164 if err := unmarshal(&serviceRequest, packet, msgServiceRequest); err !=
nil { | 172 if err := unmarshal(&serviceRequest, packet, msgServiceRequest); err !=
nil { |
165 return err | 173 return err |
166 } | 174 } |
167 if serviceRequest.Service != serviceUserAuth { | 175 if serviceRequest.Service != serviceUserAuth { |
168 return errors.New("ssh: requested service '" + serviceRequest.Se
rvice + "' before authenticating") | 176 return errors.New("ssh: requested service '" + serviceRequest.Se
rvice + "' before authenticating") |
169 } | 177 } |
170 serviceAccept := serviceAcceptMsg{ | 178 serviceAccept := serviceAcceptMsg{ |
171 Service: serviceUserAuth, | 179 Service: serviceUserAuth, |
172 } | 180 } |
173 » if err := s.writePacket(marshal(msgServiceAccept, serviceAccept)); err !
= nil { | 181 » if err := s.transport.writePacket(marshal(msgServiceAccept, serviceAccep
t)); err != nil { |
174 » » return err | 182 » » return err |
175 » } | 183 » } |
176 | 184 |
177 » if err := s.authenticate(s.transport.sessionID); err != nil { | 185 » if err := s.authenticate(); err != nil { |
178 » » return err | 186 » » return err |
179 » } | 187 » } |
180 | 188 |
| 189 » s.mux = newMux(s.transport) |
| 190 » go s.handleGlobalRequests() |
181 go s.mux.Loop() | 191 go s.mux.Loop() |
182 go s.handleGlobalRequests() | |
183 return err | 192 return err |
184 } | 193 } |
185 | 194 |
186 func (s *ServerConn) handleGlobalRequests() { | 195 func (s *ServerConn) handleGlobalRequests() { |
187 » for r := range s.mux.IncomingRequests() { | 196 » for r := range s.mux.incomingRequests { |
188 if r.WantReply { | 197 if r.WantReply { |
189 s.mux.AckRequest(false, nil) | 198 s.mux.AckRequest(false, nil) |
190 } | 199 } |
191 } | 200 } |
192 } | 201 } |
193 | 202 |
194 func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexIni
tPacket []byte) (err error) { | 203 func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexIni
tPacket []byte) (err error) { |
195 serverKexInit := kexInitMsg{ | 204 serverKexInit := kexInitMsg{ |
196 KexAlgos: s.config.Crypto.kexes(), | 205 KexAlgos: s.config.Crypto.kexes(), |
197 CiphersClientServer: s.config.Crypto.ciphers(), | 206 CiphersClientServer: s.config.Crypto.ciphers(), |
198 CiphersServerClient: s.config.Crypto.ciphers(), | 207 CiphersServerClient: s.config.Crypto.ciphers(), |
199 MACsClientServer: s.config.Crypto.macs(), | 208 MACsClientServer: s.config.Crypto.macs(), |
200 MACsServerClient: s.config.Crypto.macs(), | 209 MACsServerClient: s.config.Crypto.macs(), |
201 CompressionClientServer: supportedCompressions, | 210 CompressionClientServer: supportedCompressions, |
202 CompressionServerClient: supportedCompressions, | 211 CompressionServerClient: supportedCompressions, |
203 } | 212 } |
204 for _, k := range s.config.hostKeys { | 213 for _, k := range s.config.hostKeys { |
205 serverKexInit.ServerHostKeyAlgos = append( | 214 serverKexInit.ServerHostKeyAlgos = append( |
206 serverKexInit.ServerHostKeyAlgos, k.PublicKey().PublicKe
yAlgo()) | 215 serverKexInit.ServerHostKeyAlgos, k.PublicKey().PublicKe
yAlgo()) |
207 } | 216 } |
208 | 217 |
209 serverKexInitPacket := marshal(msgKexInit, serverKexInit) | 218 serverKexInitPacket := marshal(msgKexInit, serverKexInit) |
210 » if err = s.writePacket(serverKexInitPacket); err != nil { | 219 » if err = s.transport.writePacket(serverKexInitPacket); err != nil { |
211 return | 220 return |
212 } | 221 } |
213 | 222 |
214 if clientKexInitPacket == nil { | 223 if clientKexInitPacket == nil { |
215 clientKexInit = new(kexInitMsg) | 224 clientKexInit = new(kexInitMsg) |
216 » » if clientKexInitPacket, err = s.readPacket(); err != nil { | 225 » » if clientKexInitPacket, err = s.transport.readPacket(); err != n
il { |
217 return | 226 return |
218 } | 227 } |
219 if err = unmarshal(clientKexInit, clientKexInitPacket, msgKexIni
t); err != nil { | 228 if err = unmarshal(clientKexInit, clientKexInitPacket, msgKexIni
t); err != nil { |
220 return | 229 return |
221 } | 230 } |
222 } | 231 } |
223 | 232 |
224 algs := findAgreedAlgorithms(clientKexInit, &serverKexInit) | 233 algs := findAgreedAlgorithms(clientKexInit, &serverKexInit) |
225 if algs == nil { | 234 if algs == nil { |
226 return errors.New("ssh: no common algorithms") | 235 return errors.New("ssh: no common algorithms") |
227 } | 236 } |
228 | 237 |
229 if clientKexInit.FirstKexFollows && algs.kex != clientKexInit.KexAlgos[0
] { | 238 if clientKexInit.FirstKexFollows && algs.kex != clientKexInit.KexAlgos[0
] { |
230 // The client sent a Kex message for the wrong algorithm, | 239 // The client sent a Kex message for the wrong algorithm, |
231 // which we have to ignore. | 240 // which we have to ignore. |
232 » » if _, err = s.readPacket(); err != nil { | 241 » » if _, err = s.transport.readPacket(); err != nil { |
233 return | 242 return |
234 } | 243 } |
235 } | 244 } |
236 | 245 |
237 var hostKey Signer | 246 var hostKey Signer |
238 for _, k := range s.config.hostKeys { | 247 for _, k := range s.config.hostKeys { |
239 if algs.hostKey == k.PublicKey().PublicKeyAlgo() { | 248 if algs.hostKey == k.PublicKey().PublicKeyAlgo() { |
240 hostKey = k | 249 hostKey = k |
241 } | 250 } |
242 } | 251 } |
243 | 252 |
244 kex, ok := kexAlgoMap[algs.kex] | 253 kex, ok := kexAlgoMap[algs.kex] |
245 if !ok { | 254 if !ok { |
246 return fmt.Errorf("ssh: unexpected key exchange algorithm %v", a
lgs.kex) | 255 return fmt.Errorf("ssh: unexpected key exchange algorithm %v", a
lgs.kex) |
247 } | 256 } |
248 | 257 |
249 magics := handshakeMagics{ | 258 magics := handshakeMagics{ |
250 serverVersion: s.serverVersion, | 259 serverVersion: s.serverVersion, |
251 clientVersion: s.ClientVersion, | 260 clientVersion: s.ClientVersion, |
252 serverKexInit: marshal(msgKexInit, serverKexInit), | 261 serverKexInit: marshal(msgKexInit, serverKexInit), |
253 clientKexInit: clientKexInitPacket, | 262 clientKexInit: clientKexInitPacket, |
254 } | 263 } |
255 » result, err := kex.Server(s, s.config.rand(), &magics, hostKey) | 264 » result, err := kex.Server(s.transport, s.config.rand(), &magics, hostKey
) |
256 if err != nil { | 265 if err != nil { |
257 return err | 266 return err |
258 } | 267 } |
259 | 268 |
260 if err = s.transport.prepareKeyChange(algs, result); err != nil { | 269 if err = s.transport.prepareKeyChange(algs, result); err != nil { |
261 return err | 270 return err |
262 } | 271 } |
263 | 272 |
264 » if err = s.writePacket([]byte{msgNewKeys}); err != nil { | 273 » if err = s.transport.writePacket([]byte{msgNewKeys}); err != nil { |
265 return | 274 return |
266 } | 275 } |
267 » if packet, err := s.readPacket(); err != nil { | 276 » if packet, err := s.transport.readPacket(); err != nil { |
268 return err | 277 return err |
269 } else if packet[0] != msgNewKeys { | 278 } else if packet[0] != msgNewKeys { |
270 return UnexpectedMessageError{msgNewKeys, packet[0]} | 279 return UnexpectedMessageError{msgNewKeys, packet[0]} |
271 } | 280 } |
272 | 281 |
273 return | 282 return |
274 } | 283 } |
275 | 284 |
276 func isAcceptableAlgo(algo string) bool { | 285 func isAcceptableAlgo(algo string) bool { |
277 switch algo { | 286 switch algo { |
(...skipping 24 matching lines...) Expand all Loading... |
302 pubKey: make([]byte, len(pubKey)), | 311 pubKey: make([]byte, len(pubKey)), |
303 result: result, | 312 result: result, |
304 } | 313 } |
305 copy(c.pubKey, pubKey) | 314 copy(c.pubKey, pubKey) |
306 s.cachedPubKeys = append(s.cachedPubKeys, c) | 315 s.cachedPubKeys = append(s.cachedPubKeys, c) |
307 } | 316 } |
308 | 317 |
309 return result | 318 return result |
310 } | 319 } |
311 | 320 |
312 func (s *ServerConn) authenticate(H []byte) error { | 321 func (s *ServerConn) authenticate() error { |
313 var userAuthReq userAuthRequestMsg | 322 var userAuthReq userAuthRequestMsg |
314 var err error | 323 var err error |
315 var packet []byte | 324 var packet []byte |
316 | 325 |
317 userAuthLoop: | 326 userAuthLoop: |
318 for { | 327 for { |
319 » » if packet, err = s.readPacket(); err != nil { | 328 » » if packet, err = s.transport.readPacket(); err != nil { |
320 return err | 329 return err |
321 } | 330 } |
322 if err = unmarshal(&userAuthReq, packet, msgUserAuthRequest); er
r != nil { | 331 if err = unmarshal(&userAuthReq, packet, msgUserAuthRequest); er
r != nil { |
323 return err | 332 return err |
324 } | 333 } |
325 | 334 |
326 if userAuthReq.Service != serviceSSH { | 335 if userAuthReq.Service != serviceSSH { |
327 return errors.New("ssh: client attempted to negotiate fo
r unknown service: " + userAuthReq.Service) | 336 return errors.New("ssh: client attempted to negotiate fo
r unknown service: " + userAuthReq.Service) |
328 } | 337 } |
329 | 338 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 // The client can query if the given public key | 392 // The client can query if the given public key |
384 // would be okay. | 393 // would be okay. |
385 if len(payload) > 0 { | 394 if len(payload) > 0 { |
386 return ParseError{msgUserAuthRequest} | 395 return ParseError{msgUserAuthRequest} |
387 } | 396 } |
388 if s.testPubKey(userAuthReq.User, algo, pubKey)
{ | 397 if s.testPubKey(userAuthReq.User, algo, pubKey)
{ |
389 okMsg := userAuthPubKeyOkMsg{ | 398 okMsg := userAuthPubKeyOkMsg{ |
390 Algo: algo, | 399 Algo: algo, |
391 PubKey: string(pubKey), | 400 PubKey: string(pubKey), |
392 } | 401 } |
393 » » » » » if err = s.writePacket(marshal(msgUserAu
thPubKeyOk, okMsg)); err != nil { | 402 » » » » » if err = s.transport.writePacket(marshal
(msgUserAuthPubKeyOk, okMsg)); err != nil { |
394 return err | 403 return err |
395 } | 404 } |
396 continue userAuthLoop | 405 continue userAuthLoop |
397 } | 406 } |
398 } else { | 407 } else { |
399 sig, payload, ok := parseSignature(payload) | 408 sig, payload, ok := parseSignature(payload) |
400 if !ok || len(payload) > 0 { | 409 if !ok || len(payload) > 0 { |
401 return ParseError{msgUserAuthRequest} | 410 return ParseError{msgUserAuthRequest} |
402 } | 411 } |
403 // Ensure the public key algo and signature algo | 412 // Ensure the public key algo and signature algo |
404 // are supported. Compare the private key | 413 // are supported. Compare the private key |
405 // algorithm name that corresponds to algo with | 414 // algorithm name that corresponds to algo with |
406 // sig.Format. This is usually the same, but | 415 // sig.Format. This is usually the same, but |
407 // for certs, the names differ. | 416 // for certs, the names differ. |
408 if !isAcceptableAlgo(algo) || !isAcceptableAlgo(
sig.Format) || pubAlgoToPrivAlgo(algo) != sig.Format { | 417 if !isAcceptableAlgo(algo) || !isAcceptableAlgo(
sig.Format) || pubAlgoToPrivAlgo(algo) != sig.Format { |
409 break | 418 break |
410 } | 419 } |
411 » » » » signedData := buildDataSignedForAuth(H, userAuth
Req, algoBytes, pubKey) | 420 » » » » signedData := buildDataSignedForAuth(s.transport
.sessionID, userAuthReq, algoBytes, pubKey) |
412 key, _, ok := ParsePublicKey(pubKey) | 421 key, _, ok := ParsePublicKey(pubKey) |
413 if !ok { | 422 if !ok { |
414 return ParseError{msgUserAuthRequest} | 423 return ParseError{msgUserAuthRequest} |
415 } | 424 } |
416 | 425 |
417 if !key.Verify(signedData, sig.Blob) { | 426 if !key.Verify(signedData, sig.Blob) { |
418 return ParseError{msgUserAuthRequest} | 427 return ParseError{msgUserAuthRequest} |
419 } | 428 } |
420 // TODO(jmpittman): Implement full validation fo
r certificates. | 429 // TODO(jmpittman): Implement full validation fo
r certificates. |
421 s.User = userAuthReq.User | 430 s.User = userAuthReq.User |
(...skipping 11 matching lines...) Expand all Loading... |
433 failureMsg.Methods = append(failureMsg.Methods, "publick
ey") | 442 failureMsg.Methods = append(failureMsg.Methods, "publick
ey") |
434 } | 443 } |
435 if s.config.KeyboardInteractiveCallback != nil { | 444 if s.config.KeyboardInteractiveCallback != nil { |
436 failureMsg.Methods = append(failureMsg.Methods, "keyboar
d-interactive") | 445 failureMsg.Methods = append(failureMsg.Methods, "keyboar
d-interactive") |
437 } | 446 } |
438 | 447 |
439 if len(failureMsg.Methods) == 0 { | 448 if len(failureMsg.Methods) == 0 { |
440 return errors.New("ssh: no authentication methods config
ured but NoClientAuth is also false") | 449 return errors.New("ssh: no authentication methods config
ured but NoClientAuth is also false") |
441 } | 450 } |
442 | 451 |
443 » » if err = s.writePacket(marshal(msgUserAuthFailure, failureMsg));
err != nil { | 452 » » if err = s.transport.writePacket(marshal(msgUserAuthFailure, fai
lureMsg)); err != nil { |
444 return err | 453 return err |
445 } | 454 } |
446 } | 455 } |
447 | 456 |
448 packet = []byte{msgUserAuthSuccess} | 457 packet = []byte{msgUserAuthSuccess} |
449 » if err = s.writePacket(packet); err != nil { | 458 » if err = s.transport.writePacket(packet); err != nil { |
450 return err | 459 return err |
451 } | 460 } |
452 | 461 |
453 return nil | 462 return nil |
454 } | 463 } |
455 | 464 |
456 // sshClientKeyboardInteractive implements a ClientKeyboardInteractive by | 465 // sshClientKeyboardInteractive implements a ClientKeyboardInteractive by |
457 // asking the client on the other side of a ServerConn. | 466 // asking the client on the other side of a ServerConn. |
458 type sshClientKeyboardInteractive struct { | 467 type sshClientKeyboardInteractive struct { |
459 *ServerConn | 468 *ServerConn |
460 } | 469 } |
461 | 470 |
462 func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, quest
ions []string, echos []bool) (answers []string, err error) { | 471 func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, quest
ions []string, echos []bool) (answers []string, err error) { |
463 if len(questions) != len(echos) { | 472 if len(questions) != len(echos) { |
464 return nil, errors.New("ssh: echos and questions must have equal
length") | 473 return nil, errors.New("ssh: echos and questions must have equal
length") |
465 } | 474 } |
466 | 475 |
467 var prompts []byte | 476 var prompts []byte |
468 for i := range questions { | 477 for i := range questions { |
469 prompts = appendString(prompts, questions[i]) | 478 prompts = appendString(prompts, questions[i]) |
470 prompts = appendBool(prompts, echos[i]) | 479 prompts = appendBool(prompts, echos[i]) |
471 } | 480 } |
472 | 481 |
473 » if err := c.writePacket(marshal(msgUserAuthInfoRequest, userAuthInfoRequ
estMsg{ | 482 » if err := c.transport.writePacket(marshal(msgUserAuthInfoRequest, userAu
thInfoRequestMsg{ |
474 Instruction: instruction, | 483 Instruction: instruction, |
475 NumPrompts: uint32(len(questions)), | 484 NumPrompts: uint32(len(questions)), |
476 Prompts: prompts, | 485 Prompts: prompts, |
477 })); err != nil { | 486 })); err != nil { |
478 return nil, err | 487 return nil, err |
479 } | 488 } |
480 | 489 |
481 » packet, err := c.readPacket() | 490 » packet, err := c.transport.readPacket() |
482 if err != nil { | 491 if err != nil { |
483 return nil, err | 492 return nil, err |
484 } | 493 } |
485 if packet[0] != msgUserAuthInfoResponse { | 494 if packet[0] != msgUserAuthInfoResponse { |
486 return nil, UnexpectedMessageError{msgUserAuthInfoResponse, pack
et[0]} | 495 return nil, UnexpectedMessageError{msgUserAuthInfoResponse, pack
et[0]} |
487 } | 496 } |
488 packet = packet[1:] | 497 packet = packet[1:] |
489 | 498 |
490 n, packet, ok := parseUint32(packet) | 499 n, packet, ok := parseUint32(packet) |
491 if !ok || int(n) != len(questions) { | 500 if !ok || int(n) != len(questions) { |
(...skipping 13 matching lines...) Expand all Loading... |
505 return nil, errors.New("ssh: junk at end of message") | 514 return nil, errors.New("ssh: junk at end of message") |
506 } | 515 } |
507 | 516 |
508 return answers, nil | 517 return answers, nil |
509 } | 518 } |
510 | 519 |
511 const defaultWindowSize = 32768 | 520 const defaultWindowSize = 32768 |
512 | 521 |
513 // Accept reads and processes messages on a ServerConn. It must be called | 522 // Accept reads and processes messages on a ServerConn. It must be called |
514 // in order to demultiplex messages to any resulting Channels. | 523 // in order to demultiplex messages to any resulting Channels. |
515 func (s *ServerConn) Accept() (ChannelCreationRequest, error) { | 524 func (s *ServerConn) Accept() (Channel, error) { |
516 » ch, ok := <-s.mux.IncomingChannels() | 525 » in, ok := <-s.mux.incomingChannels |
517 if !ok { | 526 if !ok { |
518 return nil, io.EOF | 527 return nil, io.EOF |
519 } | 528 } |
520 » return ch, nil | 529 » return newCompatChannel(in), nil |
521 } | 530 } |
522 | 531 |
523 // A Listener implements a network listener (net.Listener) for SSH connections. | 532 // A Listener implements a network listener (net.Listener) for SSH connections. |
524 type Listener struct { | 533 type Listener struct { |
525 listener net.Listener | 534 listener net.Listener |
526 config *ServerConfig | 535 config *ServerConfig |
527 } | 536 } |
528 | 537 |
529 // Addr returns the listener's network address. | 538 // Addr returns the listener's network address. |
530 func (l *Listener) Addr() net.Addr { | 539 func (l *Listener) Addr() net.Addr { |
(...skipping 21 matching lines...) Expand all Loading... |
552 func Listen(network, addr string, config *ServerConfig) (*Listener, error) { | 561 func Listen(network, addr string, config *ServerConfig) (*Listener, error) { |
553 l, err := net.Listen(network, addr) | 562 l, err := net.Listen(network, addr) |
554 if err != nil { | 563 if err != nil { |
555 return nil, err | 564 return nil, err |
556 } | 565 } |
557 return &Listener{ | 566 return &Listener{ |
558 l, | 567 l, |
559 config, | 568 config, |
560 }, nil | 569 }, nil |
561 } | 570 } |
LEFT | RIGHT |