Ver Fonte

Consistent style in the platform TLS implementations

Nils Maier há 11 anos atrás
pai
commit
77f0f1395c
8 ficheiros alterados com 1005 adições e 909 exclusões
  1. 146 114
      src/AppleTLSContext.cc
  2. 18 11
      src/AppleTLSContext.h
  3. 409 376
      src/AppleTLSSession.cc
  4. 11 6
      src/AppleTLSSession.h
  5. 23 32
      src/WinTLSContext.cc
  6. 22 14
      src/WinTLSContext.h
  7. 307 307
      src/WinTLSSession.cc
  8. 69 49
      src/WinTLSSession.h

+ 146 - 114
src/AppleTLSContext.cc

@@ -51,130 +51,148 @@
 #include "util.h"
 
 namespace {
-  using namespace aria2;
+using namespace aria2;
 
 #if defined(__MAC_10_6)
 
 #if defined(__MAC_10_7)
-  static const void *query_keys[] = {
-    kSecClass,
-    kSecReturnRef,
-    kSecMatchPolicy,
-    kSecMatchLimit
-  };
+static const void* query_keys[] = {
+  kSecClass,
+  kSecReturnRef,
+  kSecMatchPolicy,
+  kSecMatchLimit
+};
 #endif // defined(__MAC_10_7)
 
-  template<typename T>
-  class CFRef {
-    T ref_;
-  public:
-    CFRef() : ref_(nullptr) {}
-    CFRef(T ref) : ref_(ref) {}
-    ~CFRef() {
-      reset(nullptr);
-    }
-    void reset(T ref) {
-      if (ref_) {
-        CFRelease(ref_);
-      }
-      ref_ = ref;
-    }
-    T get() {
-      return ref_;
-    }
-    const T get() const {
-      return ref_;
-    }
-    operator bool() const {
-      return !!ref_;
-    }
-  };
+template <typename T>
+class CFRef
+{
+  T ref_;
+
+public:
+  CFRef() : ref_(nullptr) {}
+
+  CFRef(T ref) : ref_(ref) {}
 
-  static inline bool isWhitespace(char c)
+  ~CFRef()
   {
-    // Fingerprints are often separated by colons
-    return isspace(c) || c == ':';
+    reset(nullptr);
   }
 
-  static inline std::string stripWhitespace(std::string str)
+  void reset(T ref)
   {
-    str.erase(std::remove_if(str.begin(), str.end(), isWhitespace), str.end());
-    return str;
+    if (ref_) {
+      CFRelease(ref_);
+    }
+    ref_ = ref;
   }
 
-  struct hash_validator {
-    const std::string& hash_;
+  T get()
+  {
+    return ref_;
+  }
 
-    hash_validator(const std::string& hash) : hash_(hash) {}
+  const T get() const
+  {
+    return ref_;
+  }
 
-    inline bool operator()(std::string type) const {
-      return MessageDigest::isValidHash(type, hash_);
-    }
-  };
+  operator bool() const
+  {
+    return !!ref_;
+  }
+};
 
-  struct hash_finder {
-    CFDataRef data_;
-    const std::string& hash_;
+static inline bool isWhitespace(char c)
+{
+  // Fingerprints are often separated by colons
+  return isspace(c) || c == ':';
+}
 
-    hash_finder(CFDataRef data, const std::string& hash)
-      : data_(data), hash_(hash)
-    {}
+static inline std::string stripWhitespace(std::string str)
+{
+  str.erase(std::remove_if(str.begin(), str.end(), isWhitespace), str.end());
+  return str;
+}
 
-    inline bool operator()(std::string type) const {
-      std::string hash = MessageDigest::create(type)->update(
-          CFDataGetBytePtr(data_), CFDataGetLength(data_)).digest();
-      hash = util::toHex(hash);
-      return hash == hash_;
-    }
-  };
+struct hash_validator
+{
+  const std::string& hash_;
 
+  hash_validator(const std::string& hash) : hash_(hash) {}
 
-  std::string errToString(OSStatus err)
+  inline bool operator()(std::string type) const
   {
-    std::string rv = "Unkown error";
-    CFRef<CFStringRef> cerr(SecCopyErrorMessageString(err, nullptr));
-    if (!cerr) {
-      return rv;
-    }
-    size_t len = CFStringGetLength(cerr.get()) * 4;
-    auto buf = make_unique<char[]>(len);
-    if (CFStringGetCString(cerr.get(), buf.get(), len, kCFStringEncodingUTF8)) {
-      rv = buf.get();
-    }
-    return rv;
+    return MessageDigest::isValidHash(type, hash_);
   }
+};
+
+struct hash_finder
+{
+  CFDataRef data_;
+  const std::string& hash_;
 
-  bool checkIdentity(const SecIdentityRef id, const std::string& fingerPrint,
-                     const std::vector<std::string> supported)
+  hash_finder(CFDataRef data, const std::string& hash)
+    : data_(data), hash_(hash)
+  {}
+
+  inline bool operator()(std::string type) const
   {
-    CFRef<SecCertificateRef> ref;
-    SecCertificateRef raw_ref = nullptr;
-    if (SecIdentityCopyCertificate(id, &raw_ref) != errSecSuccess) {
-      A2_LOG_ERROR("Failed to get a certref!");
-      return false;
-    }
-    ref.reset(raw_ref);
+    std::string hash =
+        MessageDigest::create(type)
+            ->update(CFDataGetBytePtr(data_), CFDataGetLength(data_))
+            .digest();
+    hash = util::toHex(hash);
+    return hash == hash_;
+  }
+};
 
-    CFRef<CFDataRef> data(SecCertificateCopyData(ref.get()));
-    if (!data) {
-      A2_LOG_ERROR("Failed to get a data!");
-      return false;
-    }
+std::string errToString(OSStatus err)
+{
+  std::string rv = "Unkown error";
+  CFRef<CFStringRef> cerr(SecCopyErrorMessageString(err, nullptr));
+  if (!cerr) {
+    return rv;
+  }
+  size_t len = CFStringGetLength(cerr.get()) * 4;
+  auto buf = make_unique<char[]>(len);
+  if (CFStringGetCString(cerr.get(), buf.get(), len, kCFStringEncodingUTF8)) {
+    rv = buf.get();
+  }
+  return rv;
+}
 
-    // Do try all supported hash algorithms.
-    // Usually the fingerprint would be sha1 or md5, however this is more
-    // future-proof. Also "usually" doesn't cut it; there is already software
-    // using SHA-2 class algos, and SHA-3 is standardized and potential users
-    // cannot be far.
-    return std::find_if(
-        supported.begin(), supported.end(), hash_finder(data.get(), fingerPrint))
-      != supported.end();
+bool checkIdentity(const SecIdentityRef id,
+                   const std::string& fingerPrint,
+                   const std::vector<std::string> supported)
+{
+  CFRef<SecCertificateRef> ref;
+  SecCertificateRef raw_ref = nullptr;
+  if (SecIdentityCopyCertificate(id, &raw_ref) != errSecSuccess) {
+    A2_LOG_ERROR("Failed to get a certref!");
+    return false;
   }
+  ref.reset(raw_ref);
 
-#endif // defined(__MAC_10_6)
+  CFRef<CFDataRef> data(SecCertificateCopyData(ref.get()));
+  if (!data) {
+    A2_LOG_ERROR("Failed to get a data!");
+    return false;
+  }
 
+  // Do try all supported hash algorithms.
+  // Usually the fingerprint would be sha1 or md5, however this is more
+  // future-proof. Also "usually" doesn't cut it; there is already software
+  // using SHA-2 class algos, and SHA-3 is standardized and potential users
+  // cannot be far.
+  return std::find_if(supported.begin(),
+                      supported.end(),
+                      hash_finder(data.get(), fingerPrint)) != supported.end();
 }
 
+#endif // defined(__MAC_10_6)
+} // namespace
+
 namespace aria2 {
 
 TLSContext* TLSContext::make(TLSSessionSide side)
@@ -204,13 +222,15 @@ bool AppleTLSContext::addCredentialFile(const std::string& certfile,
     return true;
   }
 
-  A2_LOG_WARN("Only PKCS12/PFX files with a blank password and fingerprints of certificates in your KeyChain are supported. See the manual.");
+  A2_LOG_WARN("Only PKCS12/PFX files with a blank password and fingerprints of "
+              "certificates in your KeyChain are supported. See the manual.");
   return false;
 }
 
 bool AppleTLSContext::addTrustedCACertFile(const std::string& certfile)
 {
-  A2_LOG_INFO("TLS CA bundle files are not supported. Use the KeyChain to manage your certificates.");
+  A2_LOG_INFO("TLS CA bundle files are not supported. Use the KeyChain to "
+              "manage your certificates.");
   return false;
 }
 
@@ -228,7 +248,8 @@ bool AppleTLSContext::tryAsFingerprint(const std::string& fingerprint)
   // Verify this can represent a hash
   auto ht = MessageDigest::getSupportedHashTypes();
   if (std::find_if(ht.begin(), ht.end(), hash_validator(fp)) == ht.end()) {
-    A2_LOG_INFO(fmt("%s is not a fingerprint, invalid hash representation", fingerprint.c_str()));
+    A2_LOG_INFO(fmt("%s is not a fingerprint, invalid hash representation",
+                    fingerprint.c_str()));
     return false;
   }
 
@@ -241,14 +262,14 @@ bool AppleTLSContext::tryAsFingerprint(const std::string& fingerprint)
     A2_LOG_ERROR("Failed to create SecPolicy");
     return false;
   }
-  const void *query_values[] = {
+  const void* query_values[] = {
     kSecClassIdentity,
     kCFBooleanTrue,
     policy.get(),
     kSecMatchLimitAll
   };
   CFRef<CFDictionaryRef> query(CFDictionaryCreate(
-        nullptr, query_keys, query_values, 4, nullptr, nullptr));
+      nullptr, query_keys, query_values, 4, nullptr, nullptr));
   if (!query) {
     A2_LOG_ERROR("Failed to create identity query");
     return false;
@@ -277,7 +298,8 @@ bool AppleTLSContext::tryAsFingerprint(const std::string& fingerprint)
     return true;
   }
 
-  A2_LOG_ERROR(fmt("Failed to lookup %s in your KeyChain", fingerprint.c_str()));
+  A2_LOG_ERROR(
+      fmt("Failed to lookup %s in your KeyChain", fingerprint.c_str()));
   return false;
 
 #else // defined(__MAC_10_7)
@@ -303,12 +325,14 @@ bool AppleTLSContext::tryAsFingerprint(const std::string& fingerprint)
     return true;
   }
 
-  A2_LOG_ERROR(fmt("Failed to lookup %s in your KeyChain", fingerprint.c_str()));
+  A2_LOG_ERROR(
+      fmt("Failed to lookup %s in your KeyChain", fingerprint.c_str()));
   return false;
 
 #else // defined(__MAC_10_6)
 
-  A2_LOG_ERROR("Your system does not support creditials via fingerprints; Upgrade to OSX 10.6 or later");
+  A2_LOG_ERROR("Your system does not support creditials via fingerprints; "
+               "Upgrade to OSX 10.6 or later");
   return false;
 
 #endif // defined(__MAC_10_6)
@@ -325,8 +349,8 @@ bool AppleTLSContext::tryAsPKCS12(const std::string& certfile)
     A2_LOG_ERROR("Couldn't read certificate file.");
     return false;
   }
