dcerpc-netlogon: improve NetrLogonGetCapabilities dissection
[wireshark-sm.git] / epan / tvbuff_lz77.c
bloba609912636998ca3ca958615d4a6c20d506e81fe
1 /*
2 * Decompression code for Plain LZ77. This encoding is used by
3 * Microsoft in various file formats and protocols including SMB3.
5 * See MS-XCA.
7 * Copyright (C) 2019 Aurélien Aptel
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include <glib.h>
13 #include <epan/exceptions.h>
14 #include <epan/tvbuff.h>
15 #include <epan/wmem_scopes.h>
17 #define MAX_INPUT_SIZE (16*1024*1024) /* 16MB */
19 static bool do_uncompress(tvbuff_t *tvb, int offset, int in_size,
20 wmem_array_t *obuf)
22 unsigned buf_flags = 0, buf_flag_count = 0;
23 int in_off = 0;
24 int last_length_half_byte = 0;
25 unsigned match_bytes, match_len, match_off;
26 unsigned i;
28 if (!tvb)
29 return false;
31 if (!in_size || in_size > MAX_INPUT_SIZE)
32 return false;
34 while (1) {
35 if (buf_flag_count == 0) {
36 buf_flags = tvb_get_letohl(tvb, offset+in_off);
37 in_off += 4;
38 buf_flag_count = 32;
40 buf_flag_count--;
41 if ((buf_flags & (1u << buf_flag_count)) == 0) {
42 uint8_t v = tvb_get_uint8(tvb, offset+in_off);
43 wmem_array_append_one(obuf, v);
44 in_off++;
45 } else {
46 if (in_off == in_size)
47 return true;
48 match_bytes = tvb_get_letohs(tvb, offset+in_off);
49 in_off += 2;
50 match_len = match_bytes % 8;
51 match_off = (match_bytes/8) + 1;
52 if (match_len == 7) {
53 if (last_length_half_byte == 0) {
54 match_len = tvb_get_uint8(tvb, offset+in_off);
55 match_len = match_len % 16;
56 last_length_half_byte = in_off;
57 in_off++;
58 } else {
59 match_len = tvb_get_uint8(tvb, offset+last_length_half_byte);
60 match_len = match_len / 16;
61 last_length_half_byte = 0;
63 if (match_len == 15) {
64 match_len = tvb_get_uint8(tvb, offset+in_off);
65 in_off++;
66 if (match_len == 255) {
67 match_len = tvb_get_letohs(tvb, offset+in_off);
68 in_off += 2;
69 if (match_len == 0) {
70 /* This case isn't documented */
71 match_len = tvb_get_letohs(tvb, offset+in_off);
72 in_off += 4;
74 if (match_len < 15+7)
75 return false;
76 match_len -= (15 + 7);
78 match_len += 15;
80 match_len += 7;
82 match_len += 3;
83 for (i = 0; i < match_len; i++) {
84 uint8_t byte;
85 if (match_off > wmem_array_get_count(obuf))
86 return false;
87 if (wmem_array_try_index(obuf, wmem_array_get_count(obuf)-match_off, &byte))
88 return false;
89 wmem_array_append_one(obuf, byte);
94 return true;
97 tvbuff_t *
98 tvb_uncompress_lz77(tvbuff_t *tvb, const int offset, int in_size)
100 volatile bool ok = false;
101 wmem_allocator_t *pool;
102 wmem_array_t *obuf;
103 tvbuff_t *out;
105 pool = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
106 obuf = wmem_array_sized_new(pool, 1, in_size*2);
108 TRY {
109 ok = do_uncompress(tvb, offset, in_size, obuf);
110 } CATCH_ALL {
111 ok = false;
113 ENDTRY;
115 if (ok) {
117 * Cannot pass a tvb free callback that frees the wmem
118 * pool, so we make an extra copy that uses bare
119 * pointers. This could be optimized if tvb API had a
120 * free pool callback of some sort.
122 unsigned size = wmem_array_get_count(obuf);
123 uint8_t *p = (uint8_t *)g_malloc(size);
124 memcpy(p, wmem_array_get_raw(obuf), size);
125 out = tvb_new_real_data(p, size, size);
126 tvb_set_free_cb(out, g_free);
127 } else {
128 out = NULL;
131 wmem_destroy_allocator(pool);
133 return out;
136 tvbuff_t *
137 tvb_child_uncompress_lz77(tvbuff_t *parent, tvbuff_t *tvb, const int offset, int in_size)
139 tvbuff_t *new_tvb = tvb_uncompress_lz77(tvb, offset, in_size);
140 if (new_tvb)
141 tvb_set_child_real_data_tvbuff(parent, new_tvb);
142 return new_tvb;
147 * Editor modelines - https://www.wireshark.org/tools/modelines.html
149 * Local variables:
150 * c-basic-offset: 8
151 * tab-width: 8
152 * indent-tabs-mode: t
153 * End:
155 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
156 * :indentSize=8:tabSize=8:noTabs=false: