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 <freerdp/log.h>
31#define TAG FREERDP_TAG("utils.http")
33static const char get_header_fmt[] =
"GET %s HTTP/1.1\r\n"
37static const char post_header_fmt[] =
"POST %s HTTP/1.1\r\n"
39 "Content-Type: application/x-www-form-urlencoded\r\n"
40 "Content-Length: %lu\r\n"
43#define log_errors(log, msg) log_errors_(log, msg, __FILE__, __func__, __LINE__)
44static void log_errors_(wLog* log,
const char* msg,
const char* file,
const char* fkt,
size_t line)
46 const DWORD level = WLOG_ERROR;
49 if (!WLog_IsLevelActive(log, level))
52 BOOL error_logged = FALSE;
53 while ((ec = ERR_get_error()))
56 WLog_PrintTextMessage(log, level, line, file, fkt,
"%s: %s", msg,
57 ERR_error_string(ec, NULL));
60 WLog_PrintTextMessage(log, level, line, file, fkt,
"%s (no details available)", msg);
63static int get_line(BIO* bio,
char* buffer,
size_t size)
65#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
72 int rc = BIO_read(bio, &buffer[pos], 1);
75 char cur = buffer[pos];
77 if ((cur ==
'\n') || (pos >= size - 1))
86 return BIO_get_line(bio, buffer, (
int)size);
90BOOL freerdp_http_request(
const char* url,
const char* body,
long* status_code, BYTE** response,
91 size_t* response_length)
94 char* hostname = NULL;
95 const char* path = NULL;
99 char buffer[1024] = { 0 };
101 SSL_CTX* ssl_ctx = NULL;
104 WINPR_ASSERT(status_code);
105 WINPR_ASSERT(response);
106 WINPR_ASSERT(response_length);
108 wLog* log = WLog_Get(TAG);
113 if (!url || strnlen(url, 8) < 8 || strncmp(url,
"https://", 8) != 0 ||
114 !(path = strchr(url + 8,
'/')))
116 WLog_Print(log, WLOG_ERROR,
"invalid url provided");
121 const size_t len = WINPR_ASSERTING_INT_CAST(
size_t, path - (url + 8));
122 hostname = strndup(&url[8], len);
132 if (winpr_asprintf(&headers, &size, post_header_fmt, path, hostname, blen) < 0)
140 if (winpr_asprintf(&headers, &size, get_header_fmt, path, hostname) < 0)
147 ssl_ctx = SSL_CTX_new(TLS_client_method());
151 log_errors(log,
"could not set up ssl context");
155 if (!SSL_CTX_set_default_verify_paths(ssl_ctx))
157 log_errors(log,
"could not set ssl context verify paths");
161 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
163 bio = BIO_new_ssl_connect(ssl_ctx);
166 log_errors(log,
"could not set up connection");
170 if (BIO_set_conn_port(bio,
"https") <= 0)
172 log_errors(log,
"could not set port");
176 if (!BIO_set_conn_hostname(bio, hostname))
178 log_errors(log,
"could not set hostname");
182 BIO_get_ssl(bio, &ssl);
185 log_errors(log,
"could not get ssl");
189 if (!SSL_set_tlsext_host_name(ssl, hostname))
191 log_errors(log,
"could not set sni hostname");
195 WLog_Print(log, WLOG_DEBUG,
"headers:\n%s", headers);
199 const size_t hlen = strnlen(headers, size);
200 if (hlen > INT32_MAX)
203 if (BIO_write(bio, headers, (
int)hlen) < 0)
205 log_errors(log,
"could not write headers");
211 WLog_Print(log, WLOG_DEBUG,
"body:\n%s", body);
215 WLog_Print(log, WLOG_ERROR,
"body too long!");
220 if (BIO_write(bio, body, (
int)blen) < 0)
222 log_errors(log,
"could not write body");
229 status = get_line(bio, buffer,
sizeof(buffer));
232 log_errors(log,
"could not read response");
236 const char header[9] = {
'H',
'T',
'T',
'P',
'/',
'1',
'.',
'1',
' ' };
237 if ((status < (INT64)
sizeof(header)) || (strncmp(header, buffer,
sizeof(header)) != 0))
239 WLog_Print(log, WLOG_ERROR,
"invalid HTTP status header");
244 *status_code = strtol(&buffer[
sizeof(header)], NULL, 0);
247 char ebuffer[256] = { 0 };
248 WLog_Print(log, WLOG_ERROR,
"invalid HTTP status line: %s [%d]",
249 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
255 status = get_line(bio, buffer,
sizeof(buffer));
258 log_errors(log,
"could not read response");
263 char* name = strtok_s(buffer,
":", &val);
264 if (name && (_stricmp(name,
"content-length") == 0))
267 *response_length = strtoul(val, NULL, 10);
270 char ebuffer[256] = { 0 };
271 WLog_Print(log, WLOG_ERROR,
"could not parse content length (%s): %s [%d]", val,
272 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
276 }
while (strcmp(buffer,
"\r\n") != 0);
278 if (*response_length > 0)
280 if (*response_length > INT_MAX)
282 WLog_Print(log, WLOG_ERROR,
"response too long!");
286 *response = calloc(1, *response_length + 1);
291 size_t left = *response_length;
294 const int rd = (left < INT32_MAX) ? (
int)left : INT32_MAX;
295 status = BIO_read(bio, p, rd);
298 log_errors(log,
"could not read response");
302 if ((
size_t)status > left)
304 left -= (size_t)status;
308 WLog_Print(log, WLOG_DEBUG,
"response[%" PRIuz
"]:\n%s", *response_length,
309 (
const char*)(*response));
317 *response_length = 0;
322 SSL_CTX_free(ssl_ctx);
326const char* freerdp_http_status_string(
long status)
330 case HTTP_STATUS_CONTINUE:
331 return "HTTP_STATUS_CONTINUE";
332 case HTTP_STATUS_SWITCH_PROTOCOLS:
333 return "HTTP_STATUS_SWITCH_PROTOCOLS";
335 return "HTTP_STATUS_OK";
336 case HTTP_STATUS_CREATED:
337 return "HTTP_STATUS_CREATED";
338 case HTTP_STATUS_ACCEPTED:
339 return "HTTP_STATUS_ACCEPTED";
340 case HTTP_STATUS_PARTIAL:
341 return "HTTP_STATUS_PARTIAL";
342 case HTTP_STATUS_NO_CONTENT:
343 return "HTTP_STATUS_NO_CONTENT";
344 case HTTP_STATUS_RESET_CONTENT:
345 return "HTTP_STATUS_RESET_CONTENT";
346 case HTTP_STATUS_PARTIAL_CONTENT:
347 return "HTTP_STATUS_PARTIAL_CONTENT";
348 case HTTP_STATUS_WEBDAV_MULTI_STATUS:
349 return "HTTP_STATUS_WEBDAV_MULTI_STATUS";
350 case HTTP_STATUS_AMBIGUOUS:
351 return "HTTP_STATUS_AMBIGUOUS";
352 case HTTP_STATUS_MOVED:
353 return "HTTP_STATUS_MOVED";
354 case HTTP_STATUS_REDIRECT:
355 return "HTTP_STATUS_REDIRECT";
356 case HTTP_STATUS_REDIRECT_METHOD:
357 return "HTTP_STATUS_REDIRECT_METHOD";
358 case HTTP_STATUS_NOT_MODIFIED:
359 return "HTTP_STATUS_NOT_MODIFIED";
360 case HTTP_STATUS_USE_PROXY:
361 return "HTTP_STATUS_USE_PROXY";
362 case HTTP_STATUS_REDIRECT_KEEP_VERB:
363 return "HTTP_STATUS_REDIRECT_KEEP_VERB";
364 case HTTP_STATUS_BAD_REQUEST:
365 return "HTTP_STATUS_BAD_REQUEST";
366 case HTTP_STATUS_DENIED:
367 return "HTTP_STATUS_DENIED";
368 case HTTP_STATUS_PAYMENT_REQ:
369 return "HTTP_STATUS_PAYMENT_REQ";
370 case HTTP_STATUS_FORBIDDEN:
371 return "HTTP_STATUS_FORBIDDEN";
372 case HTTP_STATUS_NOT_FOUND:
373 return "HTTP_STATUS_NOT_FOUND";
374 case HTTP_STATUS_BAD_METHOD:
375 return "HTTP_STATUS_BAD_METHOD";
376 case HTTP_STATUS_NONE_ACCEPTABLE:
377 return "HTTP_STATUS_NONE_ACCEPTABLE";
378 case HTTP_STATUS_PROXY_AUTH_REQ:
379 return "HTTP_STATUS_PROXY_AUTH_REQ";
380 case HTTP_STATUS_REQUEST_TIMEOUT:
381 return "HTTP_STATUS_REQUEST_TIMEOUT";
382 case HTTP_STATUS_CONFLICT:
383 return "HTTP_STATUS_CONFLICT";
384 case HTTP_STATUS_GONE:
385 return "HTTP_STATUS_GONE";
386 case HTTP_STATUS_LENGTH_REQUIRED:
387 return "HTTP_STATUS_LENGTH_REQUIRED";
388 case HTTP_STATUS_PRECOND_FAILED:
389 return "HTTP_STATUS_PRECOND_FAILED";
390 case HTTP_STATUS_REQUEST_TOO_LARGE:
391 return "HTTP_STATUS_REQUEST_TOO_LARGE";
392 case HTTP_STATUS_URI_TOO_LONG:
393 return "HTTP_STATUS_URI_TOO_LONG";
394 case HTTP_STATUS_UNSUPPORTED_MEDIA:
395 return "HTTP_STATUS_UNSUPPORTED_MEDIA";
396 case HTTP_STATUS_RETRY_WITH:
397 return "HTTP_STATUS_RETRY_WITH";
398 case HTTP_STATUS_SERVER_ERROR:
399 return "HTTP_STATUS_SERVER_ERROR";
400 case HTTP_STATUS_NOT_SUPPORTED:
401 return "HTTP_STATUS_NOT_SUPPORTED";
402 case HTTP_STATUS_BAD_GATEWAY:
403 return "HTTP_STATUS_BAD_GATEWAY";
404 case HTTP_STATUS_SERVICE_UNAVAIL:
405 return "HTTP_STATUS_SERVICE_UNAVAIL";
406 case HTTP_STATUS_GATEWAY_TIMEOUT:
407 return "HTTP_STATUS_GATEWAY_TIMEOUT";
408 case HTTP_STATUS_VERSION_NOT_SUP:
409 return "HTTP_STATUS_VERSION_NOT_SUP";
411 return "HTTP_STATUS_UNKNOWN";
415const char* freerdp_http_status_string_format(
long status,
char* buffer,
size_t size)
417 const char* code = freerdp_http_status_string(status);
418 (void)_snprintf(buffer, size,
"%s [%ld]", code, status);