From bfe3e5999a43fce23472c310ed133b0dd6cc37b9 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Mon, 6 Feb 2023 12:50:28 +0800 Subject: [PATCH] Update HTTP for WIN --- CMakeLists.txt | 9 +-- src/http_win.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 src/http_win.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 4760453d..162775a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,6 @@ set(src src/tls12.c src/tls13.c src/file.c - src/http.c ) set(tools @@ -222,13 +221,13 @@ if (ENABLE_GMT_0105_RNG) endif() if (WIN32) - list(APPEND src src/rand_win.c) + list(APPEND src src/rand_win.c src/http_win.c) elseif (APPLE) - list(APPEND src src/rand_apple.c) + list(APPEND src src/rand_apple.c src/http.c) elseif (ANDROID) - list(APPEND src src/rand.c) + list(APPEND src src/rand.c src/http.c) else() - list(APPEND src src/rand_unix.c) + list(APPEND src src/rand_unix.c src/http.c) endif() diff --git a/src/http_win.c b/src/http_win.c new file mode 100644 index 00000000..b0c5128c --- /dev/null +++ b/src/http_win.c @@ -0,0 +1,207 @@ +/* + * Copyright 2014-2023 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 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +int http_parse_uri(const char *uri, char host[128], int *port, char path[256]) +{ + if (!uri || !host || !port || !path) { + error_print(); + return -1; + } + + *host = 0; + *port = 80; + *path++ = '/'; + *path = 0; + + if (sscanf(uri, "http://%127[^:]:%i/%254[^\n]", host, port, path) == 3); + else if (sscanf(uri, "http://%127[^/]/%254[^\n]", host, path) == 2); + else if (sscanf(uri, "http://%127[^:]:%i[^/][^\n]", host, port) == 2); + else if (sscanf(uri, "http://%127[^/][^\n]", host) == 1); + else { + error_print(); + return -1; + } + if (!host[0] || strchr(host, '/') || strchr(host, ':')) { + error_print(); + return -1; + } + if (*port <= 0) { + error_print(); + return -1; + } + + return 1; +} + +static int socket_recv_all(tls_socket_t sock, uint8_t *buf, size_t len) +{ + size_t n; + while (len) { + if ((n = tls_socket_recv(sock, buf, len, 0)) <= 0) { + error_print(); + return -1; + } + buf += n; + len -= n; + } + return 1; +} + +int http_parse_response(char *buf, size_t buflen, uint8_t **content, size_t *contentlen, size_t *left) +{ + char *ok = "HTTP/1.1 200 OK\r\n"; + char *p; + size_t headerlen; + + if (buflen < strlen(ok) || memcmp(buf, ok, strlen(ok)) != 0) { + error_print(); + return -1; + } + if (buf[buflen] != 0) { + error_print(); + return -1; + } + if (!(p = strstr(buf, "\r\n\r\n"))) { + error_print(); + return -1; + } + *content = (uint8_t *)(p + 4); + headerlen = *content - (uint8_t *)buf; + *p = 0; + + if (!(p = strstr(buf, "\r\nContent-Length: "))) { + error_print(); + return -1; + } + p += strlen("\r\nContent-Length: "); + *contentlen = atoi(p); + if (*contentlen <= 0 || *contentlen > INT_MAX) { + error_print(); + return -1; + } + + buflen -= headerlen; + if (buflen < *contentlen) + *left = *contentlen - buflen; + else *left = 0; + + return 1; +} + + +#define HTTP_GET_TEMPLATE "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "\r\n\r\n" + +int http_get(const char *uri, uint8_t *buf, size_t *contentlen, size_t buflen) +{ + int ret = -1; + char host[128]; + int port; + char path[256]; + struct hostent *hp; + struct sockaddr_in server; + tls_socket_t sock; + char get[sizeof(HTTP_GET_TEMPLATE) + sizeof(host) + sizeof(path)]; + int getlen; + char response[1024]; + uint8_t *p; + size_t len; + size_t left; + + // WIN32 + WORD wVersion; + WSADATA wsaData; + wVersion = MAKEWORD(2, 2); + int err; + sock_inited = 1; + + // parse uri and compose request + if (http_parse_uri(uri, host, &port, path) != 1) { + error_print(); + return -1; + } + if ((getlen = snprintf(get, sizeof(get), HTTP_GET_TEMPLATE, path, host)) <= 0) { + error_print(); + return -1; + } + + // WIN32 + if ((err = WSAStartup(wVersion, &wsaData)) != 0) { + fprintf(stderr, "WSAStartup error %d\n", err); + return -1; + } + + // connect and send request + if (!(hp = gethostbyname(host))) { + error_print(); + goto end; + } + server.sin_addr = *((struct in_addr *)hp->h_addr_list[0]); + server.sin_family = AF_INET; + server.sin_port = htons(port); + + int sock_inited = 0; + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + error_print(); + fprintf(stderr, "%s: open socket error : %u\n", prog, WSAGetLastError()); + goto end; + } + + if (connect(sock, (struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR) { + error_print(); + fprintf(stderr, "%s: connect error : %u\n", prog, WSAGetLastError()); + goto end; + } + if (tls_socket_send(sock, get, strlen(get), 0) != getlen) { + error_print(); + goto end; + } + if ((len = tls_socket_recv(sock, response, sizeof(response) - 1, 0)) <= 0) { + error_print(); + goto end; + } + response[len] = 0; + + // process response header and retrieve left + if (http_parse_response(response, len, &p, contentlen, &left) != 1) { + + response[len] = 0; + fprintf(stderr, "Response:\n%s\n", response); + + error_print(); + goto end; + } + if (!buf || buflen < *contentlen) { + ret = 0; + goto end; + } + memcpy(buf, p, *contentlen - left); + if (left) { + if (socket_recv_all(sock, buf + *contentlen - left, left) != 1) { + error_print(); + goto end; + } + } + ret = 1; + +end: + if (sock_inited) tls_socket_close(sock); + WSACleanup(); + return ret; +}