20#include <freerdp/config.h> 
   26#include <winpr/print.h> 
   27#include <winpr/stream.h> 
   28#include <winpr/string.h> 
   30#include <winpr/sysinfo.h> 
   32#include <freerdp/log.h> 
   33#include <freerdp/crypto/crypto.h> 
   36#include <winpr/crypto.h> 
   38#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H 
   39#include <valgrind/memcheck.h> 
   45#define TAG FREERDP_TAG("core.gateway.http") 
   47#define RESPONSE_SIZE_LIMIT (64ULL * 1024ULL * 1024ULL) 
   49#define WEBSOCKET_MAGIC_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 
   62  char* RdgConnectionId;
 
   63  char* RdgCorrelationId;
 
   65  BOOL websocketUpgrade;
 
   66  char* SecWebsocketKey;
 
   67  wListDictionary* cookies;
 
   79  TRANSFER_ENCODING TransferEncoding;
 
   88  const char* ReasonPhrase;
 
   91  const char* ContentType;
 
   92  TRANSFER_ENCODING TransferEncoding;
 
   93  const char* SecWebsocketVersion;
 
   94  const char* SecWebsocketAccept;
 
   99  wListDictionary* Authenticates;
 
  100  wListDictionary* SetCookie;
 
  104static char* string_strnstr(
char* str1, 
const char* str2, 
size_t slen)
 
  110  if ((c = *str2++) != 
'\0')
 
  112    len = strnlen(str2, slen + 1);
 
  118        if (slen-- < 1 || (sc = *str1++) == 
'\0')
 
  124    } 
