FreeRDP
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 
44 BOOL 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 
87 BOOL 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 
94 wStream* Stream_New(BYTE* buffer, size_t size)
95 {
96  wStream* s = NULL;
97 
98  if (!buffer && !size)
99  return NULL;
100 
101  s = malloc(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 = 0;
122  s->isAllocatedStream = TRUE;
123  s->isOwner = TRUE;
124  return s;
125 }
126 
127 wStream* 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 
139 wStream* 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 = 0;
151  s->isAllocatedStream = FALSE;
152  s->isOwner = FALSE;
153  return s;
154 }
155 
156 void 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 
168 void 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 
181 BOOL 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 
192 BOOL 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 
203 void 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)
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 {
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 
267 size_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 
284 BOOL 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 
300 BOOL 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 
314 BOOL 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 
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 {
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 
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 {
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 
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 {
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 
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 {
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 
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 {
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 
423 BOOL 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 
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 {
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 
466 SSIZE_T Stream_Write_UTF16_String_From_UTF8(wStream* s, size_t dlen, const char* src, size_t length,
467  BOOL fill)
468 {
469  WCHAR* str = Stream_PointerAs(s, WCHAR);
470 
471  if (length == 0)
472  return 0;
473 
474  if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, s, dlen, sizeof(WCHAR)))
475  return -1;
476 
477  SSIZE_T rc = ConvertUtf8NToWChar(src, length, str, dlen);
478  if (rc < 0)
479  return -1;
480 
481  Stream_Seek(s, (size_t)rc * sizeof(WCHAR));
482 
483  if (fill)
484  Stream_Zero(s, (dlen - (size_t)rc) * sizeof(WCHAR));
485  return rc;
486 }
487 
488 char* Stream_Read_UTF16_String_As_UTF8(wStream* s, size_t dlen, size_t* psize)
489 {
490  const WCHAR* str = Stream_ConstPointer(s);
491  if (dlen > SIZE_MAX / sizeof(WCHAR))
492  return NULL;
493 
494  if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, dlen * sizeof(WCHAR)))
495  return NULL;
496 
497  Stream_Seek(s, dlen * sizeof(WCHAR));
498  return ConvertWCharNToUtf8Alloc(str, dlen, psize);
499 }
500 
501 SSIZE_T Stream_Read_UTF16_String_As_UTF8_Buffer(wStream* s, size_t wcharLength, char* utfBuffer,
502  size_t utfBufferCharLength)
503 {
504  const WCHAR* ptr = Stream_ConstPointer(s);
505  if (wcharLength > SIZE_MAX / sizeof(WCHAR))
506  return -1;
507 
508  if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR)))
509  return -1;
510 
511  Stream_Seek(s, wcharLength * sizeof(WCHAR));
512  return ConvertWCharNToUtf8(ptr, wcharLength, utfBuffer, utfBufferCharLength);
513 }
514 
515 BOOL Stream_SafeSeekEx(wStream* s, size_t size, const char* file, size_t line, const char* fkt)
516 {
517  if (!Stream_CheckAndLogRequiredLengthEx(STREAM_TAG, WLOG_WARN, s, size, 1, "%s(%s:%" PRIuz ")",
518  fkt, file, line))
519  return FALSE;
520 
521  Stream_Seek(s, size);
522  return TRUE;
523 }