message: Support IP while verifying SSL certificates
diff: #include <znc/SSLVerifyHost.h>
  #include <znc/Translation.h>
  +#include <arpa/inet.h>
  #ifdef HAVE_LIBSSL
  #if defined(OPENSSL_VERSION_NUMBER) && !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100007
  @@ -369,6 +370,14 @@ static HostnameValidationResult matches_subject_alternative_name(
  }
  san_names_nb = sk_GENERAL_NAME_num(san_names);
  + // Precompute binary representation of hostname in case if it's IP address.
  + // Not the other way around, because there can be multiple text
  + // representation of the same IP.
  + char ip4[4] = {};
  + char ip6[16] = {};
  + const int ip4try = inet_pton(AF_INET, hostname, ip4);
  + const int ip6try = inet_pton(AF_INET6, hostname, ip6);
  +
  // Check each name within the extension
  for (i = 0; i < san_names_nb; i++) {
  const GENERAL_NAME* current_name = sk_GENERAL_NAME_value(san_names, i);
  @@ -381,16 +390,40 @@ static HostnameValidationResult matches_subject_alternative_name(
  // Make sure there isn't an embedded NUL character in the DNS name
  if (ASN1_STRING_length(current_name->d.dNSName) !=
  - static_cast<int>(strlen(dns_name))) {
  + static_cast<int>(strnlen(
  + dns_name, ASN1_STRING_length(current_name->d.dNSName)))) {
  + DEBUG("SSLVerifyHost: embedded null in DNS SAN");
  result = MalformedCertificate;
  break;
  } else { // Compare expected hostname with the DNS name
  - DEBUG("SSLVerifyHost: Found SAN " << dns_name);
  + DEBUG("SSLVerifyHost: Found DNS SAN " << dns_name);
  if (ZNC_Curl::Curl_cert_hostcheck(dns_name, hostname)) {
  result = MatchFound;
  break;
  }
  }
  + } else if (current_name->type == GEN_IPADD) {
  + CString ip(reinterpret_cast<const char*>(
  + ASN1_STRING_get0_data(current_name->d.iPAddress)),
  + ASN1_STRING_length(current_name->d.iPAddress));
  + DEBUG("SSLVerifyHost: Found IP SAN "
  + << ip.Escape_n(CString::EHEXCOLON));
  + if (ip4try && ASN1_STRING_length(current_name->d.iPAddress) == 4) {
  + if (memcmp(ip4,
  + ASN1_STRING_get0_data(current_name->d.iPAddress),
  + 4) == 0) {
  + result = MatchFound;
  + break;
  + }
  + } else if (ip6try &&
  + ASN1_STRING_length(current_name->d.iPAddress) == 16) {
  + if (memcmp(ip6,
  + ASN1_STRING_get0_data(current_name->d.iPAddress),
  + 16) == 0) {
  + result = MatchFound;
  + break;
  + }
  + }
  }
  }
  sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
  