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