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
5package ssh
6
7import (
8 "crypto"
9 "crypto/fips140"
10 "crypto/rand"
11 "fmt"
12 "io"
13 "math"
14 "slices"
15 "sync"
16
17 _ "crypto/sha1"
18 _ "crypto/sha256"
19 _ "crypto/sha512"
20)
21
22// These are string constants in the SSH protocol.
23const (
24 compressionNone = "none"
25 serviceUserAuth = "ssh-userauth"
26 serviceSSH = "ssh-connection"
27)
28
29// The ciphers currently or previously implemented by this library, to use in
30// [Config.Ciphers]. For a list, see the [Algorithms.Ciphers] returned by
31// [SupportedAlgorithms] or [InsecureAlgorithms].
32const (
33 CipherAES128GCM = "aes128-gcm@openssh.com"
34 CipherAES256GCM = "aes256-gcm@openssh.com"
35 CipherChaCha20Poly1305 = "chacha20-poly1305@openssh.com"
36 CipherAES128CTR = "aes128-ctr"
37 CipherAES192CTR = "aes192-ctr"
38 CipherAES256CTR = "aes256-ctr"
39 InsecureCipherAES128CBC = "aes128-cbc"
40 InsecureCipherTripleDESCBC = "3des-cbc"
41 InsecureCipherRC4 = "arcfour"
42 InsecureCipherRC4128 = "arcfour128"
43 InsecureCipherRC4256 = "arcfour256"
44)
45
46// The key exchanges currently or previously implemented by this library, to use
47// in [Config.KeyExchanges]. For a list, see the
48// [Algorithms.KeyExchanges] returned by [SupportedAlgorithms] or
49// [InsecureAlgorithms].
50const (
51 InsecureKeyExchangeDH1SHA1 = "diffie-hellman-group1-sha1"
52 InsecureKeyExchangeDH14SHA1 = "diffie-hellman-group14-sha1"
53 KeyExchangeDH14SHA256 = "diffie-hellman-group14-sha256"
54 KeyExchangeDH16SHA512 = "diffie-hellman-group16-sha512"
55 KeyExchangeECDHP256 = "ecdh-sha2-nistp256"
56 KeyExchangeECDHP384 = "ecdh-sha2-nistp384"
57 KeyExchangeECDHP521 = "ecdh-sha2-nistp521"
58 KeyExchangeCurve25519 = "curve25519-sha256"
59 InsecureKeyExchangeDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
60 KeyExchangeDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
61 // KeyExchangeMLKEM768X25519 is supported from Go 1.24.
62 KeyExchangeMLKEM768X25519 = "mlkem768x25519-sha256"
63
64 // An alias for KeyExchangeCurve25519SHA256. This kex ID will be added if
65 // KeyExchangeCurve25519SHA256 is requested for backward compatibility with
66 // OpenSSH versions up to 7.2.
67 keyExchangeCurve25519LibSSH = "curve25519-sha256@libssh.org"
68)
69
70// The message authentication code (MAC) currently or previously implemented by
71// this library, to use in [Config.MACs]. For a list, see the
72// [Algorithms.MACs] returned by [SupportedAlgorithms] or
73// [InsecureAlgorithms].
74const (
75 HMACSHA256ETM = "hmac-sha2-256-etm@openssh.com"
76 HMACSHA512ETM = "hmac-sha2-512-etm@openssh.com"
77 HMACSHA256 = "hmac-sha2-256"
78 HMACSHA512 = "hmac-sha2-512"
79 HMACSHA1 = "hmac-sha1"
80 InsecureHMACSHA196 = "hmac-sha1-96"
81)
82
83var (
84 // supportedKexAlgos specifies key-exchange algorithms implemented by this
85 // package in preference order, excluding those with security issues.
86 supportedKexAlgos = []string{
87 KeyExchangeMLKEM768X25519,
88 KeyExchangeCurve25519,
89 KeyExchangeECDHP256,
90 KeyExchangeECDHP384,
91 KeyExchangeECDHP521,
92 KeyExchangeDH14SHA256,
93 KeyExchangeDH16SHA512,
94 KeyExchangeDHGEXSHA256,
95 }
96 // defaultKexAlgos specifies the default preference for key-exchange
97 // algorithms in preference order.
98 defaultKexAlgos = []string{
99 KeyExchangeMLKEM768X25519,
100 KeyExchangeCurve25519,
101 KeyExchangeECDHP256,
102 KeyExchangeECDHP384,
103 KeyExchangeECDHP521,
104 KeyExchangeDH14SHA256,
105 InsecureKeyExchangeDH14SHA1,
106 }
107 // insecureKexAlgos specifies key-exchange algorithms implemented by this
108 // package and which have security issues.
109 insecureKexAlgos = []string{
110 InsecureKeyExchangeDH14SHA1,
111 InsecureKeyExchangeDH1SHA1,
112 InsecureKeyExchangeDHGEXSHA1,
113 }
114 // supportedCiphers specifies cipher algorithms implemented by this package
115 // in preference order, excluding those with security issues.
116 supportedCiphers = []string{
117 CipherAES128GCM,
118 CipherAES256GCM,
119 CipherChaCha20Poly1305,
120 CipherAES128CTR,
121 CipherAES192CTR,
122 CipherAES256CTR,
123 }
124 // defaultCiphers specifies the default preference for ciphers algorithms
125 // in preference order.
126 defaultCiphers = supportedCiphers
127 // insecureCiphers specifies cipher algorithms implemented by this
128 // package and which have security issues.
129 insecureCiphers = []string{
130 InsecureCipherAES128CBC,
131 InsecureCipherTripleDESCBC,
132 InsecureCipherRC4256,
133 InsecureCipherRC4128,
134 InsecureCipherRC4,
135 }
136 // supportedMACs specifies MAC algorithms implemented by this package in
137 // preference order, excluding those with security issues.
138 supportedMACs = []string{
139 HMACSHA256ETM,
140 HMACSHA512ETM,
141 HMACSHA256,
142 HMACSHA512,
143 HMACSHA1,
144 }
145 // defaultMACs specifies the default preference for MAC algorithms in
146 // preference order.
147 defaultMACs = []string{
148 HMACSHA256ETM,
149 HMACSHA512ETM,
150 HMACSHA256,
151 HMACSHA512,
152 HMACSHA1,
153 InsecureHMACSHA196,
154 }
155 // insecureMACs specifies MAC algorithms implemented by this
156 // package and which have security issues.
157 insecureMACs = []string{
158 InsecureHMACSHA196,
159 }
160 // supportedHostKeyAlgos specifies the supported host-key algorithms (i.e.
161 // methods of authenticating servers) implemented by this package in
162 // preference order, excluding those with security issues.
163 supportedHostKeyAlgos = []string{
164 CertAlgoRSASHA256v01,
165 CertAlgoRSASHA512v01,
166 CertAlgoECDSA256v01,
167 CertAlgoECDSA384v01,
168 CertAlgoECDSA521v01,
169 CertAlgoED25519v01,
170 KeyAlgoRSASHA256,
171 KeyAlgoRSASHA512,
172 KeyAlgoECDSA256,
173 KeyAlgoECDSA384,
174 KeyAlgoECDSA521,
175 KeyAlgoED25519,
176 }
177 // defaultHostKeyAlgos specifies the default preference for host-key
178 // algorithms in preference order.
179 defaultHostKeyAlgos = []string{
180 CertAlgoRSASHA256v01,
181 CertAlgoRSASHA512v01,
182 CertAlgoRSAv01,
183 InsecureCertAlgoDSAv01,
184 CertAlgoECDSA256v01,
185 CertAlgoECDSA384v01,
186 CertAlgoECDSA521v01,
187 CertAlgoED25519v01,
188 KeyAlgoECDSA256,
189 KeyAlgoECDSA384,
190 KeyAlgoECDSA521,
191 KeyAlgoRSASHA256,
192 KeyAlgoRSASHA512,
193 KeyAlgoRSA,
194 InsecureKeyAlgoDSA,
195 KeyAlgoED25519,
196 }
197 // insecureHostKeyAlgos specifies host-key algorithms implemented by this
198 // package and which have security issues.
199 insecureHostKeyAlgos = []string{
200 KeyAlgoRSA,
201 InsecureKeyAlgoDSA,
202 CertAlgoRSAv01,
203 InsecureCertAlgoDSAv01,
204 }
205 // supportedPubKeyAuthAlgos specifies the supported client public key
206 // authentication algorithms. Note that this doesn't include certificate
207 // types since those use the underlying algorithm. Order is irrelevant.
208 supportedPubKeyAuthAlgos = []string{
209 KeyAlgoED25519,
210 KeyAlgoSKED25519,
211 KeyAlgoSKECDSA256,
212 KeyAlgoECDSA256,
213 KeyAlgoECDSA384,
214 KeyAlgoECDSA521,
215 KeyAlgoRSASHA256,
216 KeyAlgoRSASHA512,
217 }
218
219 // defaultPubKeyAuthAlgos specifies the preferred client public key
220 // authentication algorithms. This list is sent to the client if it supports
221 // the server-sig-algs extension. Order is irrelevant.
222 defaultPubKeyAuthAlgos = []string{
223 KeyAlgoED25519,
224 KeyAlgoSKED25519,
225 KeyAlgoSKECDSA256,
226 KeyAlgoECDSA256,
227 KeyAlgoECDSA384,
228 KeyAlgoECDSA521,
229 KeyAlgoRSASHA256,
230 KeyAlgoRSASHA512,
231 KeyAlgoRSA,
232 InsecureKeyAlgoDSA,
233 }
234 // insecurePubKeyAuthAlgos specifies client public key authentication
235 // algorithms implemented by this package and which have security issues.
236 insecurePubKeyAuthAlgos = []string{
237 KeyAlgoRSA,
238 InsecureKeyAlgoDSA,
239 }
240)
241
242// NegotiatedAlgorithms defines algorithms negotiated between client and server.
243type NegotiatedAlgorithms struct {
244 KeyExchange string
245 HostKey string
246 Read DirectionAlgorithms
247 Write DirectionAlgorithms
248}
249
250// Algorithms defines a set of algorithms that can be configured in the client
251// or server config for negotiation during a handshake.
252type Algorithms struct {
253 KeyExchanges []string
254 Ciphers []string
255 MACs []string
256 HostKeys []string
257 PublicKeyAuths []string
258}
259
260func init() {
261 if fips140.Enabled() {
262 defaultHostKeyAlgos = slices.DeleteFunc(defaultHostKeyAlgos, func(algo string) bool {
263 _, err := hashFunc(underlyingAlgo(algo))
264 return err != nil
265 })
266 defaultPubKeyAuthAlgos = slices.DeleteFunc(defaultPubKeyAuthAlgos, func(algo string) bool {
267 _, err := hashFunc(underlyingAlgo(algo))
268 return err != nil
269 })
270 }
271}
272
273func hashFunc(format string) (crypto.Hash, error) {
274 switch format {
275 case KeyAlgoRSASHA256, KeyAlgoECDSA256, KeyAlgoSKED25519, KeyAlgoSKECDSA256:
276 return crypto.SHA256, nil
277 case KeyAlgoECDSA384:
278 return crypto.SHA384, nil
279 case KeyAlgoRSASHA512, KeyAlgoECDSA521:
280 return crypto.SHA512, nil
281 case KeyAlgoED25519:
282 // KeyAlgoED25519 doesn't pre-hash.
283 return 0, nil
284 case KeyAlgoRSA, InsecureKeyAlgoDSA:
285 if fips140.Enabled() {
286 return 0, fmt.Errorf("ssh: hash algorithm for format %q not allowed in FIPS 140 mode", format)
287 }
288 return crypto.SHA1, nil
289 default:
290 return 0, fmt.Errorf("ssh: hash algorithm for format %q not mapped", format)
291 }
292}
293
294// SupportedAlgorithms returns algorithms currently implemented by this package,
295// excluding those with security issues, which are returned by
296// InsecureAlgorithms. The algorithms listed here are in preference order.
297func SupportedAlgorithms() Algorithms {
298 return Algorithms{
299 Ciphers: slices.Clone(supportedCiphers),
300 MACs: slices.Clone(supportedMACs),
301 KeyExchanges: slices.Clone(supportedKexAlgos),
302 HostKeys: slices.Clone(supportedHostKeyAlgos),
303 PublicKeyAuths: slices.Clone(supportedPubKeyAuthAlgos),
304 }
305}
306
307// InsecureAlgorithms returns algorithms currently implemented by this package
308// and which have security issues.
309func InsecureAlgorithms() Algorithms {
310 return Algorithms{
311 KeyExchanges: slices.Clone(insecureKexAlgos),
312 Ciphers: slices.Clone(insecureCiphers),
313 MACs: slices.Clone(insecureMACs),
314 HostKeys: slices.Clone(insecureHostKeyAlgos),
315 PublicKeyAuths: slices.Clone(insecurePubKeyAuthAlgos),
316 }
317}
318
319var supportedCompressions = []string{compressionNone}
320
321// algorithmsForKeyFormat returns the supported signature algorithms for a given
322// public key format (PublicKey.Type), in order of preference. See RFC 8332,
323// Section 2. See also the note in sendKexInit on backwards compatibility.
324func algorithmsForKeyFormat(keyFormat string) []string {
325 switch keyFormat {
326 case KeyAlgoRSA:
327 return []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA}
328 case CertAlgoRSAv01:
329 return []string{CertAlgoRSASHA256v01, CertAlgoRSASHA512v01, CertAlgoRSAv01}
330 default:
331 return []string{keyFormat}
332 }
333}
334
335// keyFormatForAlgorithm returns the key format corresponding to the given
336// signature algorithm. It returns an empty string if the signature algorithm is
337// invalid or unsupported.
338func keyFormatForAlgorithm(sigAlgo string) string {
339 switch sigAlgo {
340 case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512:
341 return KeyAlgoRSA
342 case CertAlgoRSAv01, CertAlgoRSASHA256v01, CertAlgoRSASHA512v01:
343 return CertAlgoRSAv01
344 case KeyAlgoED25519,
345 KeyAlgoSKED25519,
346 KeyAlgoSKECDSA256,
347 KeyAlgoECDSA256,
348 KeyAlgoECDSA384,
349 KeyAlgoECDSA521,
350 InsecureKeyAlgoDSA,
351 InsecureCertAlgoDSAv01,
352 CertAlgoECDSA256v01,
353 CertAlgoECDSA384v01,
354 CertAlgoECDSA521v01,
355 CertAlgoSKECDSA256v01,
356 CertAlgoED25519v01,
357 CertAlgoSKED25519v01:
358 return sigAlgo
359 default:
360 return ""
361 }
362}
363
364// isRSA returns whether algo is a supported RSA algorithm, including certificate
365// algorithms.
366func isRSA(algo string) bool {
367 algos := algorithmsForKeyFormat(KeyAlgoRSA)
368 return slices.Contains(algos, underlyingAlgo(algo))
369}
370
371func isRSACert(algo string) bool {
372 _, ok := certKeyAlgoNames[algo]
373 if !ok {
374 return false
375 }
376 return isRSA(algo)
377}
378
379// unexpectedMessageError results when the SSH message that we received didn't
380// match what we wanted.
381func unexpectedMessageError(expected, got uint8) error {
382 return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
383}
384
385// parseError results from a malformed SSH message.
386func parseError(tag uint8) error {
387 return fmt.Errorf("ssh: parse error in message type %d", tag)
388}
389
390func findCommon(what string, client []string, server []string, isClient bool) (string, error) {
391 for _, c := range client {
392 for _, s := range server {
393 if c == s {
394 return c, nil
395 }
396 }
397 }
398 err := &AlgorithmNegotiationError{
399 What: what,
400 }
401 if isClient {
402 err.SupportedAlgorithms = client
403 err.RequestedAlgorithms = server
404 } else {
405 err.SupportedAlgorithms = server
406 err.RequestedAlgorithms = client
407 }
408 return "", err
409}
410
411// AlgorithmNegotiationError defines the error returned if the client and the
412// server cannot agree on an algorithm for key exchange, host key, cipher, MAC.
413type AlgorithmNegotiationError struct {
414 What string
415 // RequestedAlgorithms lists the algorithms supported by the peer.
416 RequestedAlgorithms []string
417 // SupportedAlgorithms lists the algorithms supported on our side.
418 SupportedAlgorithms []string
419}
420
421func (a *AlgorithmNegotiationError) Error() string {
422 return fmt.Sprintf("ssh: no common algorithm for %s; we offered: %v, peer offered: %v",
423 a.What, a.SupportedAlgorithms, a.RequestedAlgorithms)
424}
425
426// DirectionAlgorithms defines the algorithms negotiated in one direction
427// (either read or write).
428type DirectionAlgorithms struct {
429 Cipher string
430 MAC string
431 compression string
432}
433
434// rekeyBytes returns a rekeying intervals in bytes.
435func (a *DirectionAlgorithms) rekeyBytes() int64 {
436 // According to RFC 4344 block ciphers should rekey after
437 // 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
438 // 128.
439 switch a.Cipher {
440 case CipherAES128CTR, CipherAES192CTR, CipherAES256CTR, CipherAES128GCM, CipherAES256GCM, InsecureCipherAES128CBC:
441 return 16 * (1 << 32)
442
443 }
444
445 // For others, stick with RFC 4253 recommendation to rekey after 1 Gb of data.
446 return 1 << 30
447}
448
449var aeadCiphers = map[string]bool{
450 CipherAES128GCM: true,
451 CipherAES256GCM: true,
452 CipherChaCha20Poly1305: true,
453}
454
455func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *NegotiatedAlgorithms, err error) {
456 result := &NegotiatedAlgorithms{}
457
458 result.KeyExchange, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos, isClient)
459 if err != nil {
460 return
461 }
462
463 result.HostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos, isClient)
464 if err != nil {
465 return
466 }
467
468 stoc, ctos := &result.Write, &result.Read
469 if isClient {
470 ctos, stoc = stoc, ctos
471 }
472
473 ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer, isClient)
474 if err != nil {
475 return
476 }
477
478 stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient, isClient)
479 if err != nil {
480 return
481 }
482
483 if !aeadCiphers[ctos.Cipher] {
484 ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer, isClient)
485 if err != nil {
486 return
487 }
488 }
489
490 if !aeadCiphers[stoc.Cipher] {
491 stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient, isClient)
492 if err != nil {
493 return
494 }
495 }
496
497 ctos.compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer, isClient)
498 if err != nil {
499 return
500 }
501
502 stoc.compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient, isClient)
503 if err != nil {
504 return
505 }
506
507 return result, nil
508}
509
510// If rekeythreshold is too small, we can't make any progress sending
511// stuff.
512const minRekeyThreshold uint64 = 256
513
514// Config contains configuration data common to both ServerConfig and
515// ClientConfig.
516type Config struct {
517 // Rand provides the source of entropy for cryptographic
518 // primitives. If Rand is nil, the cryptographic random reader
519 // in package crypto/rand will be used.
520 Rand io.Reader
521
522 // The maximum number of bytes sent or received after which a
523 // new key is negotiated. It must be at least 256. If
524 // unspecified, a size suitable for the chosen cipher is used.
525 RekeyThreshold uint64
526
527 // The allowed key exchanges algorithms. If unspecified then a default set
528 // of algorithms is used. Unsupported values are silently ignored.
529 KeyExchanges []string
530
531 // The allowed cipher algorithms. If unspecified then a sensible default is
532 // used. Unsupported values are silently ignored.
533 Ciphers []string
534
535 // The allowed MAC algorithms. If unspecified then a sensible default is
536 // used. Unsupported values are silently ignored.
537 MACs []string
538}
539
540// SetDefaults sets sensible values for unset fields in config. This is
541// exported for testing: Configs passed to SSH functions are copied and have
542// default values set automatically.
543func (c *Config) SetDefaults() {
544 if c.Rand == nil {
545 c.Rand = rand.Reader
546 }
547 if c.Ciphers == nil {
548 c.Ciphers = defaultCiphers
549 }
550 var ciphers []string
551 for _, c := range c.Ciphers {
552 if cipherModes[c] != nil {
553 // Ignore the cipher if we have no cipherModes definition.
554 ciphers = append(ciphers, c)
555 }
556 }
557 c.Ciphers = ciphers
558
559 if c.KeyExchanges == nil {
560 c.KeyExchanges = defaultKexAlgos
561 }
562 var kexs []string
563 for _, k := range c.KeyExchanges {
564 if kexAlgoMap[k] != nil {
565 // Ignore the KEX if we have no kexAlgoMap definition.
566 kexs = append(kexs, k)
567 if k == KeyExchangeCurve25519 && !slices.Contains(c.KeyExchanges, keyExchangeCurve25519LibSSH) {
568 kexs = append(kexs, keyExchangeCurve25519LibSSH)
569 }
570 }
571 }
572 c.KeyExchanges = kexs
573
574 if c.MACs == nil {
575 c.MACs = defaultMACs
576 }
577 var macs []string
578 for _, m := range c.MACs {
579 if macModes[m] != nil {
580 // Ignore the MAC if we have no macModes definition.
581 macs = append(macs, m)
582 }
583 }
584 c.MACs = macs
585
586 if c.RekeyThreshold == 0 {
587 // cipher specific default
588 } else if c.RekeyThreshold < minRekeyThreshold {
589 c.RekeyThreshold = minRekeyThreshold
590 } else if c.RekeyThreshold >= math.MaxInt64 {
591 // Avoid weirdness if somebody uses -1 as a threshold.
592 c.RekeyThreshold = math.MaxInt64
593 }
594}
595
596// buildDataSignedForAuth returns the data that is signed in order to prove
597// possession of a private key. See RFC 4252, section 7. algo is the advertised
598// algorithm, and may be a certificate type.
599func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo string, pubKey []byte) []byte {
600 data := struct {
601 Session []byte
602 Type byte
603 User string
604 Service string
605 Method string
606 Sign bool
607 Algo string
608 PubKey []byte
609 }{
610 sessionID,
611 msgUserAuthRequest,
612 req.User,
613 req.Service,
614 req.Method,
615 true,
616 algo,
617 pubKey,
618 }
619 return Marshal(data)
620}
621
622func appendU16(buf []byte, n uint16) []byte {
623 return append(buf, byte(n>>8), byte(n))
624}
625
626func appendU32(buf []byte, n uint32) []byte {
627 return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
628}
629
630func appendU64(buf []byte, n uint64) []byte {
631 return append(buf,
632 byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
633 byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
634}
635
636func appendInt(buf []byte, n int) []byte {
637 return appendU32(buf, uint32(n))
638}
639
640func appendString(buf []byte, s string) []byte {
641 buf = appendU32(buf, uint32(len(s)))
642 buf = append(buf, s...)
643 return buf
644}
645
646func appendBool(buf []byte, b bool) []byte {
647 if b {
648 return append(buf, 1)
649 }
650 return append(buf, 0)
651}
652
653// newCond is a helper to hide the fact that there is no usable zero
654// value for sync.Cond.
655func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
656
657// window represents the buffer available to clients
658// wishing to write to a channel.
659type window struct {
660 *sync.Cond
661 win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
662 writeWaiters int
663 closed bool
664}
665
666// add adds win to the amount of window available
667// for consumers.
668func (w *window) add(win uint32) bool {
669 // a zero sized window adjust is a noop.
670 if win == 0 {
671 return true
672 }
673 w.L.Lock()
674 if w.win+win < win {
675 w.L.Unlock()
676 return false
677 }
678 w.win += win
679 // It is unusual that multiple goroutines would be attempting to reserve
680 // window space, but not guaranteed. Use broadcast to notify all waiters
681 // that additional window is available.
682 w.Broadcast()
683 w.L.Unlock()
684 return true
685}
686
687// close sets the window to closed, so all reservations fail
688// immediately.
689func (w *window) close() {
690 w.L.Lock()
691 w.closed = true
692 w.Broadcast()
693 w.L.Unlock()
694}
695
696// reserve reserves win from the available window capacity.
697// If no capacity remains, reserve will block. reserve may
698// return less than requested.
699func (w *window) reserve(win uint32) (uint32, error) {
700 var err error
701 w.L.Lock()
702 w.writeWaiters++
703 w.Broadcast()
704 for w.win == 0 && !w.closed {
705 w.Wait()
706 }
707 w.writeWaiters--
708 if w.win < win {
709 win = w.win
710 }
711 w.win -= win
712 if w.closed {
713 err = io.EOF
714 }
715 w.L.Unlock()
716 return win, err
717}
718
719// waitWriterBlocked waits until some goroutine is blocked for further
720// writes. It is used in tests only.
721func (w *window) waitWriterBlocked() {
722 w.Cond.L.Lock()
723 for w.writeWaiters == 0 {
724 w.Cond.Wait()
725 }
726 w.Cond.L.Unlock()
727}