/src/wireshark/epan/dissectors/packet-wcp.c
Line | Count | Source |
1 | | /* packet-wcp.c |
2 | | * Routines for Wellfleet Compression frame disassembly |
3 | | * Copyright 2001, Jeffrey C. Foster <jfoste@woodward.com> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | * |
11 | | * ToDo: |
12 | | * Add preference to allow/disallow decompression |
13 | | * Calculate and verify check byte (last byte), if only we knew how! |
14 | | * Handle Wellfleet compression over PPP links. |
15 | | * - This will require changing the sub-dissector call |
16 | | * routine to determine if layer 2 is frame relay or |
17 | | * or PPP and different sub-dissector routines for each. |
18 | | * |
19 | | * Based upon information in the Nortel TCL based Pcaptap code. |
20 | | *http://www.mynetworkforum.com/tools/PCAPTAP/pcaptap-Win32-3.00.exe |
21 | | * |
22 | | * And lzss algorithm |
23 | | *http://www.rasip.fer.hr/research/compress/algorithms/fund/lz/lzss.html |
24 | | */ |
25 | | |
26 | | /* |
27 | | * Wellfleet compression is a variation on LZSS encoding. |
28 | | * |
29 | | * Compression is done by keeping a sliding window of previous |
30 | | * data transmited. The sender will use a pattern match to |
31 | | * encode repeated data as a data pointer field. Then a stream |
32 | | * of pointers and actual data bytes. The pointer values include |
33 | | * an offset to previous data in the stream and the length of the |
34 | | * matching data. |
35 | | * |
36 | | * The data pattern matching is done on the octets. |
37 | | * |
38 | | * The data is encoded as 8 field blocks with a compression flag |
39 | | * byte at the beginning. If the bit is set in the compression |
40 | | * flag, then that field has a compression field. If it isn't set |
41 | | * then the byte is raw data. |
42 | | * |
43 | | * The compression field is either 2 or 3 bytes long. The length |
44 | | * is determined by the length of the matching data, for short |
45 | | * matches the match length is encoded in the high nibble of the |
46 | | * first byte. Otherwise the third byte of the field contains |
47 | | * the match length. |
48 | | * |
49 | | * First byte - |
50 | | * lower 4 bits: |
51 | | * High order nibble of the offset |
52 | | * |
53 | | * upper 4 bits: |
54 | | * 1 = length is in 3rd byte |
55 | | * 2-F = length of matching data - 1 |
56 | | * |
57 | | * Second byte - |
58 | | * Lower byte of the source offset. |
59 | | * |
60 | | * Third byte - |
61 | | * Length of match - 1 if First byte upper nibble = 1, otherwise |
62 | | * this byte isn't added to data stream. |
63 | | * |
64 | | * Example: |
65 | | * Uncompressed data (hex): 11 22 22 22 22 33 44 55 66 77 |
66 | | * |
67 | | * |
68 | | * Compression data : |
69 | | * Flag bits: 0x20 (third field is compressed) |
70 | | * Data: 11 22 20 00 33 44 55 |
71 | | * / / / / |
72 | | * raw data ------+--+ / / |
73 | | * (Comp length - 1)<<4+ / |
74 | | * Data offset ----------+ |
75 | | * |
76 | | * Output data (hex): 20 11 22 20 00 33 44 55 66 77 |
77 | | * |
78 | | * In this example the copy src is one byte behind the copy destination |
79 | | * so if appears as if output is being loaded with the source byte. |
80 | | * |
81 | | */ |
82 | | |
83 | | |
84 | | |
85 | | #include "config.h" |
86 | | |
87 | | |
88 | | #include <epan/packet.h> |
89 | | #include <epan/proto_data.h> |
90 | | |
91 | | #include <wiretap/wtap.h> |
92 | | #include <wsutil/pint.h> |
93 | | #include <epan/conversation.h> |
94 | | #include <epan/etypes.h> |
95 | | #include <epan/expert.h> |
96 | | #include <epan/exceptions.h> |
97 | | #include "packet-osi.h" |
98 | | |
99 | 1.74k | #define MAX_WIN_BUF_LEN 0x7fff /* storage size for decompressed data */ |
100 | 1.36k | #define MAX_WCP_BUF_LEN 2048 /* storage size for compressed data */ |
101 | 347 | #define FROM_DCE 0x80 /* for direction setting */ |
102 | | |
103 | | void proto_register_wcp(void); |
104 | | void proto_reg_handoff_wcp(void); |
105 | | |
106 | | typedef struct { |
107 | | uint8_t *buf_cur; |
108 | | uint8_t buffer[MAX_WIN_BUF_LEN]; |
109 | | /* initialized bytes in the buffer (since buf_cur may wrap around) */ |
110 | | uint16_t initialized; |
111 | | } wcp_window_t; |
112 | | |
113 | | typedef struct { |
114 | | wcp_window_t recv; |
115 | | wcp_window_t send; |
116 | | } wcp_circuit_data_t; |
117 | | |
118 | | /* XXX do I really want the length in here */ |
119 | | typedef struct { |
120 | | uint16_t len; |
121 | | uint8_t buffer[MAX_WCP_BUF_LEN]; |
122 | | } wcp_pdata_t; |
123 | | |
124 | | |
125 | | static int proto_wcp; |
126 | | static int hf_wcp_cmd; |
127 | | static int hf_wcp_ext_cmd; |
128 | | static int hf_wcp_seq; |
129 | | static int hf_wcp_chksum; |
130 | | static int hf_wcp_tid; |
131 | | static int hf_wcp_rev; |
132 | | static int hf_wcp_init; |
133 | | static int hf_wcp_seq_size; |
134 | | static int hf_wcp_alg; |
135 | | static int hf_wcp_alg_cnt; |
136 | | static int hf_wcp_alg_a; |
137 | | static int hf_wcp_alg_b; |
138 | | static int hf_wcp_alg_c; |
139 | | static int hf_wcp_alg_d; |
140 | | /* static int hf_wcp_rexmit; */ |
141 | | |
142 | | static int hf_wcp_hist_size; |
143 | | static int hf_wcp_ppc; |
144 | | static int hf_wcp_pib; |
145 | | |
146 | | static int hf_wcp_compressed_data; |
147 | | static int hf_wcp_comp_bits; |
148 | | /* static int hf_wcp_comp_marker; */ |
149 | | static int hf_wcp_short_len; |
150 | | static int hf_wcp_long_len; |
151 | | static int hf_wcp_short_run; |
152 | | static int hf_wcp_long_run; |
153 | | static int hf_wcp_offset; |
154 | | |
155 | | static int ett_wcp; |
156 | | static int ett_wcp_comp_data; |
157 | | static int ett_wcp_field; |
158 | | |
159 | | static expert_field ei_wcp_compressed_data_exceeds; |
160 | | static expert_field ei_wcp_uncompressed_data_exceeds; |
161 | | static expert_field ei_wcp_invalid_window_offset; |
162 | | static expert_field ei_wcp_buffer_too_long; |
163 | | /* static expert_field ei_wcp_invalid_match_length; */ |
164 | | |
165 | | static dissector_handle_t wcp_handle; |
166 | | static dissector_handle_t fr_uncompressed_handle; |
167 | | |
168 | | /* |
169 | | * Bits in the address field. |
170 | | */ |
171 | 16 | #define WCP_CMD 0xf0 /* WCP Command */ |
172 | 16 | #define WCP_EXT_CMD 0x0f /* WCP Extended Command */ |
173 | 16 | #define WCP_SEQ 0x0fff /* WCP Sequence number */ |
174 | 118 | #define WCP_OFFSET_MASK 0x0fff /* WCP Pattern source offset */ |
175 | | |
176 | | #define PPC_COMPRESSED_IND 0x0 |
177 | | #define PPC_UNCOMPRESSED_IND 0x1 |
178 | | #define PPC_TPPC_COMPRESSED_IND 0x2 |
179 | | #define PPC_TPPC_UNCOMPRESSED_IND 0x3 |
180 | 3 | #define CONNECT_REQ 0x4 |
181 | 1 | #define CONNECT_ACK 0x5 |
182 | | #define CONNECT_NAK 0x6 |
183 | | #define DISCONNECT_REQ 0x7 |
184 | | #define DISCONNECT_ACK 0x8 |
185 | 1 | #define INIT_REQ 0x9 |
186 | 5 | #define INIT_ACK 0xa |
187 | 1 | #define RESET_REQ 0xb |
188 | 2 | #define RESET_ACK 0xc |
189 | | #define REXMIT_NAK 0xd |
190 | | |
191 | | |
192 | | static const value_string cmd_string[] = { |
193 | | {0, "Compressed Data"}, |
194 | | {1, "Uncompressed Data"}, |
195 | | {15, "Extended"}, |
196 | | { 0, NULL } |
197 | | }; |
198 | | |
199 | | static const value_string ext_cmd_string[] = { |
200 | | {0, "Per Packet Compression"}, |
201 | | {4, "Connect Req"}, |
202 | | {5, "Connect Ack"}, |
203 | | {9, "Init Req"}, |
204 | | {0x0a, "Init Ack"}, |
205 | | |
206 | | { 0, NULL } |
207 | | }; |
208 | | |
209 | | |
210 | | |
211 | | static tvbuff_t *wcp_uncompress(tvbuff_t *src_tvb, unsigned offset, packet_info *pinfo, proto_tree *tree); |
212 | | static wcp_window_t *get_wcp_window_ptr(packet_info *pinfo); |
213 | | |
214 | | static void |
215 | 3 | dissect_wcp_con_req(tvbuff_t *tvb, unsigned offset, proto_tree *tree) { |
216 | | |
217 | | /* WCP connector request message */ |
218 | 3 | uint32_t alg_cnt; |
219 | | |
220 | 3 | proto_tree_add_item(tree, hf_wcp_tid, tvb, offset, 2, ENC_BIG_ENDIAN); |
221 | 3 | proto_tree_add_item(tree, hf_wcp_rev, tvb, offset + 2, 1, ENC_NA); |
222 | 3 | proto_tree_add_item(tree, hf_wcp_init, tvb, offset + 3, 1, ENC_NA); |
223 | 3 | proto_tree_add_item(tree, hf_wcp_seq_size, tvb, offset + 4, 1, ENC_NA); |
224 | 3 | proto_tree_add_item_ret_uint(tree, hf_wcp_alg_cnt, tvb, offset + 5, 1, ENC_NA, &alg_cnt); |
225 | 3 | proto_tree_add_item(tree, hf_wcp_alg_a, tvb, offset + 6, 1, ENC_NA); |
226 | 3 | if (alg_cnt > 1) |
227 | 2 | proto_tree_add_item(tree, hf_wcp_alg_b, tvb, offset + 7, 1, ENC_NA); |
228 | 3 | if (alg_cnt > 2) |
229 | 2 | proto_tree_add_item(tree, hf_wcp_alg_c, tvb, offset + 8, 1, ENC_NA); |
230 | 3 | if (alg_cnt > 3) |
231 | 2 | proto_tree_add_item(tree, hf_wcp_alg_d, tvb, offset + 9, 1, ENC_NA); |
232 | 3 | } |
233 | | |
234 | | static void |
235 | 1 | dissect_wcp_con_ack(tvbuff_t *tvb, unsigned offset, proto_tree *tree) { |
236 | | |
237 | | /* WCP connector ack message */ |
238 | | |
239 | 1 | proto_tree_add_item(tree, hf_wcp_tid, tvb, offset, 2, ENC_BIG_ENDIAN); |
240 | 1 | proto_tree_add_item(tree, hf_wcp_rev, tvb, offset + 2, 1, ENC_NA); |
241 | 1 | proto_tree_add_item(tree, hf_wcp_seq_size, tvb, offset + 3, 1, ENC_NA); |
242 | 1 | proto_tree_add_item(tree, hf_wcp_alg, tvb, offset + 4, 1, ENC_NA); |
243 | 1 | } |
244 | | |
245 | | static void |
246 | 5 | dissect_wcp_init(tvbuff_t *tvb, unsigned offset, proto_tree *tree) { |
247 | | |
248 | | /* WCP Initiate Request/Ack message */ |
249 | | |
250 | 5 | proto_tree_add_item(tree, hf_wcp_tid, tvb, offset, 2, ENC_BIG_ENDIAN); |
251 | 5 | proto_tree_add_item(tree, hf_wcp_rev, tvb, offset + 2, 1, ENC_NA); |
252 | 5 | proto_tree_add_item(tree, hf_wcp_hist_size, tvb, offset + 3, 1, ENC_NA); |
253 | 5 | proto_tree_add_item(tree, hf_wcp_ppc, tvb, offset + 4, 1, ENC_NA); |
254 | 5 | proto_tree_add_item(tree, hf_wcp_pib, tvb, offset + 5, 1, ENC_NA); |
255 | 5 | } |
256 | | |
257 | | |
258 | | static void |
259 | 2 | dissect_wcp_reset(tvbuff_t *tvb, unsigned offset, proto_tree *tree) { |
260 | | |
261 | | /* Process WCP Reset Request/Ack message */ |
262 | | |
263 | 2 | proto_tree_add_item(tree, hf_wcp_tid, tvb, offset, 2, ENC_BIG_ENDIAN); |
264 | 2 | } |
265 | | |
266 | | |
267 | 277 | static void wcp_save_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree* tree) { |
268 | | |
269 | 277 | wcp_window_t *buf_ptr = 0; |
270 | 277 | size_t len; |
271 | | |
272 | | /* discard first 2 bytes, header and last byte (check byte) */ |
273 | 277 | len = tvb_reported_length(tvb) - 3; |
274 | 277 | buf_ptr = get_wcp_window_ptr(pinfo); |
275 | | |
276 | 277 | if ((buf_ptr->buf_cur + len) <= (buf_ptr->buffer + MAX_WIN_BUF_LEN)) { |
277 | 237 | tvb_memcpy(tvb, buf_ptr->buf_cur, 2, len); |
278 | 237 | buf_ptr->buf_cur += len; |
279 | 237 | } else { |
280 | 40 | uint8_t *buf_end = buf_ptr->buffer + MAX_WIN_BUF_LEN; |
281 | 40 | tvb_memcpy(tvb, buf_ptr->buf_cur, 2, buf_end - buf_ptr->buf_cur); |
282 | 40 | if (buf_ptr->buf_cur + len <= buf_end) { |
283 | 0 | tvb_memcpy(tvb, buf_ptr->buffer, (int) (buf_end - buf_ptr->buf_cur-2), |
284 | 0 | len - (buf_end - buf_ptr->buf_cur)); |
285 | 0 | buf_ptr->buf_cur += len - MAX_WIN_BUF_LEN; |
286 | 40 | } else { |
287 | 40 | proto_tree_add_expert_remaining(tree, pinfo, &ei_wcp_buffer_too_long, tvb, 0); |
288 | 40 | } |
289 | 40 | } |
290 | 277 | } |
291 | | |
292 | | |
293 | 367 | static int dissect_wcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { |
294 | | |
295 | 367 | proto_tree *wcp_tree; |
296 | 367 | proto_item *ti; |
297 | 367 | int wcp_header_len; |
298 | 367 | uint16_t temp, cmd, ext_cmd, seq; |
299 | 367 | tvbuff_t *next_tvb; |
300 | | |
301 | 367 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "WCP"); |
302 | 367 | col_clear(pinfo->cinfo, COL_INFO); |
303 | | |
304 | 367 | temp = tvb_get_ntohs(tvb, 0); |
305 | | |
306 | 367 | cmd = (temp & 0xf000) >> 12; |
307 | 367 | ext_cmd = (temp & 0x0f00) >> 8; |
308 | | |
309 | 367 | if (cmd == 0xf) |
310 | 18 | wcp_header_len = 1; |
311 | 349 | else |
312 | 349 | wcp_header_len = 2; |
313 | | |
314 | 367 | seq = temp & 0x0fff; |
315 | | |
316 | | /* XXX should test seq to be sure it the last + 1 !! */ |
317 | | |
318 | 367 | col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(cmd, cmd_string, "Unknown")); |
319 | 367 | if (cmd == 0xf) |
320 | 18 | col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", |
321 | 18 | val_to_str_const(ext_cmd, ext_cmd_string, "Unknown")); |
322 | | |
323 | 367 | ti = proto_tree_add_item(tree, proto_wcp, tvb, 0, wcp_header_len, ENC_NA); |
324 | 367 | wcp_tree = proto_item_add_subtree(ti, ett_wcp); |
325 | | |
326 | 367 | proto_tree_add_item(wcp_tree, hf_wcp_cmd, tvb, 0, 1, ENC_NA); |
327 | 367 | if (cmd == 0xf) { |
328 | 18 | proto_tree_add_item(wcp_tree, hf_wcp_ext_cmd, tvb, 1, 1, ENC_NA); |
329 | 18 | switch (ext_cmd) { |
330 | 3 | case CONNECT_REQ: |
331 | 3 | dissect_wcp_con_req(tvb, 1, wcp_tree); |
332 | 3 | break; |
333 | | |
334 | 1 | case CONNECT_ACK: |
335 | 1 | dissect_wcp_con_ack(tvb, 1, wcp_tree); |
336 | 1 | break; |
337 | 1 | case INIT_REQ: |
338 | 5 | case INIT_ACK: |
339 | 5 | dissect_wcp_init(tvb, 1, wcp_tree); |
340 | 5 | break; |
341 | 1 | case RESET_REQ: |
342 | 2 | case RESET_ACK: |
343 | 2 | dissect_wcp_reset(tvb, 1, wcp_tree); |
344 | 2 | break; |
345 | 7 | default: |
346 | 7 | break; |
347 | 18 | } |
348 | 349 | } else { |
349 | 349 | proto_tree_add_uint(wcp_tree, hf_wcp_seq, tvb, 0, 2, seq); |
350 | 349 | } |
351 | | |
352 | | |
353 | | /* exit if done */ |
354 | 367 | if (cmd != 1 && cmd != 0 && !(cmd == 0xf && ext_cmd == 0)) |
355 | 20 | return 2; |
356 | | |
357 | 347 | if (cmd == 1) { /* uncompressed data */ |
358 | 277 | if (!pinfo->fd->visited) { /* if first pass */ |
359 | 277 | wcp_save_data(tvb, pinfo, wcp_tree); |
360 | 277 | } |
361 | 277 | next_tvb = tvb_new_subset_remaining(tvb, wcp_header_len); |
362 | 277 | } else { /* cmd == 0 || (cmd == 0xf && ext_cmd == 0) */ |
363 | | |
364 | 70 | next_tvb = wcp_uncompress(tvb, wcp_header_len, pinfo, wcp_tree); |
365 | | |
366 | 70 | if (!next_tvb) { |
367 | 57 | return tvb_captured_length(tvb); |
368 | 57 | } |
369 | 70 | } |
370 | | |
371 | | /* add the check byte */ |
372 | 290 | proto_tree_add_checksum(wcp_tree, tvb, tvb_reported_length(tvb) - 1, hf_wcp_chksum, -1, NULL, pinfo, 0, ENC_NA, PROTO_CHECKSUM_NO_FLAGS); |
373 | | |
374 | 290 | call_dissector(fr_uncompressed_handle, next_tvb, pinfo, tree); |
375 | | |
376 | 290 | return tvb_captured_length(tvb); |
377 | 347 | } |
378 | | |
379 | | |
380 | | static uint8_t * |
381 | | decompressed_entry(uint8_t *dst, uint16_t data_offset, |
382 | | uint16_t data_cnt, unsigned *len, wcp_window_t *buf_ptr) |
383 | 58 | { |
384 | 58 | const uint8_t *src; |
385 | 58 | uint8_t *buf_start, *buf_end; |
386 | | |
387 | 58 | buf_start = buf_ptr->buffer; |
388 | 58 | buf_end = buf_ptr->buffer + MAX_WIN_BUF_LEN; |
389 | | |
390 | | /* do the decompression for one field */ |
391 | | |
392 | 58 | src = (dst - 1 - data_offset); |
393 | 58 | if (src < buf_start) |
394 | 2 | src += MAX_WIN_BUF_LEN; |
395 | | |
396 | | |
397 | | /* XXX could do some fancy memory moves, later if speed is problem */ |
398 | | |
399 | 200 | while(data_cnt--) { |
400 | 142 | *dst = *src; |
401 | 142 | if (buf_ptr->initialized < MAX_WIN_BUF_LEN) |
402 | 142 | buf_ptr->initialized++; |
403 | 142 | if ( ++(*len) >MAX_WCP_BUF_LEN) { |
404 | 0 | return NULL; /* end of buffer error */ |
405 | 0 | } |
406 | 142 | if (dst++ == buf_end) |
407 | 0 | dst = buf_start; |
408 | 142 | if (src++ == buf_end) |
409 | 0 | src = buf_start; |
410 | | |
411 | 142 | } |
412 | 58 | return dst; |
413 | 58 | } |
414 | | |
415 | | |
416 | | static |
417 | 347 | wcp_window_t *get_wcp_window_ptr(packet_info *pinfo) { |
418 | | |
419 | | /* find the circuit for this DLCI, create one if needed */ |
420 | | /* and return the wcp_window data structure pointer */ |
421 | | /* for the direction of this packet */ |
422 | | |
423 | 347 | conversation_t *conv; |
424 | 347 | wcp_circuit_data_t *wcp_circuit_data; |
425 | | |
426 | 347 | conv = find_or_create_conversation(pinfo); |
427 | | |
428 | 347 | wcp_circuit_data = (wcp_circuit_data_t *)conversation_get_proto_data(conv, proto_wcp); |
429 | 347 | if (!wcp_circuit_data) { |
430 | 41 | wcp_circuit_data = wmem_new0(wmem_file_scope(), wcp_circuit_data_t); |
431 | 41 | wcp_circuit_data->recv.buf_cur = wcp_circuit_data->recv.buffer; |
432 | 41 | wcp_circuit_data->send.buf_cur = wcp_circuit_data->send.buffer; |
433 | 41 | conversation_add_proto_data(conv, proto_wcp, wcp_circuit_data); |
434 | 41 | } |
435 | 347 | if (pinfo->pseudo_header->dte_dce.flags & FROM_DCE) |
436 | 0 | return &wcp_circuit_data->recv; |
437 | 347 | else |
438 | 347 | return &wcp_circuit_data->send; |
439 | 347 | } |
440 | | |
441 | | |
442 | 70 | static tvbuff_t *wcp_uncompress(tvbuff_t *src_tvb, unsigned offset, packet_info *pinfo, proto_tree *tree) { |
443 | | |
444 | | /* do the packet data uncompression and load it into the dst buffer */ |
445 | | |
446 | 70 | proto_tree *cd_tree, *sub_tree; |
447 | 70 | proto_item *cd_item, *ti; |
448 | | |
449 | 70 | unsigned len; |
450 | 70 | int i; |
451 | 70 | unsigned cnt = tvb_reported_length(src_tvb) - 1;/* don't include check byte */ |
452 | | |
453 | 70 | uint8_t *dst, *src, *buf_start, *buf_end, comp_flag_bits = 0; |
454 | 70 | uint16_t data_offset, data_cnt; |
455 | 70 | uint8_t src_buf[ MAX_WCP_BUF_LEN]; |
456 | 70 | tvbuff_t *tvb; |
457 | 70 | wcp_window_t *buf_ptr = 0; |
458 | 70 | wcp_pdata_t *pdata_ptr; |
459 | | |
460 | 70 | buf_ptr = get_wcp_window_ptr(pinfo); |
461 | | |
462 | 70 | buf_start = buf_ptr->buffer; |
463 | 70 | buf_end = buf_start + MAX_WIN_BUF_LEN; |
464 | | |
465 | 70 | cd_item = proto_tree_add_item(tree, hf_wcp_compressed_data, |
466 | 70 | src_tvb, offset, cnt - offset, ENC_NA); |
467 | 70 | cd_tree = proto_item_add_subtree(cd_item, ett_wcp_comp_data); |
468 | 70 | if (cnt - offset > MAX_WCP_BUF_LEN) { |
469 | 0 | expert_add_info_format(pinfo, cd_item, &ei_wcp_compressed_data_exceeds, |
470 | 0 | "Compressed data exceeds maximum buffer length (%d > %d)", |
471 | 0 | cnt - offset, MAX_WCP_BUF_LEN); |
472 | 0 | return NULL; |
473 | 0 | } |
474 | | |
475 | | /* |
476 | | * XXX - this will throw an exception if a snapshot length cut short |
477 | | * the data. We may want to try to dissect the data in that case, |
478 | | * and we may even want to try to decompress it, *but* we will |
479 | | * want to mark the buffer of decompressed data as incomplete, so |
480 | | * that we don't try to use it for decompressing later packets. |
481 | | */ |
482 | 70 | src = (uint8_t *)tvb_memcpy(src_tvb, src_buf, offset, cnt - offset); |
483 | 70 | dst = buf_ptr->buf_cur; |
484 | 70 | len = 0; |
485 | 70 | i = -1; |
486 | | |
487 | 1.48k | while(offset < cnt) { |
488 | | /* There are i bytes left for this byte of flag bits */ |
489 | 1.47k | if ( --i >= 0) { |
490 | | /* |
491 | | * There's still at least one more byte left for |
492 | | * the current set of compression flag bits; is |
493 | | * it compressed data or uncompressed data? |
494 | | */ |
495 | 1.27k | if (comp_flag_bits & 0x80) { |
496 | | /* This byte is compressed data */ |
497 | 115 | if (!(offset + 1 < cnt)) { |
498 | | /* |
499 | | * The data offset runs past the |
500 | | * end of the data. |
501 | | */ |
502 | 13 | return NULL; |
503 | 13 | } |
504 | 102 | data_offset = pntohu16(src) & WCP_OFFSET_MASK; |
505 | 102 | if ((*src & 0xf0) == 0x10) { |
506 | | /* |
507 | | * The count of bytes to copy from |
508 | | * the dictionary window is in the |
509 | | * byte following the data offset. |
510 | | */ |
511 | 7 | if (!(offset + 2 < cnt)) { |
512 | | /* |
513 | | * The data count runs past the |
514 | | * end of the data. |
515 | | */ |
516 | 1 | return NULL; |
517 | 1 | } |
518 | 6 | data_cnt = *(src + 2) + 1; |
519 | 6 | if (tree) { |
520 | 6 | ti = proto_tree_add_item(cd_tree, hf_wcp_long_run, src_tvb, |
521 | 6 | offset, 3, ENC_NA); |
522 | 6 | sub_tree = proto_item_add_subtree(ti, ett_wcp_field); |
523 | 6 | proto_tree_add_uint(sub_tree, hf_wcp_offset, src_tvb, |
524 | 6 | offset, 2, data_offset); |
525 | | |
526 | 6 | proto_tree_add_item(sub_tree, hf_wcp_long_len, src_tvb, |
527 | 6 | offset+2, 1, ENC_BIG_ENDIAN); |
528 | 6 | } |
529 | 6 | src += 3; |
530 | 6 | offset += 3; |
531 | 95 | } else { |
532 | | /* |
533 | | * The count of bytes to copy from |
534 | | * the dictionary window is in |
535 | | * the upper 4 bits of the next |
536 | | * byte. |
537 | | */ |
538 | 95 | data_cnt = (*src >> 4) + 1; |
539 | 95 | if (tree) { |
540 | 95 | ti = proto_tree_add_item(cd_tree, hf_wcp_short_run, src_tvb, |
541 | 95 | offset, 2, ENC_NA); |
542 | 95 | sub_tree = proto_item_add_subtree(ti, ett_wcp_field); |
543 | 95 | proto_tree_add_uint(sub_tree, hf_wcp_short_len, src_tvb, |
544 | 95 | offset, 1, *src); |
545 | 95 | proto_tree_add_uint(sub_tree, hf_wcp_offset, src_tvb, |
546 | 95 | offset, 2, data_offset); |
547 | 95 | } |
548 | 95 | src += 2; |
549 | 95 | offset += 2; |
550 | 95 | } |
551 | 101 | if (data_offset + 1 > buf_ptr->initialized) { |
552 | 42 | expert_add_info_format(pinfo, cd_item, &ei_wcp_invalid_window_offset, |
553 | 42 | "Data offset exceeds valid window size (%d > %d)", |
554 | 42 | data_offset+1, buf_ptr->initialized); |
555 | 42 | return NULL; |
556 | 42 | } |
557 | | |
558 | 59 | if (data_offset + 1 < data_cnt) { |
559 | 1 | expert_add_info_format(pinfo, cd_item, &ei_wcp_invalid_window_offset, |
560 | 1 | "Data count exceeds offset (%d > %d)", |
561 | 1 | data_cnt, data_offset+1); |
562 | 1 | return NULL; |
563 | 1 | } |
564 | 58 | if ( !pinfo->fd->visited) { /* if first pass */ |
565 | 58 | dst = decompressed_entry(dst, |
566 | 58 | data_offset, data_cnt, &len, |
567 | 58 | buf_ptr); |
568 | 58 | if (dst == NULL) { |
569 | 0 | expert_add_info_format(pinfo, cd_item, &ei_wcp_uncompressed_data_exceeds, |
570 | 0 | "Uncompressed data exceeds maximum buffer length (%d > %d)", |
571 | 0 | len, MAX_WCP_BUF_LEN); |
572 | 0 | return NULL; |
573 | 0 | } |
574 | 58 | } |
575 | 1.15k | }else { |
576 | | /* |
577 | | * This byte is uncompressed data; is there |
578 | | * room for it in the buffer of uncompressed |
579 | | * data? |
580 | | */ |
581 | 1.15k | if ( ++len >MAX_WCP_BUF_LEN) { |
582 | | /* No - report an error. */ |
583 | 0 | expert_add_info_format(pinfo, cd_item, &ei_wcp_uncompressed_data_exceeds, |
584 | 0 | "Uncompressed data exceeds maximum buffer length (%d > %d)", |
585 | 0 | len, MAX_WCP_BUF_LEN); |
586 | 0 | return NULL; |
587 | 0 | } |
588 | | |
589 | 1.15k | if ( !pinfo->fd->visited) { |
590 | | /* |
591 | | * This is the first pass through |
592 | | * the packets, so copy it to the |
593 | | * buffer of uncompressed data. |
594 | | */ |
595 | 1.15k | *dst = *src; |
596 | 1.15k | if (dst++ == buf_end) |
597 | 0 | dst = buf_start; |
598 | 1.15k | if (buf_ptr->initialized < MAX_WIN_BUF_LEN) |
599 | 1.15k | buf_ptr->initialized++; |
600 | 1.15k | } |
601 | 1.15k | ++src; |
602 | 1.15k | ++offset; |
603 | 1.15k | } |
604 | | |
605 | | /* Skip to the next compression flag bit */ |
606 | 1.21k | comp_flag_bits <<= 1; |
607 | | |
608 | 1.21k | }else { |
609 | | /* |
610 | | * There are no more bytes left for the current |
611 | | * set of compression flag bits, so this byte |
612 | | * is another byte of compression flag bits. |
613 | | */ |
614 | 205 | comp_flag_bits = *src++; |
615 | 205 | proto_tree_add_uint(cd_tree, hf_wcp_comp_bits, src_tvb, offset, 1, |
616 | 205 | comp_flag_bits); |
617 | 205 | offset++; |
618 | | |
619 | 205 | i = 8; |
620 | 205 | } |
621 | 1.47k | } |
622 | | |
623 | 13 | if (pinfo->fd->visited) { /* if not first pass */ |
624 | | /* get uncompressed data */ |
625 | 0 | pdata_ptr = (wcp_pdata_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_wcp, 0); |
626 | |
|
627 | 0 | if (!pdata_ptr) { /* exit if no data */ |
628 | 0 | REPORT_DISSECTOR_BUG("Can't find uncompressed data"); |
629 | 0 | return NULL; |
630 | 0 | } |
631 | 0 | len = pdata_ptr->len; |
632 | 13 | } else { |
633 | 13 | if (buf_ptr->buf_cur + len > buf_end) { |
634 | 0 | expert_add_info_format(pinfo, cd_item, &ei_wcp_invalid_window_offset, |
635 | 0 | "Uncompressed data exceeds available buffer length (%d > %d)", |
636 | 0 | len, (int) (buf_end - buf_ptr->buf_cur)); |
637 | 0 | return NULL; |
638 | 0 | } |
639 | | |
640 | | /* save the new data as per packet data */ |
641 | 13 | pdata_ptr = wmem_new0(wmem_file_scope(), wcp_pdata_t); |
642 | 13 | memcpy( &pdata_ptr->buffer, buf_ptr->buf_cur, len); |
643 | 13 | pdata_ptr->len = len; |
644 | | |
645 | 13 | p_add_proto_data(wmem_file_scope(), pinfo, proto_wcp, 0, (void*)pdata_ptr); |
646 | | |
647 | 13 | buf_ptr->buf_cur = dst; |
648 | 13 | } |
649 | | |
650 | 13 | tvb = tvb_new_child_real_data(src_tvb, pdata_ptr->buffer, pdata_ptr->len, pdata_ptr->len); |
651 | | |
652 | | /* Add new data to the data source list */ |
653 | 13 | add_new_data_source(pinfo, tvb, "Uncompressed WCP"); |
654 | 13 | return tvb; |
655 | | |
656 | 13 | } |
657 | | |
658 | | |
659 | | void |
660 | | proto_register_wcp(void) |
661 | 16 | { |
662 | 16 | static hf_register_info hf[] = { |
663 | 16 | { &hf_wcp_cmd, |
664 | 16 | { "Command", "wcp.cmd", FT_UINT8, BASE_HEX, VALS(cmd_string), WCP_CMD, |
665 | 16 | "Compression Command", HFILL }}, |
666 | 16 | { &hf_wcp_ext_cmd, |
667 | 16 | { "Extended Command", "wcp.ext_cmd", FT_UINT8, BASE_HEX, VALS(ext_cmd_string), WCP_EXT_CMD, |
668 | 16 | "Extended Compression Command", HFILL }}, |
669 | 16 | { &hf_wcp_seq, |
670 | 16 | { "SEQ", "wcp.seq", FT_UINT16, BASE_HEX, NULL, WCP_SEQ, |
671 | 16 | "Sequence Number", HFILL }}, |
672 | 16 | { &hf_wcp_chksum, |
673 | 16 | { "Checksum", "wcp.checksum", FT_UINT8, BASE_DEC, NULL, 0, |
674 | 16 | "Packet Checksum", HFILL }}, |
675 | 16 | { &hf_wcp_tid, |
676 | 16 | { "TID", "wcp.tid", FT_UINT16, BASE_DEC, NULL, 0, |
677 | 16 | NULL, HFILL }}, |
678 | 16 | { &hf_wcp_rev, |
679 | 16 | { "Revision", "wcp.rev", FT_UINT8, BASE_DEC, NULL, 0, |
680 | 16 | NULL, HFILL }}, |
681 | 16 | { &hf_wcp_init, |
682 | 16 | { "Initiator", "wcp.init", FT_UINT8, BASE_DEC, NULL, 0, |
683 | 16 | NULL, HFILL }}, |
684 | 16 | { &hf_wcp_seq_size, |
685 | 16 | { "Seq Size", "wcp.seq_size", FT_UINT8, BASE_DEC, NULL, 0, |
686 | 16 | "Sequence Size", HFILL }}, |
687 | 16 | { &hf_wcp_alg_cnt, |
688 | 16 | { "Alg Count", "wcp.alg_cnt", FT_UINT8, BASE_DEC, NULL, 0, |
689 | 16 | "Algorithm Count", HFILL }}, |
690 | 16 | { &hf_wcp_alg_a, |
691 | 16 | { "Alg 1", "wcp.alg1", FT_UINT8, BASE_DEC, NULL, 0, |
692 | 16 | "Algorithm #1", HFILL }}, |
693 | 16 | { &hf_wcp_alg_b, |
694 | 16 | { "Alg 2", "wcp.alg2", FT_UINT8, BASE_DEC, NULL, 0, |
695 | 16 | "Algorithm #2", HFILL }}, |
696 | 16 | { &hf_wcp_alg_c, |
697 | 16 | { "Alg 3", "wcp.alg3", FT_UINT8, BASE_DEC, NULL, 0, |
698 | 16 | "Algorithm #3", HFILL }}, |
699 | 16 | { &hf_wcp_alg_d, |
700 | 16 | { "Alg 4", "wcp.alg4", FT_UINT8, BASE_DEC, NULL, 0, |
701 | 16 | "Algorithm #4", HFILL }}, |
702 | 16 | { &hf_wcp_alg, |
703 | 16 | { "Alg", "wcp.alg", FT_UINT8, BASE_DEC, NULL, 0, |
704 | 16 | "Algorithm", HFILL }}, |
705 | | #if 0 |
706 | | { &hf_wcp_rexmit, |
707 | | { "Rexmit", "wcp.rexmit", FT_UINT8, BASE_DEC, NULL, 0, |
708 | | "Retransmit", HFILL }}, |
709 | | #endif |
710 | 16 | { &hf_wcp_hist_size, |
711 | 16 | { "History", "wcp.hist", FT_UINT8, BASE_DEC, NULL, 0, |
712 | 16 | "History Size", HFILL }}, |
713 | 16 | { &hf_wcp_ppc, |
714 | 16 | { "PerPackComp", "wcp.ppc", FT_UINT8, BASE_DEC, NULL, 0, |
715 | 16 | "Per Packet Compression", HFILL }}, |
716 | 16 | { &hf_wcp_pib, |
717 | 16 | { "PIB", "wcp.pib", FT_UINT8, BASE_DEC, NULL, 0, |
718 | 16 | NULL, HFILL }}, |
719 | 16 | { &hf_wcp_compressed_data, |
720 | 16 | { "Compressed Data", "wcp.compressed_data", FT_NONE, BASE_NONE, NULL, 0, |
721 | 16 | "Raw compressed data", HFILL }}, |
722 | 16 | { &hf_wcp_comp_bits, |
723 | 16 | { "Compress Flag", "wcp.flag", FT_UINT8, BASE_HEX, NULL, 0, |
724 | 16 | "Compressed byte flag", HFILL }}, |
725 | | #if 0 |
726 | | { &hf_wcp_comp_marker, |
727 | | { "Compress Marker", "wcp.mark", FT_UINT8, BASE_DEC, NULL, 0, |
728 | | "Compressed marker", HFILL }}, |
729 | | #endif |
730 | 16 | { &hf_wcp_offset, |
731 | 16 | { "Source offset", "wcp.off", FT_UINT16, BASE_HEX, NULL, WCP_OFFSET_MASK, |
732 | 16 | "Data source offset", HFILL }}, |
733 | 16 | { &hf_wcp_short_len, |
734 | 16 | { "Compress Length", "wcp.short_len", FT_UINT8, BASE_HEX, NULL, 0xf0, |
735 | 16 | "Compressed length", HFILL }}, |
736 | 16 | { &hf_wcp_long_len, |
737 | 16 | { "Compress Length", "wcp.long_len", FT_UINT8, BASE_HEX, NULL, 0, |
738 | 16 | "Compressed length", HFILL }}, |
739 | 16 | { &hf_wcp_long_run, |
740 | 16 | { "Long Compression", "wcp.long_comp", FT_BYTES, BASE_NONE, NULL, 0, |
741 | 16 | "Long Compression type", HFILL }}, |
742 | 16 | { &hf_wcp_short_run, |
743 | 16 | { "Short Compression", "wcp.short_comp", FT_BYTES, BASE_NONE, NULL, 0, |
744 | 16 | "Short Compression type", HFILL }}, |
745 | | |
746 | 16 | }; |
747 | | |
748 | | |
749 | 16 | static int *ett[] = { |
750 | 16 | &ett_wcp, |
751 | 16 | &ett_wcp_comp_data, |
752 | 16 | &ett_wcp_field, |
753 | 16 | }; |
754 | | |
755 | 16 | static ei_register_info ei[] = { |
756 | 16 | { &ei_wcp_compressed_data_exceeds, { "wcp.compressed_data.exceeds", PI_MALFORMED, PI_ERROR, "Compressed data exceeds maximum buffer length", EXPFILL }}, |
757 | 16 | { &ei_wcp_uncompressed_data_exceeds, { "wcp.uncompressed_data.exceeds", PI_MALFORMED, PI_ERROR, "Uncompressed data exceeds maximum buffer length", EXPFILL }}, |
758 | 16 | { &ei_wcp_invalid_window_offset, { "wcp.off.invalid", PI_MALFORMED, PI_ERROR, "Offset points outside of visible window", EXPFILL }}, |
759 | 16 | { &ei_wcp_buffer_too_long, { "wcp.buffer_too_long", PI_MALFORMED, PI_ERROR, "Buffer too long", EXPFILL }}, |
760 | | #if 0 |
761 | | { &ei_wcp_invalid_match_length, { "wcp.len.invalid", PI_MALFORMED, PI_ERROR, "Length greater than offset", EXPFILL }}, |
762 | | #endif |
763 | 16 | }; |
764 | | |
765 | 16 | expert_module_t* expert_wcp; |
766 | | |
767 | 16 | proto_wcp = proto_register_protocol ("Wellfleet Compression", "WCP", "wcp"); |
768 | 16 | proto_register_field_array (proto_wcp, hf, array_length(hf)); |
769 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
770 | 16 | expert_wcp = expert_register_protocol(proto_wcp); |
771 | 16 | expert_register_field_array(expert_wcp, ei, array_length(ei)); |
772 | 16 | wcp_handle = register_dissector("wcp", dissect_wcp, proto_wcp); |
773 | 16 | } |
774 | | |
775 | | |
776 | | void |
777 | 16 | proto_reg_handoff_wcp(void) { |
778 | | /* |
779 | | * Get handle for the Frame Relay (uncompressed) dissector. |
780 | | */ |
781 | 16 | fr_uncompressed_handle = find_dissector_add_dependency("fr_uncompressed", proto_wcp); |
782 | | |
783 | 16 | dissector_add_uint("fr.nlpid", NLPID_COMPRESSED, wcp_handle); |
784 | 16 | dissector_add_uint("ethertype", ETHERTYPE_WCP, wcp_handle); |
785 | 16 | } |
786 | | |
787 | | /* |
788 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
789 | | * |
790 | | * Local variables: |
791 | | * c-basic-offset: 8 |
792 | | * tab-width: 8 |
793 | | * indent-tabs-mode: t |
794 | | * End: |
795 | | * |
796 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
797 | | * :indentSize=8:tabSize=8:noTabs=false: |
798 | | */ |