|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <uv.h> |
|
#include <openssl/ssl.h> |
|
#include <openssl/err.h> |
|
|
|
#define DEFAULT_PORT 8443 |
|
#define DEFAULT_BACKLOG 128 |
|
#define CERT_FILE "localhost.pem" |
|
#define KEY_FILE "localhost-key.pem" |
|
|
|
// HTTP レスポンス |
|
static const char http_response[] = |
|
"HTTP/1.1 200 OK\r\n" |
|
"Content-Type: text/plain\r\n" |
|
"Content-Length: 13\r\n" |
|
"Connection: close\r\n" |
|
"\r\n" |
|
"Hello, World!"; |
|
|
|
static SSL_CTX *ssl_ctx; |
|
|
|
typedef struct { |
|
uv_tcp_t tcp; |
|
SSL *ssl; |
|
BIO *read_bio; |
|
BIO *write_bio; |
|
int handshake_completed; |
|
} client_t; |
|
|
|
typedef struct { |
|
uv_write_t req; |
|
uv_buf_t buf; |
|
} write_req_t; |
|
|
|
void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { |
|
buf->base = (char*)malloc(suggested_size); |
|
buf->len = suggested_size; |
|
} |
|
|
|
void on_close(uv_handle_t* handle) { |
|
client_t *client = (client_t*)handle; |
|
if (client->ssl) { |
|
SSL_free(client->ssl); |
|
} |
|
free(client); |
|
} |
|
|
|
void on_write(uv_write_t *req, int status) { |
|
write_req_t *wr = (write_req_t*)req; |
|
free(wr->buf.base); |
|
free(wr); |
|
|
|
if (status) { |
|
fprintf(stderr, "Write error %s\n", uv_strerror(status)); |
|
uv_close((uv_handle_t*)req->handle, on_close); |
|
} |
|
} |
|
|
|
int flush_ssl_write_bio(client_t *client) { |
|
char buffer[4096]; |
|
int bytes = BIO_read(client->write_bio, buffer, sizeof(buffer)); |
|
|
|
if (bytes > 0) { |
|
write_req_t *req = (write_req_t*)malloc(sizeof(write_req_t)); |
|
req->buf = uv_buf_init((char*)malloc(bytes), bytes); |
|
memcpy(req->buf.base, buffer, bytes); |
|
|
|
int r = uv_write((uv_write_t*)req, (uv_stream_t*)&client->tcp, &req->buf, 1, on_write); |
|
if (r) { |
|
free(req->buf.base); |
|
free(req); |
|
return -1; |
|
} |
|
return bytes; |
|
} |
|
return 0; |
|
} |
|
|
|
void do_ssl_handshake(client_t *client) { |
|
int ret = SSL_accept(client->ssl); |
|
|
|
// SSL_accept後に必ずwrite_bioをフラッシュ |
|
flush_ssl_write_bio(client); |
|
|
|
if (ret == 1) { |
|
client->handshake_completed = 1; |
|
printf("SSL handshake completed\n"); |
|
} else { |
|
int err = SSL_get_error(client->ssl, ret); |
|
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { |
|
fprintf(stderr, "SSL handshake failed: %d\n", err); |
|
ERR_print_errors_fp(stderr); |
|
uv_close((uv_handle_t*)&client->tcp, on_close); |
|
} |
|
} |
|
} |
|
|
|
void process_http_request(client_t *client, const char *data, size_t len) { |
|
if (len >= 3 && strncmp(data, "GET", 3) == 0) { |
|
printf("Processing GET request\n"); |
|
int ret = SSL_write(client->ssl, http_response, sizeof(http_response) - 1); |
|
if (ret > 0) { |
|
flush_ssl_write_bio(client); |
|
// レスポンス送信後に接続を閉じる |
|
uv_close((uv_handle_t*)&client->tcp, on_close); |
|
} else { |
|
int err = SSL_get_error(client->ssl, ret); |
|
fprintf(stderr, "SSL_write failed: %d\n", err); |
|
uv_close((uv_handle_t*)&client->tcp, on_close); |
|
} |
|
} else { |
|
printf("Non-GET request, closing connection\n"); |
|
uv_close((uv_handle_t*)&client->tcp, on_close); |
|
} |
|
} |
|
|
|
void on_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { |
|
client_t *client = (client_t*)stream; |
|
|
|
if (nread < 0) { |
|
if (nread != UV_EOF) { |
|
fprintf(stderr, "Read error %s\n", uv_err_name(nread)); |
|
} |
|
uv_close((uv_handle_t*)client, on_close); |
|
goto cleanup; |
|
} |
|
|
|
if (nread > 0) { |
|
// 受信データをSSLのread_bioに書き込み |
|
BIO_write(client->read_bio, buf->base, nread); |
|
|
|
if (!client->handshake_completed) { |
|
do_ssl_handshake(client); |
|
} else { |
|
// ハンドシェイク完了後、アプリケーションデータを読み取り |
|
char ssl_buffer[4096]; |
|
int bytes = SSL_read(client->ssl, ssl_buffer, sizeof(ssl_buffer)); |
|
|
|
if (bytes > 0) { |
|
process_http_request(client, ssl_buffer, bytes); |
|
} else { |
|
int err = SSL_get_error(client->ssl, bytes); |
|
if (err != SSL_ERROR_WANT_READ) { |
|
fprintf(stderr, "SSL_read failed: %d\n", err); |
|
uv_close((uv_handle_t*)&client->tcp, on_close); |
|
} |
|
} |
|
|
|
// SSL_read後もwrite_bioをフラッシュ(Alert等のため) |
|
flush_ssl_write_bio(client); |
|
} |
|
} |
|
|
|
cleanup: |
|
if (buf->base) { |
|
free(buf->base); |
|
} |
|
} |
|
|
|
void on_new_connection(uv_stream_t *server, int status) { |
|
if (status < 0) { |
|
fprintf(stderr, "New connection error %s\n", uv_strerror(status)); |
|
return; |
|
} |
|
|
|
client_t *client = (client_t*)malloc(sizeof(client_t)); |
|
memset(client, 0, sizeof(client_t)); |
|
|
|
uv_tcp_init(uv_default_loop(), &client->tcp); |
|
|
|
if (uv_accept(server, (uv_stream_t*)&client->tcp) == 0) { |
|
printf("New connection accepted\n"); |
|
|
|
// SSL構造体の初期化 |
|
client->ssl = SSL_new(ssl_ctx); |
|
client->read_bio = BIO_new(BIO_s_mem()); |
|
client->write_bio = BIO_new(BIO_s_mem()); |
|
client->handshake_completed = 0; |
|
|
|
SSL_set_bio(client->ssl, client->read_bio, client->write_bio); |
|
SSL_set_accept_state(client->ssl); |
|
|
|
uv_read_start((uv_stream_t*)&client->tcp, alloc_buffer, on_read); |
|
} else { |
|
free(client); |
|
} |
|
} |
|
|
|
int init_ssl() { |
|
// OpenSSL 3.0対応の初期化 |
|
ssl_ctx = SSL_CTX_new(TLS_server_method()); |
|
if (!ssl_ctx) { |
|
ERR_print_errors_fp(stderr); |
|
return -1; |
|
} |
|
|
|
// 証明書と秘密鍵を読み込み |
|
if (SSL_CTX_use_certificate_file(ssl_ctx, CERT_FILE, SSL_FILETYPE_PEM) <= 0) { |
|
fprintf(stderr, "Failed to load certificate file: %s\n", CERT_FILE); |
|
ERR_print_errors_fp(stderr); |
|
return -1; |
|
} |
|
|
|
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, KEY_FILE, SSL_FILETYPE_PEM) <= 0) { |
|
fprintf(stderr, "Failed to load private key file: %s\n", KEY_FILE); |
|
ERR_print_errors_fp(stderr); |
|
return -1; |
|
} |
|
|
|
// 秘密鍵と証明書の整合性をチェック |
|
if (!SSL_CTX_check_private_key(ssl_ctx)) { |
|
fprintf(stderr, "Private key does not match certificate\n"); |
|
return -1; |
|
} |
|
|
|
printf("SSL context initialized successfully\n"); |
|
return 0; |
|
} |
|
|
|
int main() { |
|
if (init_ssl() < 0) { |
|
return 1; |
|
} |
|
|
|
uv_loop_t *loop = uv_default_loop(); |
|
|
|
uv_tcp_t server; |
|
uv_tcp_init(loop, &server); |
|
|
|
struct sockaddr_in addr; |
|
uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr); |
|
|
|
uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0); |
|
int r = uv_listen((uv_stream_t*)&server, DEFAULT_BACKLOG, on_new_connection); |
|
if (r) { |
|
fprintf(stderr, "Listen error %s\n", uv_strerror(r)); |
|
return 1; |
|
} |
|
|
|
printf("HTTPS server listening on port %d\n", DEFAULT_PORT); |
|
printf("Certificate file: %s\n", CERT_FILE); |
|
printf("Private key file: %s\n", KEY_FILE); |
|
|
|
int result = uv_run(loop, UV_RUN_DEFAULT); |
|
|
|
SSL_CTX_free(ssl_ctx); |
|
return result; |
|
} |