首页 > 解决方案 > 在纯 C 中将 HTTP 连接传输到 HTTPS

问题描述

我在将 HTTP 连接套接字程序转移到 HTTPS 连接套接字代码时遇到严重问题,如何在纯 C 中仅建立 HTTPS 连接?

我正在开发包管理器并正在重写 connection.c 文件,该文件包含的唯一内容是用于与包含包的服务器建立初始连接的代码,它什么也不做。我通过 HTTP 连接可以 100% 工作,但是我需要转移到 HTTPS 连接并且需要使用 LibreSSL;目前我正在尝试使用 OpenSSL,因为我在 LibreSSL 上找不到任何东西。我的HTTP代码如下:

#include <stdio.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <arpa/inet.h>

#include <netinet/in.h>
#include <string.h>

#include <unistd.h>
#include "repos.h"

#include "resolv.h"

short connection()
{
    short socket_desc;
    socket_desc = socket(AF_INET, SOCK_STREAM, 0); /* create socket with IPv4 and TCP protocol */

    char host[17];

    if (socket_desc == -1)
        printf("could not create socket\n");

    struct sockaddr_in *serv_addr = calloc(1, sizeof(struct sockaddr_in));

    serv_addr->sin_family = AF_INET;
    serv_addr->sin_port = htons(80);

    resolv(DEFAULT_HOST, host); /* set repository to use */

    if (inet_pton(AF_INET, host, &serv_addr->sin_addr) <= 0) {
        printf("error");
        free(serv_addr);
        return -1;
    }

    if (connect(socket_desc, (struct sockaddr *)serv_addr, sizeof(*serv_addr)) < 0) {
        printf("connection failed\n");
        return 1;
    }

    else {
        printf("connection initialized\n");
        return 0;
    }

    /* close the connection */
    free(serv_addr);
    close(socket_desc);

    return 0;
}

这 100% 有效,我只想将其移植到 HTTPS。在查看了格式可怕的 OpenSSL client.c 示例(请参见此处:https ://wiki.openssl.org/index.php/SSL/TLS_Client )后,我让该代码在我的系统上运行(必须对其进行一些更改) ,然后将我的 HTTP 代码移植到 HTTPS。我研究了一段时间,并认为我让它工作了,我一直在调试它,但不知道为什么它一直失败。代码如下:

#include <stdio.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <arpa/inet.h>

#include <netinet/in.h>
#include <string.h>

#include <unistd.h>
#include <openssl/bio.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#include "repos.h"
#include "resolv.h"

    SSL *cSSL;

void initssl()
{
    SSL_load_error_strings();
    SSL_library_init();

    OpenSSL_add_all_algorithms();
}

void destroyssl()
{
    ERR_free_strings();
    EVP_cleanup();
}

void shutdownssl()
{
    SSL_shutdown(cSSL);
    SSL_free(cSSL);
}

int main()
{
    short socket_desc;
    short socket_ssl;

    char host[17];
    socklen_t sock_size;

    SSL_CTX *sslctx;

    initssl();
    socket_desc = socket(AF_INET, SOCK_STREAM, 0); /* create socket with IPv4 and TCP protocol */

    if (socket_desc == -1)
        printf("could not create socket\n");

    struct sockaddr_in *serv_addr = calloc(1, sizeof(struct sockaddr_in));

    serv_addr->sin_family = AF_INET;
    serv_addr->sin_port = htons(443);

    resolv(DEFAULT_HOST, host); /* resolve DEFAULT_HOST and store the ip in host */

    if (inet_pton(AF_INET, host, &serv_addr->sin_addr) <= 0) {
        printf("error");
        free(serv_addr);
        return -1;
    }

    bind(socket_desc, (struct sockaddr *)serv_addr, sizeof(struct sockaddr_in));
    listen(socket_desc, 5);

    sock_size = sizeof(struct sockaddr_in);
    socket_ssl = accept(socket_desc, (struct sockaddr *)serv_addr, &sock_size); /* this is where hang occurs, however I am usnure why. I am reading docs and such and if I figure this out I will post the fix; however I would love some advice/help if anyone sees my error */

    sslctx = SSL_CTX_new(SSLv23_server_method());
    SSL_CTX_set_options(sslctx, SSL_OP_SINGLE_DH_USE);

    short use_cert = SSL_CTX_use_certificate_file(sslctx, "/serverCertificate.pem" , SSL_FILETYPE_PEM);
    short use_prv = SSL_CTX_use_PrivateKey_file(sslctx, "/serverCertificate.pem", SSL_FILETYPE_PEM);

    cSSL = SSL_new(sslctx);
    SSL_set_fd(cSSL, socket_ssl);

    char ssl_err = SSL_accept(cSSL);

    if(ssl_err <= 0) {
        printf("connection failed\n");
        shutdownssl();
    }

    else
        printf("connected\n");

    return 0;
}

现在我知道它缺少一些明显的东西,例如编写我自己的 initssl(我不确定为什么它还没有在 lib 中,但我开始明白为什么 OpenBSD 决定分叉)。我把它们排除在外,因为我对使用 LibreSSL 更感兴趣,并且不相信你需要它们来使用 LibreSSL。我尝试使用打印语句进行调试,但即使在 main() 的顶部给出它们也不会被打印。我不确定为什么这不起作用,需要一些帮助才能移植。我编写的其他文件 repos.h 和 resolv.c 如下所示:

/* repos.h */
char DEFAULT_HOST[11] = "gitlab.com";
char DEFAULT_PAGE[24] = "Puffles_the_Dragon/core";
/* resolv.c */
#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <arpa/inet.h>

#include <netdb.h>

short resolv(char *host, char *ip)
{
        struct hostent *hp = calloc(1, sizeof(struct hostent));
        hp = gethostbyname(host);

        if (hp == NULL) {
                fprintf(stderr, "gethostbyname() failed\n");
                exit(1);
        }

        else {
                short i = 0;

                while (hp->h_addr_list[i] != NULL) {
                inet_ntoa(*(struct in_addr *)(hp->h_addr_list[i]));
                i++;
                }

        strlcpy(ip, inet_ntoa(*(struct in_addr *)(hp->h_addr_list[0])), 16);
        }

        return 0;
}

我知道其中一些调用由于 IPv6 而过时,但在我让这一切正常工作并从 BSD libc 移植到 musl libc 之后,我将添加 IPv6。

我希望 HTTPS 代码能够运行并连接到服务器,从而打印连接,但它只是运行并且不会失败或打印任何内容。

标签: clibressl

解决方案


推荐阅读