From 54afcd99d538d06631c0e526546cca6b762b26b3 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 26 Jun 2026 22:21:36 +0800 Subject: [PATCH] Add -www HTTP echo mode to TLCP and TLS server commands --- CMakeLists.txt | 2 +- include/gmssl/version.h | 2 +- tools/tlcp_server.c | 37 +++++- tools/tls12_server.c | 37 +++++- tools/tls13_server.c | 45 ++++++- tools/tls_server_http.h | 253 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 371 insertions(+), 5 deletions(-) create mode 100644 tools/tls_server_http.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3eb0f04d..23aeb321 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1026,7 +1026,7 @@ endif() # set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_VENDOR "GmSSL develop team") -set(CPACK_PACKAGE_VERSION "3.3.0-dev.1175") +set(CPACK_PACKAGE_VERSION "3.3.0-dev.1176") set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md) set(CPACK_NSIS_MODIFY_PATH ON) include(CPack) diff --git a/include/gmssl/version.h b/include/gmssl/version.h index 6085c353..1a008df0 100644 --- a/include/gmssl/version.h +++ b/include/gmssl/version.h @@ -18,7 +18,7 @@ extern "C" { #define GMSSL_VERSION_NUM 30300 -#define GMSSL_VERSION_STR "GmSSL 3.3.0-dev.1175" +#define GMSSL_VERSION_STR "GmSSL 3.3.0-dev.1176" int gmssl_version_num(void); const char *gmssl_version_str(void); diff --git a/tools/tlcp_server.c b/tools/tlcp_server.c index 9a82fbd9..9e6a0b7f 100644 --- a/tools/tlcp_server.c +++ b/tools/tlcp_server.c @@ -31,9 +31,10 @@ #include #include #include "passwd.h" +#include "tls_server_http.h" -static const char *options = "[-port num] -cert pem -key pem [-pass str] [-cipher_suite str] [-alpn str] [-cert_request] [-cacert pem] [-verbose]"; +static const char *options = "[-port num] -cert pem -key pem [-pass str] [-cipher_suite str] [-alpn str] [-cert_request] [-cacert pem] [-www] [-verbose]"; static const char *help = @@ -47,6 +48,7 @@ static const char *help = " -alpn str Application protocol name, may appear multiple times, higher priority first\n" " -cert_request Client certificate request\n" " -cacert pem CA certificate for client certificate verification\n" +" -www Return HTTP 200 OK and echo client request data as text\n" " -verbose Print TLS handshake messages\n" "\n" #include "tlcp_help.h" @@ -159,12 +161,16 @@ int tlcp_server_main(int argc , char **argv) size_t alpn_protocols_cnt = 0; int cert_request = 0; char *cacertfile = NULL; + int www = 0; int verbose = 0; TLS_CTX ctx; TLS_CONNECT conn; char buf[1600] = {0}; size_t len = sizeof(buf); + uint8_t *www_request = NULL; + size_t www_request_len = 0; + size_t www_request_cap = 0; tls_socket_t sock; tls_socket_t conn_sock; struct sockaddr_in server_addr; @@ -237,6 +243,8 @@ int tlcp_server_main(int argc , char **argv) } else if (!strcmp(*argv, "-cacert")) { if (--argc < 1) goto bad; cacertfile = *(++argv); + } else if (!strcmp(*argv, "-www")) { + www = 1; } else if (!strcmp(*argv, "-verbose")) { verbose = TLS_verbose; } else { @@ -345,6 +353,7 @@ bad: restart: + www_request_len = 0; client_addrlen = sizeof(client_addr); @@ -410,6 +419,31 @@ restart: } } while (!len); + if (www) { + fwrite(buf, 1, len, stdout); + fflush(stdout); + if (tls_server_www_append(&www_request, &www_request_len, &www_request_cap, + (uint8_t *)buf, len) != 1) { + fprintf(stderr, "%s: append HTTP request failure\n", prog); + tls_socket_close(conn.sock); + goto end; + } + if (tls_server_www_request_complete(www_request, www_request_len)) { + if (tls_server_www_send_response(&conn, www_request, www_request_len) != 1) { + fprintf(stderr, "%s: send HTTP response failure\n", prog); + tls_socket_close(conn.sock); + goto end; + } + if (do_shutdown_select(&conn) != 1) { + fprintf(stderr, "%s: shutdown failure\n", prog); + } + tls_socket_close(conn.sock); + tls_cleanup(&conn); + goto restart; + } + continue; + } + if (do_send_select(&conn, (uint8_t *)buf, len) != 1) { fprintf(stderr, "%s: send failure, close connection\n", prog); tls_socket_close(conn.sock); @@ -419,6 +453,7 @@ restart: end: + free(www_request); gmssl_secure_clear(passbufs, sizeof(passbufs)); return ret; } diff --git a/tools/tls12_server.c b/tools/tls12_server.c index 34eb4bd7..673ff84b 100644 --- a/tools/tls12_server.c +++ b/tools/tls12_server.c @@ -16,9 +16,10 @@ #include #include #include "passwd.h" +#include "tls_server_http.h" -static const char *options = "[-port num] -cert pem -key pem [-pass str] [-cacert pem] [-verbose]"; +static const char *options = "[-port num] -cert pem -key pem [-pass str] [-cacert pem] [-www] [-verbose]"; static const char *help = "Options\n" @@ -35,6 +36,7 @@ static const char *help = " -verify_depth num Certificate verification depth\n" " -client_cert_optional Allow client send empty Certificate\n" " -renegotiation_info Enable RFC 5746 renegotiation_info response\n" +" -www Return HTTP 200 OK and echo client request data as text\n" " -verbose Print TLS handshake messages\n" "\n" #include "tls12_help.h" @@ -153,11 +155,15 @@ int tls12_server_main(int argc , char **argv) int verify_depth = TLS_DEFAULT_VERIFY_DEPTH; int client_cert_optional = 0; int renegotiation_info = 0; + int www = 0; int verbose = 0; TLS_CTX ctx; TLS_CONNECT conn; char buf[1600] = {0}; size_t len = sizeof(buf); + uint8_t *www_request = NULL; + size_t www_request_len = 0; + size_t www_request_cap = 0; tls_socket_t sock; tls_socket_t conn_sock; struct sockaddr_in server_addr; @@ -263,6 +269,8 @@ int tls12_server_main(int argc , char **argv) client_cert_optional = 1; } else if (!strcmp(*argv, "-renegotiation_info")) { renegotiation_info = 1; + } else if (!strcmp(*argv, "-www")) { + www = 1; } else if (!strcmp(*argv, "-verbose")) { verbose = TLS_verbose; } else { @@ -391,6 +399,7 @@ bad: tls_socket_listen(sock, 1); restart: + www_request_len = 0; //client_addrlen = sizeof(client_addr); @@ -455,6 +464,31 @@ restart: } } while (!len); + if (www) { + fwrite(buf, 1, len, stdout); + fflush(stdout); + if (tls_server_www_append(&www_request, &www_request_len, &www_request_cap, + (uint8_t *)buf, len) != 1) { + fprintf(stderr, "%s: append HTTP request failure\n", prog); + tls_socket_close(conn.sock); + goto end; + } + if (tls_server_www_request_complete(www_request, www_request_len)) { + if (tls_server_www_send_response(&conn, www_request, www_request_len) != 1) { + fprintf(stderr, "%s: send HTTP response failure\n", prog); + tls_socket_close(conn.sock); + goto end; + } + if (do_shutdown_select(&conn) != 1) { + fprintf(stderr, "%s: shutdown failure\n", prog); + } + tls_socket_close(conn.sock); + tls_cleanup(&conn); + goto restart; + } + continue; + } + if (do_send_select(&conn, (uint8_t *)buf, len) != 1) { fprintf(stderr, "%s: send failure, close connection\n", prog); tls_socket_close(conn.sock); @@ -464,6 +498,7 @@ restart: end: + free(www_request); gmssl_secure_clear(passbufs, sizeof(passbufs)); return ret; } diff --git a/tools/tls13_server.c b/tools/tls13_server.c index 814142d6..39db6cd0 100644 --- a/tools/tls13_server.c +++ b/tools/tls13_server.c @@ -17,10 +17,11 @@ #include #include #include "passwd.h" +#include "tls_server_http.h" -static const char *options = "[-port num] -cert pem -key pem [-pass str] [-cacert pem] [-verbose]"; +static const char *options = "[-port num] -cert pem -key pem [-pass str] [-cacert pem] [-www] [-verbose]"; static const char *help = "Options\n" @@ -47,6 +48,7 @@ static const char *help = " -ticket_key hex Session ticket encrypt/decrypt key in HEX format\n" " -key_update_seq_num num Send KeyUpdate handshake after sending/receiving records\n" " -tls13_change_cipher_spec Support ChangeCipherSpec in TLS 1.3 to be compatible with middlebox\n" +" -www Return HTTP 200 OK and echo client request data as text\n" " -verbose Print TLS handshake messages\n" "\n" #include "tls13_help.h" @@ -133,6 +135,9 @@ int tls13_server_main(int argc , char **argv) char buf[1600] = {0}; size_t len = sizeof(buf); + uint8_t *www_request = NULL; + size_t www_request_len = 0; + size_t www_request_cap = 0; int cipher_suites[TLS_MAX_CIPHER_SUITES]; size_t cipher_suites_cnt = 0; @@ -175,6 +180,7 @@ int tls13_server_main(int argc , char **argv) // ChangeCipherSpec int tls13_change_cipher_spec = 0; + int www = 0; int verbose = 0; @@ -330,6 +336,8 @@ int tls13_server_main(int argc , char **argv) client_cert_optional = 1; } else if (!strcmp(*argv, "-tls13_change_cipher_spec")) { tls13_change_cipher_spec = 1; + } else if (!strcmp(*argv, "-www")) { + www = 1; } else if (!strcmp(*argv, "-verbose")) { verbose = TLS_verbose; } else { @@ -599,6 +607,22 @@ bad: if (conn.early_data && conn.early_data_len) { format_string(stderr, 0, 0, "EarlyData", conn.early_data_buf, conn.early_data_len); + if (www) { + if (tls_server_www_append(&www_request, &www_request_len, &www_request_cap, + conn.early_data_buf, conn.early_data_len) != 1) { + fprintf(stderr, "%s: append HTTP request failure\n", prog); + goto end; + } + if (tls_server_www_request_complete(www_request, www_request_len)) { + if (tls_server_www_send_response(&conn, www_request, www_request_len) != 1) { + fprintf(stderr, "%s: send HTTP response failure\n", prog); + goto end; + } + do_shutdown_select(&conn); + ret = 0; + goto end; + } + } } size_t send_len = 0; @@ -663,6 +687,24 @@ bad: fwrite(buf, 1, len, stdout); fflush(stdout); + if (www) { + if (tls_server_www_append(&www_request, &www_request_len, &www_request_cap, + (uint8_t *)buf, len) != 1) { + fprintf(stderr, "%s: append HTTP request failure\n", prog); + goto end; + } + if (tls_server_www_request_complete(www_request, www_request_len)) { + if (tls_server_www_send_response(&conn, www_request, www_request_len) != 1) { + fprintf(stderr, "%s: send HTTP response failure\n", prog); + goto end; + } + do_shutdown_select(&conn); + ret = 0; + goto end; + } + continue; + } + send_len = len; send_offset = 0; /* @@ -680,6 +722,7 @@ bad: end: + free(www_request); gmssl_secure_clear(passbufs, sizeof(passbufs)); return ret; } diff --git a/tools/tls_server_http.h b/tools/tls_server_http.h new file mode 100644 index 00000000..7cb92bef --- /dev/null +++ b/tools/tls_server_http.h @@ -0,0 +1,253 @@ +/* + * Copyright 2014-2026 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#ifndef GMSSL_TOOLS_TLS_SERVER_HTTP_H +#define GMSSL_TOOLS_TLS_SERVER_HTTP_H + +#include +#include +#include +#include +#include +#include + +static int tls_server_www_append(uint8_t **buf, size_t *len, size_t *cap, + const uint8_t *data, size_t datalen) +{ + uint8_t *p; + size_t newlen; + size_t newcap; + + if (datalen > (size_t)-1 - *len) { + error_print(); + return -1; + } + newlen = *len + datalen; + if (newlen <= *cap) { + memcpy(*buf + *len, data, datalen); + *len = newlen; + return 1; + } + + newcap = *cap ? *cap : 4096; + while (newcap < newlen) { + if (newcap > ((size_t)-1)/2) { + newcap = newlen; + break; + } + newcap *= 2; + } + + if (!(p = realloc(*buf, newcap))) { + error_print(); + return -1; + } + *buf = p; + *cap = newcap; + memcpy(*buf + *len, data, datalen); + *len = newlen; + return 1; +} + +static int tls_server_www_ascii_lower(int c) +{ + if (c >= 'A' && c <= 'Z') { + return c - 'A' + 'a'; + } + return c; +} + +static int tls_server_www_memcase_equal(const uint8_t *a, const char *b, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (tls_server_www_ascii_lower(a[i]) != tls_server_www_ascii_lower((unsigned char)b[i])) { + return 0; + } + } + return 1; +} + +static int tls_server_www_starts_with(const uint8_t *buf, size_t len, const char *prefix) +{ + size_t prefix_len = strlen(prefix); + + if (len < prefix_len) { + return 0; + } + return memcmp(buf, prefix, prefix_len) == 0; +} + +static int tls_server_www_looks_like_http(const uint8_t *buf, size_t len) +{ + if (tls_server_www_starts_with(buf, len, "GET ") + || tls_server_www_starts_with(buf, len, "POST ") + || tls_server_www_starts_with(buf, len, "HEAD ") + || tls_server_www_starts_with(buf, len, "PUT ") + || tls_server_www_starts_with(buf, len, "DELETE ") + || tls_server_www_starts_with(buf, len, "PATCH ") + || tls_server_www_starts_with(buf, len, "OPTIONS ") + || tls_server_www_starts_with(buf, len, "TRACE ") + || tls_server_www_starts_with(buf, len, "CONNECT ")) { + return 1; + } + return 0; +} + +static int tls_server_www_find_header_end(const uint8_t *buf, size_t len, size_t *header_len) +{ + size_t i; + + for (i = 0; i + 3 < len; i++) { + if (buf[i] == '\r' && buf[i + 1] == '\n' && buf[i + 2] == '\r' && buf[i + 3] == '\n') { + *header_len = i + 4; + return 1; + } + } + return 0; +} + +static int tls_server_www_parse_content_length(const uint8_t *buf, size_t header_len, + size_t *content_length, int *has_content_length) +{ + const char name[] = "content-length:"; + size_t name_len = sizeof(name) - 1; + size_t pos = 0; + + *content_length = 0; + *has_content_length = 0; + + while (pos < header_len) { + size_t line_end = pos; + size_t val_pos; + size_t value = 0; + + while (line_end + 1 < header_len + && !(buf[line_end] == '\r' && buf[line_end + 1] == '\n')) { + line_end++; + } + if (line_end > pos + name_len + && tls_server_www_memcase_equal(buf + pos, name, name_len)) { + val_pos = pos + name_len; + while (val_pos < line_end && (buf[val_pos] == ' ' || buf[val_pos] == '\t')) { + val_pos++; + } + if (val_pos == line_end) { + return 1; + } + while (val_pos < line_end) { + unsigned int digit; + + if (buf[val_pos] < '0' || buf[val_pos] > '9') { + return 1; + } + digit = buf[val_pos] - '0'; + if (value > ((size_t)-1 - digit)/10) { + return 1; + } + value = value * 10 + digit; + val_pos++; + } + *content_length = value; + *has_content_length = 1; + return 1; + } + if (line_end + 1 >= header_len) { + break; + } + pos = line_end + 2; + } + + return 1; +} + +static int tls_server_www_request_complete(const uint8_t *buf, size_t len) +{ + size_t header_len; + size_t content_length; + int has_content_length; + + if (!tls_server_www_find_header_end(buf, len, &header_len)) { + if (len && !tls_server_www_looks_like_http(buf, len)) { + return 1; + } + return 0; + } + if (tls_server_www_parse_content_length(buf, header_len, &content_length, + &has_content_length) != 1) { + return 1; + } + if (!has_content_length) { + return 1; + } + if (content_length > (size_t)-1 - header_len) { + return 1; + } + return len >= header_len + content_length; +} + +static int tls_server_www_send_all(TLS_CONNECT *conn, const uint8_t *buf, size_t len) +{ + int ret; + size_t offset = 0; + fd_set rfds; + fd_set wfds; + + while (offset < len) { + size_t sentlen = 0; + + ret = tls_send(conn, buf + offset, len - offset, &sentlen); + if (ret == 1) { + offset += sentlen; + continue; + } + FD_ZERO(&rfds); + FD_ZERO(&wfds); + if (ret == TLS_ERROR_RECV_AGAIN) { + FD_SET(conn->sock, &rfds); + } else if (ret == TLS_ERROR_SEND_AGAIN) { + FD_SET(conn->sock, &wfds); + } else { + error_print(); + return -1; + } + if (select((int)(conn->sock + 1), &rfds, &wfds, NULL, NULL) < 0) { + error_print(); + return -1; + } + } + return 1; +} + +static int tls_server_www_send_response(TLS_CONNECT *conn, const uint8_t *body, size_t bodylen) +{ + char header[256]; + int header_len; + + header_len = snprintf(header, sizeof(header), + "HTTP/1.0 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: %zu\r\n" + "Connection: close\r\n" + "\r\n", bodylen); + if (header_len < 0 || (size_t)header_len >= sizeof(header)) { + error_print(); + return -1; + } + if (tls_server_www_send_all(conn, (uint8_t *)header, (size_t)header_len) != 1) { + return -1; + } + if (bodylen && tls_server_www_send_all(conn, body, bodylen) != 1) { + return -1; + } + return 1; +} + +#endif