/src/wireshark/wiretap/lanalyzer.c
Line | Count | Source |
1 | | /* lanalyzer.c |
2 | | * |
3 | | * Wiretap Library |
4 | | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> |
5 | | * |
6 | | * SPDX-License-Identifier: GPL-2.0-or-later |
7 | | */ |
8 | | |
9 | | #include "config.h" |
10 | | #define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP |
11 | | #include "lanalyzer.h" |
12 | | |
13 | | #include <stdlib.h> |
14 | | #include <errno.h> |
15 | | |
16 | | #include <wsutil/pint.h> |
17 | | |
18 | | #include "wtap_module.h" |
19 | | #include "file_wrappers.h" |
20 | | |
21 | | /* The LANalyzer format is documented (at least in part) in Novell document |
22 | | TID022037, which can be found at, among other places: |
23 | | |
24 | | http://www.blacksheepnetworks.com/security/info/nw/lan/trace.txt |
25 | | */ |
26 | | |
27 | | /* Record header format */ |
28 | | |
29 | | typedef struct { |
30 | | uint8_t record_type[2]; |
31 | | uint8_t record_length[2]; |
32 | | } LA_RecordHeader; |
33 | | |
34 | 0 | #define LA_RecordHeaderSize 4 |
35 | | |
36 | | /* Record type codes: */ |
37 | | |
38 | 0 | #define RT_HeaderRegular 0x1001 |
39 | 0 | #define RT_HeaderCyclic 0x1007 |
40 | | #define RT_RxChannelName 0x1006 |
41 | | #define RT_TxChannelName 0x100b |
42 | | #define RT_FilterName 0x1032 |
43 | | #define RT_RxTemplateName 0x1035 |
44 | | #define RT_TxTemplateName 0x1036 |
45 | | #define RT_DisplayOptions 0x100a |
46 | 0 | #define RT_Summary 0x1002 |
47 | 0 | #define RT_SubfileSummary 0x1003 |
48 | | #define RT_CyclicInformation 0x1009 |
49 | 0 | #define RT_Index 0x1004 |
50 | 0 | #define RT_PacketData 0x1005 |
51 | | |
52 | 0 | #define LA_ProFileLimit (1024 * 1024 * 32) |
53 | | |
54 | | typedef uint8_t Eadr[6]; |
55 | | typedef uint16_t TimeStamp[3]; /* 0.5 microseconds since start of trace */ |
56 | | |
57 | | /* |
58 | | * These records have only 2-byte alignment for 4-byte quantities, |
59 | | * so the structures aren't necessarily valid; they're kept as comments |
60 | | * for reference purposes. |
61 | | */ |
62 | | |
63 | | /* |
64 | | * typedef struct { |
65 | | * uint8_t day; |
66 | | * uint8_t mon; |
67 | | * int16_t year; |
68 | | * } Date; |
69 | | */ |
70 | | |
71 | | /* |
72 | | * typedef struct { |
73 | | * uint8_t second; |
74 | | * uint8_t minute; |
75 | | * uint8_t hour; |
76 | | * uint8_t day; |
77 | | * int16_t reserved; |
78 | | * } Time; |
79 | | */ |
80 | | |
81 | | /* |
82 | | * RT_Summary: |
83 | | * |
84 | | * typedef struct { |
85 | | * Date datcre; |
86 | | * Date datclo; |
87 | | * Time timeopn; |
88 | | * Time timeclo; |
89 | | * Eadr statadr; |
90 | | * int16_t mxseqno; |
91 | | * int16_t slcoff; |
92 | | * int16_t mxslc; |
93 | | * int32_t totpktt; |
94 | | * int32_t statrg; |
95 | | * int32_t stptrg; |
96 | | * int32_t mxpkta[36]; |
97 | | * int16_t board_type; |
98 | | * int16_t board_version; |
99 | | * int8_t reserved[18]; |
100 | | * } Summary; |
101 | | */ |
102 | | |
103 | 0 | #define SummarySize (18+22+(4*36)+6+6+6+4+4) |
104 | | |
105 | | /* |
106 | | * typedef struct { |
107 | | * int16_t rid; |
108 | | * int16_t rlen; |
109 | | * Summary s; |
110 | | * } LA_SummaryRecord; |
111 | | */ |
112 | | |
113 | 0 | #define LA_SummaryRecordSize (SummarySize + 4) |
114 | | |
115 | | /* LANalyzer board types (which indicate the type of network on which |
116 | | the capture was done). */ |
117 | 0 | #define BOARD_325 226 /* LANalyzer 325 (Ethernet) */ |
118 | 0 | #define BOARD_325TR 227 /* LANalyzer 325TR (Token-ring) */ |
119 | | |
120 | | |
121 | | /* |
122 | | * typedef struct { |
123 | | * int16_t rid; |
124 | | * int16_t rlen; |
125 | | * int16_t seqno; |
126 | | * int32_t totpktf; |
127 | | * } LA_SubfileSummaryRecord; |
128 | | */ |
129 | | |
130 | 0 | #define LA_SubfileSummaryRecordSize 10 |
131 | | |
132 | | |
133 | 0 | #define LA_IndexSize 500 |
134 | | |
135 | | /* |
136 | | * typedef struct { |
137 | | * int16_t rid; |
138 | | * int16_t rlen; |
139 | | * int16_t idxsp; = LA_IndexSize |
140 | | * int16_t idxct; |
141 | | * int8_t idxgranu; |
142 | | * int8_t idxvd; |
143 | | * int32_t trcidx[LA_IndexSize + 2]; +2 undocumented but used by La 2.2 |
144 | | * } LA_IndexRecord; |
145 | | */ |
146 | | |
147 | 0 | #define LA_IndexRecordSize (10 + 4 * (LA_IndexSize + 2)) |
148 | | |
149 | | |
150 | | /* |
151 | | * typedef struct { |
152 | | * uint16_t rx_channels; |
153 | | * uint16_t rx_errors; |
154 | | * int16_t rx_frm_len; |
155 | | * int16_t rx_frm_sln; |
156 | | * TimeStamp rx_time; |
157 | | * uint32_t pktno; |
158 | | * int16_t prvlen; |
159 | | * int16_t offset; |
160 | | * int16_t tx_errs; |
161 | | * int16_t rx_filters; |
162 | | * int8_t unused[2]; |
163 | | * int16_t hwcolls; |
164 | | * int16_t hwcollschans; |
165 | | * Packetdata ....; |
166 | | * } LA_PacketRecord; |
167 | | */ |
168 | | |
169 | 0 | #define LA_PacketRecordSize 32 |
170 | | |
171 | | typedef struct { |
172 | | bool init; |
173 | | nstime_t start; |
174 | | uint32_t pkts; |
175 | | int encap; |
176 | | int lastlen; |
177 | | } LA_TmpInfo; |
178 | | |
179 | | static const uint8_t LA_HeaderRegularFake[] = { |
180 | | 0x01,0x10,0x4c,0x00,0x01,0x05,0x54,0x72,0x61,0x63,0x65,0x20,0x44,0x69,0x73,0x70, |
181 | | 0x6c,0x61,0x79,0x20,0x54,0x72,0x61,0x63,0x65,0x20,0x46,0x69,0x6c,0x65,0x00,0x00, |
182 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
183 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
184 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 |
185 | | }; |
186 | | |
187 | | static const uint8_t LA_RxChannelNameFake[] = { |
188 | | 0x06,0x10,0x80,0x00,0x43,0x68,0x61,0x6e ,0x6e,0x65,0x6c,0x31,0x00,0x43,0x68,0x61, |
189 | | 0x6e,0x6e,0x65,0x6c,0x32,0x00,0x43,0x68 ,0x61,0x6e,0x6e,0x65,0x6c,0x33,0x00,0x43, |
190 | | 0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x00 ,0x43,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x35, |
191 | | 0x00,0x43,0x68,0x61,0x6e,0x6e,0x65,0x6c ,0x36,0x00,0x43,0x68,0x61,0x6e,0x6e,0x65, |
192 | | 0x6c,0x37,0x00,0x43,0x68,0x61,0x6e,0x6e ,0x65,0x6c,0x38,0x00,0x00,0x00,0x00,0x00, |
193 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
194 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
195 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
196 | | 0x00,0x00,0x00,0x00 |
197 | | }; |
198 | | |
199 | | static const uint8_t LA_TxChannelNameFake[] = { |
200 | | 0x0b,0x10,0x36,0x00 ,0x54,0x72,0x61,0x6e,0x73,0x31,0x00,0x00, |
201 | | 0x00,0x54,0x72,0x61,0x6e,0x73,0x32,0x00 ,0x00,0x00,0x54,0x72,0x61,0x6e,0x73,0x33, |
202 | | 0x00,0x00,0x00,0x54,0x72,0x61,0x6e,0x73 ,0x34,0x00,0x00,0x00,0x54,0x72,0x61,0x6e, |
203 | | 0x73,0x35,0x00,0x00,0x00,0x54,0x72,0x61 ,0x6e,0x73,0x36,0x00,0x00,0x00 |
204 | | }; |
205 | | |
206 | | static const uint8_t LA_RxTemplateNameFake[] = { |
207 | | 0x35,0x10, |
208 | | 0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
209 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
210 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
211 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
212 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
213 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
214 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
215 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
216 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
217 | | 0x00,0x00 |
218 | | }; |
219 | | |
220 | | static const uint8_t LA_TxTemplateNameFake[] = { |
221 | | 0x36,0x10,0x36,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
222 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
223 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
224 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00 |
225 | | }; |
226 | | |
227 | | static const uint8_t LA_DisplayOptionsFake[] = { |
228 | | 0x0a,0x10,0x0a,0x01, |
229 | | 0x00,0x00,0x01,0x00,0x01,0x02,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
230 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
231 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
232 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
233 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
234 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
235 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
236 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
237 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
238 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
239 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
240 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
241 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
242 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
243 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
244 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
245 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00 |
246 | | }; |
247 | | |
248 | | static const uint8_t LA_CyclicInformationFake[] = { |
249 | | 0x09,0x10,0x1a,0x00,0x00,0x00, |
250 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
251 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 |
252 | | }; |
253 | | |
254 | | static const uint8_t z64[64] = { |
255 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
256 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
257 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
258 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 |
259 | | }; |
260 | | |
261 | | typedef struct { |
262 | | time_t start; |
263 | | } lanalyzer_t; |
264 | | |
265 | | static bool lanalyzer_read(wtap *wth, wtap_rec *rec, |
266 | | int *err, char **err_info, int64_t *data_offset); |
267 | | static bool lanalyzer_seek_read(wtap *wth, int64_t seek_off, |
268 | | wtap_rec *rec, int *err, char **err_info); |
269 | | static bool lanalyzer_dump_finish(wtap_dumper *wdh, int *err, |
270 | | char **err_info); |
271 | | |
272 | | static int lanalyzer_file_type_subtype = -1; |
273 | | |
274 | | void register_lanalyzer(void); |
275 | | |
276 | | wtap_open_return_val lanalyzer_open(wtap *wth, int *err, char **err_info) |
277 | 0 | { |
278 | 0 | LA_RecordHeader rec_header; |
279 | 0 | char header_fixed[2]; |
280 | 0 | char *comment; |
281 | 0 | bool found_summary; |
282 | 0 | char summary[210]; |
283 | 0 | uint16_t board_type, mxslc; |
284 | 0 | uint16_t record_type, record_length; |
285 | 0 | uint8_t cr_day, cr_month; |
286 | 0 | uint16_t cr_year; |
287 | 0 | struct tm tm; |
288 | 0 | time_t start; |
289 | 0 | int file_encap; |
290 | 0 | lanalyzer_t *lanalyzer; |
291 | |
|
292 | 0 | if (!wtap_read_bytes(wth->fh, &rec_header, LA_RecordHeaderSize, |
293 | 0 | err, err_info)) { |
294 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
295 | 0 | return WTAP_OPEN_ERROR; |
296 | 0 | return WTAP_OPEN_NOT_MINE; |
297 | 0 | } |
298 | 0 | record_type = pletohu16(rec_header.record_type); |
299 | 0 | record_length = pletohu16(rec_header.record_length); /* make sure to do this for while() loop */ |
300 | |
|
301 | 0 | if (record_type != RT_HeaderRegular && record_type != RT_HeaderCyclic) { |
302 | 0 | return WTAP_OPEN_NOT_MINE; |
303 | 0 | } |
304 | | |
305 | | /* Read the major and minor version numbers */ |
306 | 0 | if (record_length < sizeof header_fixed) { |
307 | | /* |
308 | | * Not enough room for the major and minor version numbers. |
309 | | * Just treat that as a "not a LANalyzer file" indication. |
310 | | */ |
311 | 0 | return WTAP_OPEN_NOT_MINE; |
312 | 0 | } |
313 | 0 | if (!wtap_read_bytes(wth->fh, &header_fixed, sizeof header_fixed, |
314 | 0 | err, err_info)) { |
315 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
316 | 0 | return WTAP_OPEN_ERROR; |
317 | 0 | return WTAP_OPEN_NOT_MINE; |
318 | 0 | } |
319 | 0 | record_length -= sizeof header_fixed; |
320 | |
|
321 | 0 | if (record_length != 0) { |
322 | | /* Read the rest of the record as a comment. */ |
323 | 0 | comment = (char *)g_malloc(record_length + 1); |
324 | 0 | if (!wtap_read_bytes(wth->fh, comment, record_length, |
325 | 0 | err, err_info)) { |
326 | 0 | if (*err != WTAP_ERR_SHORT_READ) { |
327 | 0 | g_free(comment); |
328 | 0 | return WTAP_OPEN_ERROR; |
329 | 0 | } |
330 | 0 | g_free(comment); |
331 | 0 | return WTAP_OPEN_NOT_MINE; |
332 | 0 | } |
333 | 0 | wtap_block_add_string_option(g_array_index(wth->shb_hdrs, wtap_block_t, 0), OPT_COMMENT, comment, record_length); |
334 | 0 | g_free(comment); |
335 | 0 | } |
336 | | |
337 | | /* |
338 | | * Read records until we find the start of packets. |
339 | | * The document cited above claims that the first 11 records are |
340 | | * in a particular sequence of types, but at least one capture |
341 | | * doesn't have all the types listed in the order listed. |
342 | | * |
343 | | * If we don't have a summary record, we don't know the link-layer |
344 | | * header type, so we can't read the file. |
345 | | */ |
346 | 0 | found_summary = false; |
347 | 0 | while (1) { |
348 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &rec_header, |
349 | 0 | LA_RecordHeaderSize, err, err_info)) { |
350 | 0 | if (*err == 0) { |
351 | | /* |
352 | | * End of file and no packets; |
353 | | * accept this file. |
354 | | */ |
355 | 0 | break; |
356 | 0 | } |
357 | 0 | return WTAP_OPEN_ERROR; |
358 | 0 | } |
359 | | |
360 | 0 | record_type = pletohu16(rec_header.record_type); |
361 | 0 | record_length = pletohu16(rec_header.record_length); |
362 | | |
363 | | /*ws_message("Record 0x%04X Length %d", record_type, record_length);*/ |
364 | 0 | switch (record_type) { |
365 | | /* Trace Summary Record */ |
366 | 0 | case RT_Summary: |
367 | 0 | if (record_length < sizeof summary) { |
368 | 0 | *err = WTAP_ERR_BAD_FILE; |
369 | 0 | *err_info = ws_strdup_printf("lanalyzer: summary record length %u is too short", |
370 | 0 | record_length); |
371 | 0 | return WTAP_OPEN_ERROR; |
372 | 0 | } |
373 | 0 | if (!wtap_read_bytes(wth->fh, summary, |
374 | 0 | sizeof summary, err, err_info)) |
375 | 0 | return WTAP_OPEN_ERROR; |
376 | | |
377 | | /* Assume that the date of the creation of the trace file |
378 | | * is the same date of the trace. Lanalyzer doesn't |
379 | | * store the creation date/time of the trace, but only of |
380 | | * the file. Unless you traced at 11:55 PM and saved at 00:05 |
381 | | * AM, the assumption that trace.date == file.date is true. |
382 | | */ |
383 | 0 | cr_day = summary[0]; |
384 | 0 | cr_month = summary[1]; |
385 | 0 | cr_year = pletohu16(&summary[2]); |
386 | | /*ws_message("Day %d Month %d Year %d (%04X)", cr_day, cr_month, |
387 | | cr_year, cr_year);*/ |
388 | | |
389 | | /* Get capture start time. I learned how to do |
390 | | * this from Guy's code in ngsniffer.c |
391 | | */ |
392 | 0 | tm.tm_year = cr_year - 1900; |
393 | 0 | tm.tm_mon = cr_month - 1; |
394 | 0 | tm.tm_mday = cr_day; |
395 | 0 | tm.tm_hour = 0; |
396 | 0 | tm.tm_min = 0; |
397 | 0 | tm.tm_sec = 0; |
398 | 0 | tm.tm_isdst = -1; |
399 | 0 | start = mktime(&tm); |
400 | | /*ws_message("Day %d Month %d Year %d", tm.tm_mday, |
401 | | tm.tm_mon, tm.tm_year);*/ |
402 | 0 | mxslc = pletohu16(&summary[30]); |
403 | |
|
404 | 0 | board_type = pletohu16(&summary[188]); |
405 | 0 | switch (board_type) { |
406 | 0 | case BOARD_325: |
407 | 0 | file_encap = WTAP_ENCAP_ETHERNET; |
408 | 0 | break; |
409 | 0 | case BOARD_325TR: |
410 | 0 | file_encap = WTAP_ENCAP_TOKEN_RING; |
411 | 0 | break; |
412 | 0 | default: |
413 | 0 | *err = WTAP_ERR_UNSUPPORTED; |
414 | 0 | *err_info = ws_strdup_printf("lanalyzer: board type %u unknown", |
415 | 0 | board_type); |
416 | 0 | return WTAP_OPEN_ERROR; |
417 | 0 | } |
418 | | |
419 | 0 | if (found_summary) { |
420 | 0 | *err = WTAP_ERR_BAD_FILE; |
421 | 0 | *err_info = ws_strdup_printf("lanalyzer: file has more than one summary record"); |
422 | 0 | return WTAP_OPEN_ERROR; |
423 | 0 | } |
424 | 0 | found_summary = true; |
425 | | |
426 | | /* Skip the rest of the record */ |
427 | 0 | record_length -= sizeof summary; |
428 | 0 | if (record_length != 0) { |
429 | 0 | if (!wtap_read_bytes(wth->fh, NULL, record_length, err, err_info)) { |
430 | 0 | return WTAP_OPEN_ERROR; |
431 | 0 | } |
432 | 0 | } |
433 | 0 | break; |
434 | | |
435 | | /* Trace Packet Data Record */ |
436 | 0 | case RT_PacketData: |
437 | | /* Go back header number of bytes so that lanalyzer_read |
438 | | * can read this header */ |
439 | 0 | if (file_seek(wth->fh, -LA_RecordHeaderSize, SEEK_CUR, err) == -1) { |
440 | 0 | return WTAP_OPEN_ERROR; |
441 | 0 | } |
442 | 0 | goto done; |
443 | | |
444 | 0 | default: |
445 | | /* Unknown record type - skip it */ |
446 | 0 | if (!wtap_read_bytes(wth->fh, NULL, record_length, err, err_info)) { |
447 | 0 | return WTAP_OPEN_ERROR; |
448 | 0 | } |
449 | 0 | break; |
450 | 0 | } |
451 | 0 | } |
452 | | |
453 | 0 | done: |
454 | 0 | if (!found_summary) { |
455 | 0 | *err = WTAP_ERR_BAD_FILE; |
456 | 0 | *err_info = ws_strdup_printf("lanalyzer: file has no summary record"); |
457 | 0 | return WTAP_OPEN_ERROR; |
458 | 0 | } |
459 | | |
460 | | /* If we made it this far, then the file is a readable LANAlyzer file. |
461 | | * Let's get some info from it. Note that we get wth->snapshot_length |
462 | | * from a record later in the file. */ |
463 | 0 | wth->file_type_subtype = lanalyzer_file_type_subtype; |
464 | 0 | lanalyzer = g_new(lanalyzer_t, 1); |
465 | 0 | lanalyzer->start = start; |
466 | 0 | wth->priv = (void *)lanalyzer; |
467 | 0 | wth->subtype_read = lanalyzer_read; |
468 | 0 | wth->subtype_seek_read = lanalyzer_seek_read; |
469 | 0 | wth->file_encap = file_encap; |
470 | 0 | wth->snapshot_length = mxslc; |
471 | 0 | wth->file_tsprec = WTAP_TSPREC_NSEC; |
472 | | |
473 | | /* |
474 | | * Add an IDB; we don't know how many interfaces were involved, |
475 | | * so we just say one interface, about which we only know |
476 | | * the link-layer type, snapshot length, and time stamp |
477 | | * resolution. |
478 | | */ |
479 | 0 | wtap_add_generated_idb(wth); |
480 | |
|
481 | 0 | return WTAP_OPEN_MINE; |
482 | 0 | } |
483 | | |
484 | 0 | #define DESCRIPTOR_LEN 32 |
485 | | |
486 | | static bool lanalyzer_read_trace_record(wtap *wth, FILE_T fh, |
487 | | wtap_rec *rec, int *err, char **err_info) |
488 | 0 | { |
489 | 0 | char LE_record_type[2]; |
490 | 0 | char LE_record_length[2]; |
491 | 0 | uint16_t record_type, record_length; |
492 | 0 | int record_data_size; |
493 | 0 | int packet_size; |
494 | 0 | char descriptor[DESCRIPTOR_LEN]; |
495 | 0 | lanalyzer_t *lanalyzer; |
496 | 0 | uint16_t time_low, time_med, time_high, true_size; |
497 | 0 | uint64_t t; |
498 | 0 | time_t tsecs; |
499 | | |
500 | | /* read the record type and length. */ |
501 | 0 | if (!wtap_read_bytes_or_eof(fh, LE_record_type, 2, err, err_info)) |
502 | 0 | return false; |
503 | 0 | if (!wtap_read_bytes(fh, LE_record_length, 2, err, err_info)) |
504 | 0 | return false; |
505 | | |
506 | 0 | record_type = pletohu16(LE_record_type); |
507 | 0 | record_length = pletohu16(LE_record_length); |
508 | | |
509 | | /* Only Trace Packet Data Records should occur now that we're in |
510 | | * the middle of reading packets. If any other record type exists |
511 | | * after a Trace Packet Data Record, mark it as an error. */ |
512 | 0 | if (record_type != RT_PacketData) { |
513 | 0 | *err = WTAP_ERR_BAD_FILE; |
514 | 0 | *err_info = ws_strdup_printf("lanalyzer: record type %u seen after trace summary record", |
515 | 0 | record_type); |
516 | 0 | return false; |
517 | 0 | } |
518 | | |
519 | 0 | if (record_length < DESCRIPTOR_LEN) { |
520 | | /* |
521 | | * Uh-oh, the record isn't big enough to even have a |
522 | | * descriptor. |
523 | | */ |
524 | 0 | *err = WTAP_ERR_BAD_FILE; |
525 | 0 | *err_info = ws_strdup_printf("lanalyzer: file has a %u-byte record, too small to have even a packet descriptor", |
526 | 0 | record_length); |
527 | 0 | return false; |
528 | 0 | } |
529 | 0 | record_data_size = record_length - DESCRIPTOR_LEN; |
530 | | |
531 | | /* Read the descriptor data */ |
532 | 0 | if (!wtap_read_bytes(fh, descriptor, DESCRIPTOR_LEN, err, err_info)) |
533 | 0 | return false; |
534 | | |
535 | 0 | true_size = pletohu16(&descriptor[4]); |
536 | 0 | packet_size = pletohu16(&descriptor[6]); |
537 | | /* |
538 | | * The maximum value of packet_size is 65535, which is less than |
539 | | * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check |
540 | | * it. |
541 | | */ |
542 | | |
543 | | /* |
544 | | * OK, is the frame data size greater than what's left of the |
545 | | * record? |
546 | | */ |
547 | 0 | if (packet_size > record_data_size) { |
548 | | /* |
549 | | * Yes - treat this as an error. |
550 | | */ |
551 | 0 | *err = WTAP_ERR_BAD_FILE; |
552 | 0 | *err_info = g_strdup("lanalyzer: Record length is less than packet size"); |
553 | 0 | return false; |
554 | 0 | } |
555 | | |
556 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
557 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
558 | 0 | rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN; |
559 | |
|
560 | 0 | time_low = pletohu16(&descriptor[8]); |
561 | 0 | time_med = pletohu16(&descriptor[10]); |
562 | 0 | time_high = pletohu16(&descriptor[12]); |
563 | 0 | t = (((uint64_t)time_low) << 0) + (((uint64_t)time_med) << 16) + |
564 | 0 | (((uint64_t)time_high) << 32); |
565 | 0 | tsecs = (time_t) (t/2000000); |
566 | 0 | lanalyzer = (lanalyzer_t *)wth->priv; |
567 | 0 | rec->ts.secs = tsecs + lanalyzer->start; |
568 | 0 | rec->ts.nsecs = ((uint32_t) (t - tsecs*2000000)) * 500; |
569 | |
|
570 | 0 | if (true_size - 4 >= packet_size) { |
571 | | /* |
572 | | * It appears that the "true size" includes the FCS; |
573 | | * make it reflect the non-FCS size (the "packet size" |
574 | | * appears never to include the FCS, even if no slicing |
575 | | * is done). |
576 | | */ |
577 | 0 | true_size -= 4; |
578 | 0 | } |
579 | 0 | rec->rec_header.packet_header.len = true_size; |
580 | 0 | rec->rec_header.packet_header.caplen = packet_size; |
581 | |
|
582 | 0 | switch (wth->file_encap) { |
583 | | |
584 | 0 | case WTAP_ENCAP_ETHERNET: |
585 | | /* We assume there's no FCS in this frame. */ |
586 | 0 | rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0; |
587 | 0 | break; |
588 | 0 | } |
589 | | |
590 | | /* Read the packet data */ |
591 | 0 | return wtap_read_bytes_buffer(fh, &rec->data, packet_size, err, err_info); |
592 | 0 | } |
593 | | |
594 | | /* Read the next packet */ |
595 | | static bool lanalyzer_read(wtap *wth, wtap_rec *rec, |
596 | | int *err, char **err_info, int64_t *data_offset) |
597 | 0 | { |
598 | 0 | *data_offset = file_tell(wth->fh); |
599 | | |
600 | | /* Read the record */ |
601 | 0 | return lanalyzer_read_trace_record(wth, wth->fh, rec, err, err_info); |
602 | 0 | } |
603 | | |
604 | | static bool lanalyzer_seek_read(wtap *wth, int64_t seek_off, |
605 | | wtap_rec *rec, int *err, char **err_info) |
606 | 0 | { |
607 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
608 | 0 | return false; |
609 | | |
610 | | /* Read the record */ |
611 | 0 | if (!lanalyzer_read_trace_record(wth, wth->random_fh, rec, err, err_info)) { |
612 | 0 | if (*err == 0) |
613 | 0 | *err = WTAP_ERR_SHORT_READ; |
614 | 0 | return false; |
615 | 0 | } |
616 | 0 | return true; |
617 | 0 | } |
618 | | |
619 | | /*--------------------------------------------------- |
620 | | * Returns true on success, false on error |
621 | | * Write "cnt" bytes of zero with error control |
622 | | *---------------------------------------------------*/ |
623 | | static bool s0write(wtap_dumper *wdh, size_t cnt, int *err) |
624 | 0 | { |
625 | 0 | size_t snack; |
626 | |
|
627 | 0 | while (cnt) { |
628 | 0 | snack = cnt > 64 ? 64 : cnt; |
629 | |
|
630 | 0 | if (!wtap_dump_file_write(wdh, z64, snack, err)) |
631 | 0 | return false; |
632 | 0 | cnt -= snack; |
633 | 0 | } |
634 | 0 | return true; /* ok */ |
635 | 0 | } |
636 | | |
637 | | /*--------------------------------------------------- |
638 | | * Returns true on success, false on error |
639 | | * Write an 8-bit value |
640 | | *---------------------------------------------------*/ |
641 | | static bool s8write(wtap_dumper *wdh, const uint8_t s8, int *err) |
642 | 0 | { |
643 | 0 | return wtap_dump_file_write(wdh, &s8, 1, err); |
644 | 0 | } |
645 | | /*--------------------------------------------------- |
646 | | * Returns true on success, false on error |
647 | | * Write a 16-bit value as little-endian |
648 | | *---------------------------------------------------*/ |
649 | | static bool s16write(wtap_dumper *wdh, const uint16_t s16, int *err) |
650 | 0 | { |
651 | 0 | uint16_t s16_le = GUINT16_TO_LE(s16); |
652 | 0 | return wtap_dump_file_write(wdh, &s16_le, 2, err); |
653 | 0 | } |
654 | | /*--------------------------------------------------- |
655 | | * Returns true on success, false on error |
656 | | * Write a 32-bit value as little-endian |
657 | | *---------------------------------------------------*/ |
658 | | static bool s32write(wtap_dumper *wdh, const uint32_t s32, int *err) |
659 | 0 | { |
660 | 0 | uint32_t s32_le = GUINT32_TO_LE(s32); |
661 | 0 | return wtap_dump_file_write(wdh, &s32_le, 4, err); |
662 | 0 | } |
663 | | /*--------------------------------------------------- |
664 | | * Returns true on success, false on error |
665 | | * Write a 48-bit value as little-endian |
666 | | *---------------------------------------------------*/ |
667 | | static bool s48write(wtap_dumper *wdh, const uint64_t s48, int *err) |
668 | 0 | { |
669 | | #if G_BYTE_ORDER == G_BIG_ENDIAN |
670 | | uint16_t s48_upper_le = GUINT16_SWAP_LE_BE((uint16_t) (s48 >> 32)); |
671 | | uint32_t s48_lower_le = GUINT32_SWAP_LE_BE((uint32_t) (s48 & 0xFFFFFFFF)); |
672 | | #else |
673 | 0 | uint16_t s48_upper_le = (uint16_t) (s48 >> 32); |
674 | 0 | uint32_t s48_lower_le = (uint32_t) (s48 & 0xFFFFFFFF); |
675 | 0 | #endif |
676 | 0 | return wtap_dump_file_write(wdh, &s48_lower_le, 4, err) && |
677 | 0 | wtap_dump_file_write(wdh, &s48_upper_le, 2, err); |
678 | 0 | } |
679 | | /*--------------------------------------------------- |
680 | | * Write a record for a packet to a dump file. |
681 | | * Returns true on success, false on failure. |
682 | | *---------------------------------------------------*/ |
683 | | static bool lanalyzer_dump(wtap_dumper *wdh, const wtap_rec *rec, |
684 | | int *err, char **err_info _U_) |
685 | 0 | { |
686 | 0 | uint64_t x; |
687 | 0 | int len; |
688 | |
|
689 | 0 | LA_TmpInfo *itmp = (LA_TmpInfo*)(wdh->priv); |
690 | 0 | nstime_t td; |
691 | 0 | int thisSize = rec->rec_header.packet_header.caplen + LA_PacketRecordSize + LA_RecordHeaderSize; |
692 | | |
693 | | /* We can only write packet records. */ |
694 | 0 | if (rec->rec_type != REC_TYPE_PACKET) { |
695 | 0 | *err = WTAP_ERR_UNWRITABLE_REC_TYPE; |
696 | 0 | *err_info = wtap_unwritable_rec_type_err_string(rec); |
697 | 0 | return false; |
698 | 0 | } |
699 | | |
700 | | /* |
701 | | * Make sure this packet doesn't have a link-layer type that |
702 | | * differs from the one for the file. |
703 | | */ |
704 | 0 | if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) { |
705 | 0 | *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
706 | 0 | return false; |
707 | 0 | } |
708 | | |
709 | 0 | if (wdh->bytes_dumped + thisSize > LA_ProFileLimit) { |
710 | | /* printf(" LA_ProFileLimit reached\n"); */ |
711 | 0 | *err = EFBIG; |
712 | 0 | return false; /* and don't forget the header */ |
713 | 0 | } |
714 | | |
715 | 0 | len = rec->rec_header.packet_header.caplen + (rec->rec_header.packet_header.caplen ? LA_PacketRecordSize : 0); |
716 | | |
717 | | /* len goes into a 16-bit field, so there's a hard limit of 65535. */ |
718 | 0 | if (len > 65535) { |
719 | 0 | *err = WTAP_ERR_PACKET_TOO_LARGE; |
720 | 0 | return false; |
721 | 0 | } |
722 | | |
723 | 0 | if (!s16write(wdh, 0x1005, err)) |
724 | 0 | return false; |
725 | 0 | if (!s16write(wdh, (uint16_t)len, err)) |
726 | 0 | return false; |
727 | | |
728 | 0 | if (!itmp->init) { |
729 | | /* collect some information for the |
730 | | * finally written header |
731 | | */ |
732 | 0 | itmp->start = rec->ts; |
733 | 0 | itmp->pkts = 0; |
734 | 0 | itmp->init = true; |
735 | 0 | itmp->encap = wdh->file_encap; |
736 | 0 | itmp->lastlen = 0; |
737 | 0 | } |
738 | |
|
739 | 0 | if (!s16write(wdh, 0x0001, err)) /* pr.rx_channels */ |
740 | 0 | return false; |
741 | 0 | if (!s16write(wdh, 0x0008, err)) /* pr.rx_errors */ |
742 | 0 | return false; |
743 | 0 | if (!s16write(wdh, (uint16_t) (rec->rec_header.packet_header.len + 4), err)) /* pr.rx_frm_len */ |
744 | 0 | return false; |
745 | 0 | if (!s16write(wdh, (uint16_t) rec->rec_header.packet_header.caplen, err)) /* pr.rx_frm_sln */ |
746 | 0 | return false; |
747 | | |
748 | 0 | nstime_delta(&td, &rec->ts, &itmp->start); |
749 | | |
750 | | /* Convert to half-microseconds, rounded up. */ |
751 | 0 | x = (td.nsecs + 250) / 500; /* nanoseconds -> half-microseconds, rounded */ |
752 | 0 | x += td.secs * 2000000; /* seconds -> half-microseconds */ |
753 | |
|
754 | 0 | if (!s48write(wdh, x, err)) /* pr.rx_time[i] */ |
755 | 0 | return false; |
756 | | |
757 | 0 | if (!s32write(wdh, ++itmp->pkts, err)) /* pr.pktno */ |
758 | 0 | return false; |
759 | 0 | if (!s16write(wdh, (uint16_t)itmp->lastlen, err)) /* pr.prlen */ |
760 | 0 | return false; |
761 | 0 | itmp->lastlen = len; |
762 | |
|
763 | 0 | if (!s0write(wdh, 12, err)) |
764 | 0 | return false; |
765 | | |
766 | 0 | if (!wtap_dump_file_write(wdh, ws_buffer_start_ptr(&rec->data), rec->rec_header.packet_header.caplen, err)) |
767 | 0 | return false; |
768 | | |
769 | 0 | return true; |
770 | 0 | } |
771 | | |
772 | | /*--------------------------------------------------- |
773 | | * Returns 0 if we could write the specified encapsulation type, |
774 | | * an error indication otherwise. |
775 | | *---------------------------------------------------*/ |
776 | | static int lanalyzer_dump_can_write_encap(int encap) |
777 | 0 | { |
778 | | /* Per-packet encapsulations aren't supported. */ |
779 | 0 | if (encap == WTAP_ENCAP_PER_PACKET) |
780 | 0 | return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
781 | | |
782 | 0 | if ( encap != WTAP_ENCAP_ETHERNET |
783 | 0 | && encap != WTAP_ENCAP_TOKEN_RING ) |
784 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
785 | | /* |
786 | | * printf("lanalyzer_dump_can_write_encap(%d)\n",encap); |
787 | | */ |
788 | 0 | return 0; |
789 | 0 | } |
790 | | |
791 | | /*--------------------------------------------------- |
792 | | * Returns true on success, false on failure; sets "*err" to an |
793 | | * error code on failure |
794 | | *---------------------------------------------------*/ |
795 | | static bool lanalyzer_dump_open(wtap_dumper *wdh, int *err, char **err_info _U_) |
796 | 0 | { |
797 | 0 | int jump; |
798 | 0 | void *tmp; |
799 | |
|
800 | 0 | tmp = g_malloc(sizeof(LA_TmpInfo)); |
801 | 0 | if (!tmp) { |
802 | 0 | *err = errno; |
803 | 0 | return false; |
804 | 0 | } |
805 | | |
806 | 0 | ((LA_TmpInfo*)tmp)->init = false; |
807 | 0 | wdh->priv = tmp; |
808 | 0 | wdh->subtype_write = lanalyzer_dump; |
809 | 0 | wdh->subtype_finish = lanalyzer_dump_finish; |
810 | | |
811 | | /* Some of the fields in the file header aren't known yet so |
812 | | just skip over it for now. It will be created after all |
813 | | of the packets have been written. */ |
814 | |
|
815 | 0 | jump = sizeof (LA_HeaderRegularFake) |
816 | 0 | + sizeof (LA_RxChannelNameFake) |
817 | 0 | + sizeof (LA_TxChannelNameFake) |
818 | 0 | + sizeof (LA_RxTemplateNameFake) |
819 | 0 | + sizeof (LA_TxTemplateNameFake) |
820 | 0 | + sizeof (LA_DisplayOptionsFake) |
821 | 0 | + LA_SummaryRecordSize |
822 | 0 | + LA_SubfileSummaryRecordSize |
823 | 0 | + sizeof (LA_CyclicInformationFake) |
824 | 0 | + LA_IndexRecordSize; |
825 | |
|
826 | 0 | if (wtap_dump_file_seek(wdh, jump, SEEK_SET, err) == -1) |
827 | 0 | return false; |
828 | | |
829 | 0 | wdh->bytes_dumped = jump; |
830 | 0 | return true; |
831 | 0 | } |
832 | | |
833 | | /*--------------------------------------------------- |
834 | | * |
835 | | *---------------------------------------------------*/ |
836 | | static bool lanalyzer_dump_header(wtap_dumper *wdh, int *err) |
837 | 0 | { |
838 | 0 | LA_TmpInfo *itmp = (LA_TmpInfo*)(wdh->priv); |
839 | 0 | uint16_t board_type = itmp->encap == WTAP_ENCAP_TOKEN_RING |
840 | 0 | ? BOARD_325TR /* LANalyzer Board Type */ |
841 | 0 | : BOARD_325; /* LANalyzer Board Type */ |
842 | 0 | struct tm *fT; |
843 | |
|
844 | 0 | fT = localtime(&itmp->start.secs); |
845 | 0 | if (fT == NULL) |
846 | 0 | return false; |
847 | | |
848 | 0 | if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1) |
849 | 0 | return false; |
850 | | |
851 | 0 | if (!wtap_dump_file_write(wdh, &LA_HeaderRegularFake, |
852 | 0 | sizeof LA_HeaderRegularFake, err)) |
853 | 0 | return false; |
854 | 0 | if (!wtap_dump_file_write(wdh, &LA_RxChannelNameFake, |
855 | 0 | sizeof LA_RxChannelNameFake, err)) |
856 | 0 | return false; |
857 | 0 | if (!wtap_dump_file_write(wdh, &LA_TxChannelNameFake, |
858 | 0 | sizeof LA_TxChannelNameFake, err)) |
859 | 0 | return false; |
860 | 0 | if (!wtap_dump_file_write(wdh, &LA_RxTemplateNameFake, |
861 | 0 | sizeof LA_RxTemplateNameFake, err)) |
862 | 0 | return false; |
863 | 0 | if (!wtap_dump_file_write(wdh, &LA_TxTemplateNameFake, |
864 | 0 | sizeof LA_TxTemplateNameFake, err)) |
865 | 0 | return false; |
866 | 0 | if (!wtap_dump_file_write(wdh, &LA_DisplayOptionsFake, |
867 | 0 | sizeof LA_DisplayOptionsFake, err)) |
868 | 0 | return false; |
869 | | /*-----------------------------------------------------------------*/ |
870 | 0 | if (!s16write(wdh, RT_Summary, err)) /* rid */ |
871 | 0 | return false; |
872 | 0 | if (!s16write(wdh, SummarySize, err)) /* rlen */ |
873 | 0 | return false; |
874 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_mday, err)) /* s.datcre.day */ |
875 | 0 | return false; |
876 | 0 | if (!s8write(wdh, (uint8_t) (fT->tm_mon+1), err)) /* s.datcre.mon */ |
877 | 0 | return false; |
878 | 0 | if (!s16write(wdh, (uint16_t) (fT->tm_year + 1900), err)) /* s.datcre.year */ |
879 | 0 | return false; |
880 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_mday, err)) /* s.datclo.day */ |
881 | 0 | return false; |
882 | 0 | if (!s8write(wdh, (uint8_t) (fT->tm_mon+1), err)) /* s.datclo.mon */ |
883 | 0 | return false; |
884 | 0 | if (!s16write(wdh, (uint16_t) (fT->tm_year + 1900), err)) /* s.datclo.year */ |
885 | 0 | return false; |
886 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_sec, err)) /* s.timeopn.second */ |
887 | 0 | return false; |
888 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_min, err)) /* s.timeopn.minute */ |
889 | 0 | return false; |
890 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_hour, err)) /* s.timeopn.hour */ |
891 | 0 | return false; |
892 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_mday, err)) /* s.timeopn.mday */ |
893 | 0 | return false; |
894 | 0 | if (!s0write(wdh, 2, err)) |
895 | 0 | return false; |
896 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_sec, err)) /* s.timeclo.second */ |
897 | 0 | return false; |
898 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_min, err)) /* s.timeclo.minute */ |
899 | 0 | return false; |
900 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_hour, err)) /* s.timeclo.hour */ |
901 | 0 | return false; |
902 | 0 | if (!s8write(wdh, (uint8_t) fT->tm_mday, err)) /* s.timeclo.mday */ |
903 | 0 | return false; |
904 | 0 | if (!s0write(wdh, 2, err)) |
905 | 0 | return false; |
906 | 0 | if (!s0write(wdh, 6, err)) /* EAddr == 0 */ |
907 | 0 | return false; |
908 | 0 | if (!s16write(wdh, 1, err)) /* s.mxseqno */ |
909 | 0 | return false; |
910 | 0 | if (!s16write(wdh, 0, err)) /* s.slcoffo */ |
911 | 0 | return false; |
912 | 0 | if (!s16write(wdh, 1514, err)) /* s.mxslc */ |
913 | 0 | return false; |
914 | 0 | if (!s32write(wdh, itmp->pkts, err)) /* s.totpktt */ |
915 | 0 | return false; |
916 | | /* |
917 | | * statrg == 0; ? -1 |
918 | | * stptrg == 0; ? -1 |
919 | | * s.mxpkta[0]=0 |
920 | | */ |
921 | 0 | if (!s0write(wdh, 12, err)) |
922 | 0 | return false; |
923 | 0 | if (!s32write(wdh, itmp->pkts, err)) /* sr.s.mxpkta[1] */ |
924 | 0 | return false; |
925 | 0 | if (!s0write(wdh, 34*4, err)) /* s.mxpkta[2-33]=0 */ |
926 | 0 | return false; |
927 | 0 | if (!s16write(wdh, board_type, err)) |
928 | 0 | return false; |
929 | 0 | if (!s0write(wdh, 20, err)) /* board_version == 0 */ |
930 | 0 | return false; |
931 | | /*-----------------------------------------------------------------*/ |
932 | 0 | if (!s16write(wdh, RT_SubfileSummary, err)) /* ssr.rid */ |
933 | 0 | return false; |
934 | 0 | if (!s16write(wdh, LA_SubfileSummaryRecordSize-4, err)) /* ssr.rlen */ |
935 | 0 | return false; |
936 | 0 | if (!s16write(wdh, 1, err)) /* ssr.seqno */ |
937 | 0 | return false; |
938 | 0 | if (!s32write(wdh, itmp->pkts, err)) /* ssr.totpkts */ |
939 | 0 | return false; |
940 | | /*-----------------------------------------------------------------*/ |
941 | 0 | if (!wtap_dump_file_write(wdh, &LA_CyclicInformationFake, |
942 | 0 | sizeof LA_CyclicInformationFake, err)) |
943 | 0 | return false; |
944 | | /*-----------------------------------------------------------------*/ |
945 | 0 | if (!s16write(wdh, RT_Index, err)) /* rid */ |
946 | 0 | return false; |
947 | 0 | if (!s16write(wdh, LA_IndexRecordSize -4, err)) /* rlen */ |
948 | 0 | return false; |
949 | 0 | if (!s16write(wdh, LA_IndexSize, err)) /* idxsp */ |
950 | 0 | return false; |
951 | 0 | if (!s0write(wdh, LA_IndexRecordSize - 6, err)) |
952 | 0 | return false; |
953 | | |
954 | 0 | return true; |
955 | 0 | } |
956 | | |
957 | | /*--------------------------------------------------- |
958 | | * Finish writing to a dump file. |
959 | | * Returns true on success, false on failure. |
960 | | *---------------------------------------------------*/ |
961 | | static bool lanalyzer_dump_finish(wtap_dumper *wdh, int *err, |
962 | | char **err_info _U_) |
963 | 0 | { |
964 | | /* bytes_dumped already accounts for the size of the header, |
965 | | * but lanalyzer_dump_header() (via wtap_dump_file_write()) |
966 | | * will keep incrementing it. |
967 | | */ |
968 | 0 | int64_t saved_bytes_dumped = wdh->bytes_dumped; |
969 | 0 | lanalyzer_dump_header(wdh,err); |
970 | 0 | wdh->bytes_dumped = saved_bytes_dumped; |
971 | 0 | return *err ? false : true; |
972 | 0 | } |
973 | | |
974 | | static const struct supported_block_type lanalyzer_blocks_supported[] = { |
975 | | /* |
976 | | * We support packet blocks, with no comments or other options. |
977 | | */ |
978 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
979 | | }; |
980 | | |
981 | | static const struct file_type_subtype_info lanalyzer_info = { |
982 | | "Novell LANalyzer","lanalyzer", "tr1", NULL, |
983 | | true, BLOCKS_SUPPORTED(lanalyzer_blocks_supported), |
984 | | lanalyzer_dump_can_write_encap, lanalyzer_dump_open, NULL |
985 | | }; |
986 | | |
987 | | void register_lanalyzer(void) |
988 | 15 | { |
989 | 15 | lanalyzer_file_type_subtype = wtap_register_file_type_subtype(&lanalyzer_info); |
990 | | |
991 | | /* |
992 | | * Register name for backwards compatibility with the |
993 | | * wtap_filetypes table in Lua. |
994 | | */ |
995 | 15 | wtap_register_backwards_compatibility_lua_name("LANALYZER", |
996 | 15 | lanalyzer_file_type_subtype); |
997 | 15 | } |
998 | | |
999 | | /* |
1000 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1001 | | * |
1002 | | * Local variables: |
1003 | | * c-basic-offset: 6 |
1004 | | * tab-width: 8 |
1005 | | * indent-tabs-mode: nil |
1006 | | * End: |
1007 | | * |
1008 | | * vi: set shiftwidth=6 tabstop=8 expandtab: |
1009 | | * :indentSize=6:tabSize=8:noTabs=true: |
1010 | | */ |