-  CFRef<CFDataRef> dataRef(CFDataCreate(
-        nullptr, (const UInt8*)data.c_str(), data.size()));
+  CFRef<CFDataRef> dataRef(
+      CFDataCreate(nullptr, (const UInt8*)data.c_str(), data.size()));
   if (!dataRef) {
     A2_LOG_ERROR("Couldn't allocate PKCS12 data");
     return false;
@@ -346,14 +370,20 @@ bool AppleTLSContext::tryAsPKCS12(CFDataRef data, const char* password)
 #if defined(__MAC_10_6)
   CFRef<CFStringRef> passwordRef;
   if (password) {
-    passwordRef.reset(CFStringCreateWithBytes(
-        nullptr, (const UInt8*)password, strlen(password),
-        kCFStringEncodingUTF8, false));
+    passwordRef.reset(CFStringCreateWithBytes(nullptr,
+                                              (const UInt8*)password,
+                                              strlen(password),
+                                              kCFStringEncodingUTF8,
+                                              false));
   }
-  const void *keys[] = { kSecImportExportPassphrase };
-  const void *values[] = { passwordRef.get() };
-  CFRef<CFDictionaryRef> options(CFDictionaryCreate(
-      nullptr, keys, values, 1, nullptr, nullptr));
+  const void* keys[] = {
+    kSecImportExportPassphrase
+  };
+  const void* values[] = {
+    passwordRef.get()
+  };
+  CFRef<CFDictionaryRef> options(
+      CFDictionaryCreate(nullptr, keys, values, 1, nullptr, nullptr));
   if (!options) {
     A2_LOG_ERROR("Failed to create options");
     return false;
@@ -363,18 +393,20 @@ bool AppleTLSContext::tryAsPKCS12(CFDataRef data, const char* password)
   CFArrayRef raw_items = nullptr;
   OSStatus rv = SecPKCS12Import(data, options.get(), &raw_items);
   if (rv != errSecSuccess) {
-    A2_LOG_DEBUG(fmt("Failed to parse PKCS12 data: %s", errToString(rv).c_str()));
+    A2_LOG_DEBUG(
+        fmt("Failed to parse PKCS12 data: %s", errToString(rv).c_str()));
     return false;
   }
   items.reset(raw_items);
 
-  CFDictionaryRef idAndTrust = (CFDictionaryRef)CFArrayGetValueAtIndex(
-      items.get(), 0);
+  CFDictionaryRef idAndTrust =
+      (CFDictionaryRef)CFArrayGetValueAtIndex(items.get(), 0);
   if (!idAndTrust) {
     A2_LOG_ERROR("Failed to get identity and trust from PKCS12 data");
     return false;
   }
-  credentials_ = (SecIdentityRef)CFDictionaryGetValue(idAndTrust, kSecImportItemIdentity);
+  credentials_ =
+      (SecIdentityRef)CFDictionaryGetValue(idAndTrust, kSecImportItemIdentity);
   if (!credentials_) {
     A2_LOG_ERROR("Failed to get credentials PKCS12 data");
     return false;

+ 18 - 11
src/AppleTLSContext.h

@@ -46,12 +46,11 @@
 
 namespace aria2 {
 
-class AppleTLSContext : public TLSContext {
+class AppleTLSContext : public TLSContext
+{
 public:
   AppleTLSContext(TLSSessionSide side)
-    : side_(side),
-      verifyPeer_(true),
-      credentials_(nullptr)
+    : side_(side), verifyPeer_(true), credentials_(nullptr)
   {}
 
   virtual ~AppleTLSContext();
@@ -60,25 +59,31 @@ public:
   virtual bool addCredentialFile(const std::string& certfile,
                                  const std::string& keyfile) CXX11_OVERRIDE;
 
-  virtual bool addSystemTrustedCACerts() CXX11_OVERRIDE {
+  virtual bool addSystemTrustedCACerts() CXX11_OVERRIDE
+  {
     return true;
   }
 
   // certfile can contain multiple certificates.
-  virtual bool addTrustedCACertFile(const std::string& certfile)
-    CXX11_OVERRIDE;
+  virtual bool addTrustedCACertFile(const std::string& certfile) CXX11_OVERRIDE;
 
-  virtual bool good() const CXX11_OVERRIDE {
+  virtual bool good() const CXX11_OVERRIDE
+  {
     return true;
   }
-  virtual TLSSessionSide getSide() const CXX11_OVERRIDE {
+
+  virtual TLSSessionSide getSide() const CXX11_OVERRIDE
+  {
     return side_;
   }
 
-  virtual bool getVerifyPeer() const CXX11_OVERRIDE {
+  virtual bool getVerifyPeer() const CXX11_OVERRIDE
+  {
     return verifyPeer_;
   }
-  virtual void setVerifyPeer(bool verify) CXX11_OVERRIDE {
+
+  virtual void setVerifyPeer(bool verify) CXX11_OVERRIDE
+  {
     verifyPeer_ = verify;
   }
 
@@ -90,7 +95,9 @@ private:
   SecIdentityRef credentials_;
 
   bool tryAsFingerprint(const std::string& fingerprint);
+
   bool tryAsPKCS12(const std::string& certfile);
+
   bool tryAsPKCS12(CFDataRef data, const char* password);
 };
 

+ 409 - 376
src/AppleTLSSession.cc

@@ -53,15 +53,15 @@
 
 namespace {
 #if !defined(__MAC_10_8)
-  static const SSLProtocol kTLSProtocol11 = (SSLProtocol)(kSSLProtocolAll + 1);
-  static const SSLProtocol kTLSProtocol12 = (SSLProtocol)(kSSLProtocolAll + 2);
+static const SSLProtocol kTLSProtocol11 = (SSLProtocol)(kSSLProtocolAll + 1);
+static const SSLProtocol kTLSProtocol12 = (SSLProtocol)(kSSLProtocolAll + 2);
 #endif
 
 #ifndef CIPHER_NO_DHPARAM
-  // Diffie-Hellman params, to seed the engine instead of having it spend up
-  // to 30 seconds on generating them. It should be save to share these. :p
-  // This was generated using: openssl dhparam -outform DER 2048
-  static const uint8_t dhparam[] =
+// Diffie-Hellman params, to seed the engine instead of having it spend up
+// to 30 seconds on generating them. It should be save to share these. :p
+// This was generated using: openssl dhparam -outform DER 2048
+static const uint8_t dhparam[] =
     "\x30\x82\x01\x08\x02\x82\x01\x01\x00\x97\xea\xd0\x46\xf7\xae\xa7\x76\x80"
     "\x9c\x74\x56\x98\xd8\x56\x97\x2b\x20\x6c\x77\xe2\x82\xbb\xc8\x84\xbe\xe7"
     "\x63\xaf\xcc\x30\xd0\x67\x97\x7d\x1b\xab\x59\x30\xa9\x13\x67\x21\xd7\xd4"
@@ -79,270 +79,261 @@ namespace {
     "\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02";
 #endif // CIPHER_NO_DHPARAM
 
-  static inline const char *protoToString(SSLProtocol proto) {
-    switch (proto) {
-      case kSSLProtocol2:
-        return "SSLv2 (!)";
-      case kSSLProtocol3:
-        return "SSLv3";
-      case kTLSProtocol1:
-        return "TLSv1";
-      case kTLSProtocol11:
-        return "TLSv1.1";
-      case kTLSProtocol12:
-        return "TLSv1.2";
-      default:
-        return "Unknown";
-    }
+static inline const char* protoToString(SSLProtocol proto)
+{
+  switch (proto) {
+  case kSSLProtocol2:
+    return "SSLv2 (!)";
+  case kSSLProtocol3:
+    return "SSLv3";
+  case kTLSProtocol1:
+    return "TLSv1";
+  case kTLSProtocol11:
+    return "TLSv1.1";
+  case kTLSProtocol12:
+    return "TLSv1.2";
+  default:
+    return "Unknown";
   }
+}
 
-#define SUITE(s, n) { n, #s }
-  static struct {
-    SSLCipherSuite suite;
-    const char *name;
-  } kSuites[] = {
-    // From CipherSuite.h (10.9)
-    SUITE(SSL_NULL_WITH_NULL_NULL, 0x0000),
-    SUITE(SSL_RSA_WITH_NULL_MD5, 0x0001),
-    SUITE(SSL_RSA_WITH_NULL_SHA, 0x0002),
-    SUITE(SSL_RSA_EXPORT_WITH_RC4_40_MD5, 0x0003),
-    SUITE(SSL_RSA_WITH_RC4_128_MD5, 0x0004),
-    SUITE(SSL_RSA_WITH_RC4_128_SHA, 0x0005),
-    SUITE(SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, 0x0006),
-    SUITE(SSL_RSA_WITH_IDEA_CBC_SHA, 0x0007),
-    SUITE(SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, 0x0008),
-    SUITE(SSL_RSA_WITH_DES_CBC_SHA, 0x0009),
-    SUITE(SSL_RSA_WITH_3DES_EDE_CBC_SHA, 0x000A),
-    SUITE(SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, 0x000B),
-    SUITE(SSL_DH_DSS_WITH_DES_CBC_SHA, 0x000C),
-    SUITE(SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA, 0x000D),
-    SUITE(SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, 0x000E),
-    SUITE(SSL_DH_RSA_WITH_DES_CBC_SHA, 0x000F),
-    SUITE(SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA, 0x0010),
-    SUITE(SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, 0x0011),
-    SUITE(SSL_DHE_DSS_WITH_DES_CBC_SHA, 0x0012),
-    SUITE(SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, 0x0013),
-    SUITE(SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, 0x0014),
-    SUITE(SSL_DHE_RSA_WITH_DES_CBC_SHA, 0x0015),
-    SUITE(SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 0x0016),
-    SUITE(SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, 0x0017),
-    SUITE(SSL_DH_anon_WITH_RC4_128_MD5, 0x0018),
-    SUITE(SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA, 0x0019),
-    SUITE(SSL_DH_anon_WITH_DES_CBC_SHA, 0x001A),
-    SUITE(SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, 0x001B),
-    SUITE(SSL_FORTEZZA_DMS_WITH_NULL_SHA, 0x001C),
-    SUITE(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, 0x001D),
-
-    SUITE(TLS_RSA_WITH_AES_128_CBC_SHA, 0x002F),
-    SUITE(TLS_DH_DSS_WITH_AES_128_CBC_SHA, 0x0030),
-    SUITE(TLS_DH_RSA_WITH_AES_128_CBC_SHA, 0x0031),
-    SUITE(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, 0x0032),
-    SUITE(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, 0x0033),
-    SUITE(TLS_DH_anon_WITH_AES_128_CBC_SHA, 0x0034),
-    SUITE(TLS_RSA_WITH_AES_256_CBC_SHA, 0x0035),
-    SUITE(TLS_DH_DSS_WITH_AES_256_CBC_SHA, 0x0036),
-    SUITE(TLS_DH_RSA_WITH_AES_256_CBC_SHA, 0x0037),
-    SUITE(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, 0x0038),
-    SUITE(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, 0x0039),
-    SUITE(TLS_DH_anon_WITH_AES_256_CBC_SHA, 0x003A),
-
-    SUITE(TLS_ECDH_ECDSA_WITH_NULL_SHA, 0xC001),
-    SUITE(TLS_ECDH_ECDSA_WITH_RC4_128_SHA, 0xC002),
-    SUITE(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, 0xC003),
-    SUITE(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, 0xC004),
-    SUITE(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, 0xC005),
-    SUITE(TLS_ECDHE_ECDSA_WITH_NULL_SHA, 0xC006),
-    SUITE(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 0xC007),
-    SUITE(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, 0xC008),
-    SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 0xC009),
-    SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 0xC00A),
-    SUITE(TLS_ECDH_RSA_WITH_NULL_SHA, 0xC00B),
-    SUITE(TLS_ECDH_RSA_WITH_RC4_128_SHA, 0xC00C),
-    SUITE(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, 0xC00D),
-    SUITE(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, 0xC00E),
-    SUITE(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, 0xC00F),
-    SUITE(TLS_ECDHE_RSA_WITH_NULL_SHA, 0xC010),
-    SUITE(TLS_ECDHE_RSA_WITH_RC4_128_SHA, 0xC011),
-    SUITE(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 0xC012),
-    SUITE(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0xC013),
-    SUITE(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0xC014),
-    SUITE(TLS_ECDH_anon_WITH_NULL_SHA, 0xC015),
-    SUITE(TLS_ECDH_anon_WITH_RC4_128_SHA, 0xC016),
-    SUITE(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, 0xC017),
-    SUITE(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, 0xC018),
-    SUITE(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, 0xC019),
-
-    SUITE(TLS_NULL_WITH_NULL_NULL, 0x0000),
-
-    SUITE(TLS_RSA_WITH_NULL_MD5, 0x0001),
-    SUITE(TLS_RSA_WITH_NULL_SHA, 0x0002),
-    SUITE(TLS_RSA_WITH_RC4_128_MD5, 0x0004),
-    SUITE(TLS_RSA_WITH_RC4_128_SHA, 0x0005),
-    SUITE(TLS_RSA_WITH_3DES_EDE_CBC_SHA, 0x000A),
-    SUITE(TLS_RSA_WITH_NULL_SHA256, 0x003B),
-    SUITE(TLS_RSA_WITH_AES_128_CBC_SHA256, 0x003C),
-    SUITE(TLS_RSA_WITH_AES_256_CBC_SHA256, 0x003D),
-
-    SUITE(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, 0x000D),
-    SUITE(TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, 0x0010),
-    SUITE(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, 0x0013),
-    SUITE(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 0x0016),
-    SUITE(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, 0x003E),
-    SUITE(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, 0x003F),
-    SUITE(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, 0x0040),
-    SUITE(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 0x0067),
-    SUITE(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, 0x0068),
-    SUITE(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, 0x0069),
-    SUITE(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, 0x006A),
-    SUITE(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, 0x006B),
-
-    SUITE(TLS_DH_anon_WITH_RC4_128_MD5, 0x0018),
-    SUITE(TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, 0x001B),
-    SUITE(TLS_DH_anon_WITH_AES_128_CBC_SHA256, 0x006C),
-    SUITE(TLS_DH_anon_WITH_AES_256_CBC_SHA256, 0x006D),
-
-    SUITE(TLS_PSK_WITH_RC4_128_SHA, 0x008A),
-    SUITE(TLS_PSK_WITH_3DES_EDE_CBC_SHA, 0x008B),
-    SUITE(TLS_PSK_WITH_AES_128_CBC_SHA, 0x008C),
-    SUITE(TLS_PSK_WITH_AES_256_CBC_SHA, 0x008D),
-    SUITE(TLS_DHE_PSK_WITH_RC4_128_SHA, 0x008E),
-    SUITE(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, 0x008F),
-    SUITE(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, 0x0090),
-    SUITE(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, 0x0091),
-    SUITE(TLS_RSA_PSK_WITH_RC4_128_SHA, 0x0092),
-    SUITE(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, 0x0093),
-    SUITE(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, 0x0094),
-    SUITE(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, 0x0095),
-
-    SUITE(TLS_PSK_WITH_NULL_SHA, 0x002C),
-    SUITE(TLS_DHE_PSK_WITH_NULL_SHA, 0x002D),
-    SUITE(TLS_RSA_PSK_WITH_NULL_SHA, 0x002E),
-
-    SUITE(TLS_RSA_WITH_AES_128_GCM_SHA256, 0x009C),
-    SUITE(TLS_RSA_WITH_AES_256_GCM_SHA384, 0x009D),
-    SUITE(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 0x009E),
-    SUITE(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 0x009F),
-    SUITE(TLS_DH_RSA_WITH_AES_128_GCM_SHA256, 0x00A0),
-    SUITE(TLS_DH_RSA_WITH_AES_256_GCM_SHA384, 0x00A1),
-    SUITE(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, 0x00A2),
-    SUITE(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, 0x00A3),
-    SUITE(TLS_DH_DSS_WITH_AES_128_GCM_SHA256, 0x00A4),
-    SUITE(TLS_DH_DSS_WITH_AES_256_GCM_SHA384, 0x00A5),
-    SUITE(TLS_DH_anon_WITH_AES_128_GCM_SHA256, 0x00A6),
-    SUITE(TLS_DH_anon_WITH_AES_256_GCM_SHA384, 0x00A7),
-
-    SUITE(TLS_PSK_WITH_AES_128_GCM_SHA256, 0x00A8),
-    SUITE(TLS_PSK_WITH_AES_256_GCM_SHA384, 0x00A9),
-    SUITE(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, 0x00AA),
-    SUITE(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, 0x00AB),
-    SUITE(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, 0x00AC),
-    SUITE(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, 0x00AD),
-
-    SUITE(TLS_PSK_WITH_AES_128_CBC_SHA256, 0x00AE),
-    SUITE(TLS_PSK_WITH_AES_256_CBC_SHA384, 0x00AF),
-    SUITE(TLS_PSK_WITH_NULL_SHA256, 0x00B0),
-    SUITE(TLS_PSK_WITH_NULL_SHA384, 0x00B1),
-
-    SUITE(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, 0x00B2),
-    SUITE(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, 0x00B3),
-    SUITE(TLS_DHE_PSK_WITH_NULL_SHA256, 0x00B4),
-    SUITE(TLS_DHE_PSK_WITH_NULL_SHA384, 0x00B5),
-
-    SUITE(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, 0x00B6),
-    SUITE(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, 0x00B7),
-    SUITE(TLS_RSA_PSK_WITH_NULL_SHA256, 0x00B8),
-    SUITE(TLS_RSA_PSK_WITH_NULL_SHA384, 0x00B9),
-
-    SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 0xC023),
-    SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 0xC024),
-    SUITE(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, 0xC025),
-    SUITE(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, 0xC026),
-    SUITE(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 0xC027),
-    SUITE(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 0xC028),
-    SUITE(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, 0xC029),
-    SUITE(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, 0xC02A),
-
-    SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0xC02B),
-    SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 0xC02C),
-    SUITE(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, 0xC02D),
-    SUITE(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, 0xC02E),
-    SUITE(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0xC02F),
-    SUITE(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 0xC030),
-    SUITE(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, 0xC031),
-    SUITE(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, 0xC032),
-
-    SUITE(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, 0x00FF),
-    SUITE(SSL_RSA_WITH_RC2_CBC_MD5, 0xFF80),
-    SUITE(SSL_RSA_WITH_IDEA_CBC_MD5, 0xFF81),
-    SUITE(SSL_RSA_WITH_DES_CBC_MD5, 0xFF82),
-    SUITE(SSL_RSA_WITH_3DES_EDE_CBC_MD5, 0xFF83),
-    SUITE(SSL_NO_SUCH_CIPHERSUITE, 0xFFFF)
-  };
+#define SUITE(s, n)                                                            \
+  {                                                                            \
+    n, #s                                                                      \
+  }
+static struct
+{
+  SSLCipherSuite suite;
+  const char* name;
+} kSuites[] = {
+      // From CipherSuite.h (10.9)
+      SUITE(SSL_NULL_WITH_NULL_NULL, 0x0000),
+      SUITE(SSL_RSA_WITH_NULL_MD5, 0x0001),
+      SUITE(SSL_RSA_WITH_NULL_SHA, 0x0002),
+      SUITE(SSL_RSA_EXPORT_WITH_RC4_40_MD5, 0x0003),
+      SUITE(SSL_RSA_WITH_RC4_128_MD5, 0x0004),
+      SUITE(SSL_RSA_WITH_RC4_128_SHA, 0x0005),
+      SUITE(SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, 0x0006),
+      SUITE(SSL_RSA_WITH_IDEA_CBC_SHA, 0x0007),
+      SUITE(SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, 0x0008),
+      SUITE(SSL_RSA_WITH_DES_CBC_SHA, 0x0009),
+      SUITE(SSL_RSA_WITH_3DES_EDE_CBC_SHA, 0x000A),
+      SUITE(SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, 0x000B),
+      SUITE(SSL_DH_DSS_WITH_DES_CBC_SHA, 0x000C),
+      SUITE(SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA, 0x000D),
+      SUITE(SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, 0x000E),
+      SUITE(SSL_DH_RSA_WITH_DES_CBC_SHA, 0x000F),
+      SUITE(SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA, 0x0010),
+      SUITE(SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, 0x0011),
+      SUITE(SSL_DHE_DSS_WITH_DES_CBC_SHA, 0x0012),
+      SUITE(SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, 0x0013),
+      SUITE(SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, 0x0014),
+      SUITE(SSL_DHE_RSA_WITH_DES_CBC_SHA, 0x0015),
+      SUITE(SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 0x0016),
+      SUITE(SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, 0x0017),
+      SUITE(SSL_DH_anon_WITH_RC4_128_MD5, 0x0018),
+      SUITE(SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA, 0x0019),
+      SUITE(SSL_DH_anon_WITH_DES_CBC_SHA, 0x001A),
+      SUITE(SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, 0x001B),
+      SUITE(SSL_FORTEZZA_DMS_WITH_NULL_SHA, 0x001C),
+      SUITE(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, 0x001D),
+      SUITE(TLS_RSA_WITH_AES_128_CBC_SHA, 0x002F),
+      SUITE(TLS_DH_DSS_WITH_AES_128_CBC_SHA, 0x0030),
+      SUITE(TLS_DH_RSA_WITH_AES_128_CBC_SHA, 0x0031),
+      SUITE(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, 0x0032),
+      SUITE(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, 0x0033),
+      SUITE(TLS_DH_anon_WITH_AES_128_CBC_SHA, 0x0034),
+      SUITE(TLS_RSA_WITH_AES_256_CBC_SHA, 0x0035),
+      SUITE(TLS_DH_DSS_WITH_AES_256_CBC_SHA, 0x0036),
+      SUITE(TLS_DH_RSA_WITH_AES_256_CBC_SHA, 0x0037),
+      SUITE(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, 0x0038),
+      SUITE(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, 0x0039),
+      SUITE(TLS_DH_anon_WITH_AES_256_CBC_SHA, 0x003A),
+      SUITE(TLS_ECDH_ECDSA_WITH_NULL_SHA, 0xC001),
+      SUITE(TLS_ECDH_ECDSA_WITH_RC4_128_SHA, 0xC002),
+      SUITE(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, 0xC003),
+      SUITE(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, 0xC004),
+      SUITE(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, 0xC005),
+      SUITE(TLS_ECDHE_ECDSA_WITH_NULL_SHA, 0xC006),
+      SUITE(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 0xC007),
+      SUITE(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, 0xC008),
+      SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 0xC009),
+      SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 0xC00A),
+      SUITE(TLS_ECDH_RSA_WITH_NULL_SHA, 0xC00B),
+      SUITE(TLS_ECDH_RSA_WITH_RC4_128_SHA, 0xC00C),
+      SUITE(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, 0xC00D),
+      SUITE(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, 0xC00E),
+      SUITE(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, 0xC00F),
+      SUITE(TLS_ECDHE_RSA_WITH_NULL_SHA, 0xC010),
+      SUITE(TLS_ECDHE_RSA_WITH_RC4_128_SHA, 0xC011),
+      SUITE(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 0xC012),
+      SUITE(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0xC013),
+      SUITE(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0xC014),
+      SUITE(TLS_ECDH_anon_WITH_NULL_SHA, 0xC015),
+      SUITE(TLS_ECDH_anon_WITH_RC4_128_SHA, 0xC016),
+      SUITE(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, 0xC017),
+      SUITE(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, 0xC018),
+      SUITE(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, 0xC019),
+      SUITE(TLS_NULL_WITH_NULL_NULL, 0x0000),
+      SUITE(TLS_RSA_WITH_NULL_MD5, 0x0001),
+      SUITE(TLS_RSA_WITH_NULL_SHA, 0x0002),
+      SUITE(TLS_RSA_WITH_RC4_128_MD5, 0x0004),
+      SUITE(TLS_RSA_WITH_RC4_128_SHA, 0x0005),
+      SUITE(TLS_RSA_WITH_3DES_EDE_CBC_SHA, 0x000A),
+      SUITE(TLS_RSA_WITH_NULL_SHA256, 0x003B),
+      SUITE(TLS_RSA_WITH_AES_128_CBC_SHA256, 0x003C),
+      SUITE(TLS_RSA_WITH_AES_256_CBC_SHA256, 0x003D),
+      SUITE(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, 0x000D),
+      SUITE(TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, 0x0010),
+      SUITE(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, 0x0013),
+      SUITE(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 0x0016),
+      SUITE(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, 0x003E),
+      SUITE(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, 0x003F),
+      SUITE(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, 0x0040),
+      SUITE(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 0x0067),
+      SUITE(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, 0x0068),
+      SUITE(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, 0x0069),
+      SUITE(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, 0x006A),
+      SUITE(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, 0x006B),
+      SUITE(TLS_DH_anon_WITH_RC4_128_MD5, 0x0018),
+      SUITE(TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, 0x001B),
+      SUITE(TLS_DH_anon_WITH_AES_128_CBC_SHA256, 0x006C),
+      SUITE(TLS_DH_anon_WITH_AES_256_CBC_SHA256, 0x006D),
+      SUITE(TLS_PSK_WITH_RC4_128_SHA, 0x008A),
+      SUITE(TLS_PSK_WITH_3DES_EDE_CBC_SHA, 0x008B),
+      SUITE(TLS_PSK_WITH_AES_128_CBC_SHA, 0x008C),
+      SUITE(TLS_PSK_WITH_AES_256_CBC_SHA, 0x008D),
+      SUITE(TLS_DHE_PSK_WITH_RC4_128_SHA, 0x008E),
+      SUITE(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, 0x008F),
+      SUITE(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, 0x0090),
+      SUITE(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, 0x0091),
+      SUITE(TLS_RSA_PSK_WITH_RC4_128_SHA, 0x0092),
+      SUITE(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, 0x0093),
+      SUITE(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, 0x0094),
+      SUITE(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, 0x0095),
+      SUITE(TLS_PSK_WITH_NULL_SHA, 0x002C),
+      SUITE(TLS_DHE_PSK_WITH_NULL_SHA, 0x002D),
+      SUITE(TLS_RSA_PSK_WITH_NULL_SHA, 0x002E),
+      SUITE(TLS_RSA_WITH_AES_128_GCM_SHA256, 0x009C),
+      SUITE(TLS_RSA_WITH_AES_256_GCM_SHA384, 0x009D),
+      SUITE(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 0x009E),
+      SUITE(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 0x009F),
+      SUITE(TLS_DH_RSA_WITH_AES_128_GCM_SHA256, 0x00A0),
+      SUITE(TLS_DH_RSA_WITH_AES_256_GCM_SHA384, 0x00A1),
+      SUITE(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, 0x00A2),
+      SUITE(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, 0x00A3),
+      SUITE(TLS_DH_DSS_WITH_AES_128_GCM_SHA256, 0x00A4),
+      SUITE(TLS_DH_DSS_WITH_AES_256_GCM_SHA384, 0x00A5),
+      SUITE(TLS_DH_anon_WITH_AES_128_GCM_SHA256, 0x00A6),
+      SUITE(TLS_DH_anon_WITH_AES_256_GCM_SHA384, 0x00A7),
+      SUITE(TLS_PSK_WITH_AES_128_GCM_SHA256, 0x00A8),
+      SUITE(TLS_PSK_WITH_AES_256_GCM_SHA384, 0x00A9),
+      SUITE(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, 0x00AA),
+      SUITE(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, 0x00AB),
+      SUITE(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, 0x00AC),
+      SUITE(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, 0x00AD),
+      SUITE(TLS_PSK_WITH_AES_128_CBC_SHA256, 0x00AE),
+      SUITE(TLS_PSK_WITH_AES_256_CBC_SHA384, 0x00AF),
+      SUITE(TLS_PSK_WITH_NULL_SHA256, 0x00B0),
+      SUITE(TLS_PSK_WITH_NULL_SHA384, 0x00B1),
+      SUITE(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, 0x00B2),
+      SUITE(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, 0x00B3),
+      SUITE(TLS_DHE_PSK_WITH_NULL_SHA256, 0x00B4),
+      SUITE(TLS_DHE_PSK_WITH_NULL_SHA384, 0x00B5),
+      SUITE(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, 0x00B6),
+      SUITE(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, 0x00B7),
+      SUITE(TLS_RSA_PSK_WITH_NULL_SHA256, 0x00B8),
+      SUITE(TLS_RSA_PSK_WITH_NULL_SHA384, 0x00B9),
+      SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 0xC023),
+      SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 0xC024),
+      SUITE(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, 0xC025),
+      SUITE(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, 0xC026),
+      SUITE(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 0xC027),
+      SUITE(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 0xC028),
+      SUITE(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, 0xC029),
+      SUITE(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, 0xC02A),
+      SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0xC02B),
+      SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 0xC02C),
+      SUITE(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, 0xC02D),
+      SUITE(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, 0xC02E),
+      SUITE(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0xC02F),
+      SUITE(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 0xC030),
+      SUITE(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, 0xC031),
+      SUITE(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, 0xC032),
+      SUITE(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, 0x00FF),
+      SUITE(SSL_RSA_WITH_RC2_CBC_MD5, 0xFF80),
+      SUITE(SSL_RSA_WITH_IDEA_CBC_MD5, 0xFF81),
+      SUITE(SSL_RSA_WITH_DES_CBC_MD5, 0xFF82),
+      SUITE(SSL_RSA_WITH_3DES_EDE_CBC_MD5, 0xFF83),
+      SUITE(SSL_NO_SUCH_CIPHERSUITE, 0xFFFF)
+};
 #undef SUITE
 
-  static inline std::string suiteToString(const SSLCipherSuite suite)
-  {
-    for (auto & s : kSuites) {
-      if (s.suite == suite) {
-        return s.name;
-      }
+static inline std::string suiteToString(const SSLCipherSuite suite)
+{
+  for (auto& s : kSuites) {
+    if (s.suite == suite) {
+      return s.name;
     }
-    std::stringstream ss;
-    ss << "Unknown suite (0x" << std::hex << suite << ") like TLS_NULL_WITH_NULL_NULL";
-    return ss.str();
   }
+  std::stringstream ss;
+  ss << "Unknown suite (0x" << std::hex << suite
+     << ") like TLS_NULL_WITH_NULL_NULL";
+  return ss.str();
+}
 
-  static const char* kBlocked[] = {
-    "NULL", "anon", "MD5", "EXPORT", "DES", "IDEA", "NO_SUCH", "EMPTY", "PSK"
-  };
-
-  static inline bool isBlockedSuite(SSLCipherSuite suite)
-  {
-    using namespace aria2;
+static const char* kBlocked[] = {
+  "NULL", "anon",    "MD5",   "EXPORT", "DES", "IDEA", "NO_SUCH", "EMPTY", "PSK"
+};
 
-    // Don't care about SSL2 suites!
-    std::string name = suiteToString(suite);
-    for (auto& blocked : kBlocked) {
-      if (strstr(name.c_str(), blocked)) {
-        A2_LOG_DEBUG(fmt("Removing blocked cipher suite: %s", name.c_str()));
-        return true;
-      }
+static inline bool isBlockedSuite(SSLCipherSuite suite)
+{
+  using namespace aria2;
+
+  // Don't care about SSL2 suites!
+  std::string name = suiteToString(suite);
+  for (auto& blocked : kBlocked) {
+    if (strstr(name.c_str(), blocked)) {
+      A2_LOG_DEBUG(fmt("Removing blocked cipher suite: %s", name.c_str()));
+      return true;
     }
-    return false;
   }
+  return false;
+}
 
-  typedef std::vector<SSLCipherSuite> SSLCipherSuiteList;
-  static SSLCipherSuiteList constructEnabledSuites(SSLContextRef ctx)
-  {
+typedef std::vector<SSLCipherSuite> SSLCipherSuiteList;
+static SSLCipherSuiteList constructEnabledSuites(SSLContextRef ctx)
+{
 #ifndef CIPHER_CONSTRUCT_ALWAYS
-    static
+  static
 #endif
-    SSLCipherSuiteList rv(0);
+  SSLCipherSuiteList rv(0);
 
 #ifndef CIPHER_CONSTRUCT_ALWAYS
-    if (!rv.empty()) {
-      return rv;
-    }
+  if (!rv.empty()) {
+    return rv;
+  }
 #endif
 
-    size_t supported = 0;
-    OSStatus err = SSLGetNumberSupportedCiphers(ctx, &supported);
-    if (err != noErr || !supported) {
-      return rv;
-    }
-
-    rv.resize(supported, SSL_NO_SUCH_CIPHERSUITE);
-    err = SSLGetSupportedCiphers(ctx, &rv[0], &supported);
-    if (err != noErr || !supported) {
-      rv.clear();
-      return rv;
-    }
+  size_t supported = 0;
+  OSStatus err = SSLGetNumberSupportedCiphers(ctx, &supported);
+  if (err != noErr || !supported) {
+    return rv;
+  }
 
-    rv.erase(std::remove_if(rv.begin(), rv.end(), isBlockedSuite), rv.end());
+  rv.resize(supported, SSL_NO_SUCH_CIPHERSUITE);
+  err = SSLGetSupportedCiphers(ctx, &rv[0], &supported);
+  if (err != noErr || !supported) {
+    rv.clear();
     return rv;
   }
+
+  rv.erase(std::remove_if(rv.begin(), rv.end(), isBlockedSuite), rv.end());
+  return rv;
 }
 
+} // namespace
+
 namespace aria2 {
 
 TLSSession* TLSSession::make(TLSContext* ctx)
@@ -358,12 +349,11 @@ AppleTLSSession::AppleTLSSession(AppleTLSContext* ctx)
     writeBuffered_(0)
 {
 #if defined(__MAC_10_8)
-  sslCtx_ = SSLCreateContext(
-    nullptr,
-    ctx->getSide() == TLS_SERVER ? kSSLServerSide : kSSLClientSide,
-    kSSLStreamType
-    );
-  lastError_ =  sslCtx_ ? noErr : paramErr;
+  sslCtx_ = SSLCreateContext(nullptr,
+                             ctx->getSide() == TLS_SERVER ? kSSLServerSide :
+                                                            kSSLClientSide,
+                             kSSLStreamType);
+  lastError_ = sslCtx_ ? noErr : paramErr;
 #else
   lastError_ = SSLNewContext(ctx->getSide() == TLS_SERVER, &sslCtx_);
 #endif
@@ -383,25 +373,24 @@ AppleTLSSession::AppleTLSSession(AppleTLSContext* ctx)
 #endif
 
   // BEAST
-  (void)SSLSetSessionOption(
-      sslCtx_,
+  (void)SSLSetSessionOption(sslCtx_,
 #if defined(__MAC_10_9)
-      kSSLSessionOptionSendOneByteRecord,
+                            kSSLSessionOptionSendOneByteRecord,
 #else
-      (SSLSessionOption)0x4, /* kSSLSessionOptionSendOneByteRecord */
+                            (SSLSessionOption)0x4, // kSSLSessionOptionSendOneByteRecord
 #endif
-      true);
+                            true);
 
 #if defined(__MAC_10_8)
   if (!ctx->getVerifyPeer()) {
     // This disables client verification
-    (void)SSLSetSessionOption(sslCtx_, kSSLSessionOptionBreakOnServerAuth, true);
+    (void)SSLSetSessionOption(
+        sslCtx_, kSSLSessionOptionBreakOnServerAuth, true);
   }
 #else
   (void)SSLSetEnableCertVerify(sslCtx_, ctx->getVerifyPeer());
 #endif
 
-
 #ifndef CIPHER_ENABLE_ALL
   SSLCipherSuiteList enabled = constructEnabledSuites(sslCtx_);
   if (enabled.empty()) {
@@ -409,8 +398,9 @@ AppleTLSSession::AppleTLSSession(AppleTLSContext* ctx)
     state_ = st_error;
     return;
   }
-  for (const auto& suite: enabled) {
-    A2_LOG_INFO(fmt("AppleTLS: Enabled suite %s", suiteToString(suite).c_str()));
+  for (const auto& suite : enabled) {
+    A2_LOG_INFO(
+        fmt("AppleTLS: Enabled suite %s", suiteToString(suite).c_str()));
   }
   if (SSLSetEnabledCiphers(sslCtx_, &enabled[0], enabled.size()) != noErr) {
     A2_LOG_ERROR("AppleTLS: Failed to set enabled ciphers list");
@@ -436,10 +426,12 @@ AppleTLSSession::AppleTLSSession(AppleTLSContext* ctx)
     state_ = st_error;
     return;
   }
-  std::unique_ptr<void, decltype(&CFRelease)> del_certs((void*)certs, CFRelease);
+  std::unique_ptr<void, decltype(&CFRelease)> del_certs((void*)certs,
+                                                        CFRelease);
   lastError_ = SSLSetCertificate(sslCtx_, certs);
   if (lastError_ != noErr) {
-    A2_LOG_ERROR(fmt("AppleTLS: Failed to set credentials: %s", getLastErrorString().c_str()));
+    A2_LOG_ERROR(fmt("AppleTLS: Failed to set credentials: %s",
+                     getLastErrorString().c_str()));
     state_ = st_error;
     return;
   }
@@ -447,7 +439,8 @@ AppleTLSSession::AppleTLSSession(AppleTLSContext* ctx)
 #ifndef CIPHER_NO_DHPARAM
   lastError_ = SSLSetDiffieHellmanParams(sslCtx_, dhparam, sizeof(dhparam));
   if (lastError_ != noErr) {
-    A2_LOG_WARN(fmt("AppleTLS: Failed to set DHParams: %s", getLastErrorString().c_str()));
+    A2_LOG_WARN(fmt("AppleTLS: Failed to set DHParams: %s",
+                    getLastErrorString().c_str()));
     // Engine will still generate some for us, so this is no problem, except
     // it will take longer.
   }
@@ -495,7 +488,8 @@ int AppleTLSSession::setSNIHostname(const std::string& hostname)
     lastError_ = noErr;
     return TLS_ERR_ERROR;
   }
-  lastError_ = SSLSetPeerDomainName(sslCtx_, hostname.c_str(), hostname.length());
+  lastError_ =
+      SSLSetPeerDomainName(sslCtx_, hostname.c_str(), hostname.length());
   return (lastError_ != noErr) ? TLS_ERR_ERROR : TLS_ERR_OK;
 }
 
@@ -507,10 +501,11 @@ int AppleTLSSession::closeConnection()
   }
   lastError_ = SSLClose(sslCtx_);
   state_ = st_closed;
-  return lastError_ == noErr ?  TLS_ERR_OK : TLS_ERR_ERROR;
+  return lastError_ == noErr ? TLS_ERR_OK : TLS_ERR_ERROR;
 }
 
-int AppleTLSSession::checkDirection() {
+int AppleTLSSession::checkDirection()
+{
   // See: https://github.com/tatsuhiro-t/aria2/pull/61#issuecomment-16051793
   if (state_ == st_connected) {
     // Need to check read first, as SocketCore kinda expects this
@@ -539,53 +534,60 @@ ssize_t AppleTLSSession::writeData(const void* data, size_t len)
   if (writeBuffered_) {
     lastError_ = SSLWrite(sslCtx_, nullptr, 0, &processed);
     switch (lastError_) {
-      case noErr:
-        processed = writeBuffered_;
-        writeBuffered_ = 0;
-        return processed;
-      case errSSLWouldBlock:
-        return TLS_ERR_WOULDBLOCK;
-      case errSSLClosedGraceful:
-      case errSSLClosedNoNotify:
-        closeConnection();
-        return TLS_ERR_ERROR;
-      default:
-        closeConnection();
-        state_ = st_error;
-        return TLS_ERR_ERROR;
-    }
-  }
-
-  lastError_ = SSLWrite(sslCtx_, data, len, &processed);
-  switch (lastError_) {
     case noErr:
+      processed = writeBuffered_;
+      writeBuffered_ = 0;
       return processed;
+
     case errSSLWouldBlock:
-      writeBuffered_ = len;
       return TLS_ERR_WOULDBLOCK;
+
     case errSSLClosedGraceful:
     case errSSLClosedNoNotify:
       closeConnection();
       return TLS_ERR_ERROR;
+
     default:
       closeConnection();
       state_ = st_error;
       return TLS_ERR_ERROR;
+    }
+  }
+
+  lastError_ = SSLWrite(sslCtx_, data, len, &processed);
+  switch (lastError_) {
+  case noErr:
+    return processed;
+
+  case errSSLWouldBlock:
+    writeBuffered_ = len;
+    return TLS_ERR_WOULDBLOCK;
+
+  case errSSLClosedGraceful:
+  case errSSLClosedNoNotify:
+    closeConnection();
+    return TLS_ERR_ERROR;
+
+  default:
+    closeConnection();
+    state_ = st_error;
+    return TLS_ERR_ERROR;
   }
 }
 OSStatus AppleTLSSession::sockWrite(const void* data, size_t* len)
 {
   size_t remain = *len;
-  const uint8_t *buffer = static_cast<const uint8_t*>(data);
+  const uint8_t* buffer = static_cast<const uint8_t*>(data);
   *len = 0;
   while (remain) {
     ssize_t w = write(sockfd_, buffer, remain);
     if (w <= 0) {
       switch (errno) {
-        case EAGAIN:
-          return errSSLWouldBlock;
-        default:
-          return errSSLClosedAbort;
+      case EAGAIN:
+        return errSSLWouldBlock;
+
+      default:
+        return errSSLClosedAbort;
       }
     }
     remain -= w;
@@ -603,28 +605,31 @@ ssize_t AppleTLSSession::readData(void* data, size_t len)
   size_t processed = 0;
   lastError_ = SSLRead(sslCtx_, data, len, &processed);
   switch (lastError_) {
-    case noErr:
+  case noErr:
+    return processed;
+
+  case errSSLWouldBlock:
+    if (processed) {
       return processed;
-    case errSSLWouldBlock:
-      if (processed) {
-        return processed;
-      }
-      return TLS_ERR_WOULDBLOCK;
-    case errSSLClosedGraceful:
-    case errSSLClosedNoNotify:
-      closeConnection();
-      return TLS_ERR_ERROR;
-    default:
-      closeConnection();
-      state_ = st_error;
-      return TLS_ERR_ERROR;
+    }
+    return TLS_ERR_WOULDBLOCK;
+
+  case errSSLClosedGraceful:
+  case errSSLClosedNoNotify:
+    closeConnection();
+    return TLS_ERR_ERROR;
+
+  default:
+    closeConnection();
+    state_ = st_error;
+    return TLS_ERR_ERROR;
   }
 }
 
 OSStatus AppleTLSSession::sockRead(void* data, size_t* len)
 {
   size_t remain = *len;
-  uint8_t *buffer = static_cast<uint8_t*>(data);
+  uint8_t* buffer = static_cast<uint8_t*>(data);
   *len = 0;
   while (remain) {
     ssize_t r = read(sockfd_, buffer, remain);
@@ -633,14 +638,17 @@ OSStatus AppleTLSSession::sockRead(void* data, size_t* len)
     }
     if (r < 0) {
       switch (errno) {
-        case ENOENT:
-          return errSSLClosedGraceful;
-        case ECONNRESET:
-          return errSSLClosedAbort;
-        case EAGAIN:
-          return errSSLWouldBlock;
-        default:
-          return errSSLClosedAbort;
+      case ENOENT:
+        return errSSLClosedGraceful;
+
+      case ECONNRESET:
+        return errSSLClosedAbort;
+
+      case EAGAIN:
+        return errSSLWouldBlock;
+
+      default:
+        return errSSLClosedAbort;
       }
     }
     remain -= r;
@@ -650,7 +658,8 @@ OSStatus AppleTLSSession::sockRead(void* data, size_t* len)
   return noErr;
 }
 
-int AppleTLSSession::tlsConnect(const std::string& hostname, std::string& handshakeErr)
+int AppleTLSSession::tlsConnect(const std::string& hostname,
+                                std::string& handshakeErr)
 {
   if (state_ != st_initialized) {
     return TLS_ERR_ERROR;
@@ -660,15 +669,18 @@ int AppleTLSSession::tlsConnect(const std::string& hostname, std::string& handsh
   }
   lastError_ = SSLHandshake(sslCtx_);
   switch (lastError_) {
-    case noErr:
-      break;
-    case errSSLWouldBlock:
-      return TLS_ERR_WOULDBLOCK;
-    case errSSLServerAuthCompleted:
-      return tlsConnect(hostname, handshakeErr);
-    default:
-      handshakeErr = getLastErrorString();
-      return TLS_ERR_ERROR;
+  case noErr:
+    break;
+
+  case errSSLWouldBlock:
+    return TLS_ERR_WOULDBLOCK;
+
+  case errSSLServerAuthCompleted:
+    return tlsConnect(hostname, handshakeErr);
+
+  default:
+    handshakeErr = getLastErrorString();
+    return TLS_ERR_ERROR;
   }
   state_ = st_connected;
 
@@ -693,54 +705,75 @@ int AppleTLSSession::tlsAccept()
 std::string AppleTLSSession::getLastErrorString()
 {
   switch (lastError_) {
-    case errSSLProtocol:
-      return "Protocol error";
-    case errSSLNegotiation:
-      return "No common cipher suites";
-    case errSSLFatalAlert:
-      return "Received fatal alert";
-    case errSSLSessionNotFound:
-      return "Unknown session";
-    case errSSLClosedGraceful:
-      return "Closed gracefully";
-    case errSSLClosedAbort:
-      return "Connection aborted";
-    case errSSLXCertChainInvalid:
-      return "Invalid certificate chain";
-    case errSSLBadCert:
-      return "Invalid certificate format";
-    case errSSLCrypto:
-      return "Cryptographic error";
-    case paramErr:
-    case errSSLInternal:
-      return "Internal SSL error";
-    case errSSLUnknownRootCert:
-      return "Self-signed certificate";
-    case errSSLNoRootCert:
-      return "No root certificate";
-    case errSSLCertExpired:
-      return "Certificate expired";
-    case errSSLCertNotYetValid:
-      return "Certificate not yet valid";
-    case errSSLClosedNoNotify:
-      return "Closed without notification";
-    case errSSLBufferOverflow:
-      return "Buffer not large enough";
-    case errSSLBadCipherSuite:
-      return "Bad cipher suite";
-    case errSSLPeerUnexpectedMsg:
-      return "Unexpected peer message";
-    case errSSLPeerBadRecordMac:
-      return "Bad MAC";
-    case errSSLPeerDecryptionFail:
-      return "Decryption failure";
-    case errSSLHostNameMismatch:
-      return "Invalid hostname";
-    case errSSLConnectionRefused:
-      return "Connection refused";
-    default:
-      return fmt("Unspecified error %ld", (long)lastError_);
-  }
-}
+  case errSSLProtocol:
+    return "Protocol error";
+
+  case errSSLNegotiation:
+    return "No common cipher suites";
+
+  case errSSLFatalAlert:
+    return "Received fatal alert";
+
+  case errSSLSessionNotFound:
+    return "Unknown session";
+
+  case errSSLClosedGraceful:
+    return "Closed gracefully";
+
+  case errSSLClosedAbort:
+    return "Connection aborted";
+
+  case errSSLXCertChainInvalid:
+    return "Invalid certificate chain";
+
+  case errSSLBadCert:
+    return "Invalid certificate format";
+
+  case errSSLCrypto:
+    return "Cryptographic error";
+
+  case paramErr:
+  case errSSLInternal:
+    return "Internal SSL error";
 
+  case errSSLUnknownRootCert:
+    return "Self-signed certificate";
+
+  case errSSLNoRootCert:
+    return "No root certificate";
+
+  case errSSLCertExpired:
+    return "Certificate expired";
+
+  case errSSLCertNotYetValid:
+    return "Certificate not yet valid";
+
+  case errSSLClosedNoNotify:
+    return "Closed without notification";
+
+  case errSSLBufferOverflow:
+    return "Buffer not large enough";
+
+  case errSSLBadCipherSuite:
+    return "Bad cipher suite";
+
+  case errSSLPeerUnexpectedMsg:
+    return "Unexpected peer message";
+
+  case errSSLPeerBadRecordMac:
+    return "Bad MAC";
+
+  case errSSLPeerDecryptionFail:
+    return "Decryption failure";
+
+  case errSSLHostNameMismatch:
+    return "Invalid hostname";
+
+  case errSSLConnectionRefused:
+    return "Connection refused";
+
+  default:
+    return fmt("Unspecified error %ld", (long)lastError_);
+  }
 }
+} // namespace aria2

+ 11 - 6
src/AppleTLSSession.h

@@ -41,7 +41,8 @@
 
 namespace aria2 {
 
-class AppleTLSSession : public TLSSession {
+class AppleTLSSession : public TLSSession
+{
   enum state_t {
     st_constructed,
     st_initialized,
@@ -49,6 +50,7 @@ class AppleTLSSession : public TLSSession {
     st_closed,
     st_error
   };
+
 public:
   AppleTLSSession(AppleTLSContext* ctx);
 
@@ -93,8 +95,8 @@ public:
   // if the underlying transport blocks, or TLS_ERR_ERROR.
   // When returning TLS_ERR_ERROR, provide certificate validation error
   // in |handshakeErr|.
-  virtual int tlsConnect
-  (const std::string& hostname, std::string& handshakeErr) CXX11_OVERRIDE;
+  virtual int tlsConnect(const std::string& hostname,
+                         std::string& handshakeErr) CXX11_OVERRIDE;
 
   // Performs server side handshake. This function returns TLS_ERR_OK
   // if it succeeds, or TLS_ERR_WOULDBLOCK if the underlying transport
@@ -105,10 +107,14 @@ public:
   virtual std::string getLastErrorString() CXX11_OVERRIDE;
 
 private:
-  static OSStatus SocketWrite(SSLConnectionRef conn, const void* data, size_t* len) {
+  static OSStatus
+  SocketWrite(SSLConnectionRef conn, const void* data, size_t* len)
+  {
     return ((AppleTLSSession*)conn)->sockWrite(data, len);
   }
-  static OSStatus SocketRead(SSLConnectionRef conn, void* data, size_t* len) {
+
+  static OSStatus SocketRead(SSLConnectionRef conn, void* data, size_t* len)
+  {
     return ((AppleTLSSession*)conn)->sockRead(data, len);
   }
 
@@ -121,7 +127,6 @@ private:
   OSStatus sockWrite(const void* data, size_t* len);
   OSStatus sockRead(void* data, size_t* len);
 };
-
 }
 
 #endif // TLS_SESSION_H

+ 23 - 32
src/WinTLSContext.cc

@@ -57,31 +57,25 @@
 #define SP_PROT_TLS1_2_SERVER 0x00000400
 #endif
 
-
 #ifndef SCH_USE_STRONG_CRYPTO
 #define SCH_USE_STRONG_CRYPTO 0x00400000
 #endif
 
 namespace aria2 {
 
-WinTLSContext::WinTLSContext(TLSSessionSide side)
-  : side_(side), store_(0)
+WinTLSContext::WinTLSContext(TLSSessionSide side) : side_(side), store_(0)
 {
   memset(&credentials_, 0, sizeof(credentials_));
   credentials_.dwVersion = SCHANNEL_CRED_VERSION;
   if (side_ == TLS_CLIENT) {
     credentials_.grbitEnabledProtocols =
-      SP_PROT_SSL3_CLIENT |
-      SP_PROT_TLS1_CLIENT |
-      SP_PROT_TLS1_1_CLIENT |
-      SP_PROT_TLS1_2_CLIENT;
+        SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT |
+        SP_PROT_TLS1_2_CLIENT;
   }
   else {
     credentials_.grbitEnabledProtocols =
-      SP_PROT_SSL3_SERVER |
-      SP_PROT_TLS1_SERVER |
-      SP_PROT_TLS1_1_SERVER |
-      SP_PROT_TLS1_2_SERVER;
+        SP_PROT_SSL3_SERVER | SP_PROT_TLS1_SERVER | SP_PROT_TLS1_1_SERVER |
+        SP_PROT_TLS1_2_SERVER;
   }
   credentials_.dwMinimumCipherStrength = 128; // bit
 
@@ -108,24 +102,21 @@ bool WinTLSContext::getVerifyPeer() const
 
 void WinTLSContext::setVerifyPeer(bool verify)
 {
-  if (side_ == TLS_CLIENT && verify) {
-    credentials_.dwFlags =
-      SCH_CRED_NO_DEFAULT_CREDS |
-      SCH_CRED_AUTO_CRED_VALIDATION |
-      SCH_CRED_REVOCATION_CHECK_CHAIN |
-      SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
-      SCH_USE_STRONG_CRYPTO;
+  cred_.reset();
+
+  if (side_ != TLS_CLIENT || !verify) {
+    credentials_.dwFlags = SCH_CRED_NO_DEFAULT_CREDS |
+                           SCH_CRED_MANUAL_CRED_VALIDATION |
+                           SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+                           SCH_CRED_IGNORE_REVOCATION_OFFLINE |
+                           SCH_CRED_NO_SERVERNAME_CHECK | SCH_USE_STRONG_CRYPTO;
+    return;
   }
-  else {
-    credentials_.dwFlags =
-      SCH_CRED_NO_DEFAULT_CREDS |
-      SCH_CRED_MANUAL_CRED_VALIDATION |
-      SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
-      SCH_CRED_IGNORE_REVOCATION_OFFLINE |
-      SCH_CRED_NO_SERVERNAME_CHECK |
+
+  credentials_.dwFlags =
+      SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_AUTO_CRED_VALIDATION |
+      SCH_CRED_REVOCATION_CHECK_CHAIN | SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
       SCH_USE_STRONG_CRYPTO;
-  }
-  cred_.reset();
 }
 
 CredHandle* WinTLSContext::getCredHandle()
@@ -171,7 +162,7 @@ CredHandle* WinTLSContext::getCredHandle()
 }
 
 bool WinTLSContext::addCredentialFile(const std::string& certfile,
-                                        const std::string& keyfile)
+                                      const std::string& keyfile)
 {
   std::stringstream ss;
   BufferedFile(certfile.c_str(), "rb").transfer(ss);
@@ -184,11 +175,11 @@ bool WinTLSContext::addCredentialFile(const std::string& certfile,
     A2_LOG_ERROR("Not a valid PKCS12 file");
     return false;
   }
-  HCERTSTORE store = ::PFXImportCertStore(&blob, L"",
-                                          CRYPT_EXPORTABLE | CRYPT_USER_KEYSET);
+  HCERTSTORE store =
+      ::PFXImportCertStore(&blob, L"", CRYPT_EXPORTABLE | CRYPT_USER_KEYSET);
   if (!store_) {
-    store = ::PFXImportCertStore(&blob, nullptr,
-                                  CRYPT_EXPORTABLE | CRYPT_USER_KEYSET);
+    store = ::PFXImportCertStore(
+        &blob, nullptr, CRYPT_EXPORTABLE | CRYPT_USER_KEYSET);
   }
   if (!store) {
     A2_LOG_ERROR("Failed to import PKCS12 store");

+ 22 - 14
src/WinTLSContext.h

@@ -51,42 +51,50 @@
 namespace aria2 {
 
 namespace wintls {
-  struct cred_deleter{
-    void operator()(CredHandle* handle) {
-      if (handle) {
-        FreeCredentialsHandle(handle);
-        delete handle;
-      }
+struct cred_deleter
+{
+  void operator()(CredHandle* handle)
+  {
+    if (handle) {
+      FreeCredentialsHandle(handle);
+      delete handle;
     }
-  };
-  typedef std::unique_ptr<CredHandle, cred_deleter> CredPtr;
+  }
+};
+typedef std::unique_ptr<CredHandle, cred_deleter> CredPtr;
 } // namespace wintls
 
-class WinTLSContext : public TLSContext {
+class WinTLSContext : public TLSContext
+{
 public:
   WinTLSContext(TLSSessionSide side);
+
   virtual ~WinTLSContext();
 
   // private key `keyfile' must be decrypted.
   virtual bool addCredentialFile(const std::string& certfile,
                                  const std::string& keyfile) CXX11_OVERRIDE;
 
-  virtual bool addSystemTrustedCACerts() CXX11_OVERRIDE {
+  virtual bool addSystemTrustedCACerts() CXX11_OVERRIDE
+  {
     return true;
   }
 
   // certfile can contain multiple certificates.
-  virtual bool addTrustedCACertFile(const std::string& certfile)
-    CXX11_OVERRIDE;
+  virtual bool addTrustedCACertFile(const std::string& certfile) CXX11_OVERRIDE;
 
-  virtual bool good() const CXX11_OVERRIDE {
+  virtual bool good() const CXX11_OVERRIDE
+  {
     return true;
   }
-  virtual TLSSessionSide getSide() const CXX11_OVERRIDE {
+
+  virtual TLSSessionSide getSide() const CXX11_OVERRIDE
+  {
     return side_;
   }
 
   virtual bool getVerifyPeer() const CXX11_OVERRIDE;
+
   virtual void setVerifyPeer(bool verify) CXX11_OVERRIDE;
 
   CredHandle* getCredHandle();

+ 307 - 307
src/WinTLSSession.cc

@@ -53,74 +53,73 @@
 #define SECPKGCONTEXT_CIPHERINFO_V1 1
 #endif
 #ifndef SECPKG_ATTR_CIPHER_INFO
-#define SECPKG_ATTR_CIPHER_INFO  0x64
+#define SECPKG_ATTR_CIPHER_INFO 0x64
 #endif
 
 namespace {
-  using namespace aria2;
-
-  struct WinSecPkgContext_CipherInfo {
-      DWORD dwVersion;
-      DWORD dwProtocol;
-      DWORD dwCipherSuite;
-      DWORD dwBaseCipherSuite;
-      WCHAR szCipherSuite[SZ_ALG_MAX_SIZE];
-      WCHAR szCipher[SZ_ALG_MAX_SIZE];
-      DWORD dwCipherLen;
-      DWORD dwCipherBlockLen;    // in bytes
-      WCHAR szHash[SZ_ALG_MAX_SIZE];
-      DWORD dwHashLen;
-      WCHAR szExchange[SZ_ALG_MAX_SIZE];
-      DWORD dwMinExchangeLen;
-      DWORD dwMaxExchangeLen;
-      WCHAR szCertificate[SZ_ALG_MAX_SIZE];
-      DWORD dwKeyType;
-  };
-
-  static const ULONG kReqFlags = ISC_REQ_SEQUENCE_DETECT |
-                                 ISC_REQ_REPLAY_DETECT |
-                                 ISC_REQ_CONFIDENTIALITY |
-                                 ISC_REQ_ALLOCATE_MEMORY |
-                                 ISC_REQ_USE_SUPPLIED_CREDS |
-                                 ISC_REQ_STREAM;
-  static const ULONG kReqAFlags = ASC_REQ_SEQUENCE_DETECT |
-                                  ASC_REQ_REPLAY_DETECT |
-                                  ASC_REQ_CONFIDENTIALITY |
-                                  ASC_REQ_EXTENDED_ERROR |
-                                  ASC_REQ_ALLOCATE_MEMORY |
-                                  ASC_REQ_STREAM;
-
-  class TLSBuffer : public ::SecBuffer {
-  public:
-    explicit TLSBuffer(ULONG type, ULONG size, void *data)
-    {
-      cbBuffer = size;
-      BufferType = type;
-      pvBuffer = data;
-    }
-  };
-
-  class TLSBufferDesc: public ::SecBufferDesc {
-  public:
-    explicit TLSBufferDesc(SecBuffer *arr, ULONG buffers)
-    {
-      ulVersion = SECBUFFER_VERSION;
-      cBuffers = buffers;
-      pBuffers = arr;
-    }
-  };
-
-  inline static std::string getCipherSuite(CtxtHandle *handle)
+using namespace aria2;
+
+struct WinSecPkgContext_CipherInfo
+{
+  DWORD dwVersion;
+  DWORD dwProtocol;
+  DWORD dwCipherSuite;
+  DWORD dwBaseCipherSuite;
+  WCHAR szCipherSuite[SZ_ALG_MAX_SIZE];
+  WCHAR szCipher[SZ_ALG_MAX_SIZE];
+  DWORD dwCipherLen;
+  DWORD dwCipherBlockLen; // in bytes
+  WCHAR szHash[SZ_ALG_MAX_SIZE];
+  DWORD dwHashLen;
+  WCHAR szExchange[SZ_ALG_MAX_SIZE];
+  DWORD dwMinExchangeLen;
+  DWORD dwMaxExchangeLen;
+  WCHAR szCertificate[SZ_ALG_MAX_SIZE];
+  DWORD dwKeyType;
+};
+
+static const ULONG kReqFlags =
+    ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY |
+    ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_USE_SUPPLIED_CREDS | ISC_REQ_STREAM;
+
+static const ULONG kReqAFlags =
+    ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | ASC_REQ_CONFIDENTIALITY |
+    ASC_REQ_EXTENDED_ERROR | ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_STREAM;
+
+class TLSBuffer : public ::SecBuffer
+{
+public:
+  explicit TLSBuffer(ULONG type, ULONG size, void* data)
   {
-    WinSecPkgContext_CipherInfo info = { SECPKGCONTEXT_CIPHERINFO_V1 };
-    if (QueryContextAttributes(handle, SECPKG_ATTR_CIPHER_INFO, &info) ==
-        SEC_E_OK) {
-      return wCharToUtf8(info.szCipherSuite);
-    }
-    return "Unknown";
+    cbBuffer = size;
+    BufferType = type;
+    pvBuffer = data;
+  }
+};
+
+class TLSBufferDesc : public ::SecBufferDesc
+{
+public:
+  explicit TLSBufferDesc(SecBuffer* arr, ULONG buffers)
+  {
+    ulVersion = SECBUFFER_VERSION;
+    cBuffers = buffers;
+    pBuffers = arr;
+  }
+};
+
+inline static std::string getCipherSuite(CtxtHandle* handle)
+{
+  WinSecPkgContext_CipherInfo info = {SECPKGCONTEXT_CIPHERINFO_V1};
+  if (QueryContextAttributes(handle, SECPKG_ATTR_CIPHER_INFO, &info) ==
+      SEC_E_OK) {
+    return wCharToUtf8(info.szCipherSuite);
   }
+  return "Unknown";
 }
 
+} // namespace
+
 namespace aria2 {
 
 TLSSession* TLSSession::make(TLSContext* ctx)
@@ -189,35 +188,33 @@ int WinTLSSession::closeConnection()
     ULONG flags = 0;
     if (side_ == TLS_CLIENT) {
       SEC_CHAR* host = hostname_.empty() ?
-        nullptr :
-        const_cast<SEC_CHAR*>(hostname_.c_str());
-      status_ = ::InitializeSecurityContext(
-          cred_,
-          &handle_,
-          host,
-          kReqFlags,
-          0,
-          0,
-          nullptr,
-          0,
-          &handle_,
-          &desc,
-          &flags,
-          nullptr);
+                           nullptr :
+                           const_cast<SEC_CHAR*>(hostname_.c_str());
+      status_ = ::InitializeSecurityContext(cred_,
+                                            &handle_,
+                                            host,
+                                            kReqFlags,
+                                            0,
+                                            0,
+                                            nullptr,
+                                            0,
+                                            &handle_,
+                                            &desc,
+                                            &flags,
+                                            nullptr);
     }
     else {
-      status_ = ::AcceptSecurityContext(
-          cred_,
-          &handle_,
-          nullptr,
-          kReqAFlags,
-          0,
-          &handle_,
-          &desc,
-          &flags,
-          nullptr);
-    }
-    if (status_ == SEC_E_OK || status_== SEC_I_CONTEXT_EXPIRED) {
+      status_ = ::AcceptSecurityContext(cred_,
+                                        &handle_,
+                                        nullptr,
+                                        kReqAFlags,
+                                        0,
+                                        &handle_,
+                                        &desc,
+                                        &flags,
+                                        nullptr);
+    }
+    if (status_ == SEC_E_OK || status_ == SEC_I_CONTEXT_EXPIRED) {
       size_t len = ctx.cbBuffer;
       ssize_t rv = writeData(ctx.pvBuffer, ctx.cbBuffer);
       ::FreeContextBuffer(ctx.pvBuffer);
@@ -246,7 +243,7 @@ int WinTLSSession::closeConnection()
 
 int WinTLSSession::checkDirection()
 {
-  if (state_ == st_handshake_write || state_  == st_handshake_write_last) {
+  if (state_ == st_handshake_write || state_ == st_handshake_write_last) {
     return TLS_WANT_WRITE;
   }
   if (state_ == st_handshake_read) {
@@ -280,7 +277,8 @@ ssize_t WinTLSSession::writeData(const void* data, size_t len)
   }
 
   A2_LOG_DEBUG(fmt("WinTLS: Write request: %" PRIu64 " buffered: %" PRIu64,
-                   (uint64_t)len, (uint64_t)writeBuf_.size()));
+                   (uint64_t)len,
+                   (uint64_t)writeBuf_.size()));
 
   // Write remaining buffered data, if any.
   size_t written = 0;
@@ -310,8 +308,8 @@ ssize_t WinTLSSession::writeData(const void* data, size_t len)
 
   if (!streamSizes_) {
     streamSizes_.reset(new SecPkgContext_StreamSizes());
-    status_ = ::QueryContextAttributes(&handle_, SECPKG_ATTR_STREAM_SIZES,
-                                       streamSizes_.get());
+    status_ = ::QueryContextAttributes(
+        &handle_, SECPKG_ATTR_STREAM_SIZES, streamSizes_.get());
     if (status_ != SEC_E_OK || !streamSizes_->cbMaximumMessage) {
       state_ = st_error;
       return TLS_ERR_ERROR;
@@ -346,16 +344,17 @@ ssize_t WinTLSSession::writeData(const void* data, size_t len)
   while (process) {
     // Set up an outgoing message, according to streamSizes_
     writeBuffered_ = std::min(process, (size_t)streamSizes_->cbMaximumMessage);
-    size_t dl = streamSizes_->cbHeader + writeBuffered_ +
-                streamSizes_->cbTrailer;
+    size_t dl =
+        streamSizes_->cbHeader + writeBuffered_ + streamSizes_->cbTrailer;
     auto buf = make_unique<char[]>(dl);
     TLSBuffer buffers[] = {
-      TLSBuffer(SECBUFFER_STREAM_HEADER, streamSizes_->cbHeader, buf.get()),
-      TLSBuffer(SECBUFFER_DATA, writeBuffered_,
-                buf.get() + streamSizes_->cbHeader),
-      TLSBuffer(SECBUFFER_STREAM_TRAILER, streamSizes_->cbTrailer,
-                buf.get() + streamSizes_->cbHeader + writeBuffered_),
-      TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
+        TLSBuffer(SECBUFFER_STREAM_HEADER, streamSizes_->cbHeader, buf.get()),
+        TLSBuffer(
+            SECBUFFER_DATA, writeBuffered_, buf.get() + streamSizes_->cbHeader),
+        TLSBuffer(SECBUFFER_STREAM_TRAILER,
+                  streamSizes_->cbTrailer,
+                  buf.get() + streamSizes_->cbHeader + writeBuffered_),
+        TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
     };
     TLSBufferDesc desc(buffers, 4);
     memcpy(buffers[1].pvBuffer, bytes, writeBuffered_);
@@ -418,7 +417,8 @@ ssize_t WinTLSSession::writeData(const void* data, size_t len)
   }
 
   A2_LOG_DEBUG(fmt("WinTLS: Write result: %" PRIu64 " buffered: %" PRIu64,
-                   (uint64_t)len, (uint64_t)writeBuf_.size()));
+                   (uint64_t)len,
+                   (uint64_t)writeBuf_.size()));
   if (!len) {
     return TLS_ERR_WOULDBLOCK;
   }
@@ -428,7 +428,8 @@ ssize_t WinTLSSession::writeData(const void* data, size_t len)
 ssize_t WinTLSSession::readData(void* data, size_t len)
 {
   A2_LOG_DEBUG(fmt("WinTLS: Read request: %" PRIu64 " buffered: %" PRIu64,
-                   (uint64_t)len, (uint64_t)readBuf_.size()));
+                   (uint64_t)len,
+                   (uint64_t)readBuf_.size()));
   if (len == 0) {
     return 0;
   }
@@ -481,10 +482,10 @@ ssize_t WinTLSSession::readData(void* data, size_t len)
   // Try to decrypt as many messages as possible from the readBuf_.
   while (readBuf_.size()) {
     TLSBuffer bufs[] = {
-      TLSBuffer(SECBUFFER_DATA, readBuf_.size(), readBuf_.data()),
-      TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
-      TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
-      TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
+        TLSBuffer(SECBUFFER_DATA, readBuf_.size(), readBuf_.data()),
+        TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
+        TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
+        TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
     };
     TLSBufferDesc desc(bufs, 4);
     status_ = ::DecryptMessage(&handle_, &desc, 0, nullptr);
@@ -563,222 +564,220 @@ int WinTLSSession::tlsConnect(const std::string& hostname,
 restart:
 
   switch (state_) {
-    default:
-      A2_LOG_ERROR("WinTLS: Invalid state");
-      status_ = SEC_E_INVALID_HANDLE;
+  default:
+    A2_LOG_ERROR("WinTLS: Invalid state");
+    status_ = SEC_E_INVALID_HANDLE;
+    return TLS_ERR_ERROR;
+
+  case st_initialized: {
+    if (side_ == TLS_SERVER) {
+      goto read;
+    }
+
+    if (!hostname.empty()) {
+      setSNIHostname(hostname);
+    }
+    A2_LOG_DEBUG("WinTLS: Initializing handshake");
+    TLSBuffer buf(SECBUFFER_EMPTY, 0, nullptr);
+    TLSBufferDesc desc(&buf, 1);
+    SEC_CHAR* host =
+        hostname_.empty() ? nullptr : const_cast<SEC_CHAR*>(hostname_.c_str());
+    status_ = ::InitializeSecurityContext(cred_,
+                                          nullptr,
+                                          host,
+                                          kReqFlags,
+                                          0,
+                                          0,
+                                          nullptr,
+                                          0,
+                                          &handle_,
+                                          &desc,
+                                          &flags,
+                                          nullptr);
+    if (status_ != SEC_I_CONTINUE_NEEDED) {
+      // Has to be SEC_I_CONTINUE_NEEDED, as we did not actually send data
+      // at this point.
+      state_ = st_error;
       return TLS_ERR_ERROR;
+    }
 
-    case st_initialized: {
-      if (side_ == TLS_SERVER) {
-        goto read;
-      }
+    // Queue the initial message...
+    writeBuf_.write(buf.pvBuffer, buf.cbBuffer);
+    FreeContextBuffer(buf.pvBuffer);
+
+    // ... and start sending it
+    state_ = st_handshake_write;
+  }
+  // Fall through
 
-      if (!hostname.empty()) {
-        setSNIHostname(hostname);
+  case st_handshake_write_last:
+  case st_handshake_write: {
+    A2_LOG_DEBUG("WinTLS: Writing handshake");
+
+    // Write the currently queued handshake message until all data is sent.
+    while (writeBuf_.size()) {
+      ssize_t writ = ::send(sockfd_, writeBuf_.data(), writeBuf_.size(), 0);
+      errno = ::WSAGetLastError();
+      if (writ < 0 && errno == WSAEINTR) {
+        continue;
       }
-      A2_LOG_DEBUG("WinTLS: Initializing handshake");
-      TLSBuffer buf(SECBUFFER_EMPTY, 0, nullptr);
-      TLSBufferDesc desc(&buf, 1);
-      SEC_CHAR* host = hostname_.empty() ?
-        nullptr :
-        const_cast<SEC_CHAR*>(hostname_.c_str());
-      status_ = ::InitializeSecurityContext(
-          cred_,
-          nullptr,
-          host,
-          kReqFlags,
-          0,
-          0,
-          nullptr,
-          0,
-          &handle_,
-          &desc,
-          &flags,
-          nullptr);
-      if (status_ != SEC_I_CONTINUE_NEEDED) {
-        // Has to be SEC_I_CONTINUE_NEEDED, as we did not actually send data
-        // at this point.
+      if (writ < 0 && errno == WSAEWOULDBLOCK) {
+        return TLS_ERR_WOULDBLOCK;
+      }
+      if (writ <= 0) {
+        status_ = SEC_E_INCOMPLETE_MESSAGE;
         state_ = st_error;
         return TLS_ERR_ERROR;
       }
+      writeBuf_.eat(writ);
+    }
 
-      // Queue the initial message...
-      writeBuf_.write(buf.pvBuffer, buf.cbBuffer);
-      FreeContextBuffer(buf.pvBuffer);
-
-      // ... and start sending it
-      state_ = st_handshake_write;
-    }
-    // Fall through
-
-    case st_handshake_write_last:
-    case st_handshake_write: {
-      A2_LOG_DEBUG("WinTLS: Writing handshake");
-
-      // Write the currently queued handshake message until all data is sent.
-      while(writeBuf_.size()) {
-        ssize_t writ = ::send(sockfd_, writeBuf_.data(), writeBuf_.size(), 0);
-        errno = ::WSAGetLastError();
-        if (writ < 0 && errno == WSAEINTR) {
-          continue;
-        }
-        if (writ < 0 && errno == WSAEWOULDBLOCK) {
-          return TLS_ERR_WOULDBLOCK;
-        }
-        if (writ <= 0) {
-          status_ = SEC_E_INCOMPLETE_MESSAGE;
-          state_ = st_error;
-          return TLS_ERR_ERROR;
-        }
-        writeBuf_.eat(writ);
-      }
+    if (state_ == st_handshake_write_last) {
+      state_ = st_handshake_done;
+      goto restart;
+    }
 
-      if (state_ == st_handshake_write_last) {
-        state_ = st_handshake_done;
-        goto restart;
-      }
+    // Have to read one or more response messages.
+    state_ = st_handshake_read;
+  }
+  // Fall through
 
-      // Have to read one or more response messages.
-      state_ = st_handshake_read;
-    }
-    // Fall through
-
-    case st_handshake_read: {
-read:
-      A2_LOG_DEBUG("WinTLS: Reading handshake...");
-
-      // All write buffered data is invalid at this point!
-      writeBuf_.clear();
-
-      // Read as many bytes as possible, up to 4k new bytes.
-      // We do not know how many bytes will arrive from the server at this
-      // point.
-      readBuf_.resize(readBuf_.size() + 4096);
-      while (readBuf_.free()) {
-        ssize_t read = ::recv(sockfd_, readBuf_.end(), readBuf_.free(), 0);
-        errno = ::WSAGetLastError();
-        if (read < 0 && errno == WSAEINTR) {
-          continue;
-        }
-        if (read < 0 && errno == WSAEWOULDBLOCK) {
-          break;
-        }
-        if (read <= 0) {
-          status_ = SEC_E_INCOMPLETE_MESSAGE;
-          state_ = st_error;
-          return TLS_ERR_ERROR;
-        }
-        readBuf_.advance(read);
-        break;
-      }
-      if (!readBuf_.size()) {
-        return TLS_ERR_WOULDBLOCK;
-      }
+  case st_handshake_read: {
+  read:
+    A2_LOG_DEBUG("WinTLS: Reading handshake...");
 
-      // Need to copy the data, as Schannel is free to mess with it. But we
-      // might later need unmodified data from the original read buffer.
-      auto bufcopy = make_unique<char[]>(readBuf_.size());
-      memcpy(bufcopy.get(), readBuf_.data(), readBuf_.size());
+    // All write buffered data is invalid at this point!
+    writeBuf_.clear();
 
-      // Set up buffers. inbufs will be the raw bytes the library has to decode.
-      // outbufs will contain generated responses, if any.
-      TLSBuffer inbufs[] = {
-        TLSBuffer(SECBUFFER_TOKEN, readBuf_.size(), bufcopy.get()),
-        TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
-      };
-      TLSBufferDesc indesc(inbufs, 2);
-      TLSBuffer outbufs[] = {
-        TLSBuffer(SECBUFFER_TOKEN, 0, nullptr),
-        TLSBuffer(SECBUFFER_ALERT, 0, nullptr),
-      };
-      TLSBufferDesc outdesc(outbufs, 2);
-      if (side_ == TLS_CLIENT) {
-        SEC_CHAR* host = hostname_.empty() ?
-          nullptr :
-          const_cast<SEC_CHAR*>(hostname_.c_str());
-        status_ = ::InitializeSecurityContext(
-            cred_,
-            &handle_,
-            host,
-            kReqFlags,
-            0,
-            0,
-            &indesc,
-            0,
-            nullptr,
-            &outdesc,
-            &flags,
-            nullptr);
-      }
-      else {
-        status_ = ::AcceptSecurityContext(
-            cred_,
-            state_ == st_initialized ? nullptr : &handle_,
-            &indesc,
-            kReqAFlags,
-            0,
-            state_ == st_initialized ? &handle_ : nullptr,
-            &outdesc,
-            &flags,
-            nullptr);
+    // Read as many bytes as possible, up to 4k new bytes.
+    // We do not know how many bytes will arrive from the server at this
+    // point.
+    readBuf_.resize(readBuf_.size() + 4096);
+    while (readBuf_.free()) {
+      ssize_t read = ::recv(sockfd_, readBuf_.end(), readBuf_.free(), 0);
+      errno = ::WSAGetLastError();
+      if (read < 0 && errno == WSAEINTR) {
+        continue;
       }
-      if (status_ == SEC_E_INCOMPLETE_MESSAGE) {
-        // Not enough raw bytes read yet to decode a full message.
-        return TLS_ERR_WOULDBLOCK;
+      if (read < 0 && errno == WSAEWOULDBLOCK) {
+        break;
       }
-      if (status_ != SEC_E_OK && status_ != SEC_I_CONTINUE_NEEDED) {
+      if (read <= 0) {
+        status_ = SEC_E_INCOMPLETE_MESSAGE;
         state_ = st_error;
         return TLS_ERR_ERROR;
       }
+      readBuf_.advance(read);
+      break;
+    }
+    if (!readBuf_.size()) {
+      return TLS_ERR_WOULDBLOCK;
+    }
 
-      // Raw bytes where not entirely consumed, i.e. readBuf_ still contains
-      // unprocessed data from the next message?
-      if (inbufs[1].BufferType == SECBUFFER_EXTRA && inbufs[1].cbBuffer > 0) {
-        readBuf_.eat(readBuf_.size() - inbufs[1].cbBuffer);
-      }
-      else {
-        readBuf_.clear();
-      }
+    // Need to copy the data, as Schannel is free to mess with it. But we
+    // might later need unmodified data from the original read buffer.
+    auto bufcopy = make_unique<char[]>(readBuf_.size());
+    memcpy(bufcopy.get(), readBuf_.data(), readBuf_.size());
 
-      // Check if the library produced a new outgoing message and queue it.
-      for (auto& buf : outbufs) {
-        if (buf.BufferType == SECBUFFER_TOKEN && buf.cbBuffer > 0) {
-          writeBuf_.write(buf.pvBuffer, buf.cbBuffer);
-          FreeContextBuffer(buf.pvBuffer);
-          state_ = st_handshake_write;
-        }
-      }
+    // Set up buffers. inbufs will be the raw bytes the library has to decode.
+    // outbufs will contain generated responses, if any.
+    TLSBuffer inbufs[] = {
+        TLSBuffer(SECBUFFER_TOKEN, readBuf_.size(), bufcopy.get()),
+        TLSBuffer(SECBUFFER_EMPTY, 0, nullptr),
+    };
+    TLSBufferDesc indesc(inbufs, 2);
+    TLSBuffer outbufs[] = {
+      TLSBuffer(SECBUFFER_TOKEN, 0, nullptr),
+      TLSBuffer(SECBUFFER_ALERT, 0, nullptr),
+    };
+    TLSBufferDesc outdesc(outbufs, 2);
+    if (side_ == TLS_CLIENT) {
+      SEC_CHAR* host = hostname_.empty() ?
+                           nullptr :
+                           const_cast<SEC_CHAR*>(hostname_.c_str());
+      status_ = ::InitializeSecurityContext(cred_,
+                                            &handle_,
+                                            host,
+                                            kReqFlags,
+                                            0,
+                                            0,
+                                            &indesc,
+                                            0,
+                                            nullptr,
+                                            &outdesc,
+                                            &flags,
+                                            nullptr);
+    }
+    else {
+      status_ =
+          ::AcceptSecurityContext(cred_,
+                                  state_ == st_initialized ? nullptr : &handle_,
+                                  &indesc,
+                                  kReqAFlags,
+                                  0,
+                                  state_ == st_initialized ? &handle_ : nullptr,
+                                  &outdesc,
+                                  &flags,
+                                  nullptr);
+    }
+    if (status_ == SEC_E_INCOMPLETE_MESSAGE) {
+      // Not enough raw bytes read yet to decode a full message.
+      return TLS_ERR_WOULDBLOCK;
+    }
+    if (status_ != SEC_E_OK && status_ != SEC_I_CONTINUE_NEEDED) {
+      state_ = st_error;
+      return TLS_ERR_ERROR;
+    }
 
-      // Need to read additional messages?
-      if (status_ == SEC_I_CONTINUE_NEEDED) {
-        A2_LOG_DEBUG("WinTLS: Continuing with handshake");
-        goto restart;
-      }
+    // Raw bytes where not entirely consumed, i.e. readBuf_ still contains
+    // unprocessed data from the next message?
+    if (inbufs[1].BufferType == SECBUFFER_EXTRA && inbufs[1].cbBuffer > 0) {
+      readBuf_.eat(readBuf_.size() - inbufs[1].cbBuffer);
+    }
+    else {
+      readBuf_.clear();
+    }
 
-      if (side_ == TLS_CLIENT && flags != kReqFlags) {
-        A2_LOG_ERROR(fmt("WinTLS: Channel setup failed. Schannel provider did "
-                         "not fulfill requested flags. "
-                         "Excepted: %lu Actual: %lu",
-                         kReqFlags, flags));
-        status_ = SEC_E_INTERNAL_ERROR;
-        state_ = st_error;
-        return TLS_ERR_ERROR;
+    // Check if the library produced a new outgoing message and queue it.
+    for (auto& buf : outbufs) {
+      if (buf.BufferType == SECBUFFER_TOKEN && buf.cbBuffer > 0) {
+        writeBuf_.write(buf.pvBuffer, buf.cbBuffer);
+        FreeContextBuffer(buf.pvBuffer);
+        state_ = st_handshake_write;
       }
+    }
 
-      if (state_ == st_handshake_write) {
-        A2_LOG_DEBUG("WinTLS: Continuing with handshake (last write)");
-        state_ = st_handshake_write_last;
-        goto restart;
-      }
+    // Need to read additional messages?
+    if (status_ == SEC_I_CONTINUE_NEEDED) {
+      A2_LOG_DEBUG("WinTLS: Continuing with handshake");
+      goto restart;
+    }
+
+    if (side_ == TLS_CLIENT && flags != kReqFlags) {
+      A2_LOG_ERROR(fmt("WinTLS: Channel setup failed. Schannel provider did "
+                       "not fulfill requested flags. "
+                       "Excepted: %lu Actual: %lu",
+                       kReqFlags,
+                       flags));
+      status_ = SEC_E_INTERNAL_ERROR;
+      state_ = st_error;
+      return TLS_ERR_ERROR;
     }
-    // Fall through
 
-    case st_handshake_done:
-      // All ready now :D
-      state_ = st_connected;
-      A2_LOG_INFO(fmt("WinTLS: connected with: %s",
-                      getCipherSuite(&handle_).c_str()));
-      return TLS_ERR_OK;
+    if (state_ == st_handshake_write) {
+      A2_LOG_DEBUG("WinTLS: Continuing with handshake (last write)");
+      state_ = st_handshake_write_last;
+      goto restart;
+    }
+  }
+  // Fall through
+
+  case st_handshake_done:
+    // All ready now :D
+    state_ = st_connected;
+    A2_LOG_INFO(
+        fmt("WinTLS: connected with: %s", getCipherSuite(&handle_).c_str()));
+    return TLS_ERR_OK;
   }
 
   A2_LOG_ERROR("WinTLS: Unreachable reached during tlsConnect! This is a bug!");
@@ -796,16 +795,17 @@ std::string WinTLSSession::getLastErrorString()
 {
   std::stringstream ss;
   wchar_t* buf = nullptr;
-  if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
-                    FORMAT_MESSAGE_FROM_SYSTEM |
-                    FORMAT_MESSAGE_IGNORE_INSERTS,
-                    nullptr,
-                    status_,
-                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                    (LPWSTR)&buf,
-                    1024,
-                    nullptr) && buf) {
-    ss << "Error: " << wCharToUtf8(buf) << "(" << std::hex << status_ <<  ")";
+  auto rv = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                               FORMAT_MESSAGE_FROM_SYSTEM |
+                               FORMAT_MESSAGE_IGNORE_INSERTS,
+                           nullptr,
+                           status_,
+                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                           (LPWSTR) & buf,
+                           1024,
+                           nullptr);
+  if (rv && buf) {
+    ss << "Error: " << wCharToUtf8(buf) << "(" << std::hex << status_ << ")";
     LocalFree(buf);
   }
   else {

+ 69 - 49
src/WinTLSSession.h

@@ -45,60 +45,79 @@
 namespace aria2 {
 
 namespace wintls {
-  struct Buffer {
-  private:
-    size_t off_, free_, cap_;
-    std::vector<char> buf_;
-
-  public:
-    inline Buffer() : off_(0), free_(0), cap_(0) {}
+struct Buffer
+{
+private:
+  size_t off_, free_, cap_;
+  std::vector<char> buf_;
 
-    inline size_t size() const {
-      return off_;
-    }
-    inline size_t free() const {
-      return free_;
-    }
-    inline void resize(size_t len) {
-      if (cap_ >= len) {
-        return;
-      }
-      buf_.resize(len);
-      cap_ = buf_.size();
-      free_ = cap_ - off_;
-    }
-    inline char* data() {
-      return buf_.data();
-    }
-    inline char* end() {
-      return buf_.data() + off_;
-    }
-    inline void eat(size_t len) {
-      off_ -= len;
-      if (off_) {
-        memmove(buf_.data(), buf_.data() + len, off_);
-      }
-      free_ = cap_ - off_;
-    }
-    inline void clear() {
-      eat(off_);
+public:
+  inline Buffer() : off_(0), free_(0), cap_(0) {}
+
+  inline size_t size() const
+  {
+    return off_;
+  }
+
+  inline size_t free() const
+  {
+    return free_;
+  }
+
+  inline void resize(size_t len)
+  {
+    if (cap_ >= len) {
+      return;
     }
-    inline void advance(size_t len) {
-      off_ += len;
-      free_ = cap_ - off_;
+    buf_.resize(len);
+    cap_ = buf_.size();
+    free_ = cap_ - off_;
+  }
+
+  inline char* data()
+  {
+    return buf_.data();
+  }
+
+  inline char* end()
+  {
+    return buf_.data() + off_;
+  }
+
+  inline void eat(size_t len)
+  {
+    off_ -= len;
+    if (off_) {
+      memmove(buf_.data(), buf_.data() + len, off_);
     }
-    inline void write(const void* data, size_t len) {
-      if (!len) {
-        return;
-      }
-      resize(off_ + len);
-      memcpy(end(), data, len);
-      advance(len);
+    free_ = cap_ - off_;
+  }
+
+  inline void clear()
+  {
+    eat(off_);
+  }
+
+  inline void advance(size_t len)
+  {
+    off_ += len;
+    free_ = cap_ - off_;
+  }
+
+  inline void write(const void* data, size_t len)
+  {
+    if (!len) {
+      return;
     }
-  };
+    resize(off_ + len);
+    memcpy(end(), data, len);
+    advance(len);
+  }
+};
 } // namespace wintls
 
-class WinTLSSession : public TLSSession {
+class WinTLSSession : public TLSSession
+{
   enum state_t {
     st_constructed,
     st_initialized,
@@ -156,7 +175,8 @@ public:
   // if the underlying transport blocks, or TLS_ERR_ERROR.
   // When returning TLS_ERR_ERROR, provide certificate validation error
   // in |handshakeErr|.
-  virtual int tlsConnect(const std::string& hostname, std::string& handshakeErr) CXX11_OVERRIDE;
+  virtual int tlsConnect(const std::string& hostname,
+                         std::string& handshakeErr) CXX11_OVERRIDE;
 
   // Performs server side handshake. This function returns TLS_ERR_OK
   // if it succeeds, or TLS_ERR_WOULDBLOCK if the underlying transport