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