225 lines
6.2 KiB
C++
225 lines
6.2 KiB
C++
#include <iostream>
|
||
#include <string>
|
||
#include <openssl/ssl.h>
|
||
#include <openssl/err.h>
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
#include <unistd.h>
|
||
#include <arpa/inet.h>
|
||
#include <fcntl.h>
|
||
#include <signal.h>
|
||
#include <sys/select.h>
|
||
#include <vector>
|
||
#include <netinet/tcp.h>
|
||
|
||
const int PORT = 7271;
|
||
const char* SERVER_CERT_FILE = "server.crt";
|
||
const char* SERVER_KEY_FILE = "server.key";
|
||
const char* CA_CERT_FILE = "ca.crt";
|
||
|
||
volatile sig_atomic_t server_running = 1;
|
||
|
||
void signal_handler(int sig) {
|
||
std::cout << "\n收到信号 " << sig << ",正在关闭服务器..." << std::endl;
|
||
server_running = 0;
|
||
}
|
||
|
||
void print_openssl_errors() {
|
||
BIO* bio = BIO_new_fp(stderr, BIO_NOCLOSE);
|
||
ERR_print_errors(bio);
|
||
BIO_free(bio);
|
||
}
|
||
|
||
void init_openssl() {
|
||
SSL_load_error_strings();
|
||
OpenSSL_add_ssl_algorithms();
|
||
}
|
||
|
||
void cleanup_openssl() {
|
||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||
EVP_cleanup();
|
||
#endif
|
||
}
|
||
|
||
SSL_CTX* create_context() {
|
||
const SSL_METHOD* method = TLS_server_method();
|
||
SSL_CTX* ctx = SSL_CTX_new(method);
|
||
|
||
if (!ctx) {
|
||
std::cerr << "无法创建SSL上下文" << std::endl;
|
||
print_openssl_errors();
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
||
SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
|
||
|
||
return ctx;
|
||
}
|
||
|
||
int verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
|
||
if (!preverify_ok) {
|
||
int err = X509_STORE_CTX_get_error(ctx);
|
||
if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
|
||
return 1;
|
||
}
|
||
}
|
||
return preverify_ok;
|
||
}
|
||
|
||
void configure_context(SSL_CTX* ctx) {
|
||
if (SSL_CTX_use_certificate_file(ctx, SERVER_CERT_FILE, SSL_FILETYPE_PEM) <= 0) {
|
||
std::cerr << "无法加载服务器证书" << std::endl;
|
||
print_openssl_errors();
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
if (SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY_FILE, SSL_FILETYPE_PEM) <= 0) {
|
||
std::cerr << "无法加载服务器私钥" << std::endl;
|
||
print_openssl_errors();
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
if (!SSL_CTX_check_private_key(ctx)) {
|
||
std::cerr << "证书和私钥不匹配" << std::endl;
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
if (SSL_CTX_load_verify_locations(ctx, CA_CERT_FILE, nullptr) <= 0) {
|
||
std::cerr << "无法加载CA证书" << std::endl;
|
||
print_openssl_errors();
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
|
||
SSL_CTX_set_verify_depth(ctx, 4);
|
||
|
||
// 关键优化:简化SSL选项
|
||
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
|
||
SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES128-GCM-SHA256");
|
||
}
|
||
|
||
void graceful_ssl_close(SSL* ssl, int fd) {
|
||
if (!ssl || fd < 0) return;
|
||
|
||
// 简化的关闭逻辑
|
||
SSL_shutdown(ssl);
|
||
SSL_free(ssl);
|
||
close(fd);
|
||
}
|
||
|
||
void handle_connection(SSL* ssl, int client_fd, const char* client_ip, uint16_t client_port) {
|
||
std::cout << "\n处理连接 " << client_ip << ":" << client_port << std::endl;
|
||
std::cout << "协议: " << SSL_get_version(ssl) << std::endl;
|
||
|
||
// 验证客户端证书
|
||
X509* client_cert = SSL_get_peer_certificate(ssl);
|
||
if (!client_cert) {
|
||
std::cerr << "客户端未提供证书" << std::endl;
|
||
graceful_ssl_close(ssl, client_fd);
|
||
return;
|
||
}
|
||
X509_free(client_cert);
|
||
|
||
// 设置TCP_NODELAY
|
||
int optval = 1;
|
||
setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
||
|
||
// 设置60秒读取超时
|
||
struct timeval timeout = {60, 0};
|
||
setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||
|
||
std::cout << "等待客户端数据..." << std::endl;
|
||
|
||
// 读取客户端数据
|
||
char buffer[4096];
|
||
int bytes = SSL_read(ssl, buffer, sizeof(buffer));
|
||
|
||
if (bytes > 0) {
|
||
std::cout << "收到数据: " << bytes << "字节" << std::endl;
|
||
|
||
// 发送响应
|
||
const char* response =
|
||
"HTTP/1.1 200 OK\r\n"
|
||
"Content-Type: text/plain\r\n"
|
||
"Connection: close\r\n"
|
||
"\r\n"
|
||
"Hello from TLS server!";
|
||
SSL_write(ssl, response, strlen(response));
|
||
std::cout << "已发送响应" << std::endl;
|
||
} else if (bytes == 0) {
|
||
std::cout << "客户端关闭连接" << std::endl;
|
||
} else {
|
||
std::cout << "客户端未发送数据(超时)" << std::endl;
|
||
}
|
||
|
||
graceful_ssl_close(ssl, client_fd);
|
||
}
|
||
|
||
int main() {
|
||
signal(SIGINT, signal_handler);
|
||
signal(SIGTERM, signal_handler);
|
||
|
||
init_openssl();
|
||
SSL_CTX* ctx = create_context();
|
||
configure_context(ctx);
|
||
|
||
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||
if (sockfd < 0) {
|
||
perror("socket");
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
int optval = 1;
|
||
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||
|
||
sockaddr_in addr{};
|
||
addr.sin_family = AF_INET;
|
||
addr.sin_port = htons(PORT);
|
||
addr.sin_addr.s_addr = INADDR_ANY;
|
||
|
||
if (bind(sockfd, (sockaddr*)&addr, sizeof(addr)) < 0) {
|
||
perror("bind");
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
if (listen(sockfd, 10) < 0) {
|
||
perror("listen");
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
std::cout << "服务器监听端口 " << PORT << std::endl;
|
||
|
||
while (server_running) {
|
||
sockaddr_in client_addr{};
|
||
socklen_t client_len = sizeof(client_addr);
|
||
int client_fd = accept(sockfd, (sockaddr*)&client_addr, &client_len);
|
||
|
||
if (client_fd < 0) {
|
||
if (server_running) perror("accept");
|
||
continue;
|
||
}
|
||
|
||
char client_ip[INET_ADDRSTRLEN];
|
||
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
|
||
uint16_t client_port = ntohs(client_addr.sin_port);
|
||
|
||
SSL* ssl = SSL_new(ctx);
|
||
SSL_set_fd(ssl, client_fd);
|
||
|
||
if (SSL_accept(ssl) <= 0) {
|
||
std::cerr << "SSL握手失败: ";
|
||
print_openssl_errors();
|
||
SSL_free(ssl);
|
||
close(client_fd);
|
||
continue;
|
||
}
|
||
|
||
handle_connection(ssl, client_fd, client_ip, client_port);
|
||
}
|
||
|
||
close(sockfd);
|
||
SSL_CTX_free(ctx);
|
||
cleanup_openssl();
|
||
return 0;
|
||
} |