blob: e6a77f26a099c89752e0a3c88e4829f19ac262cb [file] [log] [blame]
Russ Cox470549d2012-01-25 15:31:12 -05001// 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 (
Han-Wen Nienhuysd7d50b02013-08-28 10:50:25 -04008 "crypto"
Adam Langleyfa50e742014-04-09 13:57:52 -07009 "crypto/rand"
Dave Cheney1582bf02012-10-30 18:13:59 +110010 "fmt"
Adam Langleyfa50e742014-04-09 13:57:52 -070011 "io"
Han-Wen Nienhuysc78caca2017-04-04 17:27:40 +020012 "math"
Filippo Valsorda6fad3df2022-11-03 19:27:01 +010013 "strings"
Russ Cox470549d2012-01-25 15:31:12 -050014 "sync"
Jonathan Pittman6a743c52013-09-09 13:06:04 -040015
16 _ "crypto/sha1"
17 _ "crypto/sha256"
18 _ "crypto/sha512"
Russ Cox470549d2012-01-25 15:31:12 -050019)
20
21// These are string constants in the SSH protocol.
22const (
Russ Cox470549d2012-01-25 15:31:12 -050023 compressionNone = "none"
24 serviceUserAuth = "ssh-userauth"
25 serviceSSH = "ssh-connection"
26)
27
Han-Wen Nienhuys18353192018-01-10 11:56:48 +010028// supportedCiphers lists ciphers we support but might not recommend.
Adam Langleyfa50e742014-04-09 13:57:52 -070029var supportedCiphers = []string{
30 "aes128-ctr", "aes192-ctr", "aes256-ctr",
Nicola Murinoebe92622023-02-15 18:36:09 +000031 "aes128-gcm@openssh.com", gcm256CipherID,
Han-Wen Nienhuysee41a252018-01-10 12:42:30 +010032 chacha20Poly1305ID,
Han-Wen Nienhuys18353192018-01-10 11:56:48 +010033 "arcfour256", "arcfour128", "arcfour",
34 aes128cbcID,
35 tripledescbcID,
36}
37
38// preferredCiphers specifies the default preference for ciphers.
39var preferredCiphers = []string{
Nicola Murinoebe92622023-02-15 18:36:09 +000040 "aes128-gcm@openssh.com", gcm256CipherID,
Han-Wen Nienhuysd94f6bc2018-01-22 20:45:19 +010041 chacha20Poly1305ID,
42 "aes128-ctr", "aes192-ctr", "aes256-ctr",
Adam Langleyfa50e742014-04-09 13:57:52 -070043}
44
45// supportedKexAlgos specifies the supported key-exchange algorithms in
46// preference order.
Han-Wen Nienhuysd7d50b02013-08-28 10:50:25 -040047var supportedKexAlgos = []string{
Михаил Патин9b076912022-02-12 11:49:26 +030048 kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
Adam Langleyfa50e742014-04-09 13:57:52 -070049 // P384 and P521 are not constant-time yet, but since we don't
50 // reuse ephemeral keys, using them for ECDH should be OK.
Han-Wen Nienhuysd7d50b02013-08-28 10:50:25 -040051 kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
Filippo Valsordae4b36782022-03-12 10:21:59 -050052 kexAlgoDH14SHA256, kexAlgoDH14SHA1, kexAlgoDH1SHA1,
Han-Wen Nienhuysd7d50b02013-08-28 10:50:25 -040053}
54
Lucas Bremgartner57b3e212019-06-03 19:41:45 +000055// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
56// for the server half.
57var serverForbiddenKexAlgos = map[string]struct{}{
58 kexAlgoDHGEXSHA1: {}, // server half implementation is only minimal to satisfy the automated tests
59 kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
60}
61
Eric Brown094676d2018-07-12 08:07:55 -070062// preferredKexAlgos specifies the default preference for key-exchange algorithms
63// in preference order.
64var preferredKexAlgos = []string{
Михаил Патин9b076912022-02-12 11:49:26 +030065 kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
Eric Brown094676d2018-07-12 08:07:55 -070066 kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
Filippo Valsordae4b36782022-03-12 10:21:59 -050067 kexAlgoDH14SHA256, kexAlgoDH14SHA1,
Eric Brown094676d2018-07-12 08:07:55 -070068}
69
Brad Fitzpatrick3cb07272017-03-30 16:02:45 +000070// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
Adam Langleyfa50e742014-04-09 13:57:52 -070071// of authenticating servers) in preference order.
Han-Wen Nienhuys41400fe2013-10-07 18:30:34 -040072var supportedHostKeyAlgos = []string{
Filippo Valsordafcc990c2022-03-14 06:24:16 -040073 CertAlgoRSASHA512v01, CertAlgoRSASHA256v01,
74 CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
Peter Moody10c26742016-07-08 11:32:35 -070075 CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
Adam Langleyfa50e742014-04-09 13:57:52 -070076
Han-Wen Nienhuys41400fe2013-10-07 18:30:34 -040077 KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
Filippo Valsordafcc990c2022-03-14 06:24:16 -040078 KeyAlgoRSASHA512, KeyAlgoRSASHA256,
79 KeyAlgoRSA, KeyAlgoDSA,
Martin Garton1e61df82016-04-30 22:10:58 +010080
81 KeyAlgoED25519,
Han-Wen Nienhuys41400fe2013-10-07 18:30:34 -040082}
83
Adam Langleyfa50e742014-04-09 13:57:52 -070084// supportedMACs specifies a default set of MAC algorithms in preference order.
85// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
86// because they have reached the end of their useful life.
87var supportedMACs = []string{
MiLk84bacda2017-01-20 08:44:06 +090088 "hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96",
Adam Langleyfa50e742014-04-09 13:57:52 -070089}
90
Russ Cox470549d2012-01-25 15:31:12 -050091var supportedCompressions = []string{compressionNone}
92
Filippo Valsorda1baeb1c2022-03-14 09:16:43 -040093// hashFuncs keeps the mapping of supported signature algorithms to their
94// respective hashes needed for signing and verification.
Jonathan Pittman6a743c52013-09-09 13:06:04 -040095var hashFuncs = map[string]crypto.Hash{
Filippo Valsorda1baeb1c2022-03-14 09:16:43 -040096 KeyAlgoRSA: crypto.SHA1,
97 KeyAlgoRSASHA256: crypto.SHA256,
98 KeyAlgoRSASHA512: crypto.SHA512,
99 KeyAlgoDSA: crypto.SHA1,
100 KeyAlgoECDSA256: crypto.SHA256,
101 KeyAlgoECDSA384: crypto.SHA384,
102 KeyAlgoECDSA521: crypto.SHA512,
103 // KeyAlgoED25519 doesn't pre-hash.
104 KeyAlgoSKECDSA256: crypto.SHA256,
105 KeyAlgoSKED25519: crypto.SHA256,
106}
107
108// algorithmsForKeyFormat returns the supported signature algorithms for a given
109// public key format (PublicKey.Type), in order of preference. See RFC 8332,
110// Section 2. See also the note in sendKexInit on backwards compatibility.
111func algorithmsForKeyFormat(keyFormat string) []string {
112 switch keyFormat {
113 case KeyAlgoRSA:
114 return []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA}
115 case CertAlgoRSAv01:
116 return []string{CertAlgoRSASHA256v01, CertAlgoRSASHA512v01, CertAlgoRSAv01}
117 default:
118 return []string{keyFormat}
119 }
Jonathan Pittman6a743c52013-09-09 13:06:04 -0400120}
121
Filippo Valsorda6fad3df2022-11-03 19:27:01 +0100122// supportedPubKeyAuthAlgos specifies the supported client public key
123// authentication algorithms. Note that this doesn't include certificate types
124// since those use the underlying algorithm. This list is sent to the client if
125// it supports the server-sig-algs extension. Order is irrelevant.
126var supportedPubKeyAuthAlgos = []string{
127 KeyAlgoED25519,
128 KeyAlgoSKED25519, KeyAlgoSKECDSA256,
129 KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
130 KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
131 KeyAlgoDSA,
132}
133
134var supportedPubKeyAuthAlgosList = strings.Join(supportedPubKeyAuthAlgos, ",")
135
Adam Langleyfa50e742014-04-09 13:57:52 -0700136// unexpectedMessageError results when the SSH message that we received didn't
Russ Cox470549d2012-01-25 15:31:12 -0500137// match what we wanted.
Adam Langleyfa50e742014-04-09 13:57:52 -0700138func unexpectedMessageError(expected, got uint8) error {
139 return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
Russ Cox470549d2012-01-25 15:31:12 -0500140}
141
Adam Langleyfa50e742014-04-09 13:57:52 -0700142// parseError results from a malformed SSH message.
143func parseError(tag uint8) error {
144 return fmt.Errorf("ssh: parse error in message type %d", tag)
Russ Cox470549d2012-01-25 15:31:12 -0500145}
146
Thomas Desrosiers6c2080b2015-08-21 21:25:57 -0400147func findCommon(what string, client []string, server []string) (common string, err error) {
148 for _, c := range client {
149 for _, s := range server {
150 if c == s {
151 return c, nil
Russ Cox470549d2012-01-25 15:31:12 -0500152 }
153 }
154 }
Thomas Desrosiers6c2080b2015-08-21 21:25:57 -0400155 return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
Russ Cox470549d2012-01-25 15:31:12 -0500156}
157
Han-Wen Nienhuysdf01cb22019-04-17 10:53:58 +0200158// directionAlgorithms records algorithm choices in one direction (either read or write)
Adam Langleyfa50e742014-04-09 13:57:52 -0700159type directionAlgorithms struct {
160 Cipher string
161 MAC string
162 Compression string
163}
164
Han-Wen Nienhuysa59c1272017-01-17 18:15:27 +0100165// rekeyBytes returns a rekeying intervals in bytes.
166func (a *directionAlgorithms) rekeyBytes() int64 {
Axel Wagner56aed062022-10-11 09:15:35 +0200167 // According to RFC 4344 block ciphers should rekey after
Han-Wen Nienhuysa59c1272017-01-17 18:15:27 +0100168 // 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
169 // 128.
170 switch a.Cipher {
Nicola Murinoebe92622023-02-15 18:36:09 +0000171 case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcm128CipherID, gcm256CipherID, aes128cbcID:
Han-Wen Nienhuysa59c1272017-01-17 18:15:27 +0100172 return 16 * (1 << 32)
173
174 }
175
Axel Wagner56aed062022-10-11 09:15:35 +0200176 // For others, stick with RFC 4253 recommendation to rekey after 1 Gb of data.
Han-Wen Nienhuysa59c1272017-01-17 18:15:27 +0100177 return 1 << 30
178}
179
Roland Shoemaker6068a2e2022-03-02 08:24:15 -0800180var aeadCiphers = map[string]bool{
Nicola Murinoebe92622023-02-15 18:36:09 +0000181 gcm128CipherID: true,
182 gcm256CipherID: true,
Roland Shoemaker6068a2e2022-03-02 08:24:15 -0800183 chacha20Poly1305ID: true,
184}
185
Han-Wen Nienhuys41472562013-10-08 15:47:04 -0400186type algorithms struct {
Adam Langleyfa50e742014-04-09 13:57:52 -0700187 kex string
188 hostKey string
189 w directionAlgorithms
190 r directionAlgorithms
Han-Wen Nienhuys41472562013-10-08 15:47:04 -0400191}
192
Han-Wen Nienhuysdf01cb22019-04-17 10:53:58 +0200193func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
Han-Wen Nienhuys41472562013-10-08 15:47:04 -0400194 result := &algorithms{}
Thomas Desrosiers6c2080b2015-08-21 21:25:57 -0400195
196 result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
197 if err != nil {
Russ Cox470549d2012-01-25 15:31:12 -0500198 return
199 }
200
Thomas Desrosiers6c2080b2015-08-21 21:25:57 -0400201 result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
202 if err != nil {
Russ Cox470549d2012-01-25 15:31:12 -0500203 return
204 }
205
Han-Wen Nienhuysdf01cb22019-04-17 10:53:58 +0200206 stoc, ctos := &result.w, &result.r
207 if isClient {
208 ctos, stoc = stoc, ctos
209 }
210
211 ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
Thomas Desrosiers6c2080b2015-08-21 21:25:57 -0400212 if err != nil {
Russ Cox470549d2012-01-25 15:31:12 -0500213 return
214 }
215
Han-Wen Nienhuysdf01cb22019-04-17 10:53:58 +0200216 stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
Thomas Desrosiers6c2080b2015-08-21 21:25:57 -0400217 if err != nil {
Russ Cox470549d2012-01-25 15:31:12 -0500218 return
219 }
220
Roland Shoemaker6068a2e2022-03-02 08:24:15 -0800221 if !aeadCiphers[ctos.Cipher] {
222 ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
223 if err != nil {
224 return
225 }
Russ Cox470549d2012-01-25 15:31:12 -0500226 }
227
Roland Shoemaker6068a2e2022-03-02 08:24:15 -0800228 if !aeadCiphers[stoc.Cipher] {
229 stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
230 if err != nil {
231 return
232 }
Russ Cox470549d2012-01-25 15:31:12 -0500233 }
234
Han-Wen Nienhuysdf01cb22019-04-17 10:53:58 +0200235 ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
Thomas Desrosiers6c2080b2015-08-21 21:25:57 -0400236 if err != nil {
Russ Cox470549d2012-01-25 15:31:12 -0500237 return
238 }
239
Han-Wen Nienhuysdf01cb22019-04-17 10:53:58 +0200240 stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
Thomas Desrosiers6c2080b2015-08-21 21:25:57 -0400241 if err != nil {
Russ Cox470549d2012-01-25 15:31:12 -0500242 return
243 }
244
Thomas Desrosiers6c2080b2015-08-21 21:25:57 -0400245 return result, nil
Russ Cox470549d2012-01-25 15:31:12 -0500246}
247
Adam Langleyfa50e742014-04-09 13:57:52 -0700248// If rekeythreshold is too small, we can't make any progress sending
249// stuff.
250const minRekeyThreshold uint64 = 256
251
252// Config contains configuration data common to both ServerConfig and
253// ClientConfig.
254type Config struct {
255 // Rand provides the source of entropy for cryptographic
256 // primitives. If Rand is nil, the cryptographic random reader
257 // in package crypto/rand will be used.
258 Rand io.Reader
259
260 // The maximum number of bytes sent or received after which a
261 // new key is negotiated. It must be at least 256. If
Han-Wen Nienhuysc78caca2017-04-04 17:27:40 +0200262 // unspecified, a size suitable for the chosen cipher is used.
Adam Langleyfa50e742014-04-09 13:57:52 -0700263 RekeyThreshold uint64
264
Han-Wen Nienhuysd7d50b02013-08-28 10:50:25 -0400265 // The allowed key exchanges algorithms. If unspecified then a
266 // default set of algorithms is used.
267 KeyExchanges []string
268
Adam Langleyfa50e742014-04-09 13:57:52 -0700269 // The allowed cipher algorithms. If unspecified then a sensible
270 // default is used.
Russ Cox470549d2012-01-25 15:31:12 -0500271 Ciphers []string
Dave Cheney6de97b52012-02-27 19:40:52 -0500272
Adam Langleyfa50e742014-04-09 13:57:52 -0700273 // The allowed MAC algorithms. If unspecified then a sensible default
274 // is used.
Dave Cheney6de97b52012-02-27 19:40:52 -0500275 MACs []string
Russ Cox470549d2012-01-25 15:31:12 -0500276}
277
Adam Langleyfa50e742014-04-09 13:57:52 -0700278// SetDefaults sets sensible values for unset fields in config. This is
279// exported for testing: Configs passed to SSH functions are copied and have
280// default values set automatically.
281func (c *Config) SetDefaults() {
282 if c.Rand == nil {
283 c.Rand = rand.Reader
284 }
Russ Cox470549d2012-01-25 15:31:12 -0500285 if c.Ciphers == nil {
Han-Wen Nienhuys18353192018-01-10 11:56:48 +0100286 c.Ciphers = preferredCiphers
Russ Cox470549d2012-01-25 15:31:12 -0500287 }
Nathan(yinian) Hu5c68cfd2015-04-08 12:39:14 +1000288 var ciphers []string
289 for _, c := range c.Ciphers {
290 if cipherModes[c] != nil {
291 // reject the cipher if we have no cipherModes definition
292 ciphers = append(ciphers, c)
293 }
294 }
295 c.Ciphers = ciphers
Russ Cox470549d2012-01-25 15:31:12 -0500296
Han-Wen Nienhuysd7d50b02013-08-28 10:50:25 -0400297 if c.KeyExchanges == nil {
Eric Brown094676d2018-07-12 08:07:55 -0700298 c.KeyExchanges = preferredKexAlgos
Han-Wen Nienhuysd7d50b02013-08-28 10:50:25 -0400299 }
Han-Wen Nienhuysd7d50b02013-08-28 10:50:25 -0400300
Dave Cheney6de97b52012-02-27 19:40:52 -0500301 if c.MACs == nil {
Adam Langleyfa50e742014-04-09 13:57:52 -0700302 c.MACs = supportedMACs
Dave Cheney6de97b52012-02-27 19:40:52 -0500303 }
Dave Cheney6de97b52012-02-27 19:40:52 -0500304
Adam Langleyfa50e742014-04-09 13:57:52 -0700305 if c.RekeyThreshold == 0 {
Han-Wen Nienhuysc78caca2017-04-04 17:27:40 +0200306 // cipher specific default
307 } else if c.RekeyThreshold < minRekeyThreshold {
Adam Langleyfa50e742014-04-09 13:57:52 -0700308 c.RekeyThreshold = minRekeyThreshold
Han-Wen Nienhuysc78caca2017-04-04 17:27:40 +0200309 } else if c.RekeyThreshold >= math.MaxInt64 {
310 // Avoid weirdness if somebody uses -1 as a threshold.
311 c.RekeyThreshold = math.MaxInt64
Adam Langleyfa50e742014-04-09 13:57:52 -0700312 }
Jonathan Pittman6a743c52013-09-09 13:06:04 -0400313}
314
Russ Cox470549d2012-01-25 15:31:12 -0500315// buildDataSignedForAuth returns the data that is signed in order to prove
Filippo Valsorda5d542ad2022-03-14 10:48:13 -0400316// possession of a private key. See RFC 4252, section 7. algo is the advertised
317// algorithm, and may be a certificate type.
318func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo string, pubKey []byte) []byte {
Adam Langleyfa50e742014-04-09 13:57:52 -0700319 data := struct {
320 Session []byte
321 Type byte
322 User string
323 Service string
324 Method string
325 Sign bool
Filippo Valsorda5d542ad2022-03-14 10:48:13 -0400326 Algo string
Adam Langleyfa50e742014-04-09 13:57:52 -0700327 PubKey []byte
328 }{
Kevin Burkee8f22982017-11-27 20:39:32 -0800329 sessionID,
Adam Langleyfa50e742014-04-09 13:57:52 -0700330 msgUserAuthRequest,
331 req.User,
332 req.Service,
333 req.Method,
334 true,
335 algo,
336 pubKey,
Russ Cox470549d2012-01-25 15:31:12 -0500337 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700338 return Marshal(data)
Russ Cox470549d2012-01-25 15:31:12 -0500339}
Dave Cheney79d53bd2012-03-04 14:34:24 -0800340
341func appendU16(buf []byte, n uint16) []byte {
342 return append(buf, byte(n>>8), byte(n))
343}
344
345func appendU32(buf []byte, n uint32) []byte {
346 return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
347}
348
Adam Langleyfa50e742014-04-09 13:57:52 -0700349func appendU64(buf []byte, n uint64) []byte {
350 return append(buf,
351 byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
352 byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
353}
354
Dave Cheney79d53bd2012-03-04 14:34:24 -0800355func appendInt(buf []byte, n int) []byte {
356 return appendU32(buf, uint32(n))
357}
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000358
Han-Wen Nienhuysc7df5652013-06-06 10:44:12 -0400359func appendString(buf []byte, s string) []byte {
360 buf = appendU32(buf, uint32(len(s)))
361 buf = append(buf, s...)
362 return buf
363}
364
365func appendBool(buf []byte, b bool) []byte {
366 if b {
Adam Langleyfa50e742014-04-09 13:57:52 -0700367 return append(buf, 1)
Han-Wen Nienhuysc7df5652013-06-06 10:44:12 -0400368 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700369 return append(buf, 0)
Han-Wen Nienhuysc7df5652013-06-06 10:44:12 -0400370}
371
Adam Langley4002be22012-12-10 18:12:36 -0500372// newCond is a helper to hide the fact that there is no usable zero
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000373// value for sync.Cond.
374func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
375
Adam Langley4002be22012-12-10 18:12:36 -0500376// window represents the buffer available to clients
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000377// wishing to write to a channel.
378type window struct {
379 *sync.Cond
Adam Langleyfa50e742014-04-09 13:57:52 -0700380 win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
381 writeWaiters int
382 closed bool
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000383}
384
385// add adds win to the amount of window available
386// for consumers.
387func (w *window) add(win uint32) bool {
Dave Cheney55aa0812012-05-22 12:04:51 +1000388 // a zero sized window adjust is a noop.
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000389 if win == 0 {
Dave Cheney55aa0812012-05-22 12:04:51 +1000390 return true
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000391 }
392 w.L.Lock()
393 if w.win+win < win {
394 w.L.Unlock()
395 return false
396 }
397 w.win += win
398 // It is unusual that multiple goroutines would be attempting to reserve
Adam Langley4002be22012-12-10 18:12:36 -0500399 // window space, but not guaranteed. Use broadcast to notify all waiters
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000400 // that additional window is available.
401 w.Broadcast()
402 w.L.Unlock()
403 return true
404}
405
Adam Langleyfa50e742014-04-09 13:57:52 -0700406// close sets the window to closed, so all reservations fail
407// immediately.
408func (w *window) close() {
409 w.L.Lock()
410 w.closed = true
411 w.Broadcast()
412 w.L.Unlock()
413}
414
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000415// reserve reserves win from the available window capacity.
416// If no capacity remains, reserve will block. reserve may
417// return less than requested.
Adam Langleyfa50e742014-04-09 13:57:52 -0700418func (w *window) reserve(win uint32) (uint32, error) {
419 var err error
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000420 w.L.Lock()
Adam Langleyfa50e742014-04-09 13:57:52 -0700421 w.writeWaiters++
422 w.Broadcast()
423 for w.win == 0 && !w.closed {
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000424 w.Wait()
425 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700426 w.writeWaiters--
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000427 if w.win < win {
428 win = w.win
429 }
430 w.win -= win
Adam Langleyfa50e742014-04-09 13:57:52 -0700431 if w.closed {
432 err = io.EOF
433 }
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000434 w.L.Unlock()
Adam Langleyfa50e742014-04-09 13:57:52 -0700435 return win, err
436}
437
438// waitWriterBlocked waits until some goroutine is blocked for further
439// writes. It is used in tests only.
440func (w *window) waitWriterBlocked() {
441 w.Cond.L.Lock()
442 for w.writeWaiters == 0 {
443 w.Cond.Wait()
444 }
445 w.Cond.L.Unlock()
Dave Cheney8a2e7c92012-05-11 05:56:44 +1000446}