diff --git a/demos/tlcp/Makefile b/demos/tlcp/Makefile new file mode 100644 index 00000000..44462eec --- /dev/null +++ b/demos/tlcp/Makefile @@ -0,0 +1,8 @@ +all: + cc tlcp_get.c url_parser.c -lgmssl -o tlcp_get + cc tlcp_post.c url_parser.c -lgmssl -o tlcp_post + +clean: + rm -fr tlcp_get + rm -fr tlcp_post + diff --git a/demos/tlcp/tlcp_get.c b/demos/tlcp/tlcp_get.c new file mode 100644 index 00000000..2980acf2 --- /dev/null +++ b/demos/tlcp/tlcp_get.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "url_parser.h" + + +int main(int argc, char *argv[]) +{ + int ret = -1; + char *prog = argv[0]; + const int cipher = TLS_cipher_ecc_sm4_cbc_sm3; + URL_COMPONENTS *url; + struct hostent *hp; + int port = 443; + struct sockaddr_in server; + int sock; + TLS_CTX ctx; + TLS_CONNECT conn; + char request[1024]; + uint8_t buf[16800]; + char *p; + size_t len; + + if (argc != 2) { + fprintf(stderr, "example: tlcp_get https://sm2only.ovssl.cn\n"); + return 1; + } + + if (!(url = parse_url(argv[1]))) { + fprintf(stderr, "parse url '%s' failure\n", argv[1]); + return 1; + } + if (!(hp = gethostbyname(url->host))) { + herror("tlcp_client: '-host' invalid"); + goto end; + } + if (url->port != -1) { + port = url->port; + } + + server.sin_addr = *((struct in_addr *)hp->h_addr_list[0]); + server.sin_family = AF_INET; + server.sin_port = htons(port); + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + goto end; + } + if (connect(sock, (struct sockaddr *)&server , sizeof(server)) < 0) { + perror("connect"); + goto end; + } + + memset(&ctx, 0, sizeof(ctx)); + memset(&conn, 0, sizeof(conn)); + + tls_ctx_init(&ctx, TLS_protocol_tlcp, TLS_client_mode); + tls_ctx_set_cipher_suites(&ctx, &cipher, 1); + tls_init(&conn, &ctx); + tls_set_socket(&conn, sock); + + if (tls_do_handshake(&conn) != 1) { + fprintf(stderr, "%s: error\n", prog); + goto end; + } + + snprintf(request, sizeof(request)-1, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", + url->path ? url->path : "/", + url->host); + + tls_send(&conn, (uint8_t *)request, strlen(request), &len); + + if (tls_recv(&conn, buf, sizeof(buf), &len) != 1) { + fprintf(stderr, "recv failure\n"); + goto end; + } + buf[len] = 0; + + p = strstr((char *)buf, "\r\n\r\n"); + if (p) { + printf("%s", p + 4); + fflush(stdout); + } + +end: + free_url_components(url); + close(sock); + tls_ctx_cleanup(&ctx); + tls_cleanup(&conn); + return 0; +} diff --git a/demos/tlcp/tlcp_post.c b/demos/tlcp/tlcp_post.c new file mode 100644 index 00000000..445e86b5 --- /dev/null +++ b/demos/tlcp/tlcp_post.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "url_parser.h" + + +int main(int argc, char *argv[]) +{ + int ret = -1; + char *prog = argv[0]; + const int cipher = TLS_cipher_ecc_sm4_cbc_sm3; + URL_COMPONENTS *url; + struct hostent *hp; + int port = 443; + struct sockaddr_in server; + int sock; + TLS_CTX ctx; + TLS_CONNECT conn; + char request[1024]; + uint8_t buf[16800]; + char *p; + size_t len; + + if (argc != 2) { + fprintf(stderr, "example: echo \"key=word\" | tlcp_post https://sm2only.ovssl.cn\n"); + return 1; + } + + if (!(url = parse_url(argv[1]))) { + fprintf(stderr, "parse url '%s' failure\n", argv[1]); + return 1; + } + if (!(hp = gethostbyname(url->host))) { + herror("tlcp_client: '-host' invalid"); + goto end; + } + if (url->port != -1) { + port = url->port; + } + + server.sin_addr = *((struct in_addr *)hp->h_addr_list[0]); + server.sin_family = AF_INET; + server.sin_port = htons(port); + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + goto end; + } + if (connect(sock, (struct sockaddr *)&server , sizeof(server)) < 0) { + perror("connect"); + goto end; + } + + memset(&ctx, 0, sizeof(ctx)); + memset(&conn, 0, sizeof(conn)); + + tls_ctx_init(&ctx, TLS_protocol_tlcp, TLS_client_mode); + tls_ctx_set_cipher_suites(&ctx, &cipher, 1); + tls_init(&conn, &ctx); + tls_set_socket(&conn, sock); + + if (tls_do_handshake(&conn) != 1) { + fprintf(stderr, "%s: error\n", prog); + goto end; + } + + snprintf(request, sizeof(request)-1, "POST %s HTTP/1.1\r\nHost: %s\r\n\r\n", + url->path ? url->path : "/", + url->host); + + tls_send(&conn, (uint8_t *)request, strlen(request), &len); + + len = fread(buf, 1, sizeof(buf), stdin); + if (len) { + tls_send(&conn, buf, len, &len); + } + + if (tls_recv(&conn, buf, sizeof(buf), &len) != 1) { + fprintf(stderr, "recv failure\n"); + goto end; + } + buf[len] = 0; + + p = strstr((char *)buf, "\r\n\r\n"); + if (p) { + printf("%s", p + 4); + fflush(stdout); + } + +end: + free_url_components(url); + close(sock); + tls_ctx_cleanup(&ctx); + tls_cleanup(&conn); + return 0; +} diff --git a/demos/tlcp/url_parser.c b/demos/tlcp/url_parser.c new file mode 100644 index 00000000..5f4861f1 --- /dev/null +++ b/demos/tlcp/url_parser.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2022 Kazuyoshi Tomita + * + * This software is released under the MIT License. + * https://opensource.org/licenses/mit-license.php + */ + +#include "url_parser.h" +#include +#include +#include + +static const char *_strnstr(const char *s, size_t s_len, const char *needle) +{ + const char *end = s + s_len; + size_t needle_len = strlen(needle); + const char *p; + + p = s; + while (p < end - needle_len + 1) { + if (strncmp(p, needle, needle_len) == 0) { + return p; + } + p++; + } + + return NULL; +} + +static const char *find_chars(const char *s, size_t s_len, const char *chars) +{ + const char *end = s + s_len; + size_t chars_n = strlen(chars); + const char *p; + int i; + + p = s; + while (p < end) { + for (i = 0 ; i < chars_n ; i++) { + if (*p == chars[i]) { + return p; + } + } + p++; + } + + return NULL; +} + +static const char *find_chars_reverse(const char *s, size_t s_len, const char *chars) +{ + const char *end = s + s_len; + size_t chars_n = strlen(chars); + const char *p; + int i; + + p = end - 1; + while (p >= s) { + for (i = 0 ; i < chars_n ; i++) { + if (*p == chars[i]) { + return p; + } + } + p--; + } + + return NULL; +} + +static int is_alpha(char c) +{ + if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z')) { + return 1; + } + return 0; +} + +static int is_digit(char c) +{ + if (c >= '0' && c <= '9') { + return 1; + } + return 0; +} + +static int is_control(char c) +{ + if ((c >= 0x00 && c <= 0x1f) || + c == 0x7f) { + return 1; + } + return 0; +} + +static const char *lookup_scheme(const char *s) +{ + const char *p = s; + char c; + + if (strlen(s) == 0) { + return NULL; + } + + if (!is_alpha(*p)) { + return NULL; + } + p++; + + while (*p != '\0') { + c = *p; + if (c == ':') { + return p; + } + if (!is_alpha(c) && + !is_digit(c) && + c != '+' && + c != '-' && + c != '.') { + return NULL; + } + p++; + } + return NULL; +} + +static int parse_user_password(const char *s, size_t s_len, URL_COMPONENTS *c) +{ + const char *end = s + s_len; + const char *found; + + found = _strnstr(s, s_len, ":"); + if (found) { + c->user = strndup(s, found - s); + if (c->user == NULL) { + return -1; /* ENOMEM */ + } + c->password = strndup(found + 1, end - found - 1); + if (c->password == NULL) { + return -1; /* ENOMEM */ + } + } else { + c->user = strndup(s, s_len); + if (c->user == NULL) { + return -1; /* ENOMEM */ + } + } + + return 0; +} + +static int parse_authority(const char *s, size_t s_len, URL_COMPONENTS *c) +{ + const char *end = s + s_len; + const char *p, *found, *host_start, *host_end; + int port; + + c->port = -1; + + if (s_len == 0) { /* empty authority */ + return 0; + } + + found = _strnstr(s, s_len, "@"); + if (found) { + if (parse_user_password(s, found - s, c) == -1) { + return -1; + } + + host_start = found + 1; + } else { + host_start = s; + } + + if (*host_start == '[') { + /* IP-literal host */ + if (find_chars(host_start + 1, end - host_start - 1, "[")) { + errno = EINVAL; + return -1; + } + host_end = find_chars(host_start + 1, end - host_start - 1, "]"); + if (!host_end) { + errno = EINVAL; + return -1; + } + /* The next character of ']' is termination or ':'. */ + if (host_end + 1 != end && host_end[1] != ':') { + errno = EINVAL; + return -1; + } + host_end++; + } else { + /* IPv4address / reg-name host */ + host_end = find_chars_reverse(host_start, end - host_start, ":"); + if (host_end == NULL) { + host_end = end; + } + if (find_chars(host_start, host_end - host_start, "[]")) { + errno = EINVAL; + return -1; + } + } + if (find_chars(host_start, host_end - host_start, " ")) { + errno = EINVAL; + return -1; + } + + /* ASSERT: host_end == end or *host_end == ':' */ + + if (host_end == end) { + /* without port number */ + if (host_start == end) { /* empty host */ + errno = EINVAL; + return -1; + } + c->host = strndup(host_start, end - host_start); + if (c->host == NULL) { + return -1; /* ENOMEM */ + } + return 0; + } + + /* ASSERT: *host_end == ':' */ + + /* host and port */ + + if (host_start == host_end) { /* empty host */ + errno = EINVAL; + return -1; + } + + if (host_end + 1 < end) { + p = host_end + 1; + port = 0; + while (p < end) { + if (*p < '0' || *p > '9') { + errno = EINVAL; + return -1; + } + + port = port * 10 + *p - '0'; + if (port > 65535) { + errno = EINVAL; + return -1; + } + + p++; + } + } else { + /* empty port number */ + port = -1; + } + + c->host = strndup(host_start, (size_t) (host_end - host_start)); + if (c->host == NULL) { + return -1; /* ENOMEM */ + } + c->port = port; + + return 0; +} + +URL_COMPONENTS *parse_url(const char *url) +{ + URL_COMPONENTS *c; + const char *p; + const char *end = url + strlen(url); + const char *found; + size_t len; + + for (p = url ; p < end ; p++) { + if (is_control(*p)) { + errno = EINVAL; + return NULL; + } + } + + c = malloc(sizeof(URL_COMPONENTS)); + if (!c) { + return NULL; + } + memset(c, 0, sizeof(URL_COMPONENTS)); + c->port = -1; + + p = url; + + /* lookup scheme */ + found = lookup_scheme(p); + if (found) { + c->scheme = strndup(url, (size_t) (found - p)); + if (c->scheme == NULL) { + goto error; + } + p = found + 1; /* skip a colon */ + if (p >= end) { + return c; + } + } + + if (strlen(p) >= 2 && + p[0] == '/' && p[1] == '/') { + /* authority */ + p = p + 2; + found = find_chars(p, strlen(p), "/?#"); + if (found == NULL) { + len = strlen(p); + } else { + len = (size_t) (found - p); + } + if (parse_authority(p, len, c) == -1) { + goto error; /* ENOMEM,EINVAL */ + } + + if (!found) { + return c; + } + + p = found; + } + + if (*p != '?' && *p != '#') { + /* path */ + found = find_chars(p, strlen(p), "?#"); + found = NULL; + if (found == NULL) { + c->path = strdup(p); + if (c->path == NULL) { + goto error; + } + } else + { + if (found != p) { + c->path = strndup(p, (size_t) (found - p)); + if (c->path == NULL) { + goto error; + } + } + } + + if (!found) { + return c; + } + + p = found; + } + + /* ASSERT: *p is '?' or '#' */ +#if 0 + if (*p == '?') { + /* query */ + p = p + 1; + found = find_chars(p, strlen(p), "#"); + if (found == NULL) { + c->query = strdup(p); + } else { + c->query = strndup(p, (size_t) (found - p)); + } + + if (c->query == NULL) { + goto error; + } + + if (!found) { + return c; + } + + p = found; + } +#endif + + /* ASSERT: *p is '#' */ + + /* fragment */ + p = p + 1; + c->fragment = strdup(p); + if (c->fragment == NULL) { + goto error; + } + + return c; + +error: + free(c); + + return NULL; +} + +void free_url_components(URL_COMPONENTS *c) +{ + if (c->scheme) { + free(c->scheme); + } + if (c->user) { + free(c->user); + } + if (c->password) { + free(c->password); + } + if (c->host) { + free(c->host); + } + if (c->path) { + free(c->path); + } + if (c->query) { + free(c->query); + } + if (c->fragment) { + free(c->fragment); + } + free(c); +} + diff --git a/demos/tlcp/url_parser.h b/demos/tlcp/url_parser.h new file mode 100644 index 00000000..b98f9fa6 --- /dev/null +++ b/demos/tlcp/url_parser.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Kazuyoshi Tomita + * + * This software is released under the MIT License. + * https://opensource.org/licenses/mit-license.php + */ + +#ifndef URL_PARSER_H +#define URL_PARSER_H + +#define URL_PARSER_VERSION 0x00000300 /* 0.0.3 */ + +typedef struct url_components { + char *scheme; + char *user; + char *password; + char *host; + int port; + char *path; + char *query; + char *fragment; +} URL_COMPONENTS; + +extern URL_COMPONENTS *parse_url(const char *url); +extern void free_url_components(URL_COMPONENTS *c); + +#endif diff --git a/include/gmssl/tls.h b/include/gmssl/tls.h index 62a9b865..3e1827d7 100644 --- a/include/gmssl/tls.h +++ b/include/gmssl/tls.h @@ -895,7 +895,7 @@ int tls13_gcm_decrypt(const BLOCK_CIPHER_KEY *key, const uint8_t iv[12], -#define TLS_DEBUG +//#define TLS_DEBUG #ifdef TLS_DEBUG # define tls_trace(s) fprintf(stderr,(s)) diff --git a/src/tlcp.c b/src/tlcp.c index 1e580831..3f5c2335 100644 --- a/src/tlcp.c +++ b/src/tlcp.c @@ -595,7 +595,7 @@ int tlcp_do_connect(TLS_CONNECT *conn) tls_send_alert(conn, TLS_alert_decrypt_error); goto end; } - printf("Connection established!\n"); + fprintf(stderr, "Connection established!\n"); conn->protocol = TLS_protocol_tlcp; @@ -1045,7 +1045,7 @@ int tlcp_do_accept(TLS_CONNECT *conn) conn->protocol = TLS_protocol_tlcp; - tls_trace("Connection Established!\n\n"); + fprintf(stderr, "Connection Established!\n\n"); ret = 1; end: diff --git a/src/tls12.c b/src/tls12.c index 34672785..d6cffffd 100644 --- a/src/tls12.c +++ b/src/tls12.c @@ -684,7 +684,7 @@ int tls12_do_connect(TLS_CONNECT *conn) tls_send_alert(conn, TLS_alert_decrypt_error); goto end; } - printf("Connection established!\n"); + fprintf(stderr, "Connection established!\n"); conn->protocol = conn->protocol; @@ -1117,7 +1117,7 @@ int tls12_do_accept(TLS_CONNECT *conn) conn->protocol = conn->protocol; - printf("Connection Established!\n\n"); + fprintf(stderr, "Connection Established!\n\n"); ret = 1; end: diff --git a/src/tls13.c b/src/tls13.c index 2eaf4ddf..986604ec 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -1923,7 +1923,7 @@ int tls13_do_connect(TLS_CONNECT *conn) format_bytes(stderr, 0, 4, "client_write_iv", conn->client_write_iv, 12); format_print(stderr, 0, 0, "\n"); */ - printf("++++ Connection established\n"); + fprintf(stderr, "Connection established\n"); end: return 1; @@ -2369,7 +2369,7 @@ int tls13_do_accept(TLS_CONNECT *conn) format_print(stderr, 0, 0, "\n"); */ - printf("Connection Established!\n\n"); + fprintf(stderr, "Connection Established!\n\n"); end: return 1;