FreeRDP
Loading...
Searching...
No Matches
stream.c
1/*
2 * WinPR: Windows Portable Runtime
3 * Stream Utils
4 *
5 * Copyright 2011 Vic Lee
6 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21#include <winpr/config.h>
22
23#include <winpr/assert.h>
24#include <winpr/crt.h>
25#include <winpr/stream.h>
26
27#include "stream.h"
28#include "../log.h"
29
30#define STREAM_TAG WINPR_TAG("wStream")
31
32#define STREAM_ASSERT(cond) \
33 do \
34 { \
35 if (!(cond)) \
36 { \
37 WLog_FATAL(STREAM_TAG, "%s [%s:%s:%" PRIuz "]", #cond, __FILE__, __func__, \
38 (size_t)__LINE__); \
39 winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); \
40 abort(); \
41 } \
42 } while (0)
43
44BOOL Stream_EnsureCapacity(wStream* s, size_t size)
45{
46 WINPR_ASSERT(s);
47 if (s->capacity < size)
48 {
49 size_t position = 0;
50 size_t old_capacity = 0;
51 size_t new_capacity = 0;
52 BYTE* new_buf = NULL;
53
54 old_capacity = s->capacity;
55 new_capacity = old_capacity;
56
57 do
58 {
59 new_capacity *= 2;
60 } while (new_capacity < size);
61
62 position = Stream_GetPosition(s);
63
64 if (!s->isOwner)
65 {
66 new_buf = (BYTE*)malloc(new_capacity);
67 CopyMemory(new_buf, s->buffer, s->capacity);
68 s->isOwner = TRUE;
69 }
70 else
71 {
72 new_buf = (BYTE*)realloc(s->buffer, new_capacity);
73 }
74
75 if (!new_buf)
76 return FALSE;
77 s->buffer = new_buf;
78 s->capacity = new_capacity;
79 s->length = new_capacity;
80 ZeroMemory(&s->buffer[old_capacity], s->capacity - old_capacity);
81
82 Stream_SetPosition(s, position);
83 }
84 return TRUE;
85}
86
87BOOL Stream_EnsureRemainingCapacity(wStream* s, size_t size)
88{
89 if (Stream_GetPosition(s) + size > Stream_Capacity(s))
90 return Stream_EnsureCapacity(s, Stream_Capacity(s) + size);
91 return TRUE;
92}
93
94wStream* Stream_New(BYTE* buffer, size_t size)
95{
96 wStream* s = NULL;
97
98 if (!buffer && !size)
99 return NULL;
100
101 s = calloc(1, sizeof(wStream));
102 if (!s)
103 return NULL;
104
105 if (buffer)
106 s->buffer = buffer;
107 else
108 s->buffer = (BYTE*)malloc(size);
109
110 if (!s->buffer)
111 {
112 free(s);
113 return NULL;
114 }
115
116 s->pointer = s->buffer;
117 s->capacity = size;
118 s->length = size;
119
120 s->pool = NULL;
121 s->count = 1;
122 s->isAllocatedStream = TRUE;
123 s->isOwner = TRUE;
124 return s;
125}
126
127wStream* Stream_StaticConstInit(wStream* s, const BYTE* buffer, size_t size)
128{
129 union
130 {
131 BYTE* b;
132 const BYTE* cb;
133 } cnv;
134
135 cnv.cb = buffer;
136 return Stream_StaticInit(s, cnv.b, size);
137}
138
139wStream* Stream_StaticInit(wStream* s, BYTE* buffer, size_t size)
140{
141 const wStream empty = { 0 };
142
143 WINPR_ASSERT(s);
144 WINPR_ASSERT(buffer);
145
146 *s = empty;
147 s->buffer = s->pointer = buffer;
148 s->capacity = s->length = size;
149 s->pool = NULL;
150 s->count = 1;
151 s->isAllocatedStream = FALSE;
152 s->isOwner = FALSE;
153 return s;
154}
155
156void Stream_EnsureValidity(wStream* s)
157{
158 size_t cur = 0;
159
160 STREAM_ASSERT(s);
161 STREAM_ASSERT(s->pointer >= s->buffer);
162
163 cur = (size_t)(s->pointer - s->buffer);
164 STREAM_ASSERT(cur <= s->capacity);
165 STREAM_ASSERT(s->length <= s->capacity);
166}
167
168void Stream_Free(wStream* s, BOOL bFreeBuffer)
169{
170 if (s)
171 {
172 Stream_EnsureValidity(s);
173 if (bFreeBuffer && s->isOwner)
174 free(s->buffer);
175
176 if (s->isAllocatedStream)
177 free(s);
178 }
179}
180
181BOOL Stream_SetLength(wStream* _s, size_t _l)
182{
183 if ((_l) > Stream_Capacity(_s))
184 {
185 _s->length = 0;
186 return FALSE;
187 }
188 _s->length = _l;
189 return TRUE;
190}
191
192BOOL Stream_SetPosition(wStream* _s, size_t _p)
193{
194 if ((_p) > Stream_Capacity(_s))
195 {
196 _s->pointer = _s->buffer;
197 return FALSE;
198 }
199 _s->pointer = _s->buffer + (_p);
200 return TRUE;
201}
202
203void Stream_SealLength(wStream* _s)
204{
205 size_t cur = 0;
206 WINPR_ASSERT(_s);
207 WINPR_ASSERT(_s->buffer <= _s->pointer);
208 cur = (size_t)(_s->pointer - _s->buffer);
209 WINPR_ASSERT(cur <= _s->capacity);
210 if (cur <= _s->capacity)
211 _s->length = cur;
212 else
213 {
214 WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds");
215 winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);
216 _s->length = 0;
217 }
218}
219
220#if defined(WITH_WINPR_DEPRECATED)
221BOOL Stream_SetPointer(wStream* _s, BYTE* _p)
222{
223 WINPR_ASSERT(_s);
224 if (!_p || (_s->buffer > _p) || (_s->buffer + _s->capacity < _p))
225 {
226 _s->pointer = _s->buffer;
227 return FALSE;
228 }
229 _s->pointer = _p;
230 return TRUE;
231}
232
233BOOL Stream_SetBuffer(wStream* _s, BYTE* _b)
234{
235 WINPR_ASSERT(_s);
236 WINPR_ASSERT(_b);
237
238 _s->buffer = _b;
239 _s->pointer = _b;
240 return _s->buffer != NULL;
241}
242
243void Stream_SetCapacity(wStream* _s, size_t _c)
244{
245 WINPR_ASSERT(_s);
246 _s->capacity = _c;
247}
248
249#endif
250
251size_t Stream_GetRemainingCapacity(const wStream* _s)
252{
253 size_t cur = 0;
254 WINPR_ASSERT(_s);
255 WINPR_ASSERT(_s->buffer <= _s->pointer);
256 cur = (size_t)(_s->pointer - _s->buffer);
257 WINPR_ASSERT(cur <= _s->capacity);
258 if (cur > _s->capacity)
259 {
260 WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds");
261 winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);
262 return 0;
263 }
264 return (_s->capacity - cur);
265}
266
267size_t Stream_GetRemainingLength(const wStream* _s)
268{
269 size_t cur = 0;
270 WINPR_ASSERT(_s);
271 WINPR_ASSERT(_s->buffer <= _s->pointer);
272 WINPR_ASSERT(_s->length <= _s->capacity);
273 cur = (size_t)(_s->pointer - _s->buffer);
274 WINPR_ASSERT(cur <= _s->length);
275 if (cur > _s->length)
276 {
277 WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was read out of bounds");
278 winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);
279 return 0;
280 }
281 return (_s->length - cur);
282}
283
284BOOL Stream_Write_UTF16_String(wStream* s, const WCHAR* src, size_t length)
285{
286 WINPR_ASSERT(s);
287 WINPR_ASSERT(src || (length == 0));
288 if (!s || !src)
289 return FALSE;
290
291 if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, (s), length, sizeof(WCHAR)))
292 return FALSE;
293
294 for (size_t x = 0; x < length; x++)
295 Stream_Write_UINT16(s, src[x]);
296
297 return TRUE;
298}
299
300BOOL Stream_Read_UTF16_String(wStream* s, WCHAR* dst, size_t length)
301{
302 WINPR_ASSERT(s);
303 WINPR_ASSERT(dst);
304
305 if (!Stream_CheckAndLogRequiredLengthOfSize(STREAM_TAG, s, length, sizeof(WCHAR)))
306 return FALSE;
307
308 for (size_t x = 0; x < length; x++)
309 Stream_Read_UINT16(s, dst[x]);
310
311 return TRUE;
312}
313
314BOOL Stream_CheckAndLogRequiredCapacityEx(const char* tag, DWORD level, wStream* s, size_t nmemb,
315 size_t size, const char* fmt, ...)
316{
317 WINPR_ASSERT(size != 0);
318 const size_t actual = Stream_GetRemainingCapacity(s) / size;
319
320 if (actual < nmemb)
321 {
322 va_list args;
323
324 va_start(args, fmt);
325 Stream_CheckAndLogRequiredCapacityExVa(tag, level, s, nmemb, size, fmt, args);
326 va_end(args);
327
328 return FALSE;
329 }
330 return TRUE;
331}
332
333BOOL Stream_CheckAndLogRequiredCapacityExVa(const char* tag, DWORD level, wStream* s, size_t nmemb,
334 size_t size, const char* fmt, va_list args)
335{
336 WINPR_ASSERT(size != 0);
337 const size_t actual = Stream_GetRemainingCapacity(s) / size;
338
339 if (actual < nmemb)
340 return Stream_CheckAndLogRequiredCapacityWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt,
341 args);
342 return TRUE;
343}
344
345WINPR_ATTR_FORMAT_ARG(6, 0)
346BOOL Stream_CheckAndLogRequiredCapacityWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb,
347 size_t size, WINPR_FORMAT_ARG const char* fmt,
348 va_list args)
349{
350
351 WINPR_ASSERT(size != 0);
352 const size_t actual = Stream_GetRemainingCapacity(s) / size;
353
354 if (actual < nmemb)
355 {
356 char prefix[1024] = { 0 };
357
358 (void)vsnprintf(prefix, sizeof(prefix), fmt, args);
359
360 WLog_Print(log, level,
361 "[%s] invalid remaining capacity, got %" PRIuz ", require at least %" PRIu64
362 " [element size=%" PRIuz "]",
363 prefix, actual, nmemb, size);
364 winpr_log_backtrace_ex(log, level, 20);
365 return FALSE;
366 }
367 return TRUE;
368}
369
370WINPR_ATTR_FORMAT_ARG(6, 7)
371BOOL Stream_CheckAndLogRequiredCapacityWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb,
372 size_t size, WINPR_FORMAT_ARG const char* fmt, ...)
373{
374
375 WINPR_ASSERT(size != 0);
376 const size_t actual = Stream_GetRemainingCapacity(s) / size;
377
378 if (actual < nmemb)
379 {
380 va_list args;
381
382 va_start(args, fmt);
383 Stream_CheckAndLogRequiredCapacityWLogExVa(log, level, s, nmemb, size, fmt, args);
384 va_end(args);
385
386 return FALSE;
387 }
388 return TRUE;
389}
390
391WINPR_ATTR_FORMAT_ARG(6, 7)
392BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, size_t nmemb,
393 size_t size, WINPR_FORMAT_ARG const char* fmt, ...)
394{
395 WINPR_ASSERT(size > 0);
396 const size_t actual = Stream_GetRemainingLength(s) / size;
397
398 if (actual < nmemb)
399 {
400 va_list args;
401
402 va_start(args, fmt);
403 Stream_CheckAndLogRequiredLengthExVa(tag, level, s, nmemb, size, fmt, args);
404 va_end(args);
405
406 return FALSE;
407 }
408 return TRUE;
409}
410
411BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, size_t nmemb,
412 size_t size, const char* fmt, va_list args)
413{
414 WINPR_ASSERT(size > 0);
415 const size_t actual = Stream_GetRemainingLength(s) / size;
416
417 if (actual < nmemb)
418 return Stream_CheckAndLogRequiredLengthWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt,
419 args);
420 return TRUE;
421}
422
423BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb,
424 size_t size, const char* fmt, ...)
425{
426 WINPR_ASSERT(size > 0);
427 const size_t actual = Stream_GetRemainingLength(s) / size;
428
429 if (actual < nmemb)
430 {
431 va_list args;
432
433 va_start(args, fmt);
434 Stream_CheckAndLogRequiredLengthWLogExVa(log, level, s, nmemb, size, fmt, args);
435 va_end(args);
436
437 return FALSE;
438 }
439 return TRUE;
440}
441
442WINPR_ATTR_FORMAT_ARG(6, 0)
443BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb,
444 size_t size, WINPR_FORMAT_ARG const char* fmt,
445 va_list args)
446{
447 WINPR_ASSERT(size > 0);
448 const size_t actual = Stream_GetRemainingLength(s) / size;
449
450 if (actual < nmemb)
451 {
452 char prefix[1024] = { 0 };
453
454 (void)vsnprintf(prefix, sizeof(prefix), fmt, args);
455
456 WLog_Print(log, level,
457 "[%s] invalid length, got %" PRIuz ", require at least %" PRIuz
458 " [element size=%" PRIuz "]",
459 prefix, actual, nmemb, size);
460 winpr_log_backtrace_ex(log, level, 20);
461 return FALSE;
462 }
463 return TRUE;
464}
465
466SSIZE_T Stream_Write_UTF16_String_From_UTF8(wStream* s, size_t wcharLength, const char* src,
467 size_t length, BOOL fill)
468{
469 SSIZE_T rc = 0;
470 WCHAR* str = Stream_PointerAs(s, WCHAR);
471
472 if (length != 0)
473 {
474 if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, s, wcharLength, sizeof(WCHAR)))
475 return -1;
476
477 rc = ConvertUtf8NToWChar(src, length, str, wcharLength);
478 if (rc < 0)
479 return -1;
480
481 Stream_Seek(s, (size_t)rc * sizeof(WCHAR));
482 }
483
484 if (fill)
485 Stream_Zero(s, (wcharLength - (size_t)rc) * sizeof(WCHAR));
486 return rc;
487}
488
489char* Stream_Read_UTF16_String_As_UTF8(wStream* s, size_t wcharLength, size_t* pUtfCharLength)
490{
491 const WCHAR* str = Stream_ConstPointer(s);
492 if (wcharLength > SIZE_MAX / sizeof(WCHAR))
493 return NULL;
494
495 if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR)))
496 return NULL;
497
498 Stream_Seek(s, wcharLength * sizeof(WCHAR));
499 return ConvertWCharNToUtf8Alloc(str, wcharLength, pUtfCharLength);
500}
501
502SSIZE_T Stream_Read_UTF16_String_As_UTF8_Buffer(wStream* s, size_t wcharLength, char* utfBuffer,
503 size_t utfBufferCharLength)
504{
505 const WCHAR* ptr = Stream_ConstPointer(s);
506 if (wcharLength > SIZE_MAX / sizeof(WCHAR))
507 return -1;
508
509 if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR)))
510 return -1;
511
512 Stream_Seek(s, wcharLength * sizeof(WCHAR));
513 return ConvertWCharNToUtf8(ptr, wcharLength, utfBuffer, utfBufferCharLength);
514}
515
516BOOL Stream_SafeSeekEx(wStream* s, size_t size, const char* file, size_t line, const char* fkt)
517{
518 if (!Stream_CheckAndLogRequiredLengthEx(STREAM_TAG, WLOG_WARN, s, size, 1, "%s(%s:%" PRIuz ")",
519 fkt, file, line))
520 return FALSE;
521
522 Stream_Seek(s, size);
523 return TRUE;
524}