suricata
util-streaming-buffer.c
Go to the documentation of this file.
1 /* Copyright (C) 2015-2023 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 #include "suricata-common.h"
19 #include "util-streaming-buffer.h"
20 #include "util-unittest.h"
21 #include "util-print.h"
22 #include "util-validate.h"
23 #include "util-debug.h"
24 #include "util-error.h"
25 
26 #include "app-layer-htp-mem.h"
27 #include "conf-yaml-loader.h"
28 #include "app-layer-htp.h"
29 
30 static void ListRegions(StreamingBuffer *sb);
31 
32 #define DUMP_REGIONS 0 // set to 1 to dump a visual representation of the regions list and sbb tree.
33 
34 /**
35  * \file
36  *
37  * \author Victor Julien <victor@inliniac.net>
38  *
39  * \brief Streaming Buffer API
40  */
41 
42 static void *ReallocFunc(void *ptr, const size_t size)
43 {
44  void *ptrmem = SCRealloc(ptr, size);
45  if (unlikely(ptrmem == NULL)) {
47  }
48  return ptrmem;
49 }
50 
51 static void *CallocFunc(const size_t nm, const size_t sz)
52 {
53  void *ptrmem = SCCalloc(nm, sz);
54  if (unlikely(ptrmem == NULL)) {
56  }
57  return ptrmem;
58 }
59 
60 /* memory handling wrappers. If config doesn't define it's own set of
61  * functions, use the defaults */
62 // TODO the default allocators don't set `sc_errno` yet.
63 #define CALLOC(cfg, n, s) (cfg)->Calloc ? (cfg)->Calloc((n), (s)) : CallocFunc((n), (s))
64 #define REALLOC(cfg, ptr, orig_s, s) \
65  (cfg)->Realloc ? (cfg)->Realloc((ptr), (orig_s), (s)) : ReallocFunc((ptr), (s))
66 #define FREE(cfg, ptr, s) \
67  (cfg)->Free ? (cfg)->Free((ptr), (s)) : SCFree((ptr))
68 
69 static void SBBFree(StreamingBuffer *sb, const StreamingBufferConfig *cfg);
70 
72 
74 {
75  SCLogDebug("a %" PRIu64 " len %u, b %" PRIu64 " len %u", a->offset, a->len, b->offset, b->len);
76 
77  if (a->offset > b->offset)
78  SCReturnInt(1);
79  else if (a->offset < b->offset)
80  SCReturnInt(-1);
81  else {
82  if (a->len == 0 || b->len == 0 || a->len == b->len)
83  SCReturnInt(0);
84  else if (a->len > b->len)
85  SCReturnInt(1);
86  else
87  SCReturnInt(-1);
88  }
89 }
90 
91 /* inclusive compare function that also considers the right edge,
92  * not just the offset. */
93 static inline int InclusiveCompare(StreamingBufferBlock *lookup, StreamingBufferBlock *intree) {
94  const uint64_t lre = lookup->offset + lookup->len;
95  const uint64_t tre = intree->offset + intree->len;
96  if (lre <= intree->offset) // entirely before
97  return -1;
98  else if (lookup->offset < tre && lre <= tre) // (some) overlap
99  return 0;
100  else
101  return 1; // entirely after
102 }
103 
105 {
106  SCLogDebug("looking up %" PRIu64, elm->offset);
107 
108  struct StreamingBufferBlock *tmp = RB_ROOT(head);
109  struct StreamingBufferBlock *res = NULL;
110  while (tmp) {
111  SCLogDebug("compare with %" PRIu64 "/%u", tmp->offset, tmp->len);
112  const int comp = InclusiveCompare(elm, tmp);
113  SCLogDebug("compare result: %d", comp);
114  if (comp < 0) {
115  res = tmp;
116  tmp = RB_LEFT(tmp, rb);
117  } else if (comp > 0) {
118  tmp = RB_RIGHT(tmp, rb);
119  } else {
120  return tmp;
121  }
122  }
123  return res;
124 }
125 
126 /** \interal
127  * \brief does data region intersect with list region 'r'
128  * Takes the max gap into account.
129  */
130 static inline bool RegionsIntersect(const StreamingBufferConfig *cfg,
131  const StreamingBufferRegion *r, const uint64_t offset, const uint64_t re)
132 {
133  /* create the data range for the region, adding the max gap */
134  const uint64_t reg_o =
135  r->stream_offset > cfg->region_gap ? (r->stream_offset - cfg->region_gap) : 0;
136  const uint64_t reg_re = r->stream_offset + r->buf_size + cfg->region_gap;
137  SCLogDebug("r %p: %" PRIu64 "/%" PRIu64 " - adjusted %" PRIu64 "/%" PRIu64, r, r->stream_offset,
138  r->stream_offset + r->buf_size, reg_o, reg_re);
139  /* check if data range intersects with region range */
140  /* [offset:re] and [reg_o:reg_re] do not intersect if and only if
141  * re < reg_o or if reg_re < offset (one segment is strictly before the other)
142  * trusting that offset<=re and reg_o<=reg_re
143  */
144  if (re < reg_o || reg_re < offset) {
145  return false;
146  }
147  return true;
148 }
149 
150 /** \internal
151  * \brief find the first region for merging.
152  */
153 static StreamingBufferRegion *FindFirstRegionForOffset(const StreamingBufferConfig *cfg,
154  StreamingBufferRegion *r, const uint64_t offset, const uint32_t len,
155  StreamingBufferRegion **prev)
156 {
157  const uint64_t data_re = offset + len;
158  SCLogDebug("looking for first region matching %" PRIu64 "/%" PRIu64, offset, data_re);
159 
160  StreamingBufferRegion *p = NULL;
161  for (; r != NULL; r = r->next) {
162  if (RegionsIntersect(cfg, r, offset, data_re)) {
163  *prev = p;
164  return r;
165  }
166  p = r;
167  }
168  *prev = NULL;
169  return NULL;
170 }
171 
172 static StreamingBufferRegion *FindLargestRegionForOffset(const StreamingBufferConfig *cfg,
173  StreamingBufferRegion *r, const uint64_t offset, const uint32_t len)
174 {
175  const uint64_t data_re = offset + len;
176  SCLogDebug("starting at %p/%" PRIu64 ", offset %" PRIu64 ", data_re %" PRIu64, r,
177  r->stream_offset, offset, data_re);
178  StreamingBufferRegion *candidate = r;
179  for (; r != NULL; r = r->next) {
180 #ifdef DEBUG
181  const uint64_t reg_re = r->stream_offset + r->buf_size;
182  SCLogDebug("checking: %p/%" PRIu64 "/%" PRIu64 ", offset %" PRIu64 "/%" PRIu64, r,
183  r->stream_offset, reg_re, offset, data_re);
184 #endif
185  if (!RegionsIntersect(cfg, r, offset, data_re))
186  return candidate;
187 
188  if (r->buf_size > candidate->buf_size) {
189  SCLogDebug("candidate %p as size %u > %u", candidate, r->buf_size, candidate->buf_size);
190  candidate = r;
191  }
192  }
193  return candidate;
194 }
195 
196 static StreamingBufferRegion *FindRightEdge(const StreamingBufferConfig *cfg,
197  StreamingBufferRegion *r, const uint64_t offset, const uint32_t len)
198 {
199  const uint64_t data_re = offset + len;
200  StreamingBufferRegion *candidate = r;
201  for (; r != NULL; r = r->next) {
202  if (!RegionsIntersect(cfg, r, offset, data_re)) {
203  SCLogDebug(
204  "r %p is out of scope: %" PRIu64 "/%u/%" PRIu64, r, offset, len, offset + len);
205  return candidate;
206  }
207  candidate = r;
208  }
209  return candidate;
210 }
211 
212 /** \note uses sc_errno to give error details */
213 static inline StreamingBufferRegion *InitBufferRegion(
214  StreamingBuffer *sb, const StreamingBufferConfig *cfg, const uint32_t min_size)
215 {
216  if (sb->regions == USHRT_MAX || (cfg->max_regions != 0 && sb->regions >= cfg->max_regions)) {
217  SCLogDebug("max regions reached");
219  return NULL;
220  }
221 
222  StreamingBufferRegion *aux_r = CALLOC(cfg, 1, sizeof(*aux_r));
223  if (aux_r == NULL) {
224  return NULL;
225  }
226 
227  aux_r->buf = CALLOC(cfg, 1, MAX(cfg->buf_size, min_size));
228  if (aux_r->buf == NULL) {
229  FREE(cfg, aux_r, sizeof(*aux_r));
230  return NULL;
231  }
232  aux_r->buf_size = MAX(cfg->buf_size, min_size);
233  sb->regions++;
234  sb->max_regions = MAX(sb->regions, sb->max_regions);
235  return aux_r;
236 }
237 
238 static inline int InitBuffer(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
239 {
240  sb->region.buf = CALLOC(cfg, 1, cfg->buf_size);
241  if (sb->region.buf == NULL) {
242  return sc_errno;
243  }
244  sb->region.buf_size = cfg->buf_size;
245  return SC_OK;
246 }
247 
249 {
250  StreamingBuffer *sb = CALLOC(cfg, 1, sizeof(StreamingBuffer));
251  if (sb == NULL) {
252  return NULL;
253  }
254 
255  sb->region.buf_size = cfg->buf_size;
256  sb->regions = sb->max_regions = 1;
257 
258  if (cfg->buf_size == 0) {
259  return sb;
260  }
261 
262  int r = InitBuffer(sb, cfg);
263  if (r != SC_OK) {
264  FREE(cfg, sb, sizeof(StreamingBuffer));
265  sc_errno = r;
266  return NULL;
267  }
268  return sb;
269 }
270 
272 {
273  if (sb != NULL) {
274  SCLogDebug("sb->region.buf_size %u max %u", sb->region.buf_size, sb->buf_size_max);
275 
276  SBBFree(sb, cfg);
277  ListRegions(sb);
278  if (sb->region.buf != NULL) {
279  FREE(cfg, sb->region.buf, sb->region.buf_size);
280  sb->region.buf = NULL;
281  }
282 
283  for (StreamingBufferRegion *r = sb->region.next; r != NULL;) {
285  FREE(cfg, r->buf, r->buf_size);
286  FREE(cfg, r, sizeof(*r));
287  r = next;
288  }
289  sb->region.next = NULL;
290  sb->regions = sb->max_regions = 1;
291  }
292 }
293 
295 {
296  if (sb != NULL) {
297  StreamingBufferClear(sb, cfg);
298  FREE(cfg, sb, sizeof(StreamingBuffer));
299  }
300 }
301 
302 #ifdef DEBUG
303 static void SBBPrintList(StreamingBuffer *sb)
304 {
305  StreamingBufferBlock *sbb = NULL;
306  RB_FOREACH(sbb, SBB, &sb->sbb_tree) {
307  SCLogDebug("sbb: offset %" PRIu64 ", len %u", sbb->offset, sbb->len);
308  StreamingBufferBlock *next = SBB_RB_NEXT(sbb);
309  if (next) {
310  if ((sbb->offset + sbb->len) != next->offset) {
311  SCLogDebug("gap: offset %" PRIu64 ", len %" PRIu64, (sbb->offset + sbb->len),
312  next->offset - (sbb->offset + sbb->len));
313  }
314  }
315  }
316 }
317 #endif
318 
319 /* setup with gap between 2 blocks
320  *
321  * [block][gap][block]
322  **/
323 static int WARN_UNUSED SBBInit(StreamingBuffer *sb, const StreamingBufferConfig *cfg,
324  StreamingBufferRegion *region, uint32_t rel_offset, uint32_t data_len)
325 {
327  DEBUG_VALIDATE_BUG_ON(region->buf_offset > region->stream_offset + rel_offset);
328 
329  /* need to set up 2: existing data block and new data block */
330  StreamingBufferBlock *sbb = CALLOC(cfg, 1, sizeof(*sbb));
331  if (sbb == NULL) {
332  return sc_errno;
333  }
334  sbb->offset = sb->region.stream_offset;
335  sbb->len = sb->region.buf_offset;
336  (void)SBB_RB_INSERT(&sb->sbb_tree, sbb);
337  sb->head = sbb;
338  sb->sbb_size = sbb->len;
339 
340  StreamingBufferBlock *sbb2 = CALLOC(cfg, 1, sizeof(*sbb2));
341  if (sbb2 == NULL) {
342  return sc_errno;
343  }
344  sbb2->offset = region->stream_offset + rel_offset;
345  sbb2->len = data_len;
346  DEBUG_VALIDATE_BUG_ON(sbb2->offset < sbb->len);
347  SCLogDebug("sbb1 %" PRIu64 ", len %u, sbb2 %" PRIu64 ", len %u", sbb->offset, sbb->len,
348  sbb2->offset, sbb2->len);
349 
350  sb->sbb_size += sbb2->len;
351  if (SBB_RB_INSERT(&sb->sbb_tree, sbb2) != NULL) {
352  FREE(cfg, sbb2, sizeof(*sbb2));
353  return SC_EINVAL;
354  }
355 
356 #ifdef DEBUG
357  SBBPrintList(sb);
358 #endif
359  return SC_OK;
360 }
361 
362 /* setup with leading gap
363  *
364  * [gap][block]
365  **/
366 static int WARN_UNUSED SBBInitLeadingGap(
367  StreamingBuffer *sb, const StreamingBufferConfig *cfg, uint64_t offset, uint32_t data_len)
368 {
370 
371  StreamingBufferBlock *sbb = CALLOC(cfg, 1, sizeof(*sbb));
372  if (sbb == NULL) {
373  return sc_errno;
374  }
375  sbb->offset = offset;
376  sbb->len = data_len;
377 
378  sb->head = sbb;
379  sb->sbb_size = sbb->len;
380  (void)SBB_RB_INSERT(&sb->sbb_tree, sbb);
381 
382  SCLogDebug("sbb %" PRIu64 ", len %u", sbb->offset, sbb->len);
383 #ifdef DEBUG
384  SBBPrintList(sb);
385 #endif
386  return SC_OK;
387 }
388 
389 static inline void ConsolidateFwd(StreamingBuffer *sb, const StreamingBufferConfig *cfg,
390  StreamingBufferRegion *region, struct SBB *tree, StreamingBufferBlock *sa)
391 {
392  uint64_t sa_re = sa->offset + sa->len;
393  StreamingBufferBlock *tr, *s = sa;
394  RB_FOREACH_FROM(tr, SBB, s) {
395  if (sa == tr)
396  continue;
397 
398  const uint64_t tr_re = tr->offset + tr->len;
399  SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u re %" PRIu64, tr, tr->offset, tr->len, tr_re);
400 
401  if (sa_re < tr->offset) {
402  SCLogDebug("entirely before: %" PRIu64 " < %" PRIu64, sa_re, tr->offset);
403  break; // entirely before
404  }
405 
406  /* new block (sa) entirely eclipsed by in tree block (tr)
407  sa: [ ]
408  tr: [ ]
409  sa: [ ]
410  tr: [ ]
411  sa: [ ]
412  tr: [ ]
413  */
414  if (sa->offset >= tr->offset && sa_re <= tr_re) {
415  sb->sbb_size -= sa->len;
416  sa->len = tr->len;
417  sa->offset = tr->offset;
418  sa_re = sa->offset + sa->len;
419  SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u REMOVED ECLIPSED (sa overlapped by tr)", tr,
420  tr->offset, tr->len);
421  SBB_RB_REMOVE(tree, tr);
422  FREE(cfg, tr, sizeof(StreamingBufferBlock));
423  /* new block (sa) entire eclipses in tree block (tr)
424  sa: [ ]
425  tr: [ ]
426  sa: [ ]
427  tr: [ ]
428  sa: [ ]
429  tr: [ ]
430  */
431  } else if (sa->offset <= tr->offset && sa_re >= tr_re) {
432  SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u REMOVED ECLIPSED (tr overlapped by sa)", tr,
433  tr->offset, tr->len);
434  SBB_RB_REMOVE(tree, tr);
435  sb->sbb_size -= tr->len;
436  FREE(cfg, tr, sizeof(StreamingBufferBlock));
437 
438  SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64 " bo %u sz %u", sa,
439  sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
440  region->buf_size);
441  if (sa->offset == region->stream_offset &&
442  sa_re > (region->stream_offset + region->buf_offset)) {
443  DEBUG_VALIDATE_BUG_ON(sa_re < region->stream_offset);
444  DEBUG_VALIDATE_BUG_ON(sa_re - region->stream_offset > UINT32_MAX);
445  region->buf_offset = (uint32_t)(sa_re - region->stream_offset);
446  SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64
447  " bo %u sz %u BUF_OFFSET UPDATED",
448  sa, sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
449  region->buf_size);
450  }
451  /* new block (sa) extended by in tree block (tr)
452  sa: [ ]
453  tr: [ ]
454  sa: [ ]
455  tr: [ ]
456  */
457  } else if (sa->offset < tr->offset && // starts before
458  sa_re >= tr->offset && sa_re < tr_re) // ends inside
459  {
460  // merge. sb->sbb_size includes both so we need to adjust that too.
461  DEBUG_VALIDATE_BUG_ON(sa->len + tr->len > UINT32_MAX);
462  uint32_t combined_len = (uint32_t)(sa->len + tr->len);
463  DEBUG_VALIDATE_BUG_ON(tr_re - sa->offset > UINT32_MAX);
464  sa->len = (uint32_t)(tr_re - sa->offset);
465  sa_re = sa->offset + sa->len;
466  SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u REMOVED MERGED", tr, tr->offset, tr->len);
467  SBB_RB_REMOVE(tree, tr);
468  sb->sbb_size -= (combined_len - sa->len); // remove what we added twice
469  FREE(cfg, tr, sizeof(StreamingBufferBlock));
470  SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u RESULT", sa, sa->offset, sa->len);
471  SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64 " bo %u sz %u", sa,
472  sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
473  region->buf_size);
474  if (sa->offset == region->stream_offset &&
475  sa_re > (region->stream_offset + region->buf_offset)) {
476  DEBUG_VALIDATE_BUG_ON(sa_re < region->stream_offset);
477  DEBUG_VALIDATE_BUG_ON(sa_re - region->stream_offset > UINT32_MAX);
478  region->buf_offset = (uint32_t)(sa_re - region->stream_offset);
479  SCLogDebug("-> (fwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64
480  " bo %u sz %u BUF_OFFSET UPDATED",
481  sa, sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
482  region->buf_size);
483  }
484  }
485  }
486 }
487 
488 static inline void ConsolidateBackward(StreamingBuffer *sb, const StreamingBufferConfig *cfg,
489  StreamingBufferRegion *region, struct SBB *tree, StreamingBufferBlock *sa)
490 {
491  uint64_t sa_re = sa->offset + sa->len;
492  StreamingBufferBlock *tr, *s = sa;
493  RB_FOREACH_REVERSE_FROM(tr, SBB, s) {
494  if (sa == tr)
495  continue;
496  const uint64_t tr_re = tr->offset + tr->len;
497  SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u", tr, tr->offset, tr->len);
498 
499  if (sa->offset > tr_re)
500  break; // entirely after
501 
502  /* new block (sa) entirely eclipsed by in tree block (tr)
503  sa: [ ]
504  tr: [ ]
505  sa: [ ]
506  tr: [ ]
507  sa: [ ]
508  tr: [ ]
509  */
510  if (sa->offset >= tr->offset && sa_re <= tr_re) {
511  sb->sbb_size -= sa->len; // sa entirely eclipsed so remove double accounting
512  sa->len = tr->len;
513  sa->offset = tr->offset;
514  sa_re = sa->offset + sa->len;
515  SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u REMOVED ECLIPSED (sa overlapped by tr)", tr,
516  tr->offset, tr->len);
517  if (sb->head == tr)
518  sb->head = sa;
519  SBB_RB_REMOVE(tree, tr);
520  FREE(cfg, tr, sizeof(StreamingBufferBlock));
521  /* new block (sa) entire eclipses in tree block (tr)
522  sa: [ ]
523  tr: [ ]
524  sa: [ ]
525  tr: [ ]
526  sa: [ ]
527  tr: [ ]
528  */
529  } else if (sa->offset <= tr->offset && sa_re >= tr_re) {
530  SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u REMOVED ECLIPSED (tr overlapped by sa)", tr,
531  tr->offset, tr->len);
532  if (sb->head == tr)
533  sb->head = sa;
534  SBB_RB_REMOVE(tree, tr);
535  sb->sbb_size -= tr->len; // tr entirely eclipsed so remove double accounting
536  FREE(cfg, tr, sizeof(StreamingBufferBlock));
537 
538  SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64 " bo %u sz %u", sa,
539  sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
540  region->buf_size);
541 
542  if (sa->offset == region->stream_offset &&
543  sa_re > (region->stream_offset + region->buf_offset)) {
544  DEBUG_VALIDATE_BUG_ON(sa_re < region->stream_offset);
545  DEBUG_VALIDATE_BUG_ON(sa_re - region->stream_offset > UINT32_MAX);
546  region->buf_offset = (uint32_t)(sa_re - region->stream_offset);
547  SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64
548  " bo %u sz %u BUF_OFFSET UPDATED",
549  sa, sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
550  region->buf_size);
551  }
552 
553  /* new block (sa) extends in tree block (tr)
554  sa: [ ]
555  tr: [ ]
556  sa: [ ]
557  tr: [ ]
558  */
559  } else if (sa->offset > tr->offset && sa_re > tr_re && sa->offset <= tr_re) {
560  // merge. sb->sbb_size includes both so we need to adjust that too.
561  DEBUG_VALIDATE_BUG_ON(sa->len + tr->len > UINT32_MAX);
562  uint32_t combined_len = (uint32_t)(sa->len + tr->len);
563  DEBUG_VALIDATE_BUG_ON(sa_re - tr->offset > UINT32_MAX);
564  sa->len = (uint32_t)(sa_re - tr->offset);
565  sa->offset = tr->offset;
566  sa_re = sa->offset + sa->len;
567  SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u REMOVED MERGED", tr, tr->offset, tr->len);
568  if (sb->head == tr)
569  sb->head = sa;
570  SBB_RB_REMOVE(tree, tr);
571  sb->sbb_size -= (combined_len - sa->len); // remove what we added twice
572  FREE(cfg, tr, sizeof(StreamingBufferBlock));
573 
574  SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64 " bo %u sz %u", sa,
575  sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
576  region->buf_size);
577  if (sa->offset == region->stream_offset &&
578  sa_re > (region->stream_offset + region->buf_offset)) {
579  DEBUG_VALIDATE_BUG_ON(sa_re < region->stream_offset);
580  DEBUG_VALIDATE_BUG_ON(sa_re - region->stream_offset > UINT32_MAX);
581  region->buf_offset = (uint32_t)(sa_re - region->stream_offset);
582  SCLogDebug("-> (bwd) tr %p %" PRIu64 "/%u region %p so %" PRIu64
583  " bo %u sz %u BUF_OFFSET UPDATED",
584  sa, sa->offset, sa->len, region, region->stream_offset, region->buf_offset,
585  region->buf_size);
586  }
587  }
588  }
589 }
590 
591 /** \internal
592  * \param region the region that holds the new data
593  */
594 static int SBBUpdate(StreamingBuffer *sb, const StreamingBufferConfig *cfg,
595  StreamingBufferRegion *region, uint32_t rel_offset, uint32_t len)
596 {
597  struct SBB *tree = &sb->sbb_tree;
598  SCLogDebug("* inserting: %u/%u", rel_offset, len);
599 
600  StreamingBufferBlock *sbb = CALLOC(cfg, 1, sizeof(*sbb));
601  if (sbb == NULL) {
602  return sc_errno;
603  }
604  sbb->offset = region->stream_offset + rel_offset;
605  sbb->len = len;
606 
607  StreamingBufferBlock *res = SBB_RB_INSERT(tree, sbb);
608  if (res) {
609  // exact overlap
610  SCLogDebug("* insert failed: exact match in tree with %p %" PRIu64 "/%u", res, res->offset,
611  res->len);
612  FREE(cfg, sbb, sizeof(StreamingBufferBlock));
613  return SC_OK;
614  }
615  sb->sbb_size += len; // may adjust based on consolidation below
616 
617  /* handle backwards and forwards overlaps */
618  if (SBB_RB_PREV(sbb) == NULL) {
619  sb->head = sbb;
620  } else {
621  ConsolidateBackward(sb, cfg, region, tree, sbb);
622  }
623  ConsolidateFwd(sb, cfg, region, tree, sbb);
624 #ifdef DEBUG
625  SBBPrintList(sb);
626 #endif
627  if (sbb->offset == region->stream_offset) {
628  SCLogDebug("insert at region head");
629  region->buf_offset = sbb->len;
630  }
631  return SC_OK;
632 }
633 
634 static void SBBFree(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
635 {
636  StreamingBufferBlock *sbb = NULL, *safe = NULL;
637  RB_FOREACH_SAFE(sbb, SBB, &sb->sbb_tree, safe) {
638  SBB_RB_REMOVE(&sb->sbb_tree, sbb);
639  sb->sbb_size -= sbb->len;
640  FREE(cfg, sbb, sizeof(StreamingBufferBlock));
641  }
642  sb->head = NULL;
643 }
644 
645 static void SBBPrune(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
646 {
647  SCLogDebug("pruning %p to %" PRIu64, sb, sb->region.stream_offset);
648  StreamingBufferBlock *sbb = NULL, *safe = NULL;
649  RB_FOREACH_SAFE(sbb, SBB, &sb->sbb_tree, safe) {
650  /* completely beyond window, we're done */
651  if (sbb->offset >= sb->region.stream_offset) {
652  sb->head = sbb;
653  if (sbb->offset == sb->region.stream_offset) {
654  SCLogDebug("set buf_offset?");
655  if (sbb->offset == sb->region.stream_offset) {
656  SCLogDebug("set buf_offset to first sbb len %u", sbb->len);
658  sb->region.buf_offset = sbb->len;
659  }
660  }
661  break;
662  }
663 
664  /* partly before, partly beyond. Adjust */
665  if (sbb->offset < sb->region.stream_offset &&
666  sbb->offset + sbb->len > sb->region.stream_offset) {
667  DEBUG_VALIDATE_BUG_ON(sb->region.stream_offset - sbb->offset > UINT32_MAX);
668  uint32_t shrink_by = (uint32_t)(sb->region.stream_offset - sbb->offset);
669  DEBUG_VALIDATE_BUG_ON(shrink_by > sbb->len);
670  if (sbb->len >= shrink_by) {
671  sbb->len -= shrink_by;
672  sbb->offset += shrink_by;
673  sb->sbb_size -= shrink_by;
674  SCLogDebug("shrunk by %u", shrink_by);
676  }
677  sb->head = sbb;
678  if (sbb->offset == sb->region.stream_offset) {
679  SCLogDebug("set buf_offset to first sbb len %u", sbb->len);
681  sb->region.buf_offset = sbb->len;
682  }
683  break;
684  }
685 
686  SBB_RB_REMOVE(&sb->sbb_tree, sbb);
687  /* either we set it again for the next sbb, or there isn't any */
688  sb->head = NULL;
689  sb->sbb_size -= sbb->len;
690  SCLogDebug("sb %p removed %p %" PRIu64 ", %u", sb, sbb, sbb->offset, sbb->len);
691  FREE(cfg, sbb, sizeof(StreamingBufferBlock));
692  }
693 #ifdef DEBUG
694  SBBPrintList(sb);
695 #endif
696 }
697 
698 static inline uint32_t ToNextMultipleOf(const uint32_t in, const uint32_t m)
699 {
700  uint32_t r = in;
701  if (m > 0) {
702  const uint32_t x = in % m;
703  if (x != 0) {
704  r = (in - x) + m;
705  }
706  }
707  return r;
708 }
709 
710 static thread_local bool g2s_warn_once = false;
711 
712 static inline int WARN_UNUSED GrowRegionToSize(StreamingBuffer *sb,
713  const StreamingBufferConfig *cfg, StreamingBufferRegion *region, const uint32_t size)
714 {
715  DEBUG_VALIDATE_BUG_ON(region->buf_size > BIT_U32(30));
716  if (size > BIT_U32(30)) { // 1GiB
717  if (!g2s_warn_once) {
718  SCLogWarning("StreamingBuffer::GrowRegionToSize() tried to alloc %u bytes, exceeds "
719  "limit of %" PRIu32,
720  size, BIT_U32(30));
721  g2s_warn_once = true;
722  }
723  return SC_ELIMIT;
724  }
725 
726  /* try to grow in multiples of cfg->buf_size */
727  const uint32_t grow = ToNextMultipleOf(size, cfg->buf_size);
728  SCLogDebug("grow %u", grow);
729  if (grow <= region->buf_size) {
730  // do not try to shrink, and do not memset with diff having unsigned underflow
731  return SC_OK;
732  }
733 
734  void *ptr = REALLOC(cfg, region->buf, region->buf_size, grow);
735  if (ptr == NULL) {
736  if (sc_errno == SC_OK)
738  return sc_errno;
739  }
740  /* for safe printing and general caution, lets memset the
741  * new data to 0 */
742  size_t diff = grow - region->buf_size;
743  void *new_mem = ((char *)ptr) + region->buf_size;
744  memset(new_mem, 0, diff);
745 
746  region->buf = ptr;
747  region->buf_size = grow;
748  SCLogDebug("grown buffer to %u", grow);
749 #ifdef DEBUG
750  if (region->buf_size > sb->buf_size_max) {
751  sb->buf_size_max = region->buf_size;
752  }
753 #endif
754  return SC_OK;
755 }
756 
757 static int WARN_UNUSED GrowToSize(
758  StreamingBuffer *sb, const StreamingBufferConfig *cfg, uint32_t size)
759 {
760  return GrowRegionToSize(sb, cfg, &sb->region, size);
761 }
762 
763 static inline bool RegionBeforeOffset(const StreamingBufferRegion *r, const uint64_t o)
764 {
765  return (r->stream_offset + r->buf_size <= o);
766 }
767 
768 static inline bool RegionContainsOffset(const StreamingBufferRegion *r, const uint64_t o)
769 {
770  return (o >= r->stream_offset && (r->stream_offset + r->buf_size) >= o);
771 }
772 
773 /** \internal
774  * \brief slide to offset for regions
775  *
776  *
777  * [ main ] [ gap ] [ aux ] [ gap ] [ aux ]
778  * ^
779  * - main reset to 0
780  *
781  *
782  * [ main ] [ gap ] [ aux ] [ gap ] [ aux ]
783  * ^
784  * - main shift
785  *
786  * [ main ] [ gap ] [ aux ] [ gap ] [ aux ]
787  * ^
788  * - main reset
789  * - move aux into main
790  * - free aux
791  * - shift
792  *
793  * [ main ] [ gap ] [ aux ] [ gap ] [ aux ]
794  * ^
795  * - main reset
796  * - move aux into main
797  * - free aux
798  * - no shift
799  */
800 static inline void StreamingBufferSlideToOffsetWithRegions(
801  StreamingBuffer *sb, const StreamingBufferConfig *cfg, const uint64_t slide_offset)
802 {
803  ListRegions(sb);
804  DEBUG_VALIDATE_BUG_ON(slide_offset == sb->region.stream_offset);
805 
806  SCLogDebug("slide_offset %" PRIu64, slide_offset);
807  SCLogDebug("main: offset %" PRIu64 " buf %p size %u offset %u", sb->region.stream_offset,
808  sb->region.buf, sb->region.buf_size, sb->region.buf_offset);
809 
810  StreamingBufferRegion *to_shift = NULL;
811  const bool main_is_oow = RegionBeforeOffset(&sb->region, slide_offset);
812  if (main_is_oow) {
813  SCLogDebug("main_is_oow");
814  if (sb->region.buf != NULL) {
815  SCLogDebug("clearing main");
816  FREE(cfg, sb->region.buf, sb->region.buf_size);
817  sb->region.buf = NULL;
818  sb->region.buf_size = 0;
819  sb->region.buf_offset = 0;
820  sb->region.stream_offset = slide_offset;
821  } else {
822  sb->region.stream_offset = slide_offset;
823  }
824 
825  /* remove regions that are out of window & select the region to
826  * become the new main */
827  StreamingBufferRegion *prev = &sb->region;
828  for (StreamingBufferRegion *r = sb->region.next; r != NULL;) {
829  SCLogDebug("r %p so %" PRIu64 ", re %" PRIu64, r, r->stream_offset,
830  r->stream_offset + r->buf_offset);
832  if (RegionBeforeOffset(r, slide_offset)) {
833  SCLogDebug("r %p so %" PRIu64 ", re %" PRIu64 " -> before", r, r->stream_offset,
834  r->stream_offset + r->buf_offset);
835  DEBUG_VALIDATE_BUG_ON(r == &sb->region);
836  prev->next = next;
837 
838  FREE(cfg, r->buf, r->buf_size);
839  FREE(cfg, r, sizeof(*r));
840  sb->regions--;
841  DEBUG_VALIDATE_BUG_ON(sb->regions == 0);
842  } else if (RegionContainsOffset(r, slide_offset)) {
843  SCLogDebug("r %p so %" PRIu64 ", re %" PRIu64 " -> within", r, r->stream_offset,
844  r->stream_offset + r->buf_offset);
845  /* remove from list, we will xfer contents to main below */
846  prev->next = next;
847  to_shift = r;
848  break;
849  } else {
850  SCLogDebug("r %p so %" PRIu64 ", re %" PRIu64 " -> post", r, r->stream_offset,
851  r->stream_offset + r->buf_offset);
852  /* implied beyond slide offset */
853  DEBUG_VALIDATE_BUG_ON(r->stream_offset < slide_offset);
854  break;
855  }
856  r = next;
857  }
858  SCLogDebug("to_shift %p", to_shift);
859 
860  // this region is main, or will xfer its buffer to main
861  if (to_shift && to_shift != &sb->region) {
862  SCLogDebug("main: offset %" PRIu64 " buf %p size %u offset %u", to_shift->stream_offset,
863  to_shift->buf, to_shift->buf_size, to_shift->buf_offset);
864  DEBUG_VALIDATE_BUG_ON(sb->region.buf != NULL);
865 
866  sb->region.buf = to_shift->buf;
867  sb->region.stream_offset = to_shift->stream_offset;
868  sb->region.buf_offset = to_shift->buf_offset;
869  sb->region.buf_size = to_shift->buf_size;
870  sb->region.next = to_shift->next;
871 
872  BUG_ON(to_shift == &sb->region);
873  FREE(cfg, to_shift, sizeof(*to_shift));
874  to_shift = &sb->region;
875  sb->regions--;
876  DEBUG_VALIDATE_BUG_ON(sb->regions == 0);
877  }
878 
879  } else {
880  to_shift = &sb->region;
881  SCLogDebug("shift start region %p", to_shift);
882  }
883 
884  // this region is main, or will xfer its buffer to main
885  if (to_shift) {
886  // Do the shift. If new region is exactly at the slide offset we can skip this.
887  DEBUG_VALIDATE_BUG_ON(to_shift->stream_offset > slide_offset);
888  DEBUG_VALIDATE_BUG_ON(slide_offset - to_shift->stream_offset > UINT32_MAX);
889  const uint32_t s = (uint32_t)(slide_offset - to_shift->stream_offset);
890  if (s > 0) {
891  const uint32_t new_data_size = to_shift->buf_size - s;
892  uint32_t new_mem_size = ToNextMultipleOf(new_data_size, cfg->buf_size);
893 
894  /* see if after the slide we'd overlap with the next region. If so, we need
895  * to consolidate them into one. Error handling is a bit peculiar. We need
896  * to grow a region, move data from another region into it, then free the
897  * other region. This could fail if we're close to the memcap. In this case
898  * we relax our rounding up logic so we only shrink and don't merge the 2
899  * regions after all. */
900  if (to_shift->next && slide_offset + new_mem_size >= to_shift->next->stream_offset) {
901  StreamingBufferRegion *start = to_shift;
902  StreamingBufferRegion *next = start->next;
903  const uint64_t next_re = next->stream_offset + next->buf_size;
904  DEBUG_VALIDATE_BUG_ON(next_re - slide_offset > UINT32_MAX);
905  const uint32_t mem_size =
906  ToNextMultipleOf((uint32_t)(next_re - slide_offset), cfg->buf_size);
907 
908  /* using next as the new main */
909  if (start->buf_size < next->buf_size) {
910  SCLogDebug("replace main with the next bigger region");
911 
912  DEBUG_VALIDATE_BUG_ON(next->stream_offset - slide_offset > UINT32_MAX);
913  const uint32_t next_data_offset =
914  (uint32_t)(next->stream_offset - slide_offset);
915  const uint32_t prev_buf_size = next->buf_size;
916  DEBUG_VALIDATE_BUG_ON(slide_offset - start->stream_offset > UINT32_MAX);
917  const uint32_t start_data_offset =
918  (uint32_t)(slide_offset - start->stream_offset);
919  DEBUG_VALIDATE_BUG_ON(start_data_offset > start->buf_size);
920  if (start_data_offset > start->buf_size) {
921  new_mem_size = new_data_size;
922  goto just_main;
923  }
924  /* expand "next" to include relevant part of "start" */
925  if (GrowRegionToSize(sb, cfg, next, mem_size) != 0) {
926  new_mem_size = new_data_size;
927  goto just_main;
928  }
929  SCLogDebug("region now sized %u", mem_size);
930 
931  // slide "next":
932  // pre-grow: [nextnextnext]
933  // post-grow [nextnextnextXXX]
934  // post-move [XXXnextnextnext]
935  memmove(next->buf + next_data_offset, next->buf, prev_buf_size);
936 
937  // move portion of "start" into "next"
938  //
939  // start: [ooooookkkkk] (o: old, k: keep)
940  // pre-next [xxxxxnextnextnext]
941  // post-next [kkkkknextnextnext]
942  const uint32_t start_data_size = start->buf_size - start_data_offset;
943  memcpy(next->buf, start->buf + start_data_offset, start_data_size);
944 
945  // free "start"s buffer, we will use the one from "next"
946  FREE(cfg, start->buf, start->buf_size);
947 
948  // update "main" to use "next"
949  start->stream_offset = slide_offset;
950  start->buf = next->buf;
951  start->buf_size = next->buf_size;
952  start->next = next->next;
953 
954  // free "next"
955  FREE(cfg, next, sizeof(*next));
956  sb->regions--;
957  DEBUG_VALIDATE_BUG_ON(sb->regions == 0);
958  goto done;
959  } else {
960  /* using "main", expand to include "next" */
961  if (mem_size > start->buf_size) {
962  // Check that start->buf_size is actually not big enough
963  // As mem_size computation and earlier checks do not make it clear.
964  if (GrowRegionToSize(sb, cfg, start, mem_size) != 0) {
965  new_mem_size = new_data_size;
966  goto just_main;
967  }
968  }
969  SCLogDebug("start->buf now size %u", mem_size);
970 
971  // slide "start"
972  // pre: [xxxxxxxAAA]
973  // post: [AAAxxxxxxx]
974  SCLogDebug("s %u new_data_size %u", s, new_data_size);
975  memmove(start->buf, start->buf + s, new_data_size);
976 
977  // copy in "next"
978  // pre: [AAAxxxxxxx]
979  // [BBBBBBB]
980  // post: [AAABBBBBBB]
981  SCLogDebug("copy next->buf %p/%u to start->buf offset %u", next->buf,
982  next->buf_size, new_data_size);
983  memcpy(start->buf + new_data_size, next->buf, next->buf_size);
984 
985  start->stream_offset = slide_offset;
986  start->next = next->next;
987 
988  // free "next"
989  FREE(cfg, next->buf, next->buf_size);
990  FREE(cfg, next, sizeof(*next));
991  sb->regions--;
992  DEBUG_VALIDATE_BUG_ON(sb->regions == 0);
993  goto done;
994  }
995  }
996 
997  just_main:
998  SCLogDebug("s %u new_data_size %u", s, new_data_size);
999  memmove(to_shift->buf, to_shift->buf + s, new_data_size);
1000  /* shrink memory region. If this fails we keep the old */
1001  void *ptr = REALLOC(cfg, to_shift->buf, to_shift->buf_size, new_mem_size);
1002  if (ptr != NULL) {
1003  to_shift->buf = ptr;
1004  to_shift->buf_size = new_mem_size;
1005  SCLogDebug("new buf_size %u", to_shift->buf_size);
1006  }
1007  if (s < to_shift->buf_offset)
1008  to_shift->buf_offset -= s;
1009  else
1010  to_shift->buf_offset = 0;
1011  to_shift->stream_offset = slide_offset;
1012  }
1013  }
1014 
1015 done:
1016  SCLogDebug("main: offset %" PRIu64 " buf %p size %u offset %u", sb->region.stream_offset,
1017  sb->region.buf, sb->region.buf_size, sb->region.buf_offset);
1018  SCLogDebug("end of slide");
1019 }
1020 
1021 /**
1022  * \brief slide to absolute offset
1023  * \todo if sliding beyond window, we could perhaps reset?
1024  */
1026  StreamingBuffer *sb, const StreamingBufferConfig *cfg, uint64_t offset)
1027 {
1028  SCLogDebug("sliding to offset %" PRIu64, offset);
1029  ListRegions(sb);
1030 #ifdef DEBUG
1031  SBBPrintList(sb);
1032 #endif
1033 
1034  if (sb->region.next) {
1035  StreamingBufferSlideToOffsetWithRegions(sb, cfg, offset);
1036  SBBPrune(sb, cfg);
1037  SCLogDebug("post SBBPrune");
1038  ListRegions(sb);
1039 #ifdef DEBUG
1040  SBBPrintList(sb);
1041 #endif
1042  DEBUG_VALIDATE_BUG_ON(sb->region.buf != NULL && sb->region.buf_size == 0);
1046  sb->head->len > sb->region.buf_offset);
1048  return;
1049  }
1050 
1051  if (offset > sb->region.stream_offset) {
1052  DEBUG_VALIDATE_BUG_ON(offset - sb->region.stream_offset > UINT32_MAX);
1053  const uint32_t slide = (uint32_t)(offset - sb->region.stream_offset);
1054  if (sb->head != NULL) {
1055  /* have sbb's, so can't rely on buf_offset for the slide */
1056  if (slide < sb->region.buf_size) {
1057  const uint32_t size = sb->region.buf_size - slide;
1058  SCLogDebug("sliding %u forward, size of original buffer left after slide %u", slide,
1059  size);
1060  memmove(sb->region.buf, sb->region.buf + slide, size);
1061  if (sb->region.buf_offset > slide) {
1062  sb->region.buf_offset -= slide;
1063  } else {
1064  sb->region.buf_offset = 0;
1065  }
1066  } else {
1067  sb->region.buf_offset = 0;
1068  }
1069  sb->region.stream_offset = offset;
1070  } else {
1071  /* no sbb's, so we can use buf_offset */
1072  if (offset <= sb->region.stream_offset + sb->region.buf_offset) {
1073  const uint32_t size = sb->region.buf_offset - slide;
1074  SCLogDebug("sliding %u forward, size of original buffer left after slide %u", slide,
1075  size);
1076  memmove(sb->region.buf, sb->region.buf + slide, size);
1077  sb->region.stream_offset = offset;
1078  sb->region.buf_offset = size;
1079  } else {
1080  /* moved past all data */
1081  sb->region.stream_offset = offset;
1082  sb->region.buf_offset = 0;
1083  }
1084  }
1085  SBBPrune(sb, cfg);
1086  }
1087 #ifdef DEBUG
1088  SBBPrintList(sb);
1089 #endif
1091 }
1092 
1093 static int DataFits(const StreamingBuffer *sb, const uint32_t len)
1094 {
1095  uint64_t buf_offset64 = sb->region.buf_offset;
1096  uint64_t len64 = len;
1097  if (len64 + buf_offset64 > UINT32_MAX) {
1098  return -1;
1099  }
1100  return sb->region.buf_offset + len <= sb->region.buf_size;
1101 }
1102 
1104  StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len)
1105 {
1106  DEBUG_VALIDATE_BUG_ON(seg == NULL);
1107  DEBUG_VALIDATE_BUG_ON(data_len > BIT_U32(27)); // 128MiB is excessive already
1108 
1109  if (sb->region.buf == NULL) {
1110  if (InitBuffer(sb, cfg) == -1)
1111  return -1;
1112  }
1113 
1114  int r = DataFits(sb, data_len);
1115  if (r < 0) {
1117  return -1;
1118  } else if (r == 0) {
1119  if (sb->region.buf_size == 0) {
1120  if (GrowToSize(sb, cfg, data_len) != SC_OK)
1121  return -1;
1122  } else {
1123  if (GrowToSize(sb, cfg, sb->region.buf_offset + data_len) != SC_OK)
1124  return -1;
1125  }
1126  }
1127  DEBUG_VALIDATE_BUG_ON(DataFits(sb, data_len) != 1);
1128  if (DataFits(sb, data_len) != 1)
1129  return -1;
1130 
1131  memcpy(sb->region.buf + sb->region.buf_offset, data, data_len);
1132  seg->stream_offset = sb->region.stream_offset + sb->region.buf_offset;
1133  seg->segment_len = data_len;
1134  uint32_t rel_offset = sb->region.buf_offset;
1135  sb->region.buf_offset += data_len;
1136 
1137  if (!RB_EMPTY(&sb->sbb_tree)) {
1138  return SBBUpdate(sb, cfg, &sb->region, rel_offset, data_len);
1139  } else {
1140  return 0;
1141  }
1142 }
1143 
1144 /**
1145  * \brief add data w/o tracking a segment
1146  */
1148  const uint8_t *data, uint32_t data_len)
1149 {
1150  DEBUG_VALIDATE_BUG_ON(data_len > BIT_U32(27)); // 128MiB is excessive already
1151 
1152  if (sb->region.buf == NULL) {
1153  if (InitBuffer(sb, cfg) == -1)
1154  return -1;
1155  }
1156 
1157  int r = DataFits(sb, data_len);
1158  if (r < 0) {
1160  return -1;
1161  } else if (r == 0) {
1162  if (sb->region.buf_size == 0) {
1163  if (GrowToSize(sb, cfg, data_len) != SC_OK)
1164  return -1;
1165  } else {
1166  if (GrowToSize(sb, cfg, sb->region.buf_offset + data_len) != SC_OK)
1167  return -1;
1168  }
1169  }
1170  DEBUG_VALIDATE_BUG_ON(DataFits(sb, data_len) != 1);
1171 
1172  memcpy(sb->region.buf + sb->region.buf_offset, data, data_len);
1173  uint32_t rel_offset = sb->region.buf_offset;
1174  sb->region.buf_offset += data_len;
1175 
1176  if (!RB_EMPTY(&sb->sbb_tree)) {
1177  return SBBUpdate(sb, cfg, &sb->region, rel_offset, data_len);
1178  } else {
1179  return 0;
1180  }
1181 }
1182 
1183 static int DataFitsAtOffset(
1184  const StreamingBufferRegion *region, const uint32_t len, const uint32_t offset)
1185 {
1186  const uint64_t offset64 = offset;
1187  const uint64_t len64 = len;
1188  if (offset64 + len64 > UINT32_MAX)
1189  return -1;
1190  return (offset + len <= region->buf_size);
1191 }
1192 
1193 #if defined(DEBUG) || defined(DEBUG_VALIDATION)
1194 static void Validate(const StreamingBuffer *sb)
1195 {
1196  bool bail = false;
1197  uint32_t cnt = 0;
1198  for (const StreamingBufferRegion *r = &sb->region; r != NULL; r = r->next) {
1199  cnt++;
1200  if (r->next) {
1201  bail |= ((r->stream_offset + r->buf_size) > r->next->stream_offset);
1202  }
1203 
1204  bail |= (r->buf != NULL && r->buf_size == 0);
1205  bail |= (r->buf_offset > r->buf_size);
1206  }
1207  // leading gap, so buf_offset should be 0?
1208  if (sb->head && sb->head->offset > sb->region.stream_offset) {
1209  SCLogDebug("leading gap of size %" PRIu64, sb->head->offset - sb->region.stream_offset);
1210  BUG_ON(sb->region.buf_offset != 0);
1211  }
1212 
1213  if (sb->head && sb->head->offset == sb->region.stream_offset) {
1214  BUG_ON(sb->head->len > sb->region.buf_offset);
1215  BUG_ON(sb->head->len < sb->region.buf_offset);
1216  }
1217  BUG_ON(sb->regions != cnt);
1218  BUG_ON(bail);
1219 }
1220 #endif
1221 
1222 static void ListRegions(StreamingBuffer *sb)
1223 {
1224  if (sb->region.buf == NULL && sb->region.buf_offset == 0 && sb->region.next == NULL)
1225  return;
1226 #if defined(DEBUG) && DUMP_REGIONS == 1
1227  uint32_t cnt = 0;
1228  for (StreamingBufferRegion *r = &sb->region; r != NULL; r = r->next) {
1229  cnt++;
1230  char gap[64] = "";
1231  if (r->next) {
1232  snprintf(gap, sizeof(gap), "[ gap:%" PRIu64 " ]",
1233  r->next->stream_offset - (r->stream_offset + r->buf_size));
1234  }
1235 
1236  printf("[ %s offset:%" PRIu64 " size:%u offset:%u ]%s", r == &sb->region ? "main" : "aux",
1237  r->stream_offset, r->buf_size, r->buf_offset, gap);
1238  }
1239  printf("(max %u, cnt %u, sb->regions %u)\n", sb->max_regions, cnt, sb->regions);
1240  bool at_least_one = false;
1241  uint64_t last_re = sb->region.stream_offset;
1242  StreamingBufferBlock *sbb = NULL;
1243  RB_FOREACH(sbb, SBB, &sb->sbb_tree)
1244  {
1245  if (last_re != sbb->offset) {
1246  printf("[ gap:%" PRIu64 " ]", sbb->offset - last_re);
1247  }
1248  printf("[ sbb offset:%" PRIu64 " len:%u ]", sbb->offset, sbb->len);
1249  at_least_one = true;
1250  last_re = sbb->offset + sbb->len;
1251  }
1252  if (at_least_one)
1253  printf("\n");
1254 #endif
1255 #if defined(DEBUG) || defined(DEBUG_VALIDATION)
1256  StreamingBufferBlock *sbb2 = NULL;
1257  RB_FOREACH(sbb2, SBB, &sb->sbb_tree)
1258  {
1259  const uint8_t *_data = NULL;
1260  uint32_t _data_len = 0;
1261  (void)StreamingBufferSBBGetData(sb, sbb2, &_data, &_data_len);
1262  }
1263  Validate(sb);
1264 #endif
1265 }
1266 
1267 /** \internal
1268  * \brief process insert by consolidating the affected regions into one
1269  * \note sets sc_errno
1270  */
1271 static StreamingBufferRegion *BufferInsertAtRegionConsolidate(StreamingBuffer *sb,
1273  StreamingBufferRegion *src_start, StreamingBufferRegion *src_end,
1274  const uint64_t data_offset, const uint32_t data_len, StreamingBufferRegion *prev,
1275  uint32_t dst_buf_size)
1276 {
1277  int retval;
1278 #ifdef DEBUG
1279  const uint64_t data_re = data_offset + data_len;
1280  SCLogDebug("sb %p dst %p src_start %p src_end %p data_offset %" PRIu64
1281  "/data_len %u/data_re %" PRIu64,
1282  sb, dst, src_start, src_end, data_offset, data_len, data_re);
1283 #endif
1284 
1285  // 1. determine size and offset for dst.
1286  const uint64_t dst_offset = MIN(src_start->stream_offset, data_offset);
1287  DEBUG_VALIDATE_BUG_ON(dst_offset < sb->region.stream_offset);
1288  const uint32_t dst_size = dst_buf_size;
1289  SCLogDebug("dst_size %u", dst_size);
1290 
1291  // 2. resize dst
1292  const uint32_t old_size = dst->buf_size;
1293  DEBUG_VALIDATE_BUG_ON(dst->stream_offset - dst_offset > UINT32_MAX);
1294  const uint32_t dst_copy_offset = (uint32_t)(dst->stream_offset - dst_offset);
1295 #ifdef DEBUG
1296  const uint32_t old_offset = dst->buf_offset;
1297  SCLogDebug("old_size %u, old_offset %u, dst_copy_offset %u", old_size, old_offset,
1298  dst_copy_offset);
1299 #endif
1300  if ((retval = GrowRegionToSize(sb, cfg, dst, dst_size)) != SC_OK) {
1301  sc_errno = retval;
1302  return NULL;
1303  }
1304  SCLogDebug("resized to %u -> %u", dst_size, dst->buf_size);
1305  /* validate that the size is exactly what we asked for */
1306  DEBUG_VALIDATE_BUG_ON(dst_size != dst->buf_size);
1307  if (dst_copy_offset != 0)
1308  memmove(dst->buf + dst_copy_offset, dst->buf, old_size);
1309  if (dst_offset != dst->stream_offset) {
1310  dst->stream_offset = dst_offset;
1311  // buf_offset no longer valid, reset.
1312  dst->buf_offset = 0;
1313  }
1314 
1315  uint32_t new_offset = src_start->buf_offset;
1316  if (data_offset == src_start->stream_offset + src_start->buf_offset) {
1317  new_offset += data_len;
1318  }
1319 
1320  bool start_is_main = false;
1321  if (src_start == &sb->region) {
1322  DEBUG_VALIDATE_BUG_ON(src_start->stream_offset != dst_offset);
1323 
1324  start_is_main = true;
1325  SCLogDebug("src_start is main region");
1326  if (src_start != dst)
1327  memcpy(dst->buf, src_start->buf, src_start->buf_offset);
1328  if (src_start == src_end) {
1329  SCLogDebug("src_start == src_end == main, we're done");
1330  DEBUG_VALIDATE_BUG_ON(src_start != dst);
1331  return src_start;
1332  }
1333  prev = src_start;
1334  src_start = src_start->next; // skip in the loop below
1335  }
1336 
1337  // 3. copy all regions from src_start to dst_start into the new region
1338  for (StreamingBufferRegion *r = src_start; r != NULL;) {
1339  SCLogDebug("r %p %" PRIu64 ", offset %u, len %u, %s, last %s", r, r->stream_offset,
1340  r->buf_offset, r->buf_size, r == &sb->region ? "main" : "aux",
1341  BOOL2STR(r == src_end));
1342  // skip dst
1343  if (r == dst) {
1344  SCLogDebug("skipping r %p as it is 'dst'", r);
1345  if (r == src_end)
1346  break;
1347  prev = r;
1348  r = r->next;
1349  continue;
1350  }
1351  DEBUG_VALIDATE_BUG_ON(r->stream_offset - dst_offset > UINT32_MAX);
1352  const uint32_t target_offset = (uint32_t)(r->stream_offset - dst_offset);
1353  SCLogDebug("r %p: target_offset %u", r, target_offset);
1354  DEBUG_VALIDATE_BUG_ON(target_offset > dst->buf_size);
1355  DEBUG_VALIDATE_BUG_ON(target_offset + r->buf_size > dst->buf_size);
1356  memcpy(dst->buf + target_offset, r->buf, r->buf_size);
1357 
1359  FREE(cfg, r->buf, r->buf_size);
1360  FREE(cfg, r, sizeof(*r));
1361  sb->regions--;
1362  DEBUG_VALIDATE_BUG_ON(sb->regions == 0);
1363 
1364  DEBUG_VALIDATE_BUG_ON(prev == NULL && src_start != &sb->region);
1365  if (prev != NULL) {
1366  SCLogDebug("setting prev %p next to %p (was %p)", prev, next, prev->next);
1367  prev->next = next;
1368  } else {
1369  SCLogDebug("no prev yet");
1370  }
1371 
1372  if (r == src_end)
1373  break;
1374  r = next;
1375  }
1376 
1377  /* special handling of main region being the start, but not the
1378  * region we expand. In this case we'll have main and dst. We will
1379  * move the buffer from dst into main and free dst. */
1380  if (start_is_main && dst != &sb->region) {
1382  SCLogDebug("start_is_main && dst != main region");
1383  FREE(cfg, sb->region.buf, sb->region.buf_size);
1384  sb->region.buf = dst->buf;
1385  sb->region.buf_size = dst->buf_size;
1386  sb->region.buf_offset = new_offset;
1387  SCLogDebug("sb->region.buf_offset set to %u", sb->region.buf_offset);
1388  sb->region.next = dst->next;
1389  FREE(cfg, dst, sizeof(*dst));
1390  dst = &sb->region;
1391  sb->regions--;
1392  DEBUG_VALIDATE_BUG_ON(sb->regions == 0);
1393  } else {
1394  SCLogDebug("dst: %p next %p", dst, dst->next);
1395  }
1396 
1397  SCLogDebug("returning dst %p stream_offset %" PRIu64 " buf_offset %u buf_size %u", dst,
1398  dst->stream_offset, dst->buf_offset, dst->buf_size);
1399  return dst;
1400 }
1401 
1402 /** \note sets sc_errno */
1403 static StreamingBufferRegion *BufferInsertAtRegionDo(StreamingBuffer *sb,
1404  const StreamingBufferConfig *cfg, const uint64_t offset, const uint32_t len)
1405 {
1406  SCLogDebug("offset %" PRIu64 ", len %u", offset, len);
1407  StreamingBufferRegion *start_prev = NULL;
1408  StreamingBufferRegion *start =
1409  FindFirstRegionForOffset(cfg, &sb->region, offset, len, &start_prev);
1410  if (start) {
1411  const uint64_t insert_re = offset + len;
1412  const uint64_t insert_start_offset = MIN(start->stream_offset, offset);
1413  uint64_t insert_adjusted_re = insert_re;
1414 
1415  SCLogDebug("start region %p/%" PRIu64 "/%u", start, start->stream_offset, start->buf_size);
1416  StreamingBufferRegion *big = FindLargestRegionForOffset(cfg, start, offset, len);
1417  DEBUG_VALIDATE_BUG_ON(big == NULL);
1418  if (big == NULL) {
1419  sc_errno = SC_EINVAL;
1420  return NULL;
1421  }
1422  SCLogDebug("big region %p/%" PRIu64 "/%u", big, big->stream_offset, big->buf_size);
1423  StreamingBufferRegion *end = FindRightEdge(cfg, big, offset, len);
1424  DEBUG_VALIDATE_BUG_ON(end == NULL);
1425  if (end == NULL) {
1426  sc_errno = SC_EINVAL;
1427  return NULL;
1428  }
1429  insert_adjusted_re = MAX(insert_adjusted_re, (end->stream_offset + end->buf_size));
1430 
1431  DEBUG_VALIDATE_BUG_ON(insert_adjusted_re - insert_start_offset > UINT32_MAX);
1432  uint32_t new_buf_size = ToNextMultipleOf(
1433  (uint32_t)(insert_adjusted_re - insert_start_offset), cfg->buf_size);
1434  SCLogDebug("new_buf_size %u", new_buf_size);
1435 
1436  /* see if our new buf size + cfg->buf_size margin leads to an overlap with the next region.
1437  * If so, include that in the consolidation. */
1438  if (end->next != NULL && new_buf_size + insert_start_offset > end->next->stream_offset) {
1439  SCLogDebug("adjusted end from %p to %p", end, end->next);
1440  end = end->next;
1441  insert_adjusted_re = MAX(insert_adjusted_re, (end->stream_offset + end->buf_size));
1442  DEBUG_VALIDATE_BUG_ON(insert_adjusted_re - insert_start_offset > UINT32_MAX);
1443  new_buf_size = ToNextMultipleOf(
1444  (uint32_t)(insert_adjusted_re - insert_start_offset), cfg->buf_size);
1445  SCLogDebug("new_buf_size %u", new_buf_size);
1446  }
1447 
1448  SCLogDebug("end region %p/%" PRIu64 "/%u", end, end->stream_offset, end->buf_size);
1449  /* sets sc_errno */
1450  StreamingBufferRegion *ret = BufferInsertAtRegionConsolidate(
1451  sb, cfg, big, start, end, offset, len, start_prev, new_buf_size);
1452  return ret;
1453  } else {
1454  /* if there was no region we can use we add a new region and insert it */
1455  StreamingBufferRegion *append = &sb->region;
1456  for (StreamingBufferRegion *r = append; r != NULL; r = r->next) {
1457  if (r->stream_offset > offset) {
1458  break;
1459  } else {
1460  append = r;
1461  }
1462  }
1463 
1464  SCLogDebug("no matching region found, append to %p (%s)", append,
1465  append == &sb->region ? "main" : "aux");
1466  /* sets sc_errno */
1467  StreamingBufferRegion *add = InitBufferRegion(sb, cfg, len);
1468  if (add == NULL)
1469  return NULL;
1470  add->stream_offset = offset;
1471  add->next = append->next;
1472  append->next = add;
1473  SCLogDebug("new region %p offset %" PRIu64, add, add->stream_offset);
1474  return add;
1475  }
1476 }
1477 
1478 /** \internal
1479  * \brief return the region to put the new data in
1480  *
1481  * Will find an existing region, expand it if needed. If no existing region exists or is
1482  * a good fit, it will try to set up a new region. If the region then overlaps or gets
1483  * too close to the next, merge them.
1484  *
1485  * \note sets sc_errno
1486  */
1487 static StreamingBufferRegion *BufferInsertAtRegion(StreamingBuffer *sb,
1488  const StreamingBufferConfig *cfg, const uint32_t data_len, const uint64_t data_offset)
1489 {
1490  SCLogDebug("data_offset %" PRIu64 ", data_len %u, re %" PRIu64, data_offset, data_len,
1491  data_offset + data_len);
1492  ListRegions(sb);
1493 
1494  if (RegionsIntersect(cfg, &sb->region, data_offset, data_offset + data_len)) {
1495  SCLogDebug("data_offset %" PRIu64 ", data_len %u intersects with main region (next %p)",
1496  data_offset, data_len, sb->region.next);
1497  if (sb->region.next == NULL ||
1498  !RegionsIntersect(cfg, sb->region.next, data_offset, data_offset + data_len)) {
1499  SCLogDebug(
1500  "data_offset %" PRIu64
1501  ", data_len %u intersects with main region, no next or way before next region",
1502  data_offset, data_len);
1503  if (sb->region.buf == NULL) {
1504  int r;
1505  if ((r = InitBuffer(sb, cfg)) != SC_OK) { // TODO init with size
1506  sc_errno = r;
1507  return NULL;
1508  }
1509  }
1510  return &sb->region;
1511  }
1512  } else if (sb->region.next == NULL) {
1513  /* InitBufferRegion sets sc_errno */
1514  StreamingBufferRegion *aux_r = sb->region.next = InitBufferRegion(sb, cfg, data_len);
1515  if (aux_r == NULL)
1516  return NULL;
1517  aux_r->stream_offset = data_offset;
1518  DEBUG_VALIDATE_BUG_ON(data_len > aux_r->buf_size);
1519  SCLogDebug("created new region %p with offset %" PRIu64 ", size %u", aux_r,
1520  aux_r->stream_offset, aux_r->buf_size);
1521  return aux_r;
1522  }
1523  /* BufferInsertAtRegionDo sets sc_errno */
1524  StreamingBufferRegion *blob = BufferInsertAtRegionDo(sb, cfg, data_offset, data_len);
1525  SCLogDebug("blob %p (%s)", blob, blob == &sb->region ? "main" : "aux");
1526  return blob;
1527 }
1528 
1529 /**
1530  * \param offset offset relative to StreamingBuffer::stream_offset
1531  *
1532  * \return SC_OK in case of success
1533  * \return SC_E* errors otherwise
1534  */
1536  StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len, uint64_t offset)
1537 {
1538  DEBUG_VALIDATE_BUG_ON(seg == NULL);
1539  DEBUG_VALIDATE_BUG_ON(data_len > BIT_U32(27)); // 128MiB is excessive already
1540  DEBUG_VALIDATE_BUG_ON(offset < sb->region.stream_offset);
1541  if (offset < sb->region.stream_offset) {
1542  return SC_EINVAL;
1543  }
1544 
1545  StreamingBufferRegion *region = BufferInsertAtRegion(sb, cfg, data_len, offset);
1546  if (region == NULL) {
1547  return sc_errno;
1548  }
1549 
1550  const bool region_is_main = region == &sb->region;
1551 
1552  SCLogDebug("inserting %" PRIu64 "/%u using %s region %p", offset, data_len,
1553  region == &sb->region ? "main" : "aux", region);
1554 
1555  DEBUG_VALIDATE_BUG_ON(offset - region->stream_offset > UINT32_MAX);
1556  uint32_t rel_offset = (uint32_t)(offset - region->stream_offset);
1557  int r = DataFitsAtOffset(region, data_len, rel_offset);
1558  if (r < 0) {
1560  return SC_ELIMIT;
1561  } else if (r == 0) {
1562  if ((r = GrowToSize(sb, cfg, (rel_offset + data_len))) != SC_OK)
1563  return r;
1564  }
1565  DEBUG_VALIDATE_BUG_ON(DataFitsAtOffset(region, data_len, rel_offset) != 1);
1566 
1567  SCLogDebug("offset %" PRIu64 " data_len %u, rel_offset %u into region offset %" PRIu64
1568  ", buf_offset %u, buf_size %u",
1569  offset, data_len, rel_offset, region->stream_offset, region->buf_offset,
1570  region->buf_size);
1571  memcpy(region->buf + rel_offset, data, data_len);
1572  seg->stream_offset = offset;
1573  seg->segment_len = data_len;
1574 
1575  SCLogDebug("rel_offset %u region->stream_offset %" PRIu64 ", buf_offset %u", rel_offset,
1576  region->stream_offset, region->buf_offset);
1577 
1578  if (RB_EMPTY(&sb->sbb_tree)) {
1579  SCLogDebug("empty sbb list");
1580 
1581  if (region_is_main) {
1582  if (sb->region.stream_offset == offset) {
1583  SCLogDebug("empty sbb list: block exactly what was expected, fall through");
1584  /* empty list, data is exactly what is expected (append),
1585  * so do nothing.
1586  * Update buf_offset if needed, but in case of overlaps it might be beyond us. */
1587  sb->region.buf_offset = MAX(sb->region.buf_offset, rel_offset + data_len);
1588  } else if ((rel_offset + data_len) <= sb->region.buf_offset) {
1589  SCLogDebug("empty sbb list: block is within existing main data region");
1590  } else {
1591  if (sb->region.buf_offset && rel_offset == sb->region.buf_offset) {
1592  SCLogDebug("exactly at expected offset");
1593  // nothing to do
1594  sb->region.buf_offset = rel_offset + data_len;
1595 
1596  } else if (rel_offset < sb->region.buf_offset) {
1597  // nothing to do
1598 
1599  SCLogDebug("before expected offset: %u < sb->region.buf_offset %u", rel_offset,
1600  sb->region.buf_offset);
1601  if (rel_offset + data_len > sb->region.buf_offset) {
1602  SCLogDebug("before expected offset, ends after: %u < sb->region.buf_offset "
1603  "%u, %u > %u",
1604  rel_offset, sb->region.buf_offset, rel_offset + data_len,
1605  sb->region.buf_offset);
1606  sb->region.buf_offset = rel_offset + data_len;
1607  }
1608 
1609  } else if (sb->region.buf_offset) {
1610  SCLogDebug("beyond expected offset: SBBInit");
1611  /* existing data, but there is a gap between us */
1612  if ((r = SBBInit(sb, cfg, region, rel_offset, data_len)) != SC_OK)
1613  return r;
1614  } else {
1615  /* gap before data in empty list */
1616  SCLogDebug("empty sbb list: invoking SBBInitLeadingGap");
1617  if ((r = SBBInitLeadingGap(sb, cfg, offset, data_len)) != SC_OK)
1618  return r;
1619  }
1620  }
1621  } else {
1622  if (sb->region.buf_offset) {
1623  /* existing data, but there is a gap between us */
1624  SCLogDebug("empty sbb list, no data in main: use SBBInit");
1625  if ((r = SBBInit(sb, cfg, region, rel_offset, data_len)) != SC_OK)
1626  return r;
1627  } else {
1628  /* gap before data in empty list */
1629  SCLogDebug("empty sbb list: invoking SBBInitLeadingGap");
1630  if ((r = SBBInitLeadingGap(sb, cfg, offset, data_len)) != SC_OK)
1631  return r;
1632  }
1633  if (rel_offset == region->buf_offset) {
1634  SCLogDebug("pre region->buf_offset %u", region->buf_offset);
1635  region->buf_offset = rel_offset + data_len;
1636  SCLogDebug("post region->buf_offset %u", region->buf_offset);
1637  }
1638  }
1639  } else {
1640  SCLogDebug("updating sbb tree");
1641  /* already have blocks, so append new block based on new data */
1642  if ((r = SBBUpdate(sb, cfg, region, rel_offset, data_len)) != SC_OK)
1643  return r;
1644  }
1645  DEBUG_VALIDATE_BUG_ON(!region_is_main && sb->head == NULL);
1646 
1647  ListRegions(sb);
1648  if (RB_EMPTY(&sb->sbb_tree)) {
1650  }
1651 
1652  return SC_OK;
1653 }
1654 
1656  const StreamingBufferSegment *seg)
1657 {
1658  if (seg->stream_offset < sb->region.stream_offset) {
1659  if (seg->stream_offset + seg->segment_len <= sb->region.stream_offset) {
1660  return 1;
1661  }
1662  }
1663  return 0;
1664 }
1665 
1666 static inline const StreamingBufferRegion *GetRegionForOffset(
1667  const StreamingBuffer *sb, const uint64_t offset)
1668 {
1669  if (sb == NULL)
1670  return NULL;
1671  if (sb->region.next == NULL) {
1672  return &sb->region;
1673  }
1674  if (offset >= sb->region.stream_offset &&
1675  offset < (sb->region.stream_offset + sb->region.buf_size)) {
1676  return &sb->region;
1677  }
1678  for (const StreamingBufferRegion *r = sb->region.next; r != NULL; r = r->next) {
1679  if (offset >= r->stream_offset && offset < (r->stream_offset + r->buf_size)) {
1680  return r;
1681  }
1682  }
1683  return NULL;
1684 }
1685 
1686 /** \brief get the data for one SBB */
1688  const StreamingBufferBlock *sbb,
1689  const uint8_t **data, uint32_t *data_len)
1690 {
1691  const StreamingBufferRegion *region = GetRegionForOffset(sb, sbb->offset);
1692  SCLogDebug("first find our region (offset %" PRIu64 ") -> %p", sbb->offset, region);
1693  if (region) {
1694  SCLogDebug("region %p found %" PRIu64 "/%u/%u", region, region->stream_offset,
1695  region->buf_size, region->buf_offset);
1697  region->stream_offset == sbb->offset && region->buf_offset > sbb->len);
1698  // buf_offset should match first sbb len if it has the same offset
1699 
1700  if (sbb->offset >= region->stream_offset) {
1701  SCLogDebug("1");
1702  uint64_t offset = sbb->offset - region->stream_offset;
1703  *data = region->buf + offset;
1704  DEBUG_VALIDATE_BUG_ON(offset + sbb->len > region->buf_size);
1705  *data_len = sbb->len;
1706  return;
1707  } else {
1708  SCLogDebug("2");
1709  uint32_t offset = (uint32_t)(region->stream_offset - sbb->offset);
1710  if (offset < sbb->len) {
1711  *data = region->buf;
1712  *data_len = sbb->len - offset;
1713  return;
1714  }
1715  SCLogDebug("3");
1716  }
1717  }
1718  *data = NULL;
1719  *data_len = 0;
1720 }
1721 
1722 /** \brief get the data for one SBB */
1724  const StreamingBufferBlock *sbb,
1725  const uint8_t **data, uint32_t *data_len,
1726  uint64_t offset)
1727 {
1728  /* validate that we are looking for a offset within the sbb */
1729  DEBUG_VALIDATE_BUG_ON(!(offset >= sbb->offset && offset < (sbb->offset + sbb->len)));
1730  if (!(offset >= sbb->offset && offset < (sbb->offset + sbb->len))) {
1731  *data = NULL;
1732  *data_len = 0;
1733  return;
1734  }
1735 
1736  const StreamingBufferRegion *region = GetRegionForOffset(sb, offset);
1737  if (region) {
1738  DEBUG_VALIDATE_BUG_ON(sbb->len - (offset - sbb->offset) > UINT32_MAX);
1739  uint32_t sbblen = (uint32_t)(sbb->len - (offset - sbb->offset));
1740 
1741  if (offset >= region->stream_offset) {
1742  uint32_t data_offset = (uint32_t)(offset - region->stream_offset);
1743  *data = region->buf + data_offset;
1744  if (data_offset + sbblen > region->buf_size) {
1745  *data_len = region->buf_size - data_offset;
1746  } else {
1747  *data_len = sbblen;
1748  }
1749  DEBUG_VALIDATE_BUG_ON(*data_len > sbblen);
1750  return;
1751  } else {
1752  uint32_t data_offset = (uint32_t)(region->stream_offset - sbb->offset);
1753  if (data_offset < sbblen) {
1754  *data = region->buf;
1755  *data_len = sbblen - data_offset;
1756  DEBUG_VALIDATE_BUG_ON(*data_len > sbblen);
1757  return;
1758  }
1759  }
1760  }
1761 
1762  *data = NULL;
1763  *data_len = 0;
1764 }
1765 
1767  const StreamingBufferSegment *seg,
1768  const uint8_t **data, uint32_t *data_len)
1769 {
1770  const StreamingBufferRegion *region = GetRegionForOffset(sb, seg->stream_offset);
1771  if (region) {
1772  if (seg->stream_offset >= region->stream_offset) {
1773  uint32_t offset = (uint32_t)(seg->stream_offset - region->stream_offset);
1774  *data = region->buf + offset;
1775  if (offset + seg->segment_len > region->buf_size) {
1776  *data_len = region->buf_size - offset;
1777  } else {
1778  *data_len = seg->segment_len;
1779  }
1780  SCLogDebug("*data_len %u", *data_len);
1781  return;
1782  } else {
1783  uint32_t offset = (uint32_t)(region->stream_offset - seg->stream_offset);
1784  if (offset < seg->segment_len) {
1785  *data = region->buf;
1786  *data_len = seg->segment_len - offset;
1787  SCLogDebug("*data_len %u", *data_len);
1788  return;
1789  }
1790  }
1791  }
1792  *data = NULL;
1793  *data_len = 0;
1794 }
1795 
1796 /**
1797  * \retval 1 data is the same
1798  * \retval 0 data is different
1799  */
1801  const StreamingBufferSegment *seg,
1802  const uint8_t *rawdata, uint32_t rawdata_len)
1803 {
1804  const uint8_t *segdata = NULL;
1805  uint32_t segdata_len = 0;
1806  StreamingBufferSegmentGetData(sb, seg, &segdata, &segdata_len);
1807  if (segdata && segdata_len &&
1808  segdata_len == rawdata_len &&
1809  memcmp(segdata, rawdata, segdata_len) == 0)
1810  {
1811  return 1;
1812  }
1813  return 0;
1814 }
1815 
1817  const uint8_t **data, uint32_t *data_len,
1818  uint64_t *stream_offset)
1819 {
1820  if (sb != NULL && sb->region.buf != NULL) {
1821  *data = sb->region.buf;
1822  *data_len = sb->region.buf_offset;
1824  return 1;
1825  } else {
1826  *data = NULL;
1827  *data_len = 0;
1828  *stream_offset = 0;
1829  return 0;
1830  }
1831 }
1832 
1834  const uint8_t **data, uint32_t *data_len,
1835  uint64_t offset)
1836 {
1837  const StreamingBufferRegion *region = GetRegionForOffset(sb, offset);
1838  if (region != NULL && region->buf != NULL && offset >= region->stream_offset &&
1839  offset < (region->stream_offset + region->buf_offset)) {
1840  DEBUG_VALIDATE_BUG_ON(offset - region->stream_offset > UINT32_MAX);
1841  uint32_t skip = (uint32_t)(offset - region->stream_offset);
1842  *data = region->buf + skip;
1843  *data_len = region->buf_offset - skip;
1844  return 1;
1845  } else {
1846  *data = NULL;
1847  *data_len = 0;
1848  return 0;
1849  }
1850 }
1851 
1852 /**
1853  * \retval 1 data is the same
1854  * \retval 0 data is different
1855  */
1857  const uint8_t *rawdata, uint32_t rawdata_len)
1858 {
1859  const uint8_t *sbdata = NULL;
1860  uint32_t sbdata_len = 0;
1861  uint64_t offset = 0;
1862  StreamingBufferGetData(sb, &sbdata, &sbdata_len, &offset);
1863  if (offset == 0 &&
1864  sbdata && sbdata_len &&
1865  sbdata_len == rawdata_len &&
1866  memcmp(sbdata, rawdata, sbdata_len) == 0)
1867  {
1868  return 1;
1869  }
1870  SCLogDebug("sbdata_len %u, offset %" PRIu64, sbdata_len, offset);
1871  printf("got:\n");
1872  PrintRawDataFp(stdout, sbdata,sbdata_len);
1873  printf("wanted:\n");
1874  PrintRawDataFp(stdout, rawdata,rawdata_len);
1875  return 0;
1876 }
1877 
1878 #ifdef UNITTESTS
1879 static void Dump(StreamingBuffer *sb)
1880 {
1881  PrintRawDataFp(stdout, sb->region.buf, sb->region.buf_offset);
1882 }
1883 
1884 static void DumpSegment(StreamingBuffer *sb, StreamingBufferSegment *seg)
1885 {
1886  const uint8_t *data = NULL;
1887  uint32_t data_len = 0;
1888  StreamingBufferSegmentGetData(sb, seg, &data, &data_len);
1889  if (data && data_len) {
1890  PrintRawDataFp(stdout, data, data_len);
1891  }
1892 }
1893 
1894 static int StreamingBufferTest02(void)
1895 {
1896  StreamingBufferConfig cfg = { 24, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
1898  FAIL_IF(sb == NULL);
1899 
1900  StreamingBufferSegment seg1;
1901  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0);
1902  StreamingBufferSegment seg2;
1903  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg2, (const uint8_t *)"01234567", 8) != 0);
1904  FAIL_IF(sb->region.stream_offset != 0);
1905  FAIL_IF(sb->region.buf_offset != 16);
1906  FAIL_IF(seg1.stream_offset != 0);
1907  FAIL_IF(seg2.stream_offset != 8);
1910  Dump(sb);
1911  DumpSegment(sb, &seg1);
1912  DumpSegment(sb, &seg2);
1913  FAIL_IF_NOT_NULL(sb->head);
1914  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1915 
1916  StreamingBufferSlideToOffset(sb, &cfg, 6);
1917  FAIL_IF_NOT_NULL(sb->head);
1918  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1919 
1920  StreamingBufferSegment seg3;
1921  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg3, (const uint8_t *)"QWERTY", 6) != 0);
1922  FAIL_IF(sb->region.stream_offset != 6);
1923  FAIL_IF(sb->region.buf_offset != 16);
1924  FAIL_IF(seg3.stream_offset != 16);
1928  Dump(sb);
1929  DumpSegment(sb, &seg1);
1930  DumpSegment(sb, &seg2);
1931  DumpSegment(sb, &seg3);
1932  FAIL_IF_NOT_NULL(sb->head);
1933  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1934 
1935  StreamingBufferSlideToOffset(sb, &cfg, 12);
1939  Dump(sb);
1940  DumpSegment(sb, &seg1);
1941  DumpSegment(sb, &seg2);
1942  DumpSegment(sb, &seg3);
1943  FAIL_IF_NOT_NULL(sb->head);
1944  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1945 
1946  StreamingBufferFree(sb, &cfg);
1947  PASS;
1948 }
1949 
1950 static int StreamingBufferTest03(void)
1951 {
1952  StreamingBufferConfig cfg = { 24, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
1954  FAIL_IF(sb == NULL);
1955 
1956  StreamingBufferSegment seg1;
1957  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0);
1958  StreamingBufferSegment seg2;
1959  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"01234567", 8, 14) != 0);
1960  FAIL_IF(sb->region.stream_offset != 0);
1961  FAIL_IF(sb->region.buf_offset != 8);
1962  FAIL_IF(seg1.stream_offset != 0);
1963  FAIL_IF(seg2.stream_offset != 14);
1966  Dump(sb);
1967  DumpSegment(sb, &seg1);
1968  DumpSegment(sb, &seg2);
1969  FAIL_IF_NULL(sb->head);
1970  FAIL_IF_NOT(sb->sbb_size == 16);
1971  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1972 
1973  StreamingBufferSegment seg3;
1974  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"QWERTY", 6, 8) != 0);
1975  FAIL_IF(sb->region.stream_offset != 0);
1976  FAIL_IF(sb->region.buf_offset != 22);
1977  FAIL_IF(seg3.stream_offset != 8);
1981  Dump(sb);
1982  DumpSegment(sb, &seg1);
1983  DumpSegment(sb, &seg2);
1984  DumpSegment(sb, &seg3);
1985  FAIL_IF_NULL(sb->head);
1986  FAIL_IF_NOT(sb->sbb_size == 22);
1987  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
1988 
1989  StreamingBufferSlideToOffset(sb, &cfg, 10);
1993  Dump(sb);
1994  DumpSegment(sb, &seg1);
1995  DumpSegment(sb, &seg2);
1996  DumpSegment(sb, &seg3);
1997  FAIL_IF_NULL(sb->head);
1998  FAIL_IF_NOT(sb->sbb_size == 12);
1999  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2000 
2001  StreamingBufferFree(sb, &cfg);
2002  PASS;
2003 }
2004 
2005 static int StreamingBufferTest04(void)
2006 {
2007  StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2009  FAIL_IF(sb == NULL);
2010 
2011  StreamingBufferSegment seg1;
2012  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0);
2013  FAIL_IF(!RB_EMPTY(&sb->sbb_tree));
2014  StreamingBufferSegment seg2;
2015  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"01234567", 8, 14) != 0);
2016  FAIL_IF(sb->region.stream_offset != 0);
2017  FAIL_IF(sb->region.buf_offset != 8);
2018  FAIL_IF(seg1.stream_offset != 0);
2019  FAIL_IF(seg2.stream_offset != 14);
2022  FAIL_IF(RB_EMPTY(&sb->sbb_tree));
2023  StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2024  FAIL_IF(sbb1 != sb->head);
2025  FAIL_IF_NULL(sbb1);
2026  FAIL_IF(sbb1->offset != 0);
2027  FAIL_IF(sbb1->len != 8);
2028  StreamingBufferBlock *sbb2 = SBB_RB_NEXT(sbb1);
2029  FAIL_IF_NULL(sbb2);
2030  FAIL_IF(sbb2 == sb->head);
2031  FAIL_IF(sbb2->offset != 14);
2032  FAIL_IF(sbb2->len != 8);
2033  Dump(sb);
2034  DumpSegment(sb, &seg1);
2035  DumpSegment(sb, &seg2);
2036  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2037 
2038  StreamingBufferSegment seg3;
2039  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"QWERTY", 6, 8) != 0);
2040  FAIL_IF(sb->region.stream_offset != 0);
2041  FAIL_IF(sb->region.buf_offset != 22);
2042  FAIL_IF(seg3.stream_offset != 8);
2046  sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2047  FAIL_IF_NULL(sbb1);
2048  FAIL_IF(sbb1 != sb->head);
2049  FAIL_IF(sbb1->offset != 0);
2050  FAIL_IF(sbb1->len != 22);
2051  FAIL_IF(SBB_RB_NEXT(sbb1));
2052  Dump(sb);
2053  DumpSegment(sb, &seg1);
2054  DumpSegment(sb, &seg2);
2055  DumpSegment(sb, &seg3);
2056  FAIL_IF_NULL(sb->head);
2057  FAIL_IF_NOT(sb->sbb_size == 22);
2058  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2059 
2060  /* far ahead of curve: */
2061  StreamingBufferSegment seg4;
2062  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"XYZ", 3, 124) != 0);
2063  FAIL_IF(sb->region.stream_offset != 0);
2064  FAIL_IF(sb->region.buf_offset != 22);
2065  FAIL_IF(sb->region.buf_size != 128);
2066  FAIL_IF(seg4.stream_offset != 124);
2071  sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2072  FAIL_IF_NULL(sbb1);
2073  FAIL_IF(sbb1 != sb->head);
2074  FAIL_IF(sbb1->offset != 0);
2075  FAIL_IF(sbb1->len != 22);
2076  FAIL_IF(!SBB_RB_NEXT(sbb1));
2077  Dump(sb);
2078  DumpSegment(sb, &seg1);
2079  DumpSegment(sb, &seg2);
2080  DumpSegment(sb, &seg3);
2081  DumpSegment(sb, &seg4);
2082  FAIL_IF_NULL(sb->head);
2083  FAIL_IF_NOT(sb->sbb_size == 25);
2084  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2085 
2086  FAIL_IF(!StreamingBufferSegmentCompareRawData(sb,&seg1,(const uint8_t *)"ABCDEFGH", 8));
2087  FAIL_IF(!StreamingBufferSegmentCompareRawData(sb,&seg2,(const uint8_t *)"01234567", 8));
2088  FAIL_IF(!StreamingBufferSegmentCompareRawData(sb,&seg3,(const uint8_t *)"QWERTY", 6));
2089  FAIL_IF(!StreamingBufferSegmentCompareRawData(sb,&seg4,(const uint8_t *)"XYZ", 3));
2090 
2091  StreamingBufferFree(sb, &cfg);
2092  PASS;
2093 }
2094 
2095 /** \test lots of gaps in block list */
2096 static int StreamingBufferTest06(void)
2097 {
2098  StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2100  FAIL_IF(sb == NULL);
2101 
2102  StreamingBufferSegment seg1;
2103  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"A", 1) != 0);
2104  StreamingBufferSegment seg2;
2105  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"C", 1, 2) != 0);
2106  Dump(sb);
2107  FAIL_IF_NULL(sb->head);
2108  FAIL_IF_NOT(sb->sbb_size == 2);
2109  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2110 
2111  StreamingBufferSegment seg3;
2112  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"F", 1, 5) != 0);
2113  Dump(sb);
2114  FAIL_IF_NULL(sb->head);
2115  FAIL_IF_NOT(sb->sbb_size == 3);
2116  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2117 
2118  StreamingBufferSegment seg4;
2119  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"H", 1, 7) != 0);
2120  Dump(sb);
2121  FAIL_IF_NULL(sb->head);
2122  FAIL_IF_NOT(sb->sbb_size == 4);
2123  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2124 
2125  StreamingBufferSegment seg5;
2126  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2127  Dump(sb);
2128  StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2129  FAIL_IF_NULL(sbb1);
2130  FAIL_IF(sbb1->offset != 0);
2131  FAIL_IF(sbb1->len != 10);
2132  FAIL_IF(SBB_RB_NEXT(sbb1));
2133  FAIL_IF_NULL(sb->head);
2134  FAIL_IF_NOT(sb->sbb_size == 10);
2135  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2136 
2137  StreamingBufferSegment seg6;
2138  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg6, (const uint8_t *)"abcdefghij", 10, 0) != 0);
2139  Dump(sb);
2140  sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2141  FAIL_IF_NULL(sbb1);
2142  FAIL_IF(sbb1->offset != 0);
2143  FAIL_IF(sbb1->len != 10);
2144  FAIL_IF(SBB_RB_NEXT(sbb1));
2145  FAIL_IF_NULL(sb->head);
2146  FAIL_IF_NOT(sb->sbb_size == 10);
2147  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2148 
2149  StreamingBufferFree(sb, &cfg);
2150  PASS;
2151 }
2152 
2153 /** \test lots of gaps in block list */
2154 static int StreamingBufferTest07(void)
2155 {
2156  StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2158  FAIL_IF(sb == NULL);
2159 
2160  StreamingBufferSegment seg1;
2161  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg1, (const uint8_t *)"B", 1, 1) != 0);
2162  StreamingBufferSegment seg2;
2163  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"D", 1, 3) != 0);
2164  Dump(sb);
2165  FAIL_IF_NULL(sb->head);
2166  FAIL_IF_NOT(sb->sbb_size == 2);
2167  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2168 
2169  StreamingBufferSegment seg3;
2170  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"F", 1, 5) != 0);
2171  Dump(sb);
2172  FAIL_IF_NULL(sb->head);
2173  FAIL_IF_NOT(sb->sbb_size == 3);
2174  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2175 
2176  StreamingBufferSegment seg4;
2177  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"H", 1, 7) != 0);
2178  Dump(sb);
2179  FAIL_IF_NULL(sb->head);
2180  FAIL_IF_NOT(sb->sbb_size == 4);
2181  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2182 
2183  StreamingBufferSegment seg5;
2184  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2185  Dump(sb);
2186  StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2187  FAIL_IF_NULL(sbb1);
2188  FAIL_IF(sbb1->offset != 0);
2189  FAIL_IF(sbb1->len != 10);
2190  FAIL_IF(SBB_RB_NEXT(sbb1));
2191  FAIL_IF_NULL(sb->head);
2192  FAIL_IF_NOT(sb->sbb_size == 10);
2193  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2194 
2195  StreamingBufferSegment seg6;
2196  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg6, (const uint8_t *)"abcdefghij", 10, 0) != 0);
2197  Dump(sb);
2198  sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2199  FAIL_IF_NULL(sbb1);
2200  FAIL_IF(sbb1->offset != 0);
2201  FAIL_IF(sbb1->len != 10);
2202  FAIL_IF(SBB_RB_NEXT(sbb1));
2203  FAIL_IF_NULL(sb->head);
2204  FAIL_IF_NOT(sb->sbb_size == 10);
2205  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2206 
2207  StreamingBufferFree(sb, &cfg);
2208  PASS;
2209 }
2210 
2211 /** \test lots of gaps in block list */
2212 static int StreamingBufferTest08(void)
2213 {
2214  StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2216  FAIL_IF(sb == NULL);
2217 
2218  StreamingBufferSegment seg1;
2219  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg1, (const uint8_t *)"B", 1, 1) != 0);
2220  StreamingBufferSegment seg2;
2221  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"D", 1, 3) != 0);
2222  Dump(sb);
2223  FAIL_IF_NULL(sb->head);
2224  FAIL_IF_NOT(sb->sbb_size == 2);
2225  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2226 
2227  StreamingBufferSegment seg3;
2228  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"F", 1, 5) != 0);
2229  Dump(sb);
2230  FAIL_IF_NULL(sb->head);
2231  FAIL_IF_NOT(sb->sbb_size == 3);
2232  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2233 
2234  StreamingBufferSegment seg4;
2235  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"H", 1, 7) != 0);
2236  Dump(sb);
2237  FAIL_IF_NULL(sb->head);
2238  FAIL_IF_NOT(sb->sbb_size == 4);
2239  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2240 
2241  StreamingBufferSegment seg5;
2242  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2243  Dump(sb);
2244  StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2245  FAIL_IF_NULL(sbb1);
2246  FAIL_IF(sbb1->offset != 0);
2247  FAIL_IF(sbb1->len != 10);
2248  FAIL_IF(SBB_RB_NEXT(sbb1));
2249  FAIL_IF_NULL(sb->head);
2250  FAIL_IF_NOT(sb->sbb_size == 10);
2251  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2252 
2253  StreamingBufferSegment seg6;
2254  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg6, (const uint8_t *)"abcdefghij", 10) != 0);
2255  Dump(sb);
2256  sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2257  FAIL_IF_NULL(sbb1);
2258  FAIL_IF(sbb1->offset != 0);
2259  FAIL_IF(sbb1->len != 20);
2260  FAIL_IF(SBB_RB_NEXT(sbb1));
2261  FAIL_IF_NULL(sb->head);
2262  FAIL_IF_NOT(sb->sbb_size == 20);
2263  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2264 
2265  StreamingBufferFree(sb, &cfg);
2266  PASS;
2267 }
2268 
2269 /** \test lots of gaps in block list */
2270 static int StreamingBufferTest09(void)
2271 {
2272  StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2274  FAIL_IF(sb == NULL);
2275 
2276  StreamingBufferSegment seg1;
2277  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg1, (const uint8_t *)"B", 1, 1) != 0);
2278  StreamingBufferSegment seg2;
2279  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"D", 1, 3) != 0);
2280  Dump(sb);
2281  FAIL_IF_NULL(sb->head);
2282  FAIL_IF_NOT(sb->sbb_size == 2);
2283  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2284 
2285  StreamingBufferSegment seg3;
2286  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"H", 1, 7) != 0);
2287  Dump(sb);
2288  FAIL_IF_NULL(sb->head);
2289  FAIL_IF_NOT(sb->sbb_size == 3);
2290  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2291 
2292  StreamingBufferSegment seg4;
2293  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"F", 1, 5) != 0);
2294  Dump(sb);
2295  FAIL_IF_NULL(sb->head);
2296  FAIL_IF_NOT(sb->sbb_size == 4);
2297  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2298 
2299  StreamingBufferSegment seg5;
2300  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2301  Dump(sb);
2302  StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2303  FAIL_IF_NULL(sbb1);
2304  FAIL_IF(sbb1->offset != 0);
2305  FAIL_IF(sbb1->len != 10);
2306  FAIL_IF(SBB_RB_NEXT(sbb1));
2307  FAIL_IF_NULL(sb->head);
2308  FAIL_IF_NOT(sb->sbb_size == 10);
2309  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2310 
2311  StreamingBufferSegment seg6;
2312  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg6, (const uint8_t *)"abcdefghij", 10, 0) != 0);
2313  Dump(sb);
2314  sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2315  FAIL_IF_NULL(sbb1);
2316  FAIL_IF(sbb1->offset != 0);
2317  FAIL_IF(sbb1->len != 10);
2318  FAIL_IF(SBB_RB_NEXT(sbb1));
2319  FAIL_IF_NULL(sb->head);
2320  FAIL_IF_NOT(sb->sbb_size == 10);
2321  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2322 
2323  StreamingBufferFree(sb, &cfg);
2324  PASS;
2325 }
2326 
2327 /** \test lots of gaps in block list */
2328 static int StreamingBufferTest10(void)
2329 {
2330  StreamingBufferConfig cfg = { 16, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2332  FAIL_IF(sb == NULL);
2333 
2334  StreamingBufferSegment seg1;
2335  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg1, (const uint8_t *)"A", 1, 0) != 0);
2336  Dump(sb);
2337  StreamingBufferSegment seg2;
2338  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg2, (const uint8_t *)"D", 1, 3) != 0);
2339  Dump(sb);
2340  StreamingBufferSegment seg3;
2341  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg3, (const uint8_t *)"H", 1, 7) != 0);
2342  Dump(sb);
2343  FAIL_IF_NULL(sb->head);
2344  FAIL_IF_NOT(sb->sbb_size == 3);
2345 
2346  StreamingBufferSegment seg4;
2347  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg4, (const uint8_t *)"B", 1, 1) != 0);
2348  Dump(sb);
2349  StreamingBufferSegment seg5;
2350  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg5, (const uint8_t *)"C", 1, 2) != 0);
2351  Dump(sb);
2352  StreamingBufferSegment seg6;
2353  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg6, (const uint8_t *)"G", 1, 6) != 0);
2354  Dump(sb);
2355  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2356  FAIL_IF_NULL(sb->head);
2357  FAIL_IF_NOT(sb->sbb_size == 6);
2358 
2359  StreamingBufferSegment seg7;
2360  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg7, (const uint8_t *)"ABCDEFGHIJ", 10, 0) != 0);
2361  Dump(sb);
2362  StreamingBufferBlock *sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2363  FAIL_IF_NULL(sbb1);
2364  FAIL_IF(sbb1->offset != 0);
2365  FAIL_IF(sbb1->len != 10);
2366  FAIL_IF(SBB_RB_NEXT(sbb1));
2367  FAIL_IF_NULL(sb->head);
2368  FAIL_IF_NOT(sb->sbb_size == 10);
2369 
2370  StreamingBufferSegment seg8;
2371  FAIL_IF(StreamingBufferInsertAt(sb, &cfg, &seg8, (const uint8_t *)"abcdefghij", 10, 0) != 0);
2372  Dump(sb);
2373  sbb1 = RB_MIN(SBB, &sb->sbb_tree);
2374  FAIL_IF_NOT(sb->head == RB_MIN(SBB, &sb->sbb_tree));
2375  FAIL_IF_NULL(sbb1);
2376  FAIL_IF(sbb1->offset != 0);
2377  FAIL_IF(sbb1->len != 10);
2378  FAIL_IF(SBB_RB_NEXT(sbb1));
2379  FAIL_IF_NULL(sb->head);
2380  FAIL_IF_NOT(sb->sbb_size == 10);
2381 
2382  StreamingBufferFree(sb, &cfg);
2383  PASS;
2384 }
2385 
2386 static int StreamingBufferTest11(void)
2387 {
2388  StreamingBufferConfig cfg = { 24, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL };
2390  FAIL_IF(sb == NULL);
2391 
2392  StreamingBufferSegment seg1;
2393  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0);
2394  StreamingBufferSegment seg2;
2395  unsigned int data_len = 0xffffffff;
2396  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg2, (const uint8_t *)"unused", data_len) != -1);
2398  sb, &cfg, &seg2, (const uint8_t *)"abcdefghij", data_len, 100000) != SC_ELIMIT);
2399  StreamingBufferFree(sb, &cfg);
2400  PASS;
2401 }
2402 
2403 static const char *dummy_conf_string = "%YAML 1.1\n"
2404  "---\n"
2405  "\n"
2406  "app-layer:\n"
2407  " protocols:\n"
2408  " http:\n"
2409  " enabled: yes\n"
2410  " memcap: 88\n"
2411  "\n";
2412 
2413 static int StreamingBufferTest12(void)
2414 {
2416  ConfInit();
2418  ConfYamlLoadString((const char *)dummy_conf_string, strlen(dummy_conf_string));
2419  HTPConfigure();
2420 
2422  HTPFree };
2424  FAIL_IF(sb == NULL);
2425 
2426  StreamingBufferSegment seg1;
2427  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGHIJKLMNOP", 16) != 0);
2428 
2429  StreamingBufferSegment seg2;
2430  FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg2,
2431  (const uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ",
2432  52) != -1);
2434 
2435  StreamingBufferFree(sb, &cfg);
2438 
2439  PASS;
2440 }
2441 #endif
2442 
2444 {
2445 #ifdef UNITTESTS
2446  UtRegisterTest("StreamingBufferTest02", StreamingBufferTest02);
2447  UtRegisterTest("StreamingBufferTest03", StreamingBufferTest03);
2448  UtRegisterTest("StreamingBufferTest04", StreamingBufferTest04);
2449  UtRegisterTest("StreamingBufferTest06", StreamingBufferTest06);
2450  UtRegisterTest("StreamingBufferTest07", StreamingBufferTest07);
2451  UtRegisterTest("StreamingBufferTest08", StreamingBufferTest08);
2452  UtRegisterTest("StreamingBufferTest09", StreamingBufferTest09);
2453  UtRegisterTest("StreamingBufferTest10", StreamingBufferTest10);
2454  UtRegisterTest("StreamingBufferTest11 Bug 6903", StreamingBufferTest11);
2455  UtRegisterTest("StreamingBufferTest12 Bug 6782", StreamingBufferTest12);
2456 #endif
2457 }
FREE
#define FREE(cfg, ptr, s)
Definition: util-streaming-buffer.c:66
StreamingBufferSlideToOffset
void StreamingBufferSlideToOffset(StreamingBuffer *sb, const StreamingBufferConfig *cfg, uint64_t offset)
slide to absolute offset
Definition: util-streaming-buffer.c:1025
len
uint8_t len
Definition: app-layer-dnp3.h:2
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
StreamingBufferConfig_::buf_size
uint32_t buf_size
Definition: util-streaming-buffer.h:66
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
StreamingBuffer_::head
StreamingBufferBlock * head
Definition: util-streaming-buffer.h:111
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
StreamingBufferRegion_::next
struct StreamingBufferRegion_ * next
Definition: util-streaming-buffer.h:89
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
StreamingBufferFree
void StreamingBufferFree(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
Definition: util-streaming-buffer.c:294
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
RB_LEFT
#define RB_LEFT(elm, field)
Definition: tree.h:322
SC_EINVAL
@ SC_EINVAL
Definition: util-error.h:30
StreamingBuffer_::max_regions
uint16_t max_regions
Definition: util-streaming-buffer.h:114
StreamingBuffer_::regions
uint16_t regions
Definition: util-streaming-buffer.h:113
StreamingBufferGetData
int StreamingBufferGetData(const StreamingBuffer *sb, const uint8_t **data, uint32_t *data_len, uint64_t *stream_offset)
Definition: util-streaming-buffer.c:1816
RB_MIN
#define RB_MIN(name, x)
Definition: tree.h:778
MIN
#define MIN(x, y)
Definition: suricata-common.h:400
StreamingBufferAppend
int StreamingBufferAppend(StreamingBuffer *sb, const StreamingBufferConfig *cfg, StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len)
Definition: util-streaming-buffer.c:1103
HTPRealloc
void * HTPRealloc(void *ptr, size_t orig_size, size_t size)
Definition: app-layer-htp-mem.c:175
segment_len
uint32_t segment_len
Definition: util-streaming-buffer.h:0
m
SCMutex m
Definition: flow-hash.h:6
StreamingBufferGetDataAtOffset
int StreamingBufferGetDataAtOffset(const StreamingBuffer *sb, const uint8_t **data, uint32_t *data_len, uint64_t offset)
Definition: util-streaming-buffer.c:1833
MAX
#define MAX(x, y)
Definition: suricata-common.h:404
StreamingBufferSegmentCompareRawData
int StreamingBufferSegmentCompareRawData(const StreamingBuffer *sb, const StreamingBufferSegment *seg, const uint8_t *rawdata, uint32_t rawdata_len)
Definition: util-streaming-buffer.c:1800
stream_offset
uint64_t stream_offset
Definition: util-streaming-buffer.h:1
StreamingBufferConfig_::max_regions
uint16_t max_regions
Definition: util-streaming-buffer.h:67
SC_ELIMIT
@ SC_ELIMIT
Definition: util-error.h:31
util-unittest.h
HTPConfigure
void HTPConfigure(void)
Definition: app-layer-htp.c:2570
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
SC_ENOMEM
@ SC_ENOMEM
Definition: util-error.h:29
app-layer-htp.h
RB_EMPTY
#define RB_EMPTY(head)
Definition: tree.h:327
FAIL_IF_NOT_NULL
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
Definition: util-unittest.h:96
util-debug.h
StreamingBufferRegion_::buf_size
uint32_t buf_size
Definition: util-streaming-buffer.h:86
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
util-error.h
RB_ROOT
#define RB_ROOT(head)
Definition: tree.h:326
StreamingBufferInit
StreamingBuffer * StreamingBufferInit(const StreamingBufferConfig *cfg)
Definition: util-streaming-buffer.c:248
HtpConfigCreateBackup
void HtpConfigCreateBackup(void)
Definition: app-layer-htp.c:2880
StreamingBufferSegmentIsBeforeWindow
int StreamingBufferSegmentIsBeforeWindow(const StreamingBuffer *sb, const StreamingBufferSegment *seg)
Definition: util-streaming-buffer.c:1655
BIT_U32
#define BIT_U32(n)
Definition: suricata-common.h:409
REALLOC
#define REALLOC(cfg, ptr, orig_s, s)
Definition: util-streaming-buffer.c:64
CALLOC
#define CALLOC(cfg, n, s)
Definition: util-streaming-buffer.c:63
BOOL2STR
#define BOOL2STR(b)
Definition: util-debug.h:527
util-print.h
RB_FOREACH_SAFE
#define RB_FOREACH_SAFE(x, name, head, y)
Definition: tree.h:791
StreamingBufferClear
void StreamingBufferClear(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
Definition: util-streaming-buffer.c:271
HTPCalloc
void * HTPCalloc(size_t n, size_t size)
Definition: app-layer-htp-mem.c:154
ConfYamlLoadString
int ConfYamlLoadString(const char *string, size_t len)
Load configuration from a YAML string.
Definition: conf-yaml-loader.c:523
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:249
PrintRawDataFp
void PrintRawDataFp(FILE *fp, const uint8_t *buf, uint32_t buflen)
Definition: util-print.c:112
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:309
RB_FOREACH
#define RB_FOREACH(x, name, head)
Definition: tree.h:781
StreamingBuffer_::sbb_size
uint32_t sbb_size
Definition: util-streaming-buffer.h:112
STREAMING_BUFFER_REGION_GAP_DEFAULT
#define STREAMING_BUFFER_REGION_GAP_DEFAULT
Definition: util-streaming-buffer.h:63
conf-yaml-loader.h
StreamingBufferRegion_::buf
uint8_t * buf
Definition: util-streaming-buffer.h:85
StreamingBufferCompareRawData
int StreamingBufferCompareRawData(const StreamingBuffer *sb, const uint8_t *rawdata, uint32_t rawdata_len)
Definition: util-streaming-buffer.c:1856
SC_OK
@ SC_OK
Definition: util-error.h:27
RB_GENERATE
RB_GENERATE(SBB, StreamingBufferBlock, rb, SBBCompare)
StreamingBufferRegion_::buf_offset
uint32_t buf_offset
Definition: util-streaming-buffer.h:87
ConfCreateContextBackup
void ConfCreateContextBackup(void)
Creates a backup of the conf_hash hash_table used by the conf API.
Definition: conf.c:669
SBB_RB_FIND_INCLUSIVE
StreamingBufferBlock * SBB_RB_FIND_INCLUSIVE(struct SBB *head, StreamingBufferBlock *elm)
Definition: util-streaming-buffer.c:104
SCRealloc
#define SCRealloc(ptr, sz)
Definition: util-mem.h:50
StreamingBuffer_::sbb_tree
struct SBB sbb_tree
Definition: util-streaming-buffer.h:110
StreamingBuffer_
Definition: util-streaming-buffer.h:108
StreamingBufferSBBGetDataAtOffset
void StreamingBufferSBBGetDataAtOffset(const StreamingBuffer *sb, const StreamingBufferBlock *sbb, const uint8_t **data, uint32_t *data_len, uint64_t offset)
get the data for one SBB
Definition: util-streaming-buffer.c:1723
cnt
uint32_t cnt
Definition: tmqh-packetpool.h:7
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
StreamingBufferAppendNoTrack
int StreamingBufferAppendNoTrack(StreamingBuffer *sb, const StreamingBufferConfig *cfg, const uint8_t *data, uint32_t data_len)
add data w/o tracking a segment
Definition: util-streaming-buffer.c:1147
suricata-common.h
util-streaming-buffer.h
ConfRestoreContextBackup
void ConfRestoreContextBackup(void)
Restores the backup of the hash_table present in backup_conf_hash back to conf_hash.
Definition: conf.c:679
util-validate.h
HtpConfigRestoreBackup
void HtpConfigRestoreBackup(void)
Definition: app-layer-htp.c:2885
StreamingBufferConfig_
Definition: util-streaming-buffer.h:65
ConfInit
void ConfInit(void)
Initialize the configuration system.
Definition: conf.c:120
HtpBodyChunk_::next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:184
RB_RIGHT
#define RB_RIGHT(elm, field)
Definition: tree.h:323
StreamingBufferInsertAt
int StreamingBufferInsertAt(StreamingBuffer *sb, const StreamingBufferConfig *cfg, StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len, uint64_t offset)
Definition: util-streaming-buffer.c:1535
head
Flow * head
Definition: flow-hash.h:1
sc_errno
thread_local SCError sc_errno
Definition: util-error.c:31
StreamingBufferRegion_
Definition: util-streaming-buffer.h:84
app-layer-htp-mem.h
RB_FOREACH_REVERSE_FROM
#define RB_FOREACH_REVERSE_FROM(x, name, y)
Definition: tree.h:801
SBBCompare
int SBBCompare(struct StreamingBufferBlock *a, struct StreamingBufferBlock *b)
Definition: util-streaming-buffer.c:73
HTPFree
void HTPFree(void *ptr, size_t size)
Definition: app-layer-htp-mem.c:199
dst
uint16_t dst
Definition: app-layer-dnp3.h:4
RB_FOREACH_FROM
#define RB_FOREACH_FROM(x, name, y)
Definition: tree.h:786
StreamingBuffer_::region
StreamingBufferRegion region
Definition: util-streaming-buffer.h:109
StreamingBufferSegmentGetData
void StreamingBufferSegmentGetData(const StreamingBuffer *sb, const StreamingBufferSegment *seg, const uint8_t **data, uint32_t *data_len)
Definition: util-streaming-buffer.c:1766
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
StreamingBufferConfig_::region_gap
uint32_t region_gap
Definition: util-streaming-buffer.h:68
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:275
StreamingBufferBlock::len
uint32_t len
Definition: util-streaming-buffer.h:98
StreamingBufferBlock::offset
uint64_t offset
Definition: util-streaming-buffer.h:96
StreamingBufferSBBGetData
void StreamingBufferSBBGetData(const StreamingBuffer *sb, const StreamingBufferBlock *sbb, const uint8_t **data, uint32_t *data_len)
get the data for one SBB
Definition: util-streaming-buffer.c:1687
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:102
StreamingBufferBlock
block of continues data
Definition: util-streaming-buffer.h:95
WARN_UNUSED
#define WARN_UNUSED
Definition: suricata-common.h:412
StreamingBufferRegion_::stream_offset
uint64_t stream_offset
Definition: util-streaming-buffer.h:88
StreamingBufferRegisterTests
void StreamingBufferRegisterTests(void)
Definition: util-streaming-buffer.c:2443