FreeRDP
Loading...
Searching...
No Matches
core/gateway/http.c
1
20#include <freerdp/config.h>
21
22#include <errno.h>
23#include <stdint.h>
24
25#include <winpr/crt.h>
26#include <winpr/print.h>
27#include <winpr/stream.h>
28#include <winpr/string.h>
29#include <winpr/rpc.h>
30#include <winpr/sysinfo.h>
31
32#include <freerdp/log.h>
33#include <freerdp/crypto/crypto.h>
34
35/* websocket need sha1 for Sec-Websocket-Accept */
36#include <winpr/crypto.h>
37
38#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H
39#include <valgrind/memcheck.h>
40#endif
41
42#include "http.h"
43#include "../tcp.h"
44#include "../utils.h"
45
46#define TAG FREERDP_TAG("core.gateway.http")
47
48#define RESPONSE_SIZE_LIMIT (64ULL * 1024ULL * 1024ULL)
49
50#define WEBSOCKET_MAGIC_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
51
52struct s_http_context
53{
54 char* Method;
55 char* URI;
56 char* Connection;
57 char* Pragma;
58 BOOL websocketUpgrade;
59 char* SecWebsocketKey;
60 wListDictionary* cookies;
61 wHashTable* headers;
62};
63
64struct s_http_request
65{
66 char* Method;
67 char* URI;
68 char* AuthScheme;
69 char* AuthParam;
70 char* Authorization;
71 size_t ContentLength;
72 TRANSFER_ENCODING TransferEncoding;
73 wHashTable* headers;
74};
75
76struct s_http_response
77{
78 size_t count;
79 char** lines;
80
81 UINT16 StatusCode;
82 char* ReasonPhrase;
83
84 size_t ContentLength;
85 char* ContentType;
86 TRANSFER_ENCODING TransferEncoding;
87 char* SecWebsocketVersion;
88 char* SecWebsocketAccept;
89
90 size_t BodyLength;
91 char* BodyContent;
92
93 wHashTable* Authenticates;
94 wHashTable* SetCookie;
95 wStream* data;
96};
97
98static wHashTable* HashTable_New_String(void);
99
100static const char* string_strnstr(const char* str1, const char* str2, size_t slen)
101{
102 char c = 0;
103 char sc = 0;
104 size_t len = 0;
105
106 if ((c = *str2++) != '\0')
107 {
108 len = strnlen(str2, slen + 1);
109
110 do
111 {
112 do
113 {
114 if (slen-- < 1 || (sc = *str1++) == '\0')
115 return NULL;
116 } while (sc != c);
117
118 if (len > slen)
119 return NULL;
120 } while (strncmp(str1, str2, len) != 0);
121
122 str1--;
123 }
124
125 return str1;
126}
127
128static BOOL strings_equals_nocase(const void* obj1, const void* obj2)
129{
130 if (!obj1 || !obj2)
131 return FALSE;
132
133 return _stricmp(obj1, obj2) == 0;
134}
135
136HttpContext* http_context_new(void)
137{
138 HttpContext* context = (HttpContext*)calloc(1, sizeof(HttpContext));
139 if (!context)
140 return NULL;
141
142 context->headers = HashTable_New_String();
143 if (!context->headers)
144 goto fail;
145
146 context->cookies = ListDictionary_New(FALSE);
147 if (!context->cookies)
148 goto fail;
149
150 wObject* key = ListDictionary_KeyObject(context->cookies);
151 wObject* value = ListDictionary_ValueObject(context->cookies);
152 if (!key || !value)
153 goto fail;
154
155 key->fnObjectFree = winpr_ObjectStringFree;
156 key->fnObjectNew = winpr_ObjectStringClone;
157 value->fnObjectFree = winpr_ObjectStringFree;
158 value->fnObjectNew = winpr_ObjectStringClone;
159
160 return context;
161
162fail:
163 WINPR_PRAGMA_DIAG_PUSH
164 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
165 http_context_free(context);
166 WINPR_PRAGMA_DIAG_POP
167 return NULL;
168}
169
170BOOL http_context_set_method(HttpContext* context, const char* Method)
171{
172 if (!context || !Method)
173 return FALSE;
174
175 free(context->Method);
176 context->Method = _strdup(Method);
177
178 if (!context->Method)
179 return FALSE;
180
181 return TRUE;
182}
183
184BOOL http_request_set_content_type(HttpRequest* request, const char* ContentType)
185{
186 if (!request || !ContentType)
187 return FALSE;
188
189 return http_request_set_header(request, "Content-Type", "%s", ContentType);
190}
191
192const char* http_context_get_uri(HttpContext* context)
193{
194 if (!context)
195 return NULL;
196
197 return context->URI;
198}
199
200BOOL http_context_set_uri(HttpContext* context, const char* URI)
201{
202 if (!context || !URI)
203 return FALSE;
204
205 free(context->URI);
206 context->URI = _strdup(URI);
207
208 if (!context->URI)
209 return FALSE;
210
211 return TRUE;
212}
213
214BOOL http_context_set_user_agent(HttpContext* context, const char* UserAgent)
215{
216 if (!context || !UserAgent)
217 return FALSE;
218
219 return http_context_set_header(context, "User-Agent", "%s", UserAgent);
220}
221
222BOOL http_context_set_x_ms_user_agent(HttpContext* context, const char* X_MS_UserAgent)
223{
224 if (!context || !X_MS_UserAgent)
225 return FALSE;
226
227 return http_context_set_header(context, "X-MS-User-Agent", "%s", X_MS_UserAgent);
228}
229
230BOOL http_context_set_host(HttpContext* context, const char* Host)
231{
232 if (!context || !Host)
233 return FALSE;
234
235 return http_context_set_header(context, "Host", "%s", Host);
236}
237
238BOOL http_context_set_accept(HttpContext* context, const char* Accept)
239{
240 if (!context || !Accept)
241 return FALSE;
242
243 return http_context_set_header(context, "Accept", "%s", Accept);
244}
245
246BOOL http_context_set_cache_control(HttpContext* context, const char* CacheControl)
247{
248 if (!context || !CacheControl)
249 return FALSE;
250
251 return http_context_set_header(context, "Cache-Control", "%s", CacheControl);
252}
253
254BOOL http_context_set_connection(HttpContext* context, const char* Connection)
255{
256 if (!context || !Connection)
257 return FALSE;
258
259 free(context->Connection);
260 context->Connection = _strdup(Connection);
261
262 if (!context->Connection)
263 return FALSE;
264
265 return TRUE;
266}
267
268WINPR_ATTR_FORMAT_ARG(2, 0)
269static BOOL list_append(HttpContext* context, WINPR_FORMAT_ARG const char* str, va_list ap)
270{
271 BOOL rc = FALSE;
272 va_list vat;
273 char* Pragma = NULL;
274 size_t PragmaSize = 0;
275
276 va_copy(vat, ap);
277 const int size = winpr_vasprintf(&Pragma, &PragmaSize, str, ap);
278 va_end(vat);
279
280 if (size <= 0)
281 goto fail;
282
283 char* sstr = NULL;
284 size_t slen = 0;
285 if (context->Pragma)
286 {
287 winpr_asprintf(&sstr, &slen, "%s, %s", context->Pragma, Pragma);
288 free(Pragma);
289 }
290 else
291 sstr = Pragma;
292 Pragma = NULL;
293
294 free(context->Pragma);
295 context->Pragma = sstr;
296
297 rc = TRUE;
298
299fail:
300 va_end(ap);
301 return rc;
302}
303
304WINPR_ATTR_FORMAT_ARG(2, 3)
305BOOL http_context_set_pragma(HttpContext* context, WINPR_FORMAT_ARG const char* Pragma, ...)
306{
307 if (!context || !Pragma)
308 return FALSE;
309
310 free(context->Pragma);
311 context->Pragma = NULL;
312
313 va_list ap = { 0 };
314 va_start(ap, Pragma);
315 return list_append(context, Pragma, ap);
316}
317
318WINPR_ATTR_FORMAT_ARG(2, 3)
319BOOL http_context_append_pragma(HttpContext* context, const char* Pragma, ...)
320{
321 if (!context || !Pragma)
322 return FALSE;
323
324 va_list ap = { 0 };
325 va_start(ap, Pragma);
326 return list_append(context, Pragma, ap);
327}
328
329static char* guid2str(const GUID* guid, char* buffer, size_t len)
330{
331 if (!guid)
332 return NULL;
333 RPC_CSTR strguid = NULL;
334
335 RPC_STATUS rpcStatus = UuidToStringA(guid, &strguid);
336
337 if (rpcStatus != RPC_S_OK)
338 return NULL;
339
340 (void)sprintf_s(buffer, len, "{%s}", strguid);
341 RpcStringFreeA(&strguid);
342 return buffer;
343}
344
345BOOL http_context_set_rdg_connection_id(HttpContext* context, const GUID* RdgConnectionId)
346{
347 if (!context || !RdgConnectionId)
348 return FALSE;
349
350 char buffer[64] = { 0 };
351 return http_context_set_header(context, "RDG-Connection-Id", "%s",
352 guid2str(RdgConnectionId, buffer, sizeof(buffer)));
353}
354
355BOOL http_context_set_rdg_correlation_id(HttpContext* context, const GUID* RdgCorrelationId)
356{
357 if (!context || !RdgCorrelationId)
358 return FALSE;
359
360 char buffer[64] = { 0 };
361 return http_context_set_header(context, "RDG-Correlation-Id", "%s",
362 guid2str(RdgCorrelationId, buffer, sizeof(buffer)));
363}
364
365BOOL http_context_enable_websocket_upgrade(HttpContext* context, BOOL enable)
366{
367 if (!context)
368 return FALSE;
369
370 if (enable)
371 {
372 GUID key = { 0 };
373 if (RPC_S_OK != UuidCreate(&key))
374 return FALSE;
375
376 free(context->SecWebsocketKey);
377 context->SecWebsocketKey = crypto_base64_encode((BYTE*)&key, sizeof(key));
378 if (!context->SecWebsocketKey)
379 return FALSE;
380 }
381
382 context->websocketUpgrade = enable;
383 return TRUE;
384}
385
386BOOL http_context_is_websocket_upgrade_enabled(HttpContext* context)
387{
388 return context->websocketUpgrade;
389}
390
391BOOL http_context_set_rdg_auth_scheme(HttpContext* context, const char* RdgAuthScheme)
392{
393 if (!context || !RdgAuthScheme)
394 return FALSE;
395
396 return http_context_set_header(context, "RDG-Auth-Scheme", "%s", RdgAuthScheme);
397}
398
399BOOL http_context_set_cookie(HttpContext* context, const char* CookieName, const char* CookieValue)
400{
401 if (!context || !CookieName || !CookieValue)
402 return FALSE;
403 if (ListDictionary_Contains(context->cookies, CookieName))
404 {
405 if (!ListDictionary_SetItemValue(context->cookies, CookieName, CookieValue))
406 return FALSE;
407 }
408 else
409 {
410 if (!ListDictionary_Add(context->cookies, CookieName, CookieValue))
411 return FALSE;
412 }
413 return TRUE;
414}
415
416void http_context_free(HttpContext* context)
417{
418 if (context)
419 {
420 free(context->SecWebsocketKey);
421 free(context->URI);
422 free(context->Method);
423 free(context->Connection);
424 free(context->Pragma);
425 HashTable_Free(context->headers);
426 ListDictionary_Free(context->cookies);
427 free(context);
428 }
429}
430
431BOOL http_request_set_method(HttpRequest* request, const char* Method)
432{
433 if (!request || !Method)
434 return FALSE;
435
436 free(request->Method);
437 request->Method = _strdup(Method);
438
439 if (!request->Method)
440 return FALSE;
441
442 return TRUE;
443}
444
445BOOL http_request_set_uri(HttpRequest* request, const char* URI)
446{
447 if (!request || !URI)
448 return FALSE;
449
450 free(request->URI);
451 request->URI = _strdup(URI);
452
453 if (!request->URI)
454 return FALSE;
455
456 return TRUE;
457}
458
459BOOL http_request_set_auth_scheme(HttpRequest* request, const char* AuthScheme)
460{
461 if (!request || !AuthScheme)
462 return FALSE;
463
464 free(request->AuthScheme);
465 request->AuthScheme = _strdup(AuthScheme);
466
467 if (!request->AuthScheme)
468 return FALSE;
469
470 return TRUE;
471}
472
473BOOL http_request_set_auth_param(HttpRequest* request, const char* AuthParam)
474{
475 if (!request || !AuthParam)
476 return FALSE;
477
478 free(request->AuthParam);
479 request->AuthParam = _strdup(AuthParam);
480
481 if (!request->AuthParam)
482 return FALSE;
483
484 return TRUE;
485}
486
487BOOL http_request_set_transfer_encoding(HttpRequest* request, TRANSFER_ENCODING TransferEncoding)
488{
489 if (!request || TransferEncoding == TransferEncodingUnknown)
490 return FALSE;
491
492 request->TransferEncoding = TransferEncoding;
493
494 return TRUE;
495}
496
497WINPR_ATTR_FORMAT_ARG(2, 3)
498static BOOL http_encode_print(wStream* s, WINPR_FORMAT_ARG const char* fmt, ...)
499{
500 char* str = NULL;
501 va_list ap = { 0 };
502 int length = 0;
503 int used = 0;
504
505 if (!s || !fmt)
506 return FALSE;
507
508 va_start(ap, fmt);
509 length = vsnprintf(NULL, 0, fmt, ap) + 1;
510 va_end(ap);
511
512 if (!Stream_EnsureRemainingCapacity(s, (size_t)length))
513 return FALSE;
514
515 str = (char*)Stream_Pointer(s);
516 va_start(ap, fmt);
517 used = vsnprintf(str, (size_t)length, fmt, ap);
518 va_end(ap);
519
520 /* Strip the trailing '\0' from the string. */
521 if ((used + 1) != length)
522 return FALSE;
523
524 Stream_Seek(s, (size_t)used);
525 return TRUE;
526}
527
528static BOOL http_encode_body_line(wStream* s, const char* param, const char* value)
529{
530 if (!s || !param || !value)
531 return FALSE;
532
533 return http_encode_print(s, "%s: %s\r\n", param, value);
534}
535
536static BOOL http_encode_content_length_line(wStream* s, size_t ContentLength)
537{
538 return http_encode_print(s, "Content-Length: %" PRIuz "\r\n", ContentLength);
539}
540
541static BOOL http_encode_header_line(wStream* s, const char* Method, const char* URI)
542{
543 if (!s || !Method || !URI)
544 return FALSE;
545
546 return http_encode_print(s, "%s %s HTTP/1.1\r\n", Method, URI);
547}
548
549static BOOL http_encode_authorization_line(wStream* s, const char* AuthScheme,
550 const char* AuthParam)
551{
552 if (!s || !AuthScheme || !AuthParam)
553 return FALSE;
554
555 return http_encode_print(s, "Authorization: %s %s\r\n", AuthScheme, AuthParam);
556}
557
558static BOOL http_encode_cookie_line(wStream* s, wListDictionary* cookies)
559{
560 ULONG_PTR* keys = NULL;
561 BOOL status = TRUE;
562
563 if (!s && !cookies)
564 return FALSE;
565
566 ListDictionary_Lock(cookies);
567 const size_t count = ListDictionary_GetKeys(cookies, &keys);
568
569 if (count == 0)
570 goto unlock;
571
572 status = http_encode_print(s, "Cookie: ");
573 if (!status)
574 goto unlock;
575
576 for (size_t x = 0; status && x < count; x++)
577 {
578 char* cur = (char*)ListDictionary_GetItemValue(cookies, (void*)keys[x]);
579 if (!cur)
580 {
581 status = FALSE;
582 continue;
583 }
584 if (x > 0)
585 {
586 status = http_encode_print(s, "; ");
587 if (!status)
588 continue;
589 }
590 status = http_encode_print(s, "%s=%s", (char*)keys[x], cur);
591 }
592
593 status = http_encode_print(s, "\r\n");
594unlock:
595 free(keys);
596 ListDictionary_Unlock(cookies);
597 return status;
598}
599
600static BOOL write_headers(const void* pkey, void* pvalue, void* arg)
601{
602 const char* key = pkey;
603 const char* value = pvalue;
604 wStream* s = arg;
605
606 WINPR_ASSERT(key);
607 WINPR_ASSERT(value);
608 WINPR_ASSERT(s);
609
610 return http_encode_body_line(s, key, value);
611}
612
613wStream* http_request_write(HttpContext* context, HttpRequest* request)
614{
615 wStream* s = NULL;
616
617 if (!context || !request)
618 return NULL;
619
620 s = Stream_New(NULL, 1024);
621
622 if (!s)
623 return NULL;
624
625 if (!http_encode_header_line(s, request->Method, request->URI) ||
626
627 !http_encode_body_line(s, "Pragma", context->Pragma))
628 goto fail;
629
630 if (!context->websocketUpgrade)
631 {
632 if (!http_encode_body_line(s, "Connection", context->Connection))
633 goto fail;
634 }
635 else
636 {
637 if (!http_encode_body_line(s, "Connection", "Upgrade") ||
638 !http_encode_body_line(s, "Upgrade", "websocket") ||
639 !http_encode_body_line(s, "Sec-Websocket-Version", "13") ||
640 !http_encode_body_line(s, "Sec-Websocket-Key", context->SecWebsocketKey))
641 goto fail;
642 }
643
644 if (request->TransferEncoding != TransferEncodingIdentity)
645 {
646 if (request->TransferEncoding == TransferEncodingChunked)
647 {
648 if (!http_encode_body_line(s, "Transfer-Encoding", "chunked"))
649 goto fail;
650 }
651 else
652 goto fail;
653 }
654 else
655 {
656 if (!http_encode_content_length_line(s, request->ContentLength))
657 goto fail;
658 }
659
660 if (!utils_str_is_empty(request->Authorization))
661 {
662 if (!http_encode_body_line(s, "Authorization", request->Authorization))
663 goto fail;
664 }
665 else if (!utils_str_is_empty(request->AuthScheme) && !utils_str_is_empty(request->AuthParam))
666 {
667 if (!http_encode_authorization_line(s, request->AuthScheme, request->AuthParam))
668 goto fail;
669 }
670
671 if (!HashTable_Foreach(context->headers, write_headers, s))
672 goto fail;
673
674 if (!HashTable_Foreach(request->headers, write_headers, s))
675 goto fail;
676
677 if (!http_encode_cookie_line(s, context->cookies))
678 goto fail;
679
680 if (!http_encode_print(s, "\r\n"))
681 goto fail;
682
683 Stream_SealLength(s);
684 return s;
685fail:
686 Stream_Free(s, TRUE);
687 return NULL;
688}
689
690HttpRequest* http_request_new(void)
691{
692 HttpRequest* request = (HttpRequest*)calloc(1, sizeof(HttpRequest));
693 if (!request)
694 return NULL;
695
696 request->headers = HashTable_New_String();
697 if (!request->headers)
698 goto fail;
699 request->TransferEncoding = TransferEncodingIdentity;
700 return request;
701fail:
702 http_request_free(request);
703 return NULL;
704}
705
706void http_request_free(HttpRequest* request)
707{
708 if (!request)
709 return;
710
711 free(request->AuthParam);
712 free(request->AuthScheme);
713 free(request->Authorization);
714 free(request->Method);
715 free(request->URI);
716 HashTable_Free(request->headers);
717 free(request);
718}
719
720static BOOL http_response_parse_header_status_line(HttpResponse* response, const char* status_line)
721{
722 BOOL rc = FALSE;
723 char* separator = NULL;
724 char* status_code = NULL;
725
726 if (!response)
727 goto fail;
728
729 if (status_line)
730 separator = strchr(status_line, ' ');
731
732 if (!separator)
733 goto fail;
734
735 status_code = separator + 1;
736 separator = strchr(status_code, ' ');
737
738 if (!separator)
739 goto fail;
740
741 const char* reason_phrase = separator + 1;
742 *separator = '\0';
743 errno = 0;
744 {
745 long val = strtol(status_code, NULL, 0);
746
747 if ((errno != 0) || (val < 0) || (val > INT16_MAX))
748 goto fail;
749
750 response->StatusCode = (UINT16)val;
751 }
752 free(response->ReasonPhrase);
753 response->ReasonPhrase = _strdup(reason_phrase);
754
755 if (!response->ReasonPhrase)
756 goto fail;
757
758 *separator = ' ';
759 rc = TRUE;
760fail:
761
762 if (!rc)
763 WLog_ERR(TAG, "http_response_parse_header_status_line failed [%s]", status_line);
764
765 return rc;
766}
767
768static BOOL http_response_parse_header_field(HttpResponse* response, const char* name,
769 const char* value)
770{
771 BOOL status = TRUE;
772
773 WINPR_ASSERT(response);
774
775 if (!name)
776 return FALSE;
777
778 if (_stricmp(name, "Content-Length") == 0)
779 {
780 unsigned long long val = 0;
781 errno = 0;
782 val = _strtoui64(value, NULL, 0);
783
784 if ((errno != 0) || (val > INT32_MAX))
785 return FALSE;
786
787 response->ContentLength = WINPR_ASSERTING_INT_CAST(size_t, val);
788 }
789 else if (_stricmp(name, "Content-Type") == 0)
790 {
791 if (!value)
792 return FALSE;
793 free(response->ContentType);
794 response->ContentType = _strdup(value);
795
796 if (!response->ContentType)
797 return FALSE;
798 }
799 else if (_stricmp(name, "Transfer-Encoding") == 0)
800 {
801 if (_stricmp(value, "identity") == 0)
802 response->TransferEncoding = TransferEncodingIdentity;
803 else if (_stricmp(value, "chunked") == 0)
804 response->TransferEncoding = TransferEncodingChunked;
805 else
806 response->TransferEncoding = TransferEncodingUnknown;
807 }
808 else if (_stricmp(name, "Sec-WebSocket-Version") == 0)
809 {
810 if (!value)
811 return FALSE;
812 free(response->SecWebsocketVersion);
813 response->SecWebsocketVersion = _strdup(value);
814
815 if (!response->SecWebsocketVersion)
816 return FALSE;
817 }
818 else if (_stricmp(name, "Sec-WebSocket-Accept") == 0)
819 {
820 if (!value)
821 return FALSE;
822 free(response->SecWebsocketAccept);
823 response->SecWebsocketAccept = _strdup(value);
824
825 if (!response->SecWebsocketAccept)
826 return FALSE;
827 }
828 else if (_stricmp(name, "WWW-Authenticate") == 0)
829 {
830 char* separator = NULL;
831 const char* authScheme = NULL;
832 char* authValue = NULL;
833 separator = strchr(value, ' ');
834
835 if (separator)
836 {
837 /* WWW-Authenticate: Basic realm=""
838 * WWW-Authenticate: NTLM base64token
839 * WWW-Authenticate: Digest realm="testrealm@host.com", qop="auth, auth-int",
840 * nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
841 * opaque="5ccc069c403ebaf9f0171e9517f40e41"
842 */
843 *separator = '\0';
844 authScheme = value;
845 authValue = separator + 1;
846
847 if (!authScheme || !authValue)
848 return FALSE;
849 }
850 else
851 {
852 authScheme = value;
853
854 if (!authScheme)
855 return FALSE;
856
857 authValue = "";
858 }
859
860 status = HashTable_Insert(response->Authenticates, authScheme, authValue);
861 }
862 else if (_stricmp(name, "Set-Cookie") == 0)
863 {
864 char* separator = NULL;
865 const char* CookieName = NULL;
866 char* CookieValue = NULL;
867 separator = strchr(value, '=');
868
869 if (separator)
870 {
871 /* Set-Cookie: name=value
872 * Set-Cookie: name=value; Attribute=value
873 * Set-Cookie: name="value with spaces"; Attribute=value
874 */
875 *separator = '\0';
876 CookieName = value;
877 CookieValue = separator + 1;
878
879 if (!CookieName || !CookieValue)
880 return FALSE;
881
882 if (*CookieValue == '"')
883 {
884 char* p = CookieValue;
885 while (*p != '"' && *p != '\0')
886 {
887 p++;
888 if (*p == '\\')
889 p++;
890 }
891 *p = '\0';
892 }
893 else
894 {
895 char* p = CookieValue;
896 while (*p != ';' && *p != '\0' && *p != ' ')
897 {
898 p++;
899 }
900 *p = '\0';
901 }
902 }
903 else
904 {
905 return FALSE;
906 }
907
908 status = HashTable_Insert(response->SetCookie, CookieName, CookieValue);
909 }
910
911 return status;
912}
913
914static BOOL http_response_parse_header(HttpResponse* response)
915{
916 BOOL rc = FALSE;
917 char c = 0;
918 char* line = NULL;
919 char* name = NULL;
920 char* colon_pos = NULL;
921 char* end_of_header = NULL;
922 char end_of_header_char = 0;
923
924 if (!response)
925 goto fail;
926
927 if (!response->lines)
928 goto fail;
929
930 if (!http_response_parse_header_status_line(response, response->lines[0]))
931 goto fail;
932
933 for (size_t count = 1; count < response->count; count++)
934 {
935 line = response->lines[count];
936
946 if (line)
947 colon_pos = strchr(line, ':');
948 else
949 colon_pos = NULL;
950
951 if ((colon_pos == NULL) || (colon_pos == line))
952 return FALSE;
953
954 /* retrieve the position just after header name */
955 for (end_of_header = colon_pos; end_of_header != line; end_of_header--)
956 {
957 c = end_of_header[-1];
958
959 if (c != ' ' && c != '\t' && c != ':')
960 break;
961 }
962
963 if (end_of_header == line)
964 goto fail;
965
966 end_of_header_char = *end_of_header;
967 *end_of_header = '\0';
968 name = line;
969
970 /* eat space and tabs before header value */
971 char* value = colon_pos + 1;
972 for (; *value; value++)
973 {
974 if ((*value != ' ') && (*value != '\t'))
975 break;
976 }
977
978 const int res = http_response_parse_header_field(response, name, value);
979 *end_of_header = end_of_header_char;
980 if (!res)
981 goto fail;
982 }
983
984 rc = TRUE;
985fail:
986
987 if (!rc)
988 WLog_ERR(TAG, "parsing failed");
989
990 return rc;
991}
992
993static void http_response_print(wLog* log, DWORD level, const HttpResponse* response,
994 const char* file, size_t line, const char* fkt)
995{
996 char buffer[64] = { 0 };
997
998 WINPR_ASSERT(log);
999 WINPR_ASSERT(response);
1000
1001 if (!WLog_IsLevelActive(log, level))
1002 return;
1003
1004 const long status = http_response_get_status_code(response);
1005 WLog_PrintTextMessage(log, level, line, file, fkt, "HTTP status: %s",
1006 freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
1007
1008 if (WLog_IsLevelActive(log, WLOG_DEBUG))
1009 {
1010 for (size_t i = 0; i < response->count; i++)
1011 WLog_PrintTextMessage(log, WLOG_DEBUG, line, file, fkt, "[%" PRIuz "] %s", i,
1012 response->lines[i]);
1013 }
1014
1015 if (response->ReasonPhrase)
1016 WLog_PrintTextMessage(log, level, line, file, fkt, "[reason] %s", response->ReasonPhrase);
1017
1018 if (WLog_IsLevelActive(log, WLOG_TRACE))
1019 {
1020 WLog_PrintTextMessage(log, WLOG_TRACE, line, file, fkt, "[body][%" PRIuz "] %s",
1021 response->BodyLength, response->BodyContent);
1022 }
1023}
1024
1025static BOOL http_use_content_length(const char* cur)
1026{
1027 size_t pos = 0;
1028
1029 if (!cur)
1030 return FALSE;
1031
1032 if (_strnicmp(cur, "application/rpc", 15) == 0)
1033 pos = 15;
1034 else if (_strnicmp(cur, "text/plain", 10) == 0)
1035 pos = 10;
1036 else if (_strnicmp(cur, "text/html", 9) == 0)
1037 pos = 9;
1038 else if (_strnicmp(cur, "application/json", 16) == 0)
1039 pos = 16;
1040
1041 if (pos > 0)
1042 {
1043 char end = cur[pos];
1044
1045 switch (end)
1046 {
1047 case ' ':
1048 case ';':
1049 case '\0':
1050 case '\r':
1051 case '\n':
1052 return TRUE;
1053
1054 default:
1055 return FALSE;
1056 }
1057 }
1058
1059 return FALSE;
1060}
1061
1062static int print_bio_error(const char* str, size_t len, void* bp)
1063{
1064 wLog* log = bp;
1065
1066 WINPR_UNUSED(bp);
1067 WLog_Print(log, WLOG_ERROR, "%s", str);
1068 if (len > INT32_MAX)
1069 return -1;
1070 return (int)len;
1071}
1072
1073int http_chuncked_read(BIO* bio, BYTE* pBuffer, size_t size,
1074 http_encoding_chunked_context* encodingContext)
1075{
1076 int status = 0;
1077 int effectiveDataLen = 0;
1078 WINPR_ASSERT(bio);
1079 WINPR_ASSERT(pBuffer);
1080 WINPR_ASSERT(encodingContext != NULL);
1081 WINPR_ASSERT(size <= INT32_MAX);
1082 while (TRUE)
1083 {
1084 switch (encodingContext->state)
1085 {
1086 case ChunkStateData:
1087 {
1088 const size_t rd =
1089 (size > encodingContext->nextOffset ? encodingContext->nextOffset : size);
1090 if (rd > INT32_MAX)
1091 return -1;
1092
1093 ERR_clear_error();
1094 status = BIO_read(bio, pBuffer, (int)rd);
1095 if (status <= 0)
1096 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1097
1098 encodingContext->nextOffset -= WINPR_ASSERTING_INT_CAST(uint32_t, status);
1099 if (encodingContext->nextOffset == 0)
1100 {
1101 encodingContext->state = ChunkStateFooter;
1102 encodingContext->headerFooterPos = 0;
1103 }
1104 effectiveDataLen += status;
1105
1106 if ((size_t)status == size)
1107 return effectiveDataLen;
1108
1109 pBuffer += status;
1110 size -= (size_t)status;
1111 }
1112 break;
1113 case ChunkStateFooter:
1114 {
1115 char _dummy[2] = { 0 };
1116 WINPR_ASSERT(encodingContext->nextOffset == 0);
1117 WINPR_ASSERT(encodingContext->headerFooterPos < 2);
1118 ERR_clear_error();
1119 status = BIO_read(bio, _dummy, (int)(2 - encodingContext->headerFooterPos));
1120 if (status >= 0)
1121 {
1122 encodingContext->headerFooterPos += (size_t)status;
1123 if (encodingContext->headerFooterPos == 2)
1124 {
1125 encodingContext->state = ChunkStateLenghHeader;
1126 encodingContext->headerFooterPos = 0;
1127 }
1128 }
1129 else
1130 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1131 }
1132 break;
1133 case ChunkStateLenghHeader:
1134 {
1135 BOOL _haveNewLine = FALSE;
1136 char* dst = &encodingContext->lenBuffer[encodingContext->headerFooterPos];
1137 WINPR_ASSERT(encodingContext->nextOffset == 0);
1138 while (encodingContext->headerFooterPos < 10 && !_haveNewLine)
1139 {
1140 ERR_clear_error();
1141 status = BIO_read(bio, dst, 1);
1142 if (status >= 0)
1143 {
1144 if (*dst == '\n')
1145 _haveNewLine = TRUE;
1146 encodingContext->headerFooterPos += (size_t)status;
1147 dst += status;
1148 }
1149 else
1150 return (effectiveDataLen > 0 ? effectiveDataLen : status);
1151 }
1152 *dst = '\0';
1153 /* strtoul is tricky, error are reported via errno, we also need
1154 * to ensure the result does not overflow */
1155 errno = 0;
1156 size_t tmp = strtoul(encodingContext->lenBuffer, NULL, 16);
1157 if ((errno != 0) || (tmp > SIZE_MAX))
1158 {
1159 /* denote end of stream if something bad happens */
1160 encodingContext->nextOffset = 0;
1161 encodingContext->state = ChunkStateEnd;
1162 return -1;
1163 }
1164 encodingContext->nextOffset = tmp;
1165 encodingContext->state = ChunkStateData;
1166
1167 if (encodingContext->nextOffset == 0)
1168 { /* end of stream */
1169 WLog_DBG(TAG, "chunked encoding end of stream received");
1170 encodingContext->headerFooterPos = 0;
1171 encodingContext->state = ChunkStateEnd;
1172 return (effectiveDataLen > 0 ? effectiveDataLen : 0);
1173 }
1174 }
1175 break;
1176 default:
1177 /* invalid state / ChunkStateEnd */
1178 return -1;
1179 }
1180 }
1181}
1182
1183#define sleep_or_timeout(tls, startMS, timeoutMS) \
1184 sleep_or_timeout_((tls), (startMS), (timeoutMS), __FILE__, __func__, __LINE__)
1185static BOOL sleep_or_timeout_(rdpTls* tls, UINT64 startMS, UINT32 timeoutMS, const char* file,
1186 const char* fkt, size_t line)
1187{
1188 WINPR_ASSERT(tls);
1189
1190 USleep(100);
1191 const UINT64 nowMS = GetTickCount64();
1192 if (nowMS - startMS > timeoutMS)
1193 {
1194 DWORD level = WLOG_ERROR;
1195 wLog* log = WLog_Get(TAG);
1196 if (WLog_IsLevelActive(log, level))
1197 WLog_PrintTextMessage(log, level, line, file, fkt, "timeout [%" PRIu32 "ms] exceeded",
1198 timeoutMS);
1199 return TRUE;
1200 }
1201 if (!BIO_should_retry(tls->bio))
1202 {
1203 DWORD level = WLOG_ERROR;
1204 wLog* log = WLog_Get(TAG);
1205 if (WLog_IsLevelActive(log, level))
1206 {
1207 WLog_PrintTextMessage(log, level, line, file, fkt, "Retries exceeded");
1208 ERR_print_errors_cb(print_bio_error, log);
1209 }
1210 return TRUE;
1211 }
1212 if (freerdp_shall_disconnect_context(tls->context))
1213 return TRUE;
1214
1215 return FALSE;
1216}
1217
1218static SSIZE_T http_response_recv_line(rdpTls* tls, HttpResponse* response)
1219{
1220 WINPR_ASSERT(tls);
1221 WINPR_ASSERT(response);
1222
1223 SSIZE_T payloadOffset = -1;
1224 const UINT32 timeoutMS =
1225 freerdp_settings_get_uint32(tls->context->settings, FreeRDP_TcpConnectTimeout);
1226 const UINT64 startMS = GetTickCount64();
1227 while (payloadOffset <= 0)
1228 {
1229 size_t bodyLength = 0;
1230 size_t position = 0;
1231 int status = -1;
1232 size_t s = 0;
1233 char* end = NULL;
1234 /* Read until we encounter \r\n\r\n */
1235 ERR_clear_error();
1236
1237 status = BIO_read(tls->bio, Stream_Pointer(response->data), 1);
1238 if (status <= 0)
1239 {
1240 if (sleep_or_timeout(tls, startMS, timeoutMS))
1241 goto out_error;
1242 continue;
1243 }
1244
1245#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H
1246 VALGRIND_MAKE_MEM_DEFINED(Stream_Pointer(response->data), status);
1247#endif
1248 Stream_Seek(response->data, (size_t)status);
1249
1250 if (!Stream_EnsureRemainingCapacity(response->data, 1024))
1251 goto out_error;
1252
1253 position = Stream_GetPosition(response->data);
1254
1255 if (position < 4)
1256 continue;
1257 else if (position > RESPONSE_SIZE_LIMIT)
1258 {
1259 WLog_ERR(TAG, "Request header too large! (%" PRIdz " bytes) Aborting!", bodyLength);
1260 goto out_error;
1261 }
1262
1263 /* Always check at most the lase 8 bytes for occurrence of the desired
1264 * sequence of \r\n\r\n */
1265 s = (position > 8) ? 8 : position;
1266 end = (char*)Stream_Pointer(response->data) - s;
1267
1268 if (string_strnstr(end, "\r\n\r\n", s) != NULL)
1269 payloadOffset = WINPR_ASSERTING_INT_CAST(SSIZE_T, Stream_GetPosition(response->data));
1270 }
1271
1272out_error:
1273 return payloadOffset;
1274}
1275
1276static BOOL http_response_recv_body(rdpTls* tls, HttpResponse* response, BOOL readContentLength,
1277 size_t payloadOffset, size_t bodyLength)
1278{
1279 BOOL rc = FALSE;
1280
1281 WINPR_ASSERT(tls);
1282 WINPR_ASSERT(response);
1283
1284 const UINT64 startMS = GetTickCount64();
1285 const UINT32 timeoutMS =
1286 freerdp_settings_get_uint32(tls->context->settings, FreeRDP_TcpConnectTimeout);
1287
1288 if ((response->TransferEncoding == TransferEncodingChunked) && readContentLength)
1289 {
1291 ctx.state = ChunkStateLenghHeader;
1292 ctx.nextOffset = 0;
1293 ctx.headerFooterPos = 0;
1294 int full_len = 0;
1295 do
1296 {
1297 if (!Stream_EnsureRemainingCapacity(response->data, 2048))
1298 goto out_error;
1299
1300 int status = http_chuncked_read(tls->bio, Stream_Pointer(response->data),
1301 Stream_GetRemainingCapacity(response->data), &ctx);
1302 if (status <= 0)
1303 {
1304 if (sleep_or_timeout(tls, startMS, timeoutMS))
1305 goto out_error;
1306 }
1307 else
1308 {
1309 Stream_Seek(response->data, (size_t)status);
1310 full_len += status;
1311 }
1312 } while (ctx.state != ChunkStateEnd);
1313 response->BodyLength = WINPR_ASSERTING_INT_CAST(uint32_t, full_len);
1314 if (response->BodyLength > 0)
1315 response->BodyContent = &(Stream_BufferAs(response->data, char))[payloadOffset];
1316 }
1317 else
1318 {
1319 while (response->BodyLength < bodyLength)
1320 {
1321 int status = 0;
1322
1323 if (!Stream_EnsureRemainingCapacity(response->data, bodyLength - response->BodyLength))
1324 goto out_error;
1325
1326 ERR_clear_error();
1327 size_t diff = bodyLength - response->BodyLength;
1328 if (diff > INT32_MAX)
1329 diff = INT32_MAX;
1330 status = BIO_read(tls->bio, Stream_Pointer(response->data), (int)diff);
1331
1332 if (status <= 0)
1333 {
1334 if (sleep_or_timeout(tls, startMS, timeoutMS))
1335 goto out_error;
1336 continue;
1337 }
1338
1339 Stream_Seek(response->data, (size_t)status);
1340 response->BodyLength += (unsigned long)status;
1341
1342 if (response->BodyLength > RESPONSE_SIZE_LIMIT)
1343 {
1344 WLog_ERR(TAG, "Request body too large! (%" PRIdz " bytes) Aborting!",
1345 response->BodyLength);
1346 goto out_error;
1347 }
1348 }
1349
1350 if (response->BodyLength > 0)
1351 response->BodyContent = &(Stream_BufferAs(response->data, char))[payloadOffset];
1352
1353 if (bodyLength != response->BodyLength)
1354 {
1355 WLog_WARN(TAG, "%s unexpected body length: actual: %" PRIuz ", expected: %" PRIuz,
1356 response->ContentType, response->BodyLength, bodyLength);
1357
1358 if (bodyLength > 0)
1359 response->BodyLength = MIN(bodyLength, response->BodyLength);
1360 }
1361
1362 /* '\0' terminate the http body */
1363 if (!Stream_EnsureRemainingCapacity(response->data, sizeof(UINT16)))
1364 goto out_error;
1365 Stream_Write_UINT16(response->data, 0);
1366 }
1367
1368 rc = TRUE;
1369out_error:
1370 return rc;
1371}
1372
1373static void clear_lines(HttpResponse* response)
1374{
1375 WINPR_ASSERT(response);
1376
1377 for (size_t x = 0; x < response->count; x++)
1378 {
1379 WINPR_ASSERT(response->lines);
1380 char* line = response->lines[x];
1381 free(line);
1382 }
1383
1384 free((void*)response->lines);
1385 response->lines = NULL;
1386 response->count = 0;
1387}
1388
1389HttpResponse* http_response_recv(rdpTls* tls, BOOL readContentLength)
1390{
1391 size_t bodyLength = 0;
1392 HttpResponse* response = http_response_new();
1393
1394 if (!response)
1395 return NULL;
1396
1397 response->ContentLength = 0;
1398
1399 const SSIZE_T payloadOffset = http_response_recv_line(tls, response);
1400 if (payloadOffset < 0)
1401 goto out_error;
1402
1403 if (payloadOffset)
1404 {
1405 size_t count = 0;
1406 char* buffer = Stream_BufferAs(response->data, char);
1407 const char* line = Stream_BufferAs(response->data, char);
1408 char* context = NULL;
1409
1410 while ((line = string_strnstr(line, "\r\n",
1411 WINPR_ASSERTING_INT_CAST(size_t, payloadOffset) -
1412 WINPR_ASSERTING_INT_CAST(size_t, (line - buffer)) - 2UL)))
1413 {
1414 line += 2;
1415 count++;
1416 }
1417
1418 clear_lines(response);
1419 response->count = count;
1420
1421 if (count)
1422 {
1423 response->lines = (char**)calloc(response->count, sizeof(char*));
1424
1425 if (!response->lines)
1426 goto out_error;
1427 }
1428
1429 buffer[payloadOffset - 1] = '\0';
1430 buffer[payloadOffset - 2] = '\0';
1431 count = 0;
1432 line = strtok_s(buffer, "\r\n", &context);
1433
1434 while (line && (response->count > count))
1435 {
1436 response->lines[count] = _strdup(line);
1437 if (!response->lines[count])
1438 goto out_error;
1439 line = strtok_s(NULL, "\r\n", &context);
1440 count++;
1441 }
1442
1443 if (!http_response_parse_header(response))
1444 goto out_error;
1445
1446 response->BodyLength =
1447 Stream_GetPosition(response->data) - WINPR_ASSERTING_INT_CAST(size_t, payloadOffset);
1448
1449 WINPR_ASSERT(response->BodyLength == 0);
1450 bodyLength = response->BodyLength; /* expected body length */
1451
1452 if (readContentLength && (response->ContentLength > 0))
1453 {
1454 const char* cur = response->ContentType;
1455
1456 while (cur != NULL)
1457 {
1458 if (http_use_content_length(cur))
1459 {
1460 if (response->ContentLength < RESPONSE_SIZE_LIMIT)
1461 bodyLength = response->ContentLength;
1462
1463 break;
1464 }
1465 else
1466 readContentLength = FALSE; /* prevent chunked read */
1467
1468 cur = strchr(cur, ';');
1469 }
1470 }
1471
1472 if (bodyLength > RESPONSE_SIZE_LIMIT)
1473 {
1474 WLog_ERR(TAG, "Expected request body too large! (%" PRIdz " bytes) Aborting!",
1475 bodyLength);
1476 goto out_error;
1477 }
1478
1479 /* Fetch remaining body! */
1480 if (!http_response_recv_body(tls, response, readContentLength,
1481 WINPR_ASSERTING_INT_CAST(size_t, payloadOffset), bodyLength))
1482 goto out_error;
1483 }
1484 Stream_SealLength(response->data);
1485
1486 /* Ensure '\0' terminated string */
1487 if (!Stream_EnsureRemainingCapacity(response->data, 2))
1488 goto out_error;
1489 Stream_Write_UINT16(response->data, 0);
1490
1491 return response;
1492out_error:
1493 WLog_ERR(TAG, "No response");
1494 http_response_free(response);
1495 return NULL;
1496}
1497
1498const char* http_response_get_body(const HttpResponse* response)
1499{
1500 if (!response)
1501 return NULL;
1502
1503 return response->BodyContent;
1504}
1505
1506wHashTable* HashTable_New_String(void)
1507{
1508 wHashTable* table = HashTable_New(FALSE);
1509 if (!table)
1510 return NULL;
1511
1512 if (!HashTable_SetupForStringData(table, TRUE))
1513 {
1514 HashTable_Free(table);
1515 return NULL;
1516 }
1517 HashTable_KeyObject(table)->fnObjectEquals = strings_equals_nocase;
1518 HashTable_ValueObject(table)->fnObjectEquals = strings_equals_nocase;
1519 return table;
1520}
1521
1522HttpResponse* http_response_new(void)
1523{
1524 HttpResponse* response = (HttpResponse*)calloc(1, sizeof(HttpResponse));
1525
1526 if (!response)
1527 return NULL;
1528
1529 response->Authenticates = HashTable_New_String();
1530
1531 if (!response->Authenticates)
1532 goto fail;
1533
1534 response->SetCookie = HashTable_New_String();
1535
1536 if (!response->SetCookie)
1537 goto fail;
1538
1539 response->data = Stream_New(NULL, 2048);
1540
1541 if (!response->data)
1542 goto fail;
1543
1544 response->TransferEncoding = TransferEncodingIdentity;
1545 return response;
1546fail:
1547 WINPR_PRAGMA_DIAG_PUSH
1548 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1549 http_response_free(response);
1550 WINPR_PRAGMA_DIAG_POP
1551 return NULL;
1552}
1553
1554void http_response_free(HttpResponse* response)
1555{
1556 if (!response)
1557 return;
1558
1559 clear_lines(response);
1560 free(response->ReasonPhrase);
1561 free(response->ContentType);
1562 free(response->SecWebsocketAccept);
1563 free(response->SecWebsocketVersion);
1564 HashTable_Free(response->Authenticates);
1565 HashTable_Free(response->SetCookie);
1566 Stream_Free(response->data, TRUE);
1567 free(response);
1568}
1569
1570const char* http_request_get_uri(HttpRequest* request)
1571{
1572 if (!request)
1573 return NULL;
1574
1575 return request->URI;
1576}
1577
1578SSIZE_T http_request_get_content_length(HttpRequest* request)
1579{
1580 if (!request)
1581 return -1;
1582
1583 return (SSIZE_T)request->ContentLength;
1584}
1585
1586BOOL http_request_set_content_length(HttpRequest* request, size_t length)
1587{
1588 if (!request)
1589 return FALSE;
1590
1591 request->ContentLength = length;
1592 return TRUE;
1593}
1594
1595UINT16 http_response_get_status_code(const HttpResponse* response)
1596{
1597 WINPR_ASSERT(response);
1598
1599 return response->StatusCode;
1600}
1601
1602size_t http_response_get_body_length(const HttpResponse* response)
1603{
1604 WINPR_ASSERT(response);
1605
1606 return response->BodyLength;
1607}
1608
1609const char* http_response_get_auth_token(const HttpResponse* response, const char* method)
1610{
1611 if (!response || !method)
1612 return NULL;
1613
1614 return HashTable_GetItemValue(response->Authenticates, method);
1615}
1616
1617const char* http_response_get_setcookie(const HttpResponse* response, const char* cookie)
1618{
1619 if (!response || !cookie)
1620 return NULL;
1621
1622 return HashTable_GetItemValue(response->SetCookie, cookie);
1623}
1624
1625TRANSFER_ENCODING http_response_get_transfer_encoding(const HttpResponse* response)
1626{
1627 if (!response)
1628 return TransferEncodingUnknown;
1629
1630 return response->TransferEncoding;
1631}
1632
1633BOOL http_response_is_websocket(const HttpContext* http, const HttpResponse* response)
1634{
1635 BOOL isWebsocket = FALSE;
1636 WINPR_DIGEST_CTX* sha1 = NULL;
1637 char* base64accept = NULL;
1638 BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH];
1639
1640 if (!http || !response)
1641 return FALSE;
1642
1643 if (!http->websocketUpgrade || response->StatusCode != HTTP_STATUS_SWITCH_PROTOCOLS)
1644 return FALSE;
1645
1646 if (response->SecWebsocketVersion && _stricmp(response->SecWebsocketVersion, "13") != 0)
1647 return FALSE;
1648
1649 if (!response->SecWebsocketAccept)
1650 return FALSE;
1651
1652 /* now check if Sec-Websocket-Accept is correct */
1653
1654 sha1 = winpr_Digest_New();
1655 if (!sha1)
1656 goto out;
1657
1658 if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1))
1659 goto out;
1660
1661 if (!winpr_Digest_Update(sha1, (BYTE*)http->SecWebsocketKey, strlen(http->SecWebsocketKey)))
1662 goto out;
1663 if (!winpr_Digest_Update(sha1, (const BYTE*)WEBSOCKET_MAGIC_GUID, strlen(WEBSOCKET_MAGIC_GUID)))
1664 goto out;
1665
1666 if (!winpr_Digest_Final(sha1, sha1_digest, sizeof(sha1_digest)))
1667 goto out;
1668
1669 base64accept = crypto_base64_encode(sha1_digest, WINPR_SHA1_DIGEST_LENGTH);
1670 if (!base64accept)
1671 goto out;
1672
1673 if (_stricmp(response->SecWebsocketAccept, base64accept) != 0)
1674 {
1675 WLog_WARN(TAG, "Webserver gave Websocket Upgrade response but sanity check failed");
1676 goto out;
1677 }
1678 isWebsocket = TRUE;
1679out:
1680 winpr_Digest_Free(sha1);
1681 free(base64accept);
1682 return isWebsocket;
1683}
1684
1685void http_response_log_error_status_(wLog* log, DWORD level, const HttpResponse* response,
1686 const char* file, size_t line, const char* fkt)
1687{
1688 WINPR_ASSERT(log);
1689 WINPR_ASSERT(response);
1690
1691 if (!WLog_IsLevelActive(log, level))
1692 return;
1693
1694 char buffer[64] = { 0 };
1695 const UINT16 status = http_response_get_status_code(response);
1696 WLog_PrintTextMessage(log, level, line, file, fkt, "Unexpected HTTP status: %s",
1697 freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
1698 http_response_print(log, level, response, file, line, fkt);
1699}
1700
1701static BOOL extract_cookie(const void* pkey, void* pvalue, void* arg)
1702{
1703 const char* key = pkey;
1704 const char* value = pvalue;
1705 HttpContext* context = arg;
1706
1707 WINPR_ASSERT(arg);
1708 WINPR_ASSERT(key);
1709 WINPR_ASSERT(value);
1710
1711 return http_context_set_cookie(context, key, value);
1712}
1713
1714BOOL http_response_extract_cookies(const HttpResponse* response, HttpContext* context)
1715{
1716 WINPR_ASSERT(response);
1717 WINPR_ASSERT(context);
1718
1719 return HashTable_Foreach(response->SetCookie, extract_cookie, context);
1720}
1721
1722FREERDP_LOCAL BOOL http_context_set_header(HttpContext* context, const char* key, const char* value,
1723 ...)
1724{
1725 WINPR_ASSERT(context);
1726 va_list ap;
1727 va_start(ap, value);
1728 const BOOL rc = http_context_set_header_va(context, key, value, ap);
1729 va_end(ap);
1730 return rc;
1731}
1732
1733BOOL http_request_set_header(HttpRequest* request, const char* key, const char* value, ...)
1734{
1735 WINPR_ASSERT(request);
1736 char* v = NULL;
1737 size_t vlen = 0;
1738 va_list ap;
1739 va_start(ap, value);
1740 winpr_vasprintf(&v, &vlen, value, ap);
1741 va_end(ap);
1742 const BOOL rc = HashTable_Insert(request->headers, key, v);
1743 free(v);
1744 return rc;
1745}
1746
1747BOOL http_context_set_header_va(HttpContext* context, const char* key, const char* value,
1748 va_list ap)
1749{
1750 char* v = NULL;
1751 size_t vlen = 0;
1752 winpr_vasprintf(&v, &vlen, value, ap);
1753 const BOOL rc = HashTable_Insert(context->headers, key, v);
1754 free(v);
1755 return rc;
1756}
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.
Definition collections.h:57