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