[toc]

使用C语言与openssl库在Linux中使用https协议浏览网页

1. 安装openssl库

  • Debian中安装openssl库

    1
    2
    sudo apt update
    sudo apt install openssl libssl-dev
  • 编译命令

    1
    gcc -o LinClient LinClient.c -lssl -lcrypto

2. 用C语言在Linux中使用https协议浏览https://www.bilibili.com/

LinClient.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// LinClient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define BUFFER_SIZE 4096

void send_request(SSL* ssl, const char* host, const char* path) {
char request[BUFFER_SIZE];
sprintf(request, "GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Connection: close\r\n\r\n", path, host);
SSL_write(ssl, request, strlen(request));
}

int main() {
char response[BUFFER_SIZE];
int bytes_received;

// 初始化OpenSSL
SSL_library_init();
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
SSL* ssl = NULL;

// 创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Could not create socket");
return 1;
}

// 设置服务器信息
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(443); // HTTPS端口

// 将主机名转换为IP地址
struct hostent* host = gethostbyname("www.bilibili.com");
if (host == NULL) {
perror("Could not resolve hostname");
close(sock);
return 1;
}
memcpy(&server.sin_addr, host->h_addr, host->h_length);

// 连接到服务器
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
perror("Connect failed");
close(sock);
return 1;
}

// 初始化SSL连接
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);

// 建立SSL连接
if (SSL_connect(ssl) <= 0) {
printf("SSL connect failed. Error Code: %ld\n", ERR_get_error());
SSL_free(ssl);
SSL_CTX_free(ctx);
close(sock);
return 1;
}

// 发送初始请求
send_request(ssl, "www.bilibili.com", "/");

// 接收响应并处理重定向
while ((bytes_received = SSL_read(ssl, response, sizeof(response) - 1)) > 0) {
response[bytes_received] = '\0'; // 添加字符串结束符
printf("%s", response); // 打印响应

// 检查是否有重定向
if (strstr(response, "HTTP/1.1 301 Moved Permanently") ||
strstr(response, "HTTP/1.1 302 Found")) {
// 提取Location头
char* location = strstr(response, "Location: ");
if (location) {
location += strlen("Location: ");
char* end = strstr(location, "\r\n");
if (end) {
*end = '\0'; // 结束字符串
printf("\nRedirecting to: %s\n", location);

// 关闭当前SSL连接
SSL_shutdown(ssl);
SSL_free(ssl);
close(sock);

// 重新连接到新的URL
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Could not create socket");
SSL_CTX_free(ctx);
return 1;
}

// 解析新的主机名
char* new_host = location; // 这里假设Location是完整的URL
char* path = strchr(new_host, '/'); // 找到路径
if (path) {
*path = '\0'; // 分离主机名
path++; // 跳过'/'
}

// 连接到新的主机
host = gethostbyname(new_host);
if (host == NULL) {
perror("Could not resolve hostname");
close(sock);
SSL_CTX_free(ctx);
return 1;
}
memcpy(&server.sin_addr, host->h_addr, host->h_length);
server.sin_port = htons(443); // HTTPS端口

// 连接到新的服务器
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
perror("Connect failed");
close(sock);
SSL_CTX_free(ctx);
return 1;
}

// 初始化新的SSL连接
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
if (SSL_connect(ssl) <= 0) {
printf("SSL connect failed. Error Code: %ld\n", ERR_get_error());
SSL_free(ssl);
SSL_CTX_free(ctx);
close(sock);
return 1;
}

// 发送请求到新的URL
send_request(ssl, new_host, path);
}
}
break; // 处理完重定向后退出循环
}
}

// 关闭SSL连接和套接字
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
close(sock);
return 0;
}

3. 用C语言与openssl在Linux中使用https协议浏览https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1(因网络问题测试失败)

LinClient.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define HOST "duckduckgo.com"
#define PORT 443
#define REQUEST "GET /?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1 HTTP/1.1\r\nHost: " HOST "\r\nConnection: close\r\n\r\n"

void init_openssl() {
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}

void cleanup_openssl() {
EVP_cleanup();
}

SSL_CTX *create_context() {
const SSL_METHOD *method;
SSL_CTX *ctx;

method = SSLv23_client_method();
ctx = SSL_CTX_new(method);
if (!ctx) {
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}

void destroy_context(SSL_CTX *ctx) {
SSL_CTX_free(ctx);
}

int main() {
int sock;
struct sockaddr_in server_addr;
SSL_CTX *ctx;
SSL *ssl;
char buffer[4096];

// 初始化 OpenSSL
init_openssl();
ctx = create_context();

// 创建 TCP socket
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Unable to create socket");
exit(EXIT_FAILURE);
}

// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr("151.101.1.67"); // DuckDuckGo 的 IP 地址

// 连接到服务器
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) {
perror("Connection failed");
close(sock);
exit(EXIT_FAILURE);
}

// 创建 SSL 连接
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
if (SSL_connect(ssl) <= 0) {
ERR_print_errors_fp(stderr);
} else {
// 发送 HTTP GET 请求
SSL_write(ssl, REQUEST, strlen(REQUEST));

// 接收响应
int bytes;
while ((bytes = SSL_read(ssl, buffer, sizeof(buffer) - 1)) > 0) {
buffer[bytes] = 0; // 确保字符串以 null 结尾
printf("%s", buffer);
}
}

// 清理
SSL_shutdown(ssl);
SSL_free(ssl);
close(sock);
destroy_context(ctx);
cleanup_openssl();

return 0;
}

使用C语言与libcurl库在Linux中使用https协议浏览网页

1.安装libcurl库

  • Debian中安装libcurl库

    1
    sudo apt-get install libcurl4-openssl-dev
  • 编译链接命令

    1
    gcc LinClient.c -o LinClient -lcurl

2.用C语言在Linux中使用https协议浏览https://www.bilibili.com/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>

// 回调函数,用于处理接收到的数据
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
// 将接收到的数据打印到标准输出
printf("%.*s", (int)(size * nmemb), (char *)ptr);
return size * nmemb; // 返回处理的字节数
}

int main(void) {
CURL *curl;
CURLcode res;

// 初始化 libcurl
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
// 设置要访问的 URL
curl_easy_setopt(curl, CURLOPT_URL, "https://www.bilibili.com/");

// 设置回调函数以处理接收到的数据
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);

// 执行请求
res = curl_easy_perform(curl);

// 检查请求是否成功
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}

// 清理
curl_easy_cleanup(curl);
}

// 全局清理
curl_global_cleanup();

return 0;
}

3. 用C语言在Linux中使用https协议浏览https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1(因网络问题测试失败)

LinClient.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//LinClient.c
#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>

// 回调函数,用于处理接收到的数据
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
// 将接收到的数据打印到标准输出
printf("%.*s", (int)(size * nmemb), (char *)ptr);
return size * nmemb; // 返回处理的字节数
}

int main(void) {
CURL *curl;
CURLcode res;

// 初始化 libcurl
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
// 设置要访问的 URL
curl_easy_setopt(curl, CURLOPT_URL, "https://www.youtube.com/");

// 设置回调函数以处理接收到的数据
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);

// 执行请求
res = curl_easy_perform(curl);

// 检查请求是否成功
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}

// 清理
curl_easy_cleanup(curl);
}

// 全局清理
curl_global_cleanup();

return 0;
}