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");
 
  120  const size_t len = WINPR_ASSERTING_INT_CAST(
size_t, path - (url + 8));
 
  121  hostname = strndup(&url[8], len);
 
  129    if (winpr_asprintf(&headers, &size, post_header_fmt, path, hostname, blen) < 0)
 
  137    if (winpr_asprintf(&headers, &size, get_header_fmt, path, hostname) < 0)
 
  144  ssl_ctx = SSL_CTX_new(TLS_client_method());
 
  148    log_errors(log, 
"could not set up ssl context");
 
  152  if (!SSL_CTX_set_default_verify_paths(ssl_ctx))
 
  154    log_errors(log, 
"could not set ssl context verify paths");
 
  158  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
 
  160  bio = BIO_new_ssl_connect(ssl_ctx);
 
  163    log_errors(log, 
"could not set up connection");
 
  167  if (BIO_set_conn_port(bio, 
"https") <= 0)
 
  169    log_errors(log, 
"could not set port");
 
  173  if (!BIO_set_conn_hostname(bio, hostname))
 
  175    log_errors(log, 
"could not set hostname");
 
  179  BIO_get_ssl(bio, &ssl);
 
  182    log_errors(log, 
"could not get ssl");
 
  186  if (!SSL_set_tlsext_host_name(ssl, hostname))
 
  188    log_errors(log, 
"could not set sni hostname");
 
  192  WLog_Print(log, WLOG_DEBUG, 
"headers:\n%s", headers);
 
  194  const size_t hlen = strnlen(headers, size);
 
  195  if (hlen > INT32_MAX)
 
  198  if (BIO_write(bio, headers, (
int)hlen) < 0)
 
  200    log_errors(log, 
"could not write headers");
 
  206    WLog_Print(log, WLOG_DEBUG, 
"body:\n%s", body);
 
  210      WLog_Print(log, WLOG_ERROR, 
"body too long!");
 
  215    if (BIO_write(bio, body, (
int)blen) < 0)
 
  217      log_errors(log, 
"could not write body");
 
  222  status = get_line(bio, buffer, 
sizeof(buffer));
 
  225    log_errors(log, 
"could not read response");
 
  230  if (sscanf(buffer, 
"HTTP/1.1 %li %*[^\r\n]\r\n", status_code) < 1)
 
  232    WLog_Print(log, WLOG_ERROR, 
"invalid HTTP status line");
 
  238    status = get_line(bio, buffer, 
sizeof(buffer));
 
  241      log_errors(log, 
"could not read response");
 
  246    char* name = strtok_s(buffer, 
":", &val);
 
  247    if (name && (_stricmp(name, 
"content-length") == 0))
 
  250      *response_length = strtoul(val, NULL, 10);
 
  253        char ebuffer[256] = { 0 };
 
  254        WLog_Print(log, WLOG_ERROR, 
"could not parse content length (%s): %s [%d]", val,
 
  255                   winpr_strerror(errno, ebuffer, 
sizeof(ebuffer)), errno);
 
  259  } 