while (strncmp(str1, str2, len) != 0);
 
  132static BOOL strings_equals_nocase(
const void* obj1, 
const void* obj2)
 
  137  return _stricmp(obj1, obj2) == 0;
 
  140HttpContext* http_context_new(
void)
 
  142  HttpContext* context = (HttpContext*)calloc(1, 
sizeof(HttpContext));
 
  146  context->cookies = ListDictionary_New(FALSE);
 
  147  if (!context->cookies)
 
  150  wObject* key = ListDictionary_KeyObject(context->cookies);
 
  151  wObject* value = ListDictionary_ValueObject(context->cookies);
 
  155  key->fnObjectFree = winpr_ObjectStringFree;
 
  156  key->fnObjectNew = winpr_ObjectStringClone;
 
  157  value->fnObjectFree = winpr_ObjectStringFree;
 
  158  value->fnObjectNew = winpr_ObjectStringClone;
 
  163  WINPR_PRAGMA_DIAG_PUSH
 
  164  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
 
  165  http_context_free(context);
 
  166  WINPR_PRAGMA_DIAG_POP
 
  170BOOL http_context_set_method(HttpContext* context, 
const char* Method)
 
  172  if (!context || !Method)
 
  175  free(context->Method);
 
  176  context->Method = _strdup(Method);
 
  178  if (!context->Method)
 
  184BOOL http_request_set_content_type(HttpRequest* request, 
const char* ContentType)
 
  186  if (!request || !ContentType)
 
  189  free(request->ContentType);
 
  190  request->ContentType = _strdup(ContentType);
 
  192  if (!request->ContentType)
 
  198const char* http_context_get_uri(HttpContext* context)
 
  206BOOL http_context_set_uri(HttpContext* context, 
const char* URI)
 
  208  if (!context || !URI)
 
  212  context->URI = _strdup(URI);
 
  220BOOL http_context_set_user_agent(HttpContext* context, 
const char* UserAgent)
 
  222  if (!context || !UserAgent)
 
  225  free(context->UserAgent);
 
  226  context->UserAgent = _strdup(UserAgent);
 
  228  if (!context->UserAgent)
 
  234BOOL http_context_set_x_ms_user_agent(HttpContext* context, 
const char* X_MS_UserAgent)
 
  236  if (!context || !X_MS_UserAgent)
 
  239  free(context->X_MS_UserAgent);
 
  240  context->X_MS_UserAgent = _strdup(X_MS_UserAgent);
 
  242  if (!context->X_MS_UserAgent)
 
  248BOOL http_context_set_host(HttpContext* context, 
const char* Host)
 
  250  if (!context || !Host)
 
  254  context->Host = _strdup(Host);
 
  262BOOL http_context_set_accept(HttpContext* context, 
const char* Accept)
 
  264  if (!context || !Accept)
 
  267  free(context->Accept);
 
  268  context->Accept = _strdup(Accept);
 
  270  if (!context->Accept)
 
  276BOOL http_context_set_cache_control(HttpContext* context, 
const char* CacheControl)
 
  278  if (!context || !CacheControl)
 
  281  free(context->CacheControl);
 
  282  context->CacheControl = _strdup(CacheControl);
 
  284  if (!context->CacheControl)
 
  290BOOL http_context_set_connection(HttpContext* context, 
const char* Connection)
 
  292  if (!context || !Connection)
 
  295  free(context->Connection);
 
  296  context->Connection = _strdup(Connection);
 
  298  if (!context->Connection)
 
  304WINPR_ATTR_FORMAT_ARG(2, 0)
 
  305static BOOL list_append(HttpContext* context, WINPR_FORMAT_ARG const 
char* str, va_list ap)
 
  310  size_t PragmaSize = 0;
 
  313  const int size = winpr_vasprintf(&Pragma, &PragmaSize, str, ap);
 
  323    winpr_asprintf(&sstr, &slen, 
"%s, %s", context->Pragma, Pragma);
 
  330  free(context->Pragma);
 
  331  context->Pragma = sstr;
 
  340WINPR_ATTR_FORMAT_ARG(2, 3)
 
  341BOOL http_context_set_pragma(HttpContext* context, WINPR_FORMAT_ARG const 
char* Pragma, ...)
 
  343  if (!context || !Pragma)
 
  346  free(context->Pragma);
 
  347  context->Pragma = NULL;
 
  350  va_start(ap, Pragma);
 
  351  return list_append(context, Pragma, ap);
 
  354WINPR_ATTR_FORMAT_ARG(2, 3)
 
  355BOOL http_context_append_pragma(HttpContext* context, const 
char* Pragma, ...)
 
  357  if (!context || !Pragma)
 
  361  va_start(ap, Pragma);
 
  362  return list_append(context, Pragma, ap);
 
  365static char* guid2str(
const GUID* guid)
 
  369  char* strguid = NULL;
 
  370  char bracedGuid[64] = { 0 };
 
  372  RPC_STATUS rpcStatus = UuidToStringA(guid, &strguid);
 
  374  if (rpcStatus != RPC_S_OK)
 
  377  (void)sprintf_s(bracedGuid, 
sizeof(bracedGuid), 
"{%s}", strguid);
 
  378  RpcStringFreeA(&strguid);
 
  379  return _strdup(bracedGuid);
 
  382BOOL http_context_set_rdg_connection_id(HttpContext* context, 
const GUID* RdgConnectionId)
 
  384  if (!context || !RdgConnectionId)
 
  387  free(context->RdgConnectionId);
 
  388  context->RdgConnectionId = guid2str(RdgConnectionId);
 
  390  if (!context->RdgConnectionId)
 
  396BOOL http_context_set_rdg_correlation_id(HttpContext* context, 
const GUID* RdgCorrelationId)
 
  398  if (!context || !RdgCorrelationId)
 
  401  free(context->RdgCorrelationId);
 
  402  context->RdgCorrelationId = guid2str(RdgCorrelationId);
 
  404  if (!context->RdgCorrelationId)
 
  410BOOL http_context_enable_websocket_upgrade(HttpContext* context, BOOL enable)
 
  418    if (RPC_S_OK != UuidCreate(&key))
 
  421    free(context->SecWebsocketKey);
 
  422    context->SecWebsocketKey = crypto_base64_encode((BYTE*)&key, 
sizeof(key));
 
  423    if (!context->SecWebsocketKey)
 
  427  context->websocketUpgrade = enable;
 
  431BOOL http_context_is_websocket_upgrade_enabled(HttpContext* context)
 
  433  return context->websocketUpgrade;
 
  436BOOL http_context_set_rdg_auth_scheme(HttpContext* context, 
const char* RdgAuthScheme)
 
  438  if (!context || !RdgAuthScheme)
 
  441  free(context->RdgAuthScheme);
 
  442  context->RdgAuthScheme = _strdup(RdgAuthScheme);
 
  443  return context->RdgAuthScheme != NULL;
 
  446BOOL http_context_set_cookie(HttpContext* context, 
const char* CookieName, 
const char* CookieValue)
 
  448  if (!context || !CookieName || !CookieValue)
 
  450  if (ListDictionary_Contains(context->cookies, CookieName))
 
  452    if (!ListDictionary_SetItemValue(context->cookies, CookieName, CookieValue))
 
  457    if (!ListDictionary_Add(context->cookies, CookieName, CookieValue))
 
  463void http_context_free(HttpContext* context)
 
  467    free(context->SecWebsocketKey);
 
  468    free(context->UserAgent);
 
  469    free(context->X_MS_UserAgent);
 
  472    free(context->Accept);
 
  473    free(context->Method);
 
  474    free(context->CacheControl);
 
  475    free(context->Connection);
 
  476    free(context->Pragma);
 
  477    free(context->RdgConnectionId);
 
  478    free(context->RdgCorrelationId);
 
  479    free(context->RdgAuthScheme);
 
  480    ListDictionary_Free(context->cookies);
 
  485BOOL http_request_set_method(HttpRequest* request, 
const char* Method)
 
  487  if (!request || !Method)
 
  490  free(request->Method);
 
  491  request->Method = _strdup(Method);
 
  493  if (!request->Method)
 
  499BOOL http_request_set_uri(HttpRequest* request, 
const char* URI)
 
  501  if (!request || !URI)
 
  505  request->URI = _strdup(URI);
 
  513BOOL http_request_set_auth_scheme(HttpRequest* request, 
const char* AuthScheme)
 
  515  if (!request || !AuthScheme)
 
  518  free(request->AuthScheme);
 
  519  request->AuthScheme = _strdup(AuthScheme);
 
  521  if (!request->AuthScheme)
 
  527BOOL http_request_set_auth_param(HttpRequest* request, 
const char* AuthParam)
 
  529  if (!request || !AuthParam)
 
  532  free(request->AuthParam);
 
  533  request->AuthParam = _strdup(AuthParam);
 
  535  if (!request->AuthParam)
 
  541BOOL http_request_set_transfer_encoding(HttpRequest* request, TRANSFER_ENCODING TransferEncoding)
 
  543  if (!request || TransferEncoding == TransferEncodingUnknown)
 
  546  request->TransferEncoding = TransferEncoding;
 
  551WINPR_ATTR_FORMAT_ARG(2, 3)
 
  552static BOOL http_encode_print(
wStream* s, WINPR_FORMAT_ARG const 
char* fmt, ...)
 
  563  length = vsnprintf(NULL, 0, fmt, ap) + 1;
 
  566  if (!Stream_EnsureRemainingCapacity(s, (
size_t)length))
 
  569  str = (
char*)Stream_Pointer(s);
 
  571  used = vsnprintf(str, (
size_t)length, fmt, ap);
 
  575  if ((used + 1) != length)
 
  578  Stream_Seek(s, (
size_t)used);
 
  582static BOOL http_encode_body_line(
wStream* s, 
const char* param, 
const char* value)
 
  584  if (!s || !param || !value)
 
  587  return http_encode_print(s, 
"%s: %s\r\n", param, value);
 
  590static BOOL http_encode_content_length_line(
wStream* s, 
size_t ContentLength)
 
  592  return http_encode_print(s, 
"Content-Length: %" PRIuz 
"\r\n", ContentLength);
 
  595static BOOL http_encode_header_line(
wStream* s, 
const char* Method, 
const char* URI)
 
  597  if (!s || !Method || !URI)
 
  600  return http_encode_print(s, 
"%s %s HTTP/1.1\r\n", Method, URI);
 
  603static BOOL http_encode_authorization_line(
wStream* s, 
const char* AuthScheme,
 
  604                                           const char* AuthParam)
 
  606  if (!s || !AuthScheme || !AuthParam)
 
  609  return http_encode_print(s, 
"Authorization: %s %s\r\n", AuthScheme, AuthParam);
 
  612static BOOL http_encode_cookie_line(
wStream* s, wListDictionary* cookies)
 
  614  ULONG_PTR* keys = NULL;
 
  620  ListDictionary_Lock(cookies);
 
  621  const size_t count = ListDictionary_GetKeys(cookies, &keys);
 
  626  status = http_encode_print(s, 
"Cookie: ");
 
  630  for (
size_t x = 0; status && x < count; x++)
 
  632    char* cur = (
char*)ListDictionary_GetItemValue(cookies, (
void*)keys[x]);
 
  640      status = http_encode_print(s, 
"; ");
 
  644    status = http_encode_print(s, 
"%s=%s", (
char*)keys[x], cur);
 
  647  status = http_encode_print(s, 
"\r\n");
 
  650  ListDictionary_Unlock(cookies);
 
  654wStream* http_request_write(HttpContext* context, HttpRequest* request)
 
  658  if (!context || !request)
 
  661  s = Stream_New(NULL, 1024);
 
  666  if (!http_encode_header_line(s, request->Method, request->URI) ||
 
  667      !http_encode_body_line(s, 
"Cache-Control", context->CacheControl) ||
 
  668      !http_encode_body_line(s, 
"Pragma", context->Pragma) ||
 
  669      !http_encode_body_line(s, 
"Accept", context->Accept) ||
 
  670      !http_encode_body_line(s, 
"User-Agent", context->UserAgent) ||
 
  671      !http_encode_body_line(s, 
"Host", context->Host))
 
  674  if (!context->websocketUpgrade)
 
  676    if (!http_encode_body_line(s, 
"Connection", context->Connection))
 
  681    if (!http_encode_body_line(s, 
"Connection", 
"Upgrade") ||
 
  682        !http_encode_body_line(s, 
"Upgrade", 
"websocket") ||
 
  683        !http_encode_body_line(s, 
"Sec-Websocket-Version", 
"13") ||
 
  684        !http_encode_body_line(s, 
"Sec-Websocket-Key", context->SecWebsocketKey))
 
  688  if (context->RdgConnectionId)
 
  690    if (!http_encode_body_line(s, 
"RDG-Connection-Id", context->RdgConnectionId))
 
  694  if (context->RdgCorrelationId)
 
  696    if (!http_encode_body_line(s, 
"RDG-Correlation-Id", context->RdgCorrelationId))
 
  700  if (context->RdgAuthScheme)
 
  702    if (!http_encode_body_line(s, 
"RDG-Auth-Scheme", context->RdgAuthScheme))
 
  706  if (request->TransferEncoding != TransferEncodingIdentity)
 
  708    if (request->TransferEncoding == TransferEncodingChunked)
 
  710      if (!http_encode_body_line(s, 
"Transfer-Encoding", 
"chunked"))
 
  718    if (!http_encode_content_length_line(s, request->ContentLength))
 
  722  if (request->Authorization)
 
  724    if (!http_encode_body_line(s, 
"Authorization", request->Authorization))
 
  727  else if (request->AuthScheme && request->AuthParam)
 
  729    if (!http_encode_authorization_line(s, request->AuthScheme, request->AuthParam))
 
  733  if (context->cookies)
 
  735    if (!http_encode_cookie_line(s, context->cookies))
 
  739  if (request->ContentType)
 
  741    if (!http_encode_body_line(s, 
"Content-Type", request->ContentType))
 
  745  if (context->X_MS_UserAgent)
 
  747    if (!http_encode_body_line(s, 
"X-MS-User-Agent", context->X_MS_UserAgent))
 
  751  if (!http_encode_print(s, 
"\r\n"))
 
  754  Stream_SealLength(s);
 
  757  Stream_Free(s, TRUE);
 
  761HttpRequest* http_request_new(
void)
 
  763  HttpRequest* request = (HttpRequest*)calloc(1, 
sizeof(HttpRequest));
 
  767  request->TransferEncoding = TransferEncodingIdentity;
 
  771void http_request_free(HttpRequest* request)
 
  776  free(request->AuthParam);
 
  777  free(request->AuthScheme);
 
  778  free(request->Authorization);
 
  779  free(request->ContentType);
 
  780  free(request->Method);
 
  785static BOOL http_response_parse_header_status_line(HttpResponse* response, 
const char* status_line)
 
  788  char* separator = NULL;
 
  789  char* status_code = NULL;
 
  790  char* reason_phrase = NULL;
 
  796    separator = strchr(status_line, 
' ');
 
  801  status_code = separator + 1;
 
  802  separator = strchr(status_code, 
' ');
 
  807  reason_phrase = separator + 1;
 
  811    long val = strtol(status_code, NULL, 0);
 
  813    if ((errno != 0) || (val < 0) || (val > INT16_MAX))
 
  816    response->StatusCode = (UINT16)val;
 
  818  response->ReasonPhrase = reason_phrase;
 
  820  if (!response->ReasonPhrase)
 
  828    WLog_ERR(TAG, 
"http_response_parse_header_status_line failed [%s]", status_line);
 
  833static BOOL http_response_parse_header_field(HttpResponse* response, 
const char* name,
 
  838  WINPR_ASSERT(response);
 
  843  if (_stricmp(name, 
"Content-Length") == 0)
 
  845    unsigned long long val = 0;
 
  847    val = _strtoui64(value, NULL, 0);
 
  849    if ((errno != 0) || (val > INT32_MAX))
 
  852    response->ContentLength = WINPR_ASSERTING_INT_CAST(
size_t, val);
 
  854  else if (_stricmp(name, 
"Content-Type") == 0)
 
  856    response->ContentType = value;
 
  858    if (!response->ContentType)
 
  861  else if (_stricmp(name, 
"Transfer-Encoding") == 0)
 
  863    if (_stricmp(value, 
"identity") == 0)
 
  864      response->TransferEncoding = TransferEncodingIdentity;
 
  865    else if (_stricmp(value, 
"chunked") == 0)
 
  866      response->TransferEncoding = TransferEncodingChunked;
 
  868      response->TransferEncoding = TransferEncodingUnknown;
 
  870  else if (_stricmp(name, 
"Sec-WebSocket-Version") == 0)
 
  872    response->SecWebsocketVersion = value;
 
  874    if (!response->SecWebsocketVersion)
 
  877  else if (_stricmp(name, 
"Sec-WebSocket-Accept") == 0)
 
  879    response->SecWebsocketAccept = value;
 
  881    if (!response->SecWebsocketAccept)
 
  884  else if (_stricmp(name, 
"WWW-Authenticate") == 0)
 
  886    char* separator = NULL;
 
  887    const char* authScheme = NULL;
 
  888    char* authValue = NULL;
 
  889    separator = strchr(value, 
' ');
 
  901      authValue = separator + 1;
 
  903      if (!authScheme || !authValue)
 
  916    status = ListDictionary_Add(response->Authenticates, authScheme, authValue);
 
  918  else if (_stricmp(name, 
"Set-Cookie") == 0)
 
  920    char* separator = NULL;
 
  921    const char* CookieName = NULL;
 
  922    char* CookieValue = NULL;
 
  923    separator = strchr(value, 
'=');
 
  933      CookieValue = separator + 1;
 
  935      if (!CookieName || !CookieValue)
 
  938      if (*CookieValue == 
'"')
 
  940        char* p = CookieValue;
 
  941        while (*p != 
'"' && *p != 
'\0')
 
  951        char* p = CookieValue;
 
  952        while (*p != 
';' && *p != 
'\0' && *p != 
' ')
 
  964    status = ListDictionary_Add(response->SetCookie, CookieName, CookieValue);
 
  970static BOOL http_response_parse_header(HttpResponse* response)
 
  976  char* colon_pos = NULL;
 
  977  char* end_of_header = NULL;
 
  978  char end_of_header_char = 0;
 
  983  if (!response->lines)
 
  986  if (!http_response_parse_header_status_line(response, response->lines[0]))
 
  989  for (
size_t count = 1; count < response->count; count++)
 
  991    line = response->lines[count];
 
 1003      colon_pos = strchr(line, 
':');
 
 1007    if ((colon_pos == NULL) || (colon_pos == line))
 
 1011    for (end_of_header = colon_pos; end_of_header != line; end_of_header--)
 
 1013      c = end_of_header[-1];
 
 1015      if (c != 
' ' && c != 
'\t' && c != 
':')
 
 1019    if (end_of_header == line)
 
 1022    end_of_header_char = *end_of_header;
 
 1023    *end_of_header = 
'\0';
 
 1027    char* value = colon_pos + 1;
 
 1028    for (; *value; value++)
 
 1030      if ((*value != 
' ') && (*value != 
'\t'))
 
 1034    const int res = http_response_parse_header_field(response, name, value);
 
 1035    *end_of_header = end_of_header_char;
 
 1044    WLog_ERR(TAG, 
"parsing failed");
 
 1049static void http_response_print(wLog* log, DWORD level, 
const HttpResponse* response,
 
 1050                                const char* file, 
size_t line, 
const char* fkt)
 
 1052  char buffer[64] = { 0 };
 
 1055  WINPR_ASSERT(response);
 
 1057  if (!WLog_IsLevelActive(log, level))
 
 1060  const long status = http_response_get_status_code(response);
 
 1061  WLog_PrintTextMessage(log, level, line, file, fkt, 
"HTTP status: %s",
 
 1062                        freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
 
 1064  if (WLog_IsLevelActive(log, WLOG_DEBUG))
 
 1066    for (
size_t i = 0; i < response->count; i++)
 
 1067      WLog_PrintTextMessage(log, WLOG_DEBUG, line, file, fkt, 
"[%" PRIuz 
"] %s", i,
 
 1068                            response->lines[i]);
 
 1071  if (response->ReasonPhrase)
 
 1072    WLog_PrintTextMessage(log, level, line, file, fkt, 
"[reason] %s", response->ReasonPhrase);
 
 1074  if (WLog_IsLevelActive(log, WLOG_TRACE))
 
 1076    WLog_PrintTextMessage(log, WLOG_TRACE, line, file, fkt, 
"[body][%" PRIuz 
"] %s",
 
 1077                          response->BodyLength, response->BodyContent);
 
 1081static BOOL http_use_content_length(
const char* cur)
 
 1088  if (_strnicmp(cur, 
"application/rpc", 15) == 0)
 
 1090  else if (_strnicmp(cur, 
"text/plain", 10) == 0)
 
 1092  else if (_strnicmp(cur, 
"text/html", 9) == 0)
 
 1094  else if (_strnicmp(cur, 
"application/json", 16) == 0)
 
 1099    char end = cur[pos];
 
 1118static int print_bio_error(
const char* str, 
size_t len, 
void* bp)
 
 1123  WLog_Print(log, WLOG_ERROR, 
"%s", str);
 
 1124  if (len > INT32_MAX)
 
 1129int http_chuncked_read(BIO* bio, BYTE* pBuffer, 
size_t size,
 
 1133  int effectiveDataLen = 0;
 
 1135  WINPR_ASSERT(pBuffer);
 
 1136  WINPR_ASSERT(encodingContext != NULL);
 
 1137  WINPR_ASSERT(size <= INT32_MAX);
 
 1140    switch (encodingContext->state)
 
 1142      case ChunkStateData:
 
 1145            (size > encodingContext->nextOffset ? encodingContext->nextOffset : size);
 
 1150        status = BIO_read(bio, pBuffer, (
int)rd);
 
 1152          return (effectiveDataLen > 0 ? effectiveDataLen : status);
 
 1154        encodingContext->nextOffset -= WINPR_ASSERTING_INT_CAST(uint32_t, status);
 
 1155        if (encodingContext->nextOffset == 0)
 
 1157          encodingContext->state = ChunkStateFooter;
 
 1158          encodingContext->headerFooterPos = 0;
 
 1160        effectiveDataLen += status;
 
 1162        if ((
size_t)status == size)
 
 1163          return effectiveDataLen;
 
 1166        size -= (size_t)status;
 
 1169      case ChunkStateFooter:
 
 1171        char _dummy[2] = { 0 };
 
 1172        WINPR_ASSERT(encodingContext->nextOffset == 0);
 
 1173        WINPR_ASSERT(encodingContext->headerFooterPos < 2);
 
 1175        status = BIO_read(bio, _dummy, (
int)(2 - encodingContext->headerFooterPos));
 
 1178          encodingContext->headerFooterPos += (size_t)status;
 
 1179          if (encodingContext->headerFooterPos == 2)
 
 1181            encodingContext->state = ChunkStateLenghHeader;
 
 1182            encodingContext->headerFooterPos = 0;
 
 1186          return (effectiveDataLen > 0 ? effectiveDataLen : status);
 
 1189      case ChunkStateLenghHeader:
 
 1191        BOOL _haveNewLine = FALSE;
 
 1192        char* dst = &encodingContext->lenBuffer[encodingContext->headerFooterPos];
 
 1193        WINPR_ASSERT(encodingContext->nextOffset == 0);
 
 1194        while (encodingContext->headerFooterPos < 10 && !_haveNewLine)
 
 1197          status = BIO_read(bio, dst, 1);
 
 1201              _haveNewLine = TRUE;
 
 1202            encodingContext->headerFooterPos += (size_t)status;
 
 1206            return (effectiveDataLen > 0 ? effectiveDataLen : status);
 
 1212        size_t tmp = strtoul(encodingContext->lenBuffer, NULL, 16);
 
 1213        if ((errno != 0) || (tmp > SIZE_MAX))
 
 1216          encodingContext->nextOffset = 0;
 
 1217          encodingContext->state = ChunkStateEnd;
 
 1220        encodingContext->nextOffset = tmp;
 
 1221        encodingContext->state = ChunkStateData;
 
 1223        if (encodingContext->nextOffset == 0)
 
 1225          WLog_DBG(TAG, 
"chunked encoding end of stream received");
 
 1226          encodingContext->headerFooterPos = 0;
 
 1227          encodingContext->state = ChunkStateEnd;
 
 1228          return (effectiveDataLen > 0 ? effectiveDataLen : 0);
 
 1239#define sleep_or_timeout(tls, startMS, timeoutMS) \ 
 1240  sleep_or_timeout_((tls), (startMS), (timeoutMS), __FILE__, __func__, __LINE__) 
 1241static BOOL sleep_or_timeout_(rdpTls* tls, UINT64 startMS, UINT32 timeoutMS, 
const char* file,
 
 1242                              const char* fkt, 
size_t line)
 
 1247  const UINT64 nowMS = GetTickCount64();
 
 1248  if (nowMS - startMS > timeoutMS)
 
 1250    DWORD level = WLOG_ERROR;
 
 1251    wLog* log = WLog_Get(TAG);
 
 1252    if (WLog_IsLevelActive(log, level))
 
 1253      WLog_PrintTextMessage(log, level, line, file, fkt, 
"timeout [%" PRIu32 
"ms] exceeded",
 
 1257  if (!BIO_should_retry(tls->bio))
 
 1259    DWORD level = WLOG_ERROR;
 
 1260    wLog* log = WLog_Get(TAG);
 
 1261    if (WLog_IsLevelActive(log, level))
 
 1263      WLog_PrintTextMessage(log, level, line, file, fkt, 
"Retries exceeded");
 
 1264      ERR_print_errors_cb(print_bio_error, log);
 
 1268  if (freerdp_shall_disconnect_context(tls->context))
 
 1274static SSIZE_T http_response_recv_line(rdpTls* tls, HttpResponse* response)
 
 1277  WINPR_ASSERT(response);
 
 1279  SSIZE_T payloadOffset = -1;
 
 1280  const UINT32 timeoutMS =
 
 1282  const UINT64 startMS = GetTickCount64();
 
 1283  while (payloadOffset <= 0)
 
 1285    size_t bodyLength = 0;
 
 1286    size_t position = 0;
 
 1293    status = BIO_read(tls->bio, Stream_Pointer(response->data), 1);
 
 1296      if (sleep_or_timeout(tls, startMS, timeoutMS))
 
 1301#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H 
 1302    VALGRIND_MAKE_MEM_DEFINED(Stream_Pointer(response->data), status);
 
 1304    Stream_Seek(response->data, (
size_t)status);
 
 1306    if (!Stream_EnsureRemainingCapacity(response->data, 1024))
 
 1309    position = Stream_GetPosition(response->data);
 
 1313    else if (position > RESPONSE_SIZE_LIMIT)
 
 1315      WLog_ERR(TAG, 
"Request header too large! (%" PRIdz 
" bytes) Aborting!", bodyLength);
 
 1321    s = (position > 8) ? 8 : position;
 
 1322    end = (
char*)Stream_Pointer(response->data) - s;
 
 1324    if (string_strnstr(end, 
"\r\n\r\n", s) != NULL)
 
 1325      payloadOffset = WINPR_ASSERTING_INT_CAST(SSIZE_T, Stream_GetPosition(response->data));
 
 1329  return payloadOffset;
 
 1332static BOOL http_response_recv_body(rdpTls* tls, HttpResponse* response, BOOL readContentLength,
 
 1333                                    size_t payloadOffset, 
size_t bodyLength)
 
 1338  WINPR_ASSERT(response);
 
 1340  const UINT64 startMS = GetTickCount64();
 
 1341  const UINT32 timeoutMS =
 
 1344  if ((response->TransferEncoding == TransferEncodingChunked) && readContentLength)
 
 1347    ctx.state = ChunkStateLenghHeader;
 
 1349    ctx.headerFooterPos = 0;
 
 1353      if (!Stream_EnsureRemainingCapacity(response->data, 2048))
 
 1356      int status = http_chuncked_read(tls->bio, Stream_Pointer(response->data),
 
 1357                                      Stream_GetRemainingCapacity(response->data), &ctx);
 
 1360        if (sleep_or_timeout(tls, startMS, timeoutMS))
 
 1365        Stream_Seek(response->data, (
size_t)status);
 
 1368    } 
while (ctx.state != ChunkStateEnd);
 
 1369    response->BodyLength = WINPR_ASSERTING_INT_CAST(uint32_t, full_len);
 
 1370    if (response->BodyLength > 0)
 
 1371      response->BodyContent = &(Stream_Buffer(response->data))[payloadOffset];
 
 1375    while (response->BodyLength < bodyLength)
 
 1379      if (!Stream_EnsureRemainingCapacity(response->data, bodyLength - response->BodyLength))
 
 1383      size_t diff = bodyLength - response->BodyLength;
 
 1384      if (diff > INT32_MAX)
 
 1386      status = BIO_read(tls->bio, Stream_Pointer(response->data), (
int)diff);
 
 1390        if (sleep_or_timeout(tls, startMS, timeoutMS))
 
 1395      Stream_Seek(response->data, (
size_t)status);
 
 1396      response->BodyLength += (
unsigned long)status;
 
 1398      if (response->BodyLength > RESPONSE_SIZE_LIMIT)
 
 1400        WLog_ERR(TAG, 
"Request body too large! (%" PRIdz 
" bytes) Aborting!",
 
 1401                 response->BodyLength);
 
 1406    if (response->BodyLength > 0)
 
 1407      response->BodyContent = &(Stream_Buffer(response->data))[payloadOffset];
 
 1409    if (bodyLength != response->BodyLength)
 
 1411      WLog_WARN(TAG, 
"%s unexpected body length: actual: %" PRIuz 
", expected: %" PRIuz,
 
 1412                response->ContentType, response->BodyLength, bodyLength);
 
 1415        response->BodyLength = MIN(bodyLength, response->BodyLength);
 
 1419    if (!Stream_EnsureRemainingCapacity(response->data, 
sizeof(UINT16)))
 
 1421    Stream_Write_UINT16(response->data, 0);
 
 1429HttpResponse* http_response_recv(rdpTls* tls, BOOL readContentLength)
 
 1431  size_t bodyLength = 0;
 
 1432  HttpResponse* response = http_response_new();
 
 1437  response->ContentLength = 0;
 
 1439  const SSIZE_T payloadOffset = http_response_recv_line(tls, response);
 
 1440  if (payloadOffset < 0)
 
 1446    char* buffer = Stream_BufferAs(response->data, 
char);
 
 1447    char* line = Stream_BufferAs(response->data, 
char);
 
 1448    char* context = NULL;
 
 1450    while ((line = string_strnstr(line, 
"\r\n",
 
 1451                                  WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset) -
 
 1452                                      WINPR_ASSERTING_INT_CAST(
size_t, (line - buffer)) - 2UL)))
 
 1458    response->count = count;
 
 1462      response->lines = (
char**)calloc(response->count, 
sizeof(
char*));
 
 1464      if (!response->lines)
 
 1468    buffer[payloadOffset - 1] = 
'\0';
 
 1469    buffer[payloadOffset - 2] = 
'\0';
 
 1471    line = strtok_s(buffer, 
"\r\n", &context);
 
 1473    while (line && (response->count > count))
 
 1475      response->lines[count] = line;
 
 1476      line = strtok_s(NULL, 
"\r\n", &context);
 
 1480    if (!http_response_parse_header(response))
 
 1483    response->BodyLength =
 
 1484        Stream_GetPosition(response->data) - WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset);
 
 1486    WINPR_ASSERT(response->BodyLength == 0);
 
 1487    bodyLength = response->BodyLength; 
 
 1489    if (readContentLength && (response->BodyLength > 0))
 
 1491      const char* cur = response->ContentType;
 
 1495        if (http_use_content_length(cur))
 
 1497          if (response->ContentLength < RESPONSE_SIZE_LIMIT)
 
 1498            bodyLength = response->ContentLength;
 
 1503          readContentLength = FALSE; 
 
 1505        cur = strchr(cur, 
';');
 
 1509    if (bodyLength > RESPONSE_SIZE_LIMIT)
 
 1511      WLog_ERR(TAG, 
"Expected request body too large! (%" PRIdz 
" bytes) Aborting!",
 
 1517    if (!http_response_recv_body(tls, response, readContentLength,
 
 1518                                 WINPR_ASSERTING_INT_CAST(
size_t, payloadOffset), bodyLength))
 
 1521  Stream_SealLength(response->data);
 
 1524  if (!Stream_EnsureRemainingCapacity(response->data, 2))
 
 1526  Stream_Write_UINT16(response->data, 0);
 
 1530  http_response_free(response);
 
 1534const BYTE* http_response_get_body(
const HttpResponse* response)
 
 1539  return response->BodyContent;
 
 1542static BOOL set_compare(wListDictionary* dict)
 
 1545  wObject* key = ListDictionary_KeyObject(dict);
 
 1546  wObject* value = ListDictionary_KeyObject(dict);
 
 1549  key->fnObjectEquals = strings_equals_nocase;
 
 1550  value->fnObjectEquals = strings_equals_nocase;
 
 1554HttpResponse* http_response_new(
void)
 
 1556  HttpResponse* response = (HttpResponse*)calloc(1, 
sizeof(HttpResponse));
 
 1561  response->Authenticates = ListDictionary_New(FALSE);
 
 1563  if (!response->Authenticates)
 
 1566  if (!set_compare(response->Authenticates))
 
 1569  response->SetCookie = ListDictionary_New(FALSE);
 
 1571  if (!response->SetCookie)
 
 1574  if (!set_compare(response->SetCookie))
 
 1577  response->data = Stream_New(NULL, 2048);
 
 1579  if (!response->data)
 
 1582  response->TransferEncoding = TransferEncodingIdentity;
 
 1585  WINPR_PRAGMA_DIAG_PUSH
 
 1586  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
 
 1587  http_response_free(response);
 
 1588  WINPR_PRAGMA_DIAG_POP
 
 1592void http_response_free(HttpResponse* response)
 
 1597  free((
void*)response->lines);
 
 1598  ListDictionary_Free(response->Authenticates);
 
 1599  ListDictionary_Free(response->SetCookie);
 
 1600  Stream_Free(response->data, TRUE);
 
 1604const char* http_request_get_uri(HttpRequest* request)
 
 1609  return request->URI;
 
 1612SSIZE_T http_request_get_content_length(HttpRequest* request)
 
 1617  return (SSIZE_T)request->ContentLength;
 
 1620BOOL http_request_set_content_length(HttpRequest* request, 
size_t length)
 
 1625  request->ContentLength = length;
 
 1629UINT16 http_response_get_status_code(
const HttpResponse* response)
 
 1631  WINPR_ASSERT(response);
 
 1633  return response->StatusCode;
 
 1636size_t http_response_get_body_length(
const HttpResponse* response)
 
 1638  WINPR_ASSERT(response);
 
 1640  return response->BodyLength;
 
 1643const char* http_response_get_auth_token(
const HttpResponse* response, 
const char* method)
 
 1645  if (!response || !method)
 
 1648  if (!ListDictionary_Contains(response->Authenticates, method))
 
 1651  return ListDictionary_GetItemValue(response->Authenticates, method);
 
 1654const char* http_response_get_setcookie(
const HttpResponse* response, 
const char* cookie)
 
 1656  if (!response || !cookie)
 
 1659  if (!ListDictionary_Contains(response->SetCookie, cookie))
 
 1662  return ListDictionary_GetItemValue(response->SetCookie, cookie);
 
 1665TRANSFER_ENCODING http_response_get_transfer_encoding(
const HttpResponse* response)
 
 1668    return TransferEncodingUnknown;
 
 1670  return response->TransferEncoding;
 
 1673BOOL http_response_is_websocket(
const HttpContext* http, 
const HttpResponse* response)
 
 1675  BOOL isWebsocket = FALSE;
 
 1676  WINPR_DIGEST_CTX* sha1 = NULL;
 
 1677  char* base64accept = NULL;
 
 1678  BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH];
 
 1680  if (!http || !response)
 
 1683  if (!http->websocketUpgrade || response->StatusCode != HTTP_STATUS_SWITCH_PROTOCOLS)
 
 1686  if (response->SecWebsocketVersion && _stricmp(response->SecWebsocketVersion, 
"13") != 0)
 
 1689  if (!response->SecWebsocketAccept)
 
 1694  sha1 = winpr_Digest_New();
 
 1698  if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1))
 
 1701  if (!winpr_Digest_Update(sha1, (BYTE*)http->SecWebsocketKey, strlen(http->SecWebsocketKey)))
 
 1703  if (!winpr_Digest_Update(sha1, (
const BYTE*)WEBSOCKET_MAGIC_GUID, strlen(WEBSOCKET_MAGIC_GUID)))
 
 1706  if (!winpr_Digest_Final(sha1, sha1_digest, 
sizeof(sha1_digest)))
 
 1709  base64accept = crypto_base64_encode(sha1_digest, WINPR_SHA1_DIGEST_LENGTH);
 
 1713  if (_stricmp(response->SecWebsocketAccept, base64accept) != 0)
 
 1715    WLog_WARN(TAG, 
"Webserver gave Websocket Upgrade response but sanity check failed");
 
 1720  winpr_Digest_Free(sha1);
 
 1725void http_response_log_error_status_(wLog* log, DWORD level, 
const HttpResponse* response,
 
 1726                                     const char* file, 
size_t line, 
const char* fkt)
 
 1729  WINPR_ASSERT(response);
 
 1731  if (!WLog_IsLevelActive(log, level))
 
 1734  char buffer[64] = { 0 };
 
 1735  const UINT16 status = http_response_get_status_code(response);
 
 1736  WLog_PrintTextMessage(log, level, line, file, fkt, 
"Unexpected HTTP status: %s",
 
 1737                        freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
 
 1738  http_response_print(log, level, response, file, line, fkt);
 
 1741WINPR_ATTR_FORMAT_ARG(3, 4)
 
 1742BOOL http_request_append_header(
wStream* stream, const 
char* param,
 
 1743                                WINPR_FORMAT_ARG const 
char* value, ...)
 
 1746  va_start(ap, value);
 
 1749  winpr_vasprintf(&str, &slen, value, ap);
 
 1751  const BOOL rc = http_encode_body_line(stream, param, str);
 
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
 
This struct contains function pointer to initialize/free objects.