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 | "crypto/dsa" |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 9 | "crypto/ecdsa" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 10 | "crypto/rsa" |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 11 | "errors" |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 12 | "fmt" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 13 | "math/big" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 14 | "sync" |
| 15 | ) |
| 16 | |
| 17 | // These are string constants in the SSH protocol. |
| 18 | const ( |
Daniel Theophanes | 36a967d | 2012-03-06 11:25:32 -0500 | [diff] [blame] | 19 | keyAlgoDH1SHA1 = "diffie-hellman-group1-sha1" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 20 | kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" |
| 21 | hostAlgoRSA = "ssh-rsa" |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 22 | hostAlgoDSA = "ssh-dss" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 23 | compressionNone = "none" |
| 24 | serviceUserAuth = "ssh-userauth" |
| 25 | serviceSSH = "ssh-connection" |
| 26 | ) |
| 27 | |
Daniel Theophanes | 36a967d | 2012-03-06 11:25:32 -0500 | [diff] [blame] | 28 | var supportedKexAlgos = []string{kexAlgoDH14SHA1, keyAlgoDH1SHA1} |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 29 | var supportedHostKeyAlgos = []string{hostAlgoRSA} |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 30 | var supportedCompressions = []string{compressionNone} |
| 31 | |
| 32 | // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. |
| 33 | type dhGroup struct { |
| 34 | g, p *big.Int |
| 35 | } |
| 36 | |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 37 | func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { |
| 38 | if theirPublic.Sign() <= 0 || theirPublic.Cmp(group.p) >= 0 { |
| 39 | return nil, errors.New("ssh: DH parameter out of bounds") |
| 40 | } |
| 41 | return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil |
| 42 | } |
| 43 | |
Daniel Theophanes | 36a967d | 2012-03-06 11:25:32 -0500 | [diff] [blame] | 44 | // dhGroup1 is the group called diffie-hellman-group1-sha1 in RFC 4253 and |
| 45 | // Oakley Group 2 in RFC 2409. |
| 46 | var dhGroup1 *dhGroup |
| 47 | |
| 48 | var dhGroup1Once sync.Once |
| 49 | |
| 50 | func initDHGroup1() { |
| 51 | p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) |
| 52 | |
| 53 | dhGroup1 = &dhGroup{ |
| 54 | g: new(big.Int).SetInt64(2), |
| 55 | p: p, |
| 56 | } |
| 57 | } |
| 58 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 59 | // dhGroup14 is the group called diffie-hellman-group14-sha1 in RFC 4253 and |
| 60 | // Oakley Group 14 in RFC 3526. |
| 61 | var dhGroup14 *dhGroup |
| 62 | |
| 63 | var dhGroup14Once sync.Once |
| 64 | |
| 65 | func initDHGroup14() { |
| 66 | p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) |
| 67 | |
| 68 | dhGroup14 = &dhGroup{ |
| 69 | g: new(big.Int).SetInt64(2), |
| 70 | p: p, |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | // UnexpectedMessageError results when the SSH message that we received didn't |
| 75 | // match what we wanted. |
| 76 | type UnexpectedMessageError struct { |
| 77 | expected, got uint8 |
| 78 | } |
| 79 | |
| 80 | func (u UnexpectedMessageError) Error() string { |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 81 | return fmt.Sprintf("ssh: unexpected message type %d (expected %d)", u.got, u.expected) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | // ParseError results from a malformed SSH message. |
| 85 | type ParseError struct { |
| 86 | msgType uint8 |
| 87 | } |
| 88 | |
| 89 | func (p ParseError) Error() string { |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 90 | return fmt.Sprintf("ssh: parse error in message type %d", p.msgType) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | type handshakeMagics struct { |
| 94 | clientVersion, serverVersion []byte |
| 95 | clientKexInit, serverKexInit []byte |
| 96 | } |
| 97 | |
| 98 | func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) { |
| 99 | for _, clientAlgo := range clientAlgos { |
| 100 | for _, serverAlgo := range serverAlgos { |
| 101 | if clientAlgo == serverAlgo { |
| 102 | return clientAlgo, true |
| 103 | } |
| 104 | } |
| 105 | } |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 106 | return |
| 107 | } |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 108 | |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 109 | func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCipher string, ok bool) { |
| 110 | for _, clientCipher := range clientCiphers { |
| 111 | for _, serverCipher := range serverCiphers { |
| 112 | // reject the cipher if we have no cipherModes definition |
| 113 | if clientCipher == serverCipher && cipherModes[clientCipher] != nil { |
| 114 | return clientCipher, true |
| 115 | } |
| 116 | } |
| 117 | } |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 118 | return |
| 119 | } |
| 120 | |
| 121 | func findAgreedAlgorithms(transport *transport, clientKexInit, serverKexInit *kexInitMsg) (kexAlgo, hostKeyAlgo string, ok bool) { |
| 122 | kexAlgo, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos) |
| 123 | if !ok { |
| 124 | return |
| 125 | } |
| 126 | |
| 127 | hostKeyAlgo, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) |
| 128 | if !ok { |
| 129 | return |
| 130 | } |
| 131 | |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 132 | transport.writer.cipherAlgo, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 133 | if !ok { |
| 134 | return |
| 135 | } |
| 136 | |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 137 | transport.reader.cipherAlgo, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 138 | if !ok { |
| 139 | return |
| 140 | } |
| 141 | |
| 142 | transport.writer.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) |
| 143 | if !ok { |
| 144 | return |
| 145 | } |
| 146 | |
| 147 | transport.reader.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) |
| 148 | if !ok { |
| 149 | return |
| 150 | } |
| 151 | |
| 152 | transport.writer.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) |
| 153 | if !ok { |
| 154 | return |
| 155 | } |
| 156 | |
| 157 | transport.reader.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) |
| 158 | if !ok { |
| 159 | return |
| 160 | } |
| 161 | |
| 162 | ok = true |
| 163 | return |
| 164 | } |
| 165 | |
| 166 | // Cryptographic configuration common to both ServerConfig and ClientConfig. |
| 167 | type CryptoConfig struct { |
| 168 | // The allowed cipher algorithms. If unspecified then DefaultCipherOrder is |
| 169 | // used. |
| 170 | Ciphers []string |
Dave Cheney | 6de97b5 | 2012-02-27 19:40:52 -0500 | [diff] [blame] | 171 | |
| 172 | // The allowed MAC algorithms. If unspecified then DefaultMACOrder is used. |
| 173 | MACs []string |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 174 | } |
| 175 | |
| 176 | func (c *CryptoConfig) ciphers() []string { |
| 177 | if c.Ciphers == nil { |
| 178 | return DefaultCipherOrder |
| 179 | } |
| 180 | return c.Ciphers |
| 181 | } |
| 182 | |
Dave Cheney | 6de97b5 | 2012-02-27 19:40:52 -0500 | [diff] [blame] | 183 | func (c *CryptoConfig) macs() []string { |
| 184 | if c.MACs == nil { |
| 185 | return DefaultMACOrder |
| 186 | } |
| 187 | return c.MACs |
| 188 | } |
| 189 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 190 | // serialize a signed slice according to RFC 4254 6.6. |
| 191 | func serializeSignature(algoname string, sig []byte) []byte { |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 192 | switch algoname { |
| 193 | // The corresponding private key to a public certificate is always a normal |
| 194 | // private key. For signature serialization purposes, ensure we use the |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 195 | // proper key algorithm name in case the public cert algorithm name is passed. |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame^] | 196 | case CertAlgoRSAv01: |
| 197 | algoname = KeyAlgoRSA |
| 198 | case CertAlgoDSAv01: |
| 199 | algoname = KeyAlgoDSA |
| 200 | case CertAlgoECDSA256v01: |
| 201 | algoname = KeyAlgoECDSA256 |
| 202 | case CertAlgoECDSA384v01: |
| 203 | algoname = KeyAlgoECDSA384 |
| 204 | case CertAlgoECDSA521v01: |
| 205 | algoname = KeyAlgoECDSA521 |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 206 | } |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 207 | length := stringLength(len(algoname)) |
| 208 | length += stringLength(len(sig)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 209 | |
| 210 | ret := make([]byte, length) |
| 211 | r := marshalString(ret, []byte(algoname)) |
| 212 | r = marshalString(r, sig) |
| 213 | |
| 214 | return ret |
| 215 | } |
| 216 | |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 217 | // serialize a *rsa.PublicKey or *dsa.PublicKey according to RFC 4253 6.6. |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 218 | func serializePublickey(key interface{}) []byte { |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 219 | var pubKeyBytes []byte |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 220 | algoname := algoName(key) |
| 221 | switch key := key.(type) { |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 222 | case *rsa.PublicKey: |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 223 | pubKeyBytes = marshalPubRSA(key) |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 224 | case *dsa.PublicKey: |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 225 | pubKeyBytes = marshalPubDSA(key) |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 226 | case *ecdsa.PublicKey: |
| 227 | pubKeyBytes = marshalPubECDSA(key) |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 228 | case *OpenSSHCertV01: |
| 229 | pubKeyBytes = marshalOpenSSHCertV01(key) |
| 230 | default: |
| 231 | panic("unexpected key type") |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 232 | } |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 233 | |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 234 | length := stringLength(len(algoname)) |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 235 | length += len(pubKeyBytes) |
| 236 | ret := make([]byte, length) |
| 237 | r := marshalString(ret, []byte(algoname)) |
| 238 | copy(r, pubKeyBytes) |
| 239 | return ret |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 240 | } |
| 241 | |
| 242 | func algoName(key interface{}) string { |
| 243 | switch key.(type) { |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 244 | case *rsa.PublicKey: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame^] | 245 | return KeyAlgoRSA |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 246 | case *dsa.PublicKey: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame^] | 247 | return KeyAlgoDSA |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 248 | case *ecdsa.PublicKey: |
| 249 | switch key.(*ecdsa.PublicKey).Params().BitSize { |
| 250 | case 256: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame^] | 251 | return KeyAlgoECDSA256 |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 252 | case 384: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame^] | 253 | return KeyAlgoECDSA384 |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 254 | case 521: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame^] | 255 | return KeyAlgoECDSA521 |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 256 | } |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 257 | case *OpenSSHCertV01: |
| 258 | return algoName(key.(*OpenSSHCertV01).Key) + "-cert-v01@openssh.com" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 259 | } |
| 260 | panic("unexpected key type") |
| 261 | } |
| 262 | |
| 263 | // buildDataSignedForAuth returns the data that is signed in order to prove |
| 264 | // posession of a private key. See RFC 4252, section 7. |
| 265 | func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { |
| 266 | user := []byte(req.User) |
| 267 | service := []byte(req.Service) |
| 268 | method := []byte(req.Method) |
| 269 | |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 270 | length := stringLength(len(sessionId)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 271 | length += 1 |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 272 | length += stringLength(len(user)) |
| 273 | length += stringLength(len(service)) |
| 274 | length += stringLength(len(method)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 275 | length += 1 |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 276 | length += stringLength(len(algo)) |
| 277 | length += stringLength(len(pubKey)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 278 | |
| 279 | ret := make([]byte, length) |
| 280 | r := marshalString(ret, sessionId) |
| 281 | r[0] = msgUserAuthRequest |
| 282 | r = r[1:] |
| 283 | r = marshalString(r, user) |
| 284 | r = marshalString(r, service) |
| 285 | r = marshalString(r, method) |
| 286 | r[0] = 1 |
| 287 | r = r[1:] |
| 288 | r = marshalString(r, algo) |
| 289 | r = marshalString(r, pubKey) |
| 290 | return ret |
| 291 | } |
| 292 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 293 | // safeString sanitises s according to RFC 4251, section 9.2. |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 294 | // All control characters except tab, carriage return and newline are |
| 295 | // replaced by 0x20. |
| 296 | func safeString(s string) string { |
| 297 | out := []byte(s) |
| 298 | for i, c := range out { |
| 299 | if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 { |
| 300 | out[i] = 0x20 |
| 301 | } |
| 302 | } |
| 303 | return string(out) |
| 304 | } |
Dave Cheney | 79d53bd | 2012-03-04 14:34:24 -0800 | [diff] [blame] | 305 | |
| 306 | func appendU16(buf []byte, n uint16) []byte { |
| 307 | return append(buf, byte(n>>8), byte(n)) |
| 308 | } |
| 309 | |
| 310 | func appendU32(buf []byte, n uint32) []byte { |
| 311 | return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) |
| 312 | } |
| 313 | |
| 314 | func appendInt(buf []byte, n int) []byte { |
| 315 | return appendU32(buf, uint32(n)) |
| 316 | } |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 317 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 318 | // newCond is a helper to hide the fact that there is no usable zero |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 319 | // value for sync.Cond. |
| 320 | func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } |
| 321 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 322 | // window represents the buffer available to clients |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 323 | // wishing to write to a channel. |
| 324 | type window struct { |
| 325 | *sync.Cond |
| 326 | win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 |
| 327 | } |
| 328 | |
| 329 | // add adds win to the amount of window available |
| 330 | // for consumers. |
| 331 | func (w *window) add(win uint32) bool { |
Dave Cheney | 55aa081 | 2012-05-22 12:04:51 +1000 | [diff] [blame] | 332 | // a zero sized window adjust is a noop. |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 333 | if win == 0 { |
Dave Cheney | 55aa081 | 2012-05-22 12:04:51 +1000 | [diff] [blame] | 334 | return true |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 335 | } |
| 336 | w.L.Lock() |
| 337 | if w.win+win < win { |
| 338 | w.L.Unlock() |
| 339 | return false |
| 340 | } |
| 341 | w.win += win |
| 342 | // It is unusual that multiple goroutines would be attempting to reserve |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 343 | // window space, but not guaranteed. Use broadcast to notify all waiters |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 344 | // that additional window is available. |
| 345 | w.Broadcast() |
| 346 | w.L.Unlock() |
| 347 | return true |
| 348 | } |
| 349 | |
| 350 | // reserve reserves win from the available window capacity. |
| 351 | // If no capacity remains, reserve will block. reserve may |
| 352 | // return less than requested. |
| 353 | func (w *window) reserve(win uint32) uint32 { |
| 354 | w.L.Lock() |
| 355 | for w.win == 0 { |
| 356 | w.Wait() |
| 357 | } |
| 358 | if w.win < win { |
| 359 | win = w.win |
| 360 | } |
| 361 | w.win -= win |
| 362 | w.L.Unlock() |
| 363 | return win |
| 364 | } |