while (strcmp(buffer, 
"\r\n") != 0);
 
  261  if (*response_length > 0)
 
  263    if (*response_length > INT_MAX)
 
  265      WLog_Print(log, WLOG_ERROR, 
"response too long!");
 
  269    *response = calloc(1, *response_length + 1);
 
  274    size_t left = *response_length;
 
  277      const int rd = (left < INT32_MAX) ? (
int)left : INT32_MAX;
 
  278      status = BIO_read(bio, p, rd);
 
  281        log_errors(log, 
"could not read response");
 
  285      if ((
size_t)status > left)
 
  287      left -= (size_t)status;
 
  298    *response_length = 0;
 
  303  SSL_CTX_free(ssl_ctx);
 
  307const char* freerdp_http_status_string(
long status)
 
  311    case HTTP_STATUS_CONTINUE:
 
  312      return "HTTP_STATUS_CONTINUE";
 
  313    case HTTP_STATUS_SWITCH_PROTOCOLS:
 
  314      return "HTTP_STATUS_SWITCH_PROTOCOLS";
 
  316      return "HTTP_STATUS_OK";
 
  317    case HTTP_STATUS_CREATED:
 
  318      return "HTTP_STATUS_CREATED";
 
  319    case HTTP_STATUS_ACCEPTED:
 
  320      return "HTTP_STATUS_ACCEPTED";
 
  321    case HTTP_STATUS_PARTIAL:
 
  322      return "HTTP_STATUS_PARTIAL";
 
  323    case HTTP_STATUS_NO_CONTENT:
 
  324      return "HTTP_STATUS_NO_CONTENT";
 
  325    case HTTP_STATUS_RESET_CONTENT:
 
  326      return "HTTP_STATUS_RESET_CONTENT";
 
  327    case HTTP_STATUS_PARTIAL_CONTENT:
 
  328      return "HTTP_STATUS_PARTIAL_CONTENT";
 
  329    case HTTP_STATUS_WEBDAV_MULTI_STATUS:
 
  330      return "HTTP_STATUS_WEBDAV_MULTI_STATUS";
 
  331    case HTTP_STATUS_AMBIGUOUS:
 
  332      return "HTTP_STATUS_AMBIGUOUS";
 
  333    case HTTP_STATUS_MOVED:
 
  334      return "HTTP_STATUS_MOVED";
 
  335    case HTTP_STATUS_REDIRECT:
 
  336      return "HTTP_STATUS_REDIRECT";
 
  337    case HTTP_STATUS_REDIRECT_METHOD:
 
  338      return "HTTP_STATUS_REDIRECT_METHOD";
 
  339    case HTTP_STATUS_NOT_MODIFIED:
 
  340      return "HTTP_STATUS_NOT_MODIFIED";
 
  341    case HTTP_STATUS_USE_PROXY:
 
  342      return "HTTP_STATUS_USE_PROXY";
 
  343    case HTTP_STATUS_REDIRECT_KEEP_VERB:
 
  344      return "HTTP_STATUS_REDIRECT_KEEP_VERB";
 
  345    case HTTP_STATUS_BAD_REQUEST:
 
  346      return "HTTP_STATUS_BAD_REQUEST";
 
  347    case HTTP_STATUS_DENIED:
 
  348      return "HTTP_STATUS_DENIED";
 
  349    case HTTP_STATUS_PAYMENT_REQ:
 
  350      return "HTTP_STATUS_PAYMENT_REQ";
 
  351    case HTTP_STATUS_FORBIDDEN:
 
  352      return "HTTP_STATUS_FORBIDDEN";
 
  353    case HTTP_STATUS_NOT_FOUND:
 
  354      return "HTTP_STATUS_NOT_FOUND";
 
  355    case HTTP_STATUS_BAD_METHOD:
 
  356      return "HTTP_STATUS_BAD_METHOD";
 
  357    case HTTP_STATUS_NONE_ACCEPTABLE:
 
  358      return "HTTP_STATUS_NONE_ACCEPTABLE";
 
  359    case HTTP_STATUS_PROXY_AUTH_REQ:
 
  360      return "HTTP_STATUS_PROXY_AUTH_REQ";
 
  361    case HTTP_STATUS_REQUEST_TIMEOUT:
 
  362      return "HTTP_STATUS_REQUEST_TIMEOUT";
 
  363    case HTTP_STATUS_CONFLICT:
 
  364      return "HTTP_STATUS_CONFLICT";
 
  365    case HTTP_STATUS_GONE:
 
  366      return "HTTP_STATUS_GONE";
 
  367    case HTTP_STATUS_LENGTH_REQUIRED:
 
  368      return "HTTP_STATUS_LENGTH_REQUIRED";
 
  369    case HTTP_STATUS_PRECOND_FAILED:
 
  370      return "HTTP_STATUS_PRECOND_FAILED";
 
  371    case HTTP_STATUS_REQUEST_TOO_LARGE:
 
  372      return "HTTP_STATUS_REQUEST_TOO_LARGE";
 
  373    case HTTP_STATUS_URI_TOO_LONG:
 
  374      return "HTTP_STATUS_URI_TOO_LONG";
 
  375    case HTTP_STATUS_UNSUPPORTED_MEDIA:
 
  376      return "HTTP_STATUS_UNSUPPORTED_MEDIA";
 
  377    case HTTP_STATUS_RETRY_WITH:
 
  378      return "HTTP_STATUS_RETRY_WITH";
 
  379    case HTTP_STATUS_SERVER_ERROR:
 
  380      return "HTTP_STATUS_SERVER_ERROR";
 
  381    case HTTP_STATUS_NOT_SUPPORTED:
 
  382      return "HTTP_STATUS_NOT_SUPPORTED";
 
  383    case HTTP_STATUS_BAD_GATEWAY:
 
  384      return "HTTP_STATUS_BAD_GATEWAY";
 
  385    case HTTP_STATUS_SERVICE_UNAVAIL:
 
  386      return "HTTP_STATUS_SERVICE_UNAVAIL";
 
  387    case HTTP_STATUS_GATEWAY_TIMEOUT:
 
  388      return "HTTP_STATUS_GATEWAY_TIMEOUT";
 
  389    case HTTP_STATUS_VERSION_NOT_SUP:
 
  390      return "HTTP_STATUS_VERSION_NOT_SUP";
 
  392      return "HTTP_STATUS_UNKNOWN";
 
  396const char* freerdp_http_status_string_format(
long status, 
char* buffer, 
size_t size)
 
  398  const char* code = freerdp_http_status_string(status);
 
  399  (void)_snprintf(buffer, size, 
"%s [%ld]", code, status);