LEFT | RIGHT |
(no file at all) | |
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 "crypto" | 8 "crypto" |
| 9 "crypto/rand" |
9 "fmt" | 10 "fmt" |
| 11 "io" |
10 "sync" | 12 "sync" |
11 | 13 |
12 _ "crypto/sha1" | 14 _ "crypto/sha1" |
13 _ "crypto/sha256" | 15 _ "crypto/sha256" |
14 _ "crypto/sha512" | 16 _ "crypto/sha512" |
15 ) | 17 ) |
16 | 18 |
17 // These are string constants in the SSH protocol. | 19 // These are string constants in the SSH protocol. |
18 const ( | 20 const ( |
19 compressionNone = "none" | 21 compressionNone = "none" |
20 serviceUserAuth = "ssh-userauth" | 22 serviceUserAuth = "ssh-userauth" |
21 serviceSSH = "ssh-connection" | 23 serviceSSH = "ssh-connection" |
22 ) | 24 ) |
23 | 25 |
| 26 // supportedCiphers specifies the supported ciphers in preference order. |
| 27 var supportedCiphers = []string{ |
| 28 "aes128-ctr", "aes192-ctr", "aes256-ctr", |
| 29 "aes128-gcm@openssh.com", |
| 30 "arcfour256", "arcfour128", |
| 31 } |
| 32 |
| 33 // supportedKexAlgos specifies the supported key-exchange algorithms in |
| 34 // preference order. |
24 var supportedKexAlgos = []string{ | 35 var supportedKexAlgos = []string{ |
| 36 // P384 and P521 are not constant-time yet, but since we don't |
| 37 // reuse ephemeral keys, using them for ECDH should be OK. |
25 kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, | 38 kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, |
26 kexAlgoDH14SHA1, kexAlgoDH1SHA1, | 39 kexAlgoDH14SHA1, kexAlgoDH1SHA1, |
27 } | 40 } |
28 | 41 |
| 42 // supportedKexAlgos specifies the supported host-key algorithms (i.e. methods |
| 43 // of authenticating servers) in preference order. |
29 var supportedHostKeyAlgos = []string{ | 44 var supportedHostKeyAlgos = []string{ |
| 45 CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, |
| 46 CertAlgoECDSA384v01, CertAlgoECDSA521v01, |
| 47 |
30 KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, | 48 KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, |
31 KeyAlgoRSA, KeyAlgoDSA, | 49 KeyAlgoRSA, KeyAlgoDSA, |
| 50 } |
| 51 |
| 52 // supportedMACs specifies a default set of MAC algorithms in preference order. |
| 53 // This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed |
| 54 // because they have reached the end of their useful life. |
| 55 var supportedMACs = []string{ |
| 56 "hmac-sha1", "hmac-sha1-96", |
32 } | 57 } |
33 | 58 |
34 var supportedCompressions = []string{compressionNone} | 59 var supportedCompressions = []string{compressionNone} |
35 | 60 |
36 // hashFuncs keeps the mapping of supported algorithms to their respective | 61 // hashFuncs keeps the mapping of supported algorithms to their respective |
37 // hashes needed for signature verification. | 62 // hashes needed for signature verification. |
38 var hashFuncs = map[string]crypto.Hash{ | 63 var hashFuncs = map[string]crypto.Hash{ |
39 KeyAlgoRSA: crypto.SHA1, | 64 KeyAlgoRSA: crypto.SHA1, |
40 KeyAlgoDSA: crypto.SHA1, | 65 KeyAlgoDSA: crypto.SHA1, |
41 KeyAlgoECDSA256: crypto.SHA256, | 66 KeyAlgoECDSA256: crypto.SHA256, |
42 KeyAlgoECDSA384: crypto.SHA384, | 67 KeyAlgoECDSA384: crypto.SHA384, |
43 KeyAlgoECDSA521: crypto.SHA512, | 68 KeyAlgoECDSA521: crypto.SHA512, |
44 CertAlgoRSAv01: crypto.SHA1, | 69 CertAlgoRSAv01: crypto.SHA1, |
45 CertAlgoDSAv01: crypto.SHA1, | 70 CertAlgoDSAv01: crypto.SHA1, |
46 CertAlgoECDSA256v01: crypto.SHA256, | 71 CertAlgoECDSA256v01: crypto.SHA256, |
47 CertAlgoECDSA384v01: crypto.SHA384, | 72 CertAlgoECDSA384v01: crypto.SHA384, |
48 CertAlgoECDSA521v01: crypto.SHA512, | 73 CertAlgoECDSA521v01: crypto.SHA512, |
49 } | 74 } |
50 | 75 |
51 // UnexpectedMessageError results when the SSH message that we received didn't | 76 // unexpectedMessageError results when the SSH message that we received didn't |
52 // match what we wanted. | 77 // match what we wanted. |
53 type UnexpectedMessageError struct { | 78 func unexpectedMessageError(expected, got uint8) error { |
54 » expected, got uint8 | 79 » return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got,
expected) |
55 } | 80 } |
56 | 81 |
57 func (u UnexpectedMessageError) Error() string { | 82 // parseError results from a malformed SSH message. |
58 » return fmt.Sprintf("ssh: unexpected message type %d (expected %d)", u.go
t, u.expected) | 83 func parseError(tag uint8) error { |
59 } | 84 » return fmt.Errorf("ssh: parse error in message type %d", tag) |
60 | |
61 // ParseError results from a malformed SSH message. | |
62 type ParseError struct { | |
63 » msgType uint8 | |
64 } | |
65 | |
66 func (p ParseError) Error() string { | |
67 » return fmt.Sprintf("ssh: parse error in message type %d", p.msgType) | |
68 } | 85 } |
69 | 86 |
70 func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo
string, ok bool) { | 87 func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo
string, ok bool) { |
71 for _, clientAlgo := range clientAlgos { | 88 for _, clientAlgo := range clientAlgos { |
72 for _, serverAlgo := range serverAlgos { | 89 for _, serverAlgo := range serverAlgos { |
73 if clientAlgo == serverAlgo { | 90 if clientAlgo == serverAlgo { |
74 return clientAlgo, true | 91 return clientAlgo, true |
75 } | 92 } |
76 } | 93 } |
77 } | 94 } |
78 return | 95 return |
79 } | 96 } |
80 | 97 |
81 func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCip
her string, ok bool) { | 98 func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCip
her string, ok bool) { |
82 for _, clientCipher := range clientCiphers { | 99 for _, clientCipher := range clientCiphers { |
83 for _, serverCipher := range serverCiphers { | 100 for _, serverCipher := range serverCiphers { |
84 // reject the cipher if we have no cipherModes definitio
n | 101 // reject the cipher if we have no cipherModes definitio
n |
85 if clientCipher == serverCipher && cipherModes[clientCip
her] != nil { | 102 if clientCipher == serverCipher && cipherModes[clientCip
her] != nil { |
86 return clientCipher, true | 103 return clientCipher, true |
87 } | 104 } |
88 } | 105 } |
89 } | 106 } |
90 return | 107 return |
91 } | 108 } |
92 | 109 |
| 110 type directionAlgorithms struct { |
| 111 Cipher string |
| 112 MAC string |
| 113 Compression string |
| 114 } |
| 115 |
93 type algorithms struct { | 116 type algorithms struct { |
94 » kex string | 117 » kex string |
95 » hostKey string | 118 » hostKey string |
96 » wCipher string | 119 » w directionAlgorithms |
97 » rCipher string | 120 » r directionAlgorithms |
98 » rMAC string | |
99 » wMAC string | |
100 » rCompression string | |
101 » wCompression string | |
102 } | 121 } |
103 | 122 |
104 func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algor
ithms) { | 123 func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algor
ithms) { |
105 var ok bool | 124 var ok bool |
106 result := &algorithms{} | 125 result := &algorithms{} |
107 result.kex, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexIn
it.KexAlgos) | 126 result.kex, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexIn
it.KexAlgos) |
108 if !ok { | 127 if !ok { |
109 return | 128 return |
110 } | 129 } |
111 | 130 |
112 result.hostKey, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgo
s, serverKexInit.ServerHostKeyAlgos) | 131 result.hostKey, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgo
s, serverKexInit.ServerHostKeyAlgos) |
113 if !ok { | 132 if !ok { |
114 return | 133 return |
115 } | 134 } |
116 | 135 |
117 » result.wCipher, ok = findCommonCipher(clientKexInit.CiphersClientServer,
serverKexInit.CiphersClientServer) | 136 » result.w.Cipher, ok = findCommonCipher(clientKexInit.CiphersClientServer
, serverKexInit.CiphersClientServer) |
118 » if !ok { | 137 » if !ok { |
119 » » return | 138 » » return |
120 » } | 139 » } |
121 | 140 |
122 » result.rCipher, ok = findCommonCipher(clientKexInit.CiphersServerClient,
serverKexInit.CiphersServerClient) | 141 » result.r.Cipher, ok = findCommonCipher(clientKexInit.CiphersServerClient
, serverKexInit.CiphersServerClient) |
123 » if !ok { | 142 » if !ok { |
124 » » return | 143 » » return |
125 » } | 144 » } |
126 | 145 |
127 » result.wMAC, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, se
rverKexInit.MACsClientServer) | 146 » result.w.MAC, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, s
erverKexInit.MACsClientServer) |
128 » if !ok { | 147 » if !ok { |
129 » » return | 148 » » return |
130 » } | 149 » } |
131 | 150 |
132 » result.rMAC, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, se
rverKexInit.MACsServerClient) | 151 » result.r.MAC, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, s
erverKexInit.MACsServerClient) |
133 » if !ok { | 152 » if !ok { |
134 » » return | 153 » » return |
135 » } | 154 » } |
136 | 155 |
137 » result.wCompression, ok = findCommonAlgorithm(clientKexInit.CompressionC
lientServer, serverKexInit.CompressionClientServer) | 156 » result.w.Compression, ok = findCommonAlgorithm(clientKexInit.Compression
ClientServer, serverKexInit.CompressionClientServer) |
138 » if !ok { | 157 » if !ok { |
139 » » return | 158 » » return |
140 » } | 159 » } |
141 | 160 |
142 » result.rCompression, ok = findCommonAlgorithm(clientKexInit.CompressionS
erverClient, serverKexInit.CompressionServerClient) | 161 » result.r.Compression, ok = findCommonAlgorithm(clientKexInit.Compression
ServerClient, serverKexInit.CompressionServerClient) |
143 if !ok { | 162 if !ok { |
144 return | 163 return |
145 } | 164 } |
146 | 165 |
147 return result | 166 return result |
148 } | 167 } |
149 | 168 |
150 // Cryptographic configuration common to both ServerConfig and ClientConfig. | 169 // If rekeythreshold is too small, we can't make any progress sending |
151 type CryptoConfig struct { | 170 // stuff. |
| 171 const minRekeyThreshold uint64 = 256 |
| 172 |
| 173 // Config contains configuration data common to both ServerConfig and |
| 174 // ClientConfig. |
| 175 type Config struct { |
| 176 » // Rand provides the source of entropy for cryptographic |
| 177 » // primitives. If Rand is nil, the cryptographic random reader |
| 178 » // in package crypto/rand will be used. |
| 179 » Rand io.Reader |
| 180 |
| 181 » // The maximum number of bytes sent or received after which a |
| 182 » // new key is negotiated. It must be at least 256. If |
| 183 » // unspecified, 1 gigabyte is used. |
| 184 » RekeyThreshold uint64 |
| 185 |
152 // The allowed key exchanges algorithms. If unspecified then a | 186 // The allowed key exchanges algorithms. If unspecified then a |
153 // default set of algorithms is used. | 187 // default set of algorithms is used. |
154 KeyExchanges []string | 188 KeyExchanges []string |
155 | 189 |
156 » // The allowed cipher algorithms. If unspecified then DefaultCipherOrder
is | 190 » // The allowed cipher algorithms. If unspecified then a sensible |
157 » // used. | 191 » // default is used. |
158 Ciphers []string | 192 Ciphers []string |
159 | 193 |
160 » // The allowed MAC algorithms. If unspecified then DefaultMACOrder is us
ed. | 194 » // The allowed MAC algorithms. If unspecified then a sensible default |
| 195 » // is used. |
161 MACs []string | 196 MACs []string |
162 } | 197 } |
163 | 198 |
164 func (c *CryptoConfig) ciphers() []string { | 199 // SetDefaults sets sensible values for unset fields in config. This is |
| 200 // exported for testing: Configs passed to SSH functions are copied and have |
| 201 // default values set automatically. |
| 202 func (c *Config) SetDefaults() { |
| 203 » if c.Rand == nil { |
| 204 » » c.Rand = rand.Reader |
| 205 » } |
165 if c.Ciphers == nil { | 206 if c.Ciphers == nil { |
166 » » return DefaultCipherOrder | 207 » » c.Ciphers = supportedCiphers |
167 » } | 208 » } |
168 » return c.Ciphers | 209 |
169 } | |
170 | |
171 func (c *CryptoConfig) kexes() []string { | |
172 if c.KeyExchanges == nil { | 210 if c.KeyExchanges == nil { |
173 » » return defaultKeyExchangeOrder | 211 » » c.KeyExchanges = supportedKexAlgos |
174 » } | 212 » } |
175 » return c.KeyExchanges | 213 |
176 } | |
177 | |
178 func (c *CryptoConfig) macs() []string { | |
179 if c.MACs == nil { | 214 if c.MACs == nil { |
180 » » return DefaultMACOrder | 215 » » c.MACs = supportedMACs |
181 » } | 216 » } |
182 » return c.MACs | 217 |
183 } | 218 » if c.RekeyThreshold == 0 { |
184 | 219 » » // RFC 4253, section 9 suggests rekeying after 1G. |
185 // serialize a signed slice according to RFC 4254 6.6. The name should | 220 » » c.RekeyThreshold = 1 << 30 |
186 // be a key type name, rather than a cert type name. | 221 » } |
187 func serializeSignature(name string, sig []byte) []byte { | 222 » if c.RekeyThreshold < minRekeyThreshold { |
188 » length := stringLength(len(name)) | 223 » » c.RekeyThreshold = minRekeyThreshold |
189 » length += stringLength(len(sig)) | 224 » } |
190 | |
191 » ret := make([]byte, length) | |
192 » r := marshalString(ret, []byte(name)) | |
193 » r = marshalString(r, sig) | |
194 | |
195 » return ret | |
196 } | |
197 | |
198 // MarshalPublicKey serializes a supported key or certificate for use | |
199 // by the SSH wire protocol. It can be used for comparison with the | |
200 // pubkey argument of ServerConfig's PublicKeyCallback as well as for | |
201 // generating an authorized_keys or host_keys file. | |
202 func MarshalPublicKey(key PublicKey) []byte { | |
203 » // See also RFC 4253 6.6. | |
204 » algoname := key.PublicKeyAlgo() | |
205 » blob := key.Marshal() | |
206 | |
207 » length := stringLength(len(algoname)) | |
208 » length += len(blob) | |
209 » ret := make([]byte, length) | |
210 » r := marshalString(ret, []byte(algoname)) | |
211 » copy(r, blob) | |
212 » return ret | |
213 } | |
214 | |
215 // pubAlgoToPrivAlgo returns the private key algorithm format name that | |
216 // corresponds to a given public key algorithm format name. For most | |
217 // public keys, the private key algorithm name is the same. For some | |
218 // situations, such as openssh certificates, the private key algorithm and | |
219 // public key algorithm names differ. This accounts for those situations. | |
220 func pubAlgoToPrivAlgo(pubAlgo string) string { | |
221 » switch pubAlgo { | |
222 » case CertAlgoRSAv01: | |
223 » » return KeyAlgoRSA | |
224 » case CertAlgoDSAv01: | |
225 » » return KeyAlgoDSA | |
226 » case CertAlgoECDSA256v01: | |
227 » » return KeyAlgoECDSA256 | |
228 » case CertAlgoECDSA384v01: | |
229 » » return KeyAlgoECDSA384 | |
230 » case CertAlgoECDSA521v01: | |
231 » » return KeyAlgoECDSA521 | |
232 » } | |
233 » return pubAlgo | |
234 } | 225 } |
235 | 226 |
236 // buildDataSignedForAuth returns the data that is signed in order to prove | 227 // buildDataSignedForAuth returns the data that is signed in order to prove |
237 // possession of a private key. See RFC 4252, section 7. | 228 // possession of a private key. See RFC 4252, section 7. |
238 func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubK
ey []byte) []byte { | 229 func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubK
ey []byte) []byte { |
239 » user := []byte(req.User) | 230 » data := struct { |
240 » service := []byte(req.Service) | 231 » » Session []byte |
241 » method := []byte(req.Method) | 232 » » Type byte |
242 | 233 » » User string |
243 » length := stringLength(len(sessionId)) | 234 » » Service string |
244 » length += 1 | 235 » » Method string |
245 » length += stringLength(len(user)) | 236 » » Sign bool |
246 » length += stringLength(len(service)) | 237 » » Algo []byte |
247 » length += stringLength(len(method)) | 238 » » PubKey []byte |
248 » length += 1 | 239 » }{ |
249 » length += stringLength(len(algo)) | 240 » » sessionId, |
250 » length += stringLength(len(pubKey)) | 241 » » msgUserAuthRequest, |
251 | 242 » » req.User, |
252 » ret := make([]byte, length) | 243 » » req.Service, |
253 » r := marshalString(ret, sessionId) | 244 » » req.Method, |
254 » r[0] = msgUserAuthRequest | 245 » » true, |
255 » r = r[1:] | 246 » » algo, |
256 » r = marshalString(r, user) | 247 » » pubKey, |
257 » r = marshalString(r, service) | 248 » } |
258 » r = marshalString(r, method) | 249 » return Marshal(data) |
259 » r[0] = 1 | |
260 » r = r[1:] | |
261 » r = marshalString(r, algo) | |
262 » r = marshalString(r, pubKey) | |
263 » return ret | |
264 } | |
265 | |
266 // safeString sanitises s according to RFC 4251, section 9.2. | |
267 // All control characters except tab, carriage return and newline are | |
268 // replaced by 0x20. | |
269 func safeString(s string) string { | |
270 » out := []byte(s) | |
271 » for i, c := range out { | |
272 » » if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 { | |
273 » » » out[i] = 0x20 | |
274 » » } | |
275 » } | |
276 » return string(out) | |
277 } | 250 } |
278 | 251 |
279 func appendU16(buf []byte, n uint16) []byte { | 252 func appendU16(buf []byte, n uint16) []byte { |
280 return append(buf, byte(n>>8), byte(n)) | 253 return append(buf, byte(n>>8), byte(n)) |
281 } | 254 } |
282 | 255 |
283 func appendU32(buf []byte, n uint32) []byte { | 256 func appendU32(buf []byte, n uint32) []byte { |
284 return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) | 257 return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) |
| 258 } |
| 259 |
| 260 func appendU64(buf []byte, n uint64) []byte { |
| 261 return append(buf, |
| 262 byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), |
| 263 byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) |
285 } | 264 } |
286 | 265 |
287 func appendInt(buf []byte, n int) []byte { | 266 func appendInt(buf []byte, n int) []byte { |
288 return appendU32(buf, uint32(n)) | 267 return appendU32(buf, uint32(n)) |
289 } | 268 } |
290 | 269 |
291 func appendString(buf []byte, s string) []byte { | 270 func appendString(buf []byte, s string) []byte { |
292 buf = appendU32(buf, uint32(len(s))) | 271 buf = appendU32(buf, uint32(len(s))) |
293 buf = append(buf, s...) | 272 buf = append(buf, s...) |
294 return buf | 273 return buf |
295 } | 274 } |
296 | 275 |
297 func appendBool(buf []byte, b bool) []byte { | 276 func appendBool(buf []byte, b bool) []byte { |
298 if b { | 277 if b { |
299 » » buf = append(buf, 1) | 278 » » return append(buf, 1) |
300 » } else { | 279 » } |
301 » » buf = append(buf, 0) | 280 » return append(buf, 0) |
302 » } | |
303 » return buf | |
304 } | 281 } |
305 | 282 |
306 // newCond is a helper to hide the fact that there is no usable zero | 283 // newCond is a helper to hide the fact that there is no usable zero |
307 // value for sync.Cond. | 284 // value for sync.Cond. |
308 func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } | 285 func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } |
309 | 286 |
310 // window represents the buffer available to clients | 287 // window represents the buffer available to clients |
311 // wishing to write to a channel. | 288 // wishing to write to a channel. |
312 type window struct { | 289 type window struct { |
313 *sync.Cond | 290 *sync.Cond |
314 » win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 | 291 » win uint32 // RFC 4254 5.2 says the window size can grow to 2^3
2-1 |
| 292 » writeWaiters int |
| 293 » closed bool |
315 } | 294 } |
316 | 295 |
317 // add adds win to the amount of window available | 296 // add adds win to the amount of window available |
318 // for consumers. | 297 // for consumers. |
319 func (w *window) add(win uint32) bool { | 298 func (w *window) add(win uint32) bool { |
320 // a zero sized window adjust is a noop. | 299 // a zero sized window adjust is a noop. |
321 if win == 0 { | 300 if win == 0 { |
322 return true | 301 return true |
323 } | 302 } |
324 w.L.Lock() | 303 w.L.Lock() |
325 if w.win+win < win { | 304 if w.win+win < win { |
326 w.L.Unlock() | 305 w.L.Unlock() |
327 return false | 306 return false |
328 } | 307 } |
329 w.win += win | 308 w.win += win |
330 // It is unusual that multiple goroutines would be attempting to reserve | 309 // It is unusual that multiple goroutines would be attempting to reserve |
331 // window space, but not guaranteed. Use broadcast to notify all waiters | 310 // window space, but not guaranteed. Use broadcast to notify all waiters |
332 // that additional window is available. | 311 // that additional window is available. |
333 w.Broadcast() | 312 w.Broadcast() |
334 w.L.Unlock() | 313 w.L.Unlock() |
335 return true | 314 return true |
336 } | 315 } |
337 | 316 |
| 317 // close sets the window to closed, so all reservations fail |
| 318 // immediately. |
| 319 func (w *window) close() { |
| 320 w.L.Lock() |
| 321 w.closed = true |
| 322 w.Broadcast() |
| 323 w.L.Unlock() |
| 324 } |
| 325 |
338 // reserve reserves win from the available window capacity. | 326 // reserve reserves win from the available window capacity. |
339 // If no capacity remains, reserve will block. reserve may | 327 // If no capacity remains, reserve will block. reserve may |
340 // return less than requested. | 328 // return less than requested. |
341 func (w *window) reserve(win uint32) uint32 { | 329 func (w *window) reserve(win uint32) (uint32, error) { |
| 330 » var err error |
342 w.L.Lock() | 331 w.L.Lock() |
343 » for w.win == 0 { | 332 » w.writeWaiters++ |
| 333 » w.Broadcast() |
| 334 » for w.win == 0 && !w.closed { |
344 w.Wait() | 335 w.Wait() |
345 } | 336 } |
| 337 w.writeWaiters-- |
346 if w.win < win { | 338 if w.win < win { |
347 win = w.win | 339 win = w.win |
348 } | 340 } |
349 w.win -= win | 341 w.win -= win |
| 342 if w.closed { |
| 343 err = io.EOF |
| 344 } |
350 w.L.Unlock() | 345 w.L.Unlock() |
351 » return win | 346 » return win, err |
352 } | 347 } |
| 348 |
| 349 // waitWriterBlocked waits until some goroutine is blocked for further |
| 350 // writes. It is used in tests only. |
| 351 func (w *window) waitWriterBlocked() { |
| 352 » w.Cond.L.Lock() |
| 353 » for w.writeWaiters == 0 { |
| 354 » » w.Cond.Wait() |
| 355 » } |
| 356 » w.Cond.L.Unlock() |
| 357 } |
LEFT | RIGHT |