📜  ssl_get_servername return null - C 编程语言(1)

📅  最后修改于: 2023-12-03 15:20:20.575000             🧑  作者: Mango

SSL_get_servername return null - C编程语言

当进行SSL握手时,SSL客户端会根据服务器提供的证书中的主机名信息来检查是否匹配服务器的主机名。如果这个主机名检查失败,客户端将会拒绝和服务器建立安全连接。在这个过程中,开发者可能会发现SSL_get_servername函数返回null。

什么是SSL_get_servername函数?

SSL_get_servername函数获取在客户端SSL对象中保存的服务器名称。该信息在客户端SSL握手过程中传递给SSL服务器。如果用户名不为空,则应用程序可以使用此信息来确定服务器的原始请求。

以下是SSL_get_servername函数的函数原型:

const char *SSL_get_servername(const SSL *ssl, const int type);
为什么SSL_get_servername返回null?

在SSL客户端应用程序中,SSL_get_servername函数可能会返回null。这可能是因为以下几种情况:

  • SSL客户端还没有从服务器接收到SNI扩展。此时,SSL_get_servername返回null并另外启动一个连接以获取扩展。这通常是在第一个连接的Hello阶段成功后进行的。
  • 客户端确实与服务器成功建立连接,但服务器没有提供可用的SNI扩展(即,服务器没有返回TLS握手消息中的主机名)。
  • SNI扩展在TLS握手中是可选的,某些服务器可能不支持使用此扩展进行握手。
如何修复?

如果SSL_get_servername返回null,可以先检查是否已成功建立连接。如果已经建立了连接,则SSL服务器可能没有提供SNI扩展。在这种情况下,您可以使用服务器的IP地址或域名。

如果尚未建立连接,并且仍然返回null,则SSL客户端可能未接收到SNI扩展。您可以尝试重新启动连接,以便在此后尝试获取扩展。

以下是一个基本的代码示例,可用于获取SSL服务器的主机名和IP地址:

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>

int main(int argc, char **argv)
{
    const char* hostname = "www.example.com"; // 主机名
    int port = 443; // 端口号
    const char* ipaddress = NULL;
    const char* servername = NULL;

    struct addrinfo* addr;
    struct addrinfo hints;
    SSL_CTX* ctx;
    SSL* ssl;
    BIO* bio;
    int sockfd;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if (getaddrinfo(hostname, NULL, &hints, &addr) != 0)
        perror("getaddrinfo");

    sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);

    connect(sockfd, addr->ai_addr, addr->ai_addrlen);

    ctx = SSL_CTX_new(TLS_client_method());
    ssl = SSL_new(ctx);

    bio = BIO_new_socket(sockfd, BIO_NOCLOSE);
    SSL_set_bio(ssl, bio, bio);

    SSL_connect(ssl);

    // 获取服务器的主机名
    servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);

    // 获取服务器的IP地址
    struct sockaddr_in sa;
    socklen_t len = sizeof(sa);
    if (getsockname(sockfd, (struct sockaddr *)(&sa), &len) == -1) {
        perror("getsockname() failed");
    } else {
        ipaddress = inet_ntoa(sa.sin_addr);
    }

    SSL_shutdown(ssl);
    SSL_free(ssl);
    SSL_CTX_free(ctx);
    close(sockfd);
    freeaddrinfo(addr);

    printf("Server hostname: %s\n", servername);
    printf("Server IP Address: %s\n", ipaddress);

    return 0;
}

上面的示例可以用来获取SSL服务器的主机名和IP地址。如果SSL_get_servername返回null,则使用getsockname获取IP地址。