20#include <freerdp/config.h>
21#include <freerdp/utils/http.h>
23#include <winpr/assert.h>
24#include <winpr/string.h>
26#include <openssl/bio.h>
27#include <openssl/ssl.h>
28#include <openssl/err.h>
30#include "../crypto/tls.h"
31#include "../core/utils.h"
32#include "../core/tcp.h"
34#include <freerdp/log.h>
35#define TAG FREERDP_TAG("utils.http")
37static const char get_header_fmt[] =
"GET %s HTTP/1.1\r\n"
41static const char post_header_fmt[] =
"POST %s HTTP/1.1\r\n"
43 "Content-Type: application/x-www-form-urlencoded\r\n"
44 "Content-Length: %lu\r\n"
47#define log_errors(log, msg) log_errors_(log, msg, __FILE__, __func__, __LINE__)
48static void log_errors_(wLog* log,
const char* msg,
const char* file,
const char* fkt,
size_t line)
50 const DWORD level = WLOG_ERROR;
53 if (!WLog_IsLevelActive(log, level))
56 BOOL error_logged = FALSE;
57 while ((ec = ERR_get_error()))
60 WLog_PrintTextMessage(log, level, line, file, fkt,
"%s: %s", msg,
61 ERR_error_string(ec,
nullptr));
64 WLog_PrintTextMessage(log, level, line, file, fkt,
"%s (no details available)", msg);
67static int get_line(BIO* bio,
char* buffer,
size_t size)
69#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
76 int rc = BIO_read(bio, &buffer[pos], 1);
79 char cur = buffer[pos];
81 if ((cur ==
'\n') || (pos >= size - 1))
90 return BIO_get_line(bio, buffer, (
int)size);
95static int get_line_context(rdpContext* context, BIO* bio,
char* buffer,
size_t size)
100 if (freerdp_shall_disconnect_context(context))
103 const int rc = get_line(bio, buffer, size);
106 if (BIO_should_retry(bio))
108 BIO_wait_read(bio, 10);
116#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
117BOOL freerdp_http_request(
const char* url,
const char* body,
long* status_code, BYTE** response,
118 size_t* response_length)
121 char* hostname =
nullptr;
122 const char* path =
nullptr;
123 char* headers =
nullptr;
126 char buffer[1024] = WINPR_C_ARRAY_INIT;
128 SSL_CTX* ssl_ctx =
nullptr;
131 WINPR_ASSERT(status_code);
132 WINPR_ASSERT(response);
133 WINPR_ASSERT(response_length);
135 wLog* log = WLog_Get(TAG);
140 WLog_WARN(TAG,
"This function is deprecated, use freerdp_http_request_context instead. "
141 "Certificate checks are not properly performed when using this function");
142 if (!url || strnlen(url, 8) < 8 || strncmp(url,
"https://", 8) != 0 ||
143 !(path = strchr(url + 8,
'/')))
145 WLog_Print(log, WLOG_ERROR,
"invalid url provided");
150 const size_t len = WINPR_ASSERTING_INT_CAST(
size_t, path - (url + 8));
151 hostname = strndup(&url[8], len);
161 if (winpr_asprintf(&headers, &size, post_header_fmt, path, hostname, blen) < 0)
169 if (winpr_asprintf(&headers, &size, get_header_fmt, path, hostname) < 0)
176 ssl_ctx = SSL_CTX_new(TLS_client_method());
180 log_errors(log,
"could not set up ssl context");
184 if (!SSL_CTX_set_default_verify_paths(ssl_ctx))
186 log_errors(log,
"could not set ssl context verify paths");
190 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER,
nullptr);
192 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
194 bio = BIO_new_ssl_connect(ssl_ctx);
197 log_errors(log,
"could not set up connection");
201 if (BIO_set_conn_port(bio,
"https") <= 0)
203 log_errors(log,
"could not set port");
207 if (!BIO_set_conn_hostname(bio, hostname))
209 log_errors(log,
"could not set hostname");
213 BIO_get_ssl(bio, &ssl);
216 log_errors(log,
"could not get ssl");
220 if (!SSL_set_tlsext_host_name(ssl, hostname))
222 log_errors(log,
"could not set sni hostname");
226 WLog_Print(log, WLOG_DEBUG,
"headers:\n%s", headers);
230 const size_t hlen = strnlen(headers, size);
231 if (hlen > INT32_MAX)
234 if (BIO_write(bio, headers, (
int)hlen) < 0)
236 log_errors(log,
"could not write headers");
242 WLog_Print(log, WLOG_DEBUG,
"body:\n%s", body);
246 WLog_Print(log, WLOG_ERROR,
"body too long!");
251 if (BIO_write(bio, body, (
int)blen) < 0)
253 log_errors(log,
"could not write body");
260 status = get_line(bio, buffer,
sizeof(buffer));
263 log_errors(log,
"could not read response");
267 const char header[9] = {
'H',
'T',
'T',
'P',
'/',
'1',
'.',
'1',
' ' };
268 if ((status < (INT64)
sizeof(header)) || (strncmp(header, buffer,
sizeof(header)) != 0))
270 WLog_Print(log, WLOG_ERROR,
"invalid HTTP status header");
275 *status_code = strtol(&buffer[
sizeof(header)],
nullptr, 0);
278 char ebuffer[256] = WINPR_C_ARRAY_INIT;
279 WLog_Print(log, WLOG_ERROR,
"invalid HTTP status line: %s [%d]",
280 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
286 status = get_line(bio, buffer,
sizeof(buffer));
289 log_errors(log,
"could not read response");
294 char* name = strtok_s(buffer,
":", &val);
295 if (name && (_stricmp(name,
"content-length") == 0))
298 *response_length = strtoul(val,
nullptr, 10);
301 char ebuffer[256] = WINPR_C_ARRAY_INIT;
302 WLog_Print(log, WLOG_ERROR,
"could not parse content length (%s): %s [%d]", val,
303 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
307 }
while (strcmp(buffer,
"\r\n") != 0);
309 if (*response_length > 0)
311 if (*response_length > INT_MAX)
313 WLog_Print(log, WLOG_ERROR,
"response too long!");
317 *response = calloc(1, *response_length + 1);
322 size_t left = *response_length;
325 const int rd = (left < INT32_MAX) ? (
int)left : INT32_MAX;
326 status = BIO_read(bio, p, rd);
329 log_errors(log,
"could not read response");
333 if ((
size_t)status > left)
335 left -= (size_t)status;
339 WLog_Print(log, WLOG_DEBUG,
"response[%" PRIuz
"]:\n%s", *response_length,
340 (
const char*)(*response));
348 *response_length = 0;
353 SSL_CTX_free(ssl_ctx);
358BOOL freerdp_http_request_context(rdpContext* context,
const char* url,
const char* body,
359 long* status_code, BYTE** response,
size_t* response_length)
362 char* hostname =
nullptr;
363 const char* path =
nullptr;
364 char* headers =
nullptr;
367 char buffer[1024] = WINPR_C_ARRAY_INIT;
369 WINPR_ASSERT(context);
370 WINPR_ASSERT(status_code);
371 WINPR_ASSERT(response);
372 WINPR_ASSERT(response_length);
373 const rdpSettings* settings = context->settings;
375 wLog* log = WLog_Get(TAG);
380 rdpTls* tls = freerdp_tls_new(context);
381 if (!tls || !url || strnlen(url, 8) < 8 || strncmp(url,
"https://", 8) != 0 ||
382 !(path = strchr(url + 8,
'/')))
384 WLog_Print(log, WLOG_ERROR,
"invalid url provided");
389 const size_t len = WINPR_ASSERTING_INT_CAST(
size_t, path - (url + 8));
390 hostname = strndup(&url[8], len);
400 if (winpr_asprintf(&headers, &size, post_header_fmt, path, hostname, blen) < 0)
405 if (winpr_asprintf(&headers, &size, get_header_fmt, path, hostname) < 0)
409 tls->hostname = hostname;
411 tls->isHttpRequest = TRUE;
413 if (!utils_tls_connect(context, tls, log, hostname, tls->port, FALSE, TcpConnectTimeout))
417 const size_t hlen = strnlen(headers, size);
418 if (hlen > INT32_MAX)
420 WLog_Print(log, WLOG_DEBUG,
"headers:\n%s", headers);
421 const int rc = freerdp_tls_write_all(tls, headers, hlen);
422 if ((rc < 0) || ((
size_t)rc < hlen))
428 WLog_Print(log, WLOG_DEBUG,
"body:\n%s", body);
432 WLog_Print(log, WLOG_ERROR,
"body too long!");
436 const int rc = freerdp_tls_write_all(tls, body, blen);
437 if ((rc < 0) || ((
size_t)rc < blen))
443 status = get_line_context(context, bio, buffer,
sizeof(buffer));
446 log_errors(log,
"could not read response");
450 const char header[9] = {
'H',
'T',
'T',
'P',
'/',
'1',
'.',
'1',
' ' };
451 if ((status < (INT64)
sizeof(header)) || (strncmp(header, buffer,
sizeof(header)) != 0))
453 WLog_Print(log, WLOG_ERROR,
"invalid HTTP status header");
458 *status_code = strtol(&buffer[
sizeof(header)],
nullptr, 0);
461 char ebuffer[256] = WINPR_C_ARRAY_INIT;
462 WLog_Print(log, WLOG_ERROR,
"invalid HTTP status line: %s [%d]",
463 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
469 status = get_line_context(context, bio, buffer,
sizeof(buffer));
472 log_errors(log,
"could not read response");
477 char* name = strtok_s(buffer,
":", &val);
478 if (name && (_stricmp(name,
"content-length") == 0))
481 *response_length = strtoul(val,
nullptr, 10);
484 char ebuffer[256] = WINPR_C_ARRAY_INIT;
485 WLog_Print(log, WLOG_ERROR,
"could not parse content length (%s): %s [%d]", val,
486 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
490 }
while (strcmp(buffer,
"\r\n") != 0);
492 if (*response_length > 0)
494 if (*response_length > INT_MAX)
496 WLog_Print(log, WLOG_ERROR,
"response too long!");
500 *response = calloc(1, *response_length + 1);
505 size_t left = *response_length;
508 const int rd = (left < INT32_MAX) ? (
int)left : INT32_MAX;
509 status = BIO_read(bio, p, rd);
512 log_errors(log,
"could not read response");
516 if ((
size_t)status > left)
518 left -= (size_t)status;
522 WLog_Print(log, WLOG_DEBUG,
"response[%" PRIuz
"]:\n%s", *response_length,
523 (
const char*)(*response));
531 *response_length = 0;
535 freerdp_tls_free(tls);
539const char* freerdp_http_status_string(
long status)
543 case HTTP_STATUS_CONTINUE:
544 return "HTTP_STATUS_CONTINUE";
545 case HTTP_STATUS_SWITCH_PROTOCOLS:
546 return "HTTP_STATUS_SWITCH_PROTOCOLS";
548 return "HTTP_STATUS_OK";
549 case HTTP_STATUS_CREATED:
550 return "HTTP_STATUS_CREATED";
551 case HTTP_STATUS_ACCEPTED:
552 return "HTTP_STATUS_ACCEPTED";
553 case HTTP_STATUS_PARTIAL:
554 return "HTTP_STATUS_PARTIAL";
555 case HTTP_STATUS_NO_CONTENT:
556 return "HTTP_STATUS_NO_CONTENT";
557 case HTTP_STATUS_RESET_CONTENT:
558 return "HTTP_STATUS_RESET_CONTENT";
559 case HTTP_STATUS_PARTIAL_CONTENT:
560 return "HTTP_STATUS_PARTIAL_CONTENT";
561 case HTTP_STATUS_WEBDAV_MULTI_STATUS:
562 return "HTTP_STATUS_WEBDAV_MULTI_STATUS";
563 case HTTP_STATUS_AMBIGUOUS:
564 return "HTTP_STATUS_AMBIGUOUS";
565 case HTTP_STATUS_MOVED:
566 return "HTTP_STATUS_MOVED";
567 case HTTP_STATUS_REDIRECT:
568 return "HTTP_STATUS_REDIRECT";
569 case HTTP_STATUS_REDIRECT_METHOD:
570 return "HTTP_STATUS_REDIRECT_METHOD";
571 case HTTP_STATUS_NOT_MODIFIED:
572 return "HTTP_STATUS_NOT_MODIFIED";
573 case HTTP_STATUS_USE_PROXY:
574 return "HTTP_STATUS_USE_PROXY";
575 case HTTP_STATUS_REDIRECT_KEEP_VERB:
576 return "HTTP_STATUS_REDIRECT_KEEP_VERB";
577 case HTTP_STATUS_BAD_REQUEST:
578 return "HTTP_STATUS_BAD_REQUEST";
579 case HTTP_STATUS_DENIED:
580 return "HTTP_STATUS_DENIED";
581 case HTTP_STATUS_PAYMENT_REQ:
582 return "HTTP_STATUS_PAYMENT_REQ";
583 case HTTP_STATUS_FORBIDDEN:
584 return "HTTP_STATUS_FORBIDDEN";
585 case HTTP_STATUS_NOT_FOUND:
586 return "HTTP_STATUS_NOT_FOUND";
587 case HTTP_STATUS_BAD_METHOD:
588 return "HTTP_STATUS_BAD_METHOD";
589 case HTTP_STATUS_NONE_ACCEPTABLE:
590 return "HTTP_STATUS_NONE_ACCEPTABLE";
591 case HTTP_STATUS_PROXY_AUTH_REQ:
592 return "HTTP_STATUS_PROXY_AUTH_REQ";
593 case HTTP_STATUS_REQUEST_TIMEOUT:
594 return "HTTP_STATUS_REQUEST_TIMEOUT";
595 case HTTP_STATUS_CONFLICT:
596 return "HTTP_STATUS_CONFLICT";
597 case HTTP_STATUS_GONE:
598 return "HTTP_STATUS_GONE";
599 case HTTP_STATUS_LENGTH_REQUIRED:
600 return "HTTP_STATUS_LENGTH_REQUIRED";
601 case HTTP_STATUS_PRECOND_FAILED:
602 return "HTTP_STATUS_PRECOND_FAILED";
603 case HTTP_STATUS_REQUEST_TOO_LARGE:
604 return "HTTP_STATUS_REQUEST_TOO_LARGE";
605 case HTTP_STATUS_URI_TOO_LONG:
606 return "HTTP_STATUS_URI_TOO_LONG";
607 case HTTP_STATUS_UNSUPPORTED_MEDIA:
608 return "HTTP_STATUS_UNSUPPORTED_MEDIA";
609 case HTTP_STATUS_RETRY_WITH:
610 return "HTTP_STATUS_RETRY_WITH";
611 case HTTP_STATUS_SERVER_ERROR:
612 return "HTTP_STATUS_SERVER_ERROR";
613 case HTTP_STATUS_NOT_SUPPORTED:
614 return "HTTP_STATUS_NOT_SUPPORTED";
615 case HTTP_STATUS_BAD_GATEWAY:
616 return "HTTP_STATUS_BAD_GATEWAY";
617 case HTTP_STATUS_SERVICE_UNAVAIL:
618 return "HTTP_STATUS_SERVICE_UNAVAIL";
619 case HTTP_STATUS_GATEWAY_TIMEOUT:
620 return "HTTP_STATUS_GATEWAY_TIMEOUT";
621 case HTTP_STATUS_VERSION_NOT_SUP:
622 return "HTTP_STATUS_VERSION_NOT_SUP";
624 return "HTTP_STATUS_UNKNOWN";
628const char* freerdp_http_status_string_format(
long status,
char* buffer,
size_t size)
630 const char* code = freerdp_http_status_string(status);
631 (void)_snprintf(buffer, size,
"%s [%ld]", code, status);
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.