Attachment 'ntartest.c'
Download 1 /*
2 * ntartest.c
3 *
4 * $Id: ntartest.c 13 2008-01-10 17:35:21Z jyoung $
5 *
6 * Copyright 2008 Jim Young
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 * ====
23 *
24 * This is an extemely simple minded utility to test certain
25 * charteristics regarding the block headers of the ntar (aka
26 * PcapNG) file format.
27 *
28 * It really only pays attention to the block headers. It does
29 * not try to parse or validate that the payload types are well
30 * formed nor if the block trailers are consistent with their
31 * respective block headers.
32 *
33 * An ntar file consists of a series of variable length blocks.
34 * Each block consists of two mandatory components a block header
35 * (8 octets) and a block trailer (4 octets), and may optionally
36 * contain a payload.
37 *
38 * The block header consists of two 32 bit components:
39 * A block type (32 bits (4 octets))
40 * A block length (32 bits (4 octets))
41 *
42 * The block trailer conists of one 32 bit component:
43 * The block length (32 bits (4 octets))
44 *
45 * Therefore a minimum length block (i.e. a block with no
46 * payload) is 12 octets in length.
47 *
48 * If the optional payload is prosent it must contain one or
49 * more octets of data.
50 *
51 * The actual length of a block must be evenly divisable by four.
52 *
53 * If the number of data octets is not a multiple of four, then
54 * 1 to 3 padding bytes (NUL characters) must be inserted after
55 * the last payload byte and before the block trailer.
56 *
57 * Please note that the reported block length (the block length
58 * written into the block header and block trailer) MAY be 1 to
59 * 3 octets less that the actual blocklength due to the padding.
60 *
61 * NOTE: The discepency of the reported block length versus the
62 * actual block length could arguably be be considered a defect
63 * in the current ntar spec and could possible be corrected.
64 * Each of currently defined payloads have their own header
65 * structure and in theory would indicate payloads whose
66 * length is not evenly divisable by 4. Would could argue that
67 * the blockLength filed should always directly indicate the
68 * actual length of the block.
69 *
70 * For the offical ntar/pcapng format see:
71 *
72 * http://www.winpcap.org/ntar/draft/PCAP-DumpFileFormat.html
73 */
74
75 #include <stdio.h>
76
77 /*
78 * The following typedef name (gunit32) was "borrowed" from glib
79 * which is defines the guint32 as follows:
80 *
81 * "An unsigned integer guaranteed to be 32 bits on all platforms.
82 * Values of this type can range from 0 to 4,294,967,295."
83 *
84 * WARNING: Within this context of this little source module the
85 * above compile time claim of the byte width is NOT assured!!!
86 * A startup runtime test within this source attempts to verify
87 * that this gunit32 is really 32 bits wide.
88 */
89
90 typedef unsigned int guint32;
91
92 /*
93 * defines
94 */
95
96 #define min(a, b) ((a) > (b) ? (b) : (a))
97
98 #define ENDIAN_MAGIC 0x1A2B3C4D
99
100 #define READ_START_OF_FILE 1
101 #define READ_BLOCK_HEADER 2
102 #define READ_MAGIC 3
103 #define READ_DATA 4
104
105
106 #define ENDIAN_UNKNOWN 0
107 #define ENDIAN_BIG 1
108 #define ENDIAN_LITTLE 2
109
110 struct ntarHeader {
111 guint32 blockType;
112 guint32 blockLength;
113 };
114
115 /*
116 * prototypes
117 */
118
119 static guint32 GetDataWord(int /* mySystemEndianness */ , int /* endiantype */ , unsigned char * /* data */ );
120 static int ReadBlockHeader(FILE * /* myFile */ , struct ntarHeader * /* myHeader */ );
121 static int GetSystemEndianness(void);
122 static int GetSectionEndianness(unsigned char * /* inData */ );
123
124 /*
125 * globals
126 */
127
128 unsigned char myBuffer[4096];
129
130 /*
131 * main():
132 */
133
134 int main(int argc, char *argv[])
135 {
136 guint32 blockType = 0;
137 guint32 blockTotalStart = 0;
138 guint32 blockTotalEnd = 0;
139 FILE *myFile;
140 char *myFileName;
141 int readLength;
142 guint32 totalRead = 0;
143 int nextRead = READ_START_OF_FILE;
144 int myBlockType = 0;
145 guint32 myDataWord;
146 struct ntarHeader myHeader;
147 int mySystemEndianness;
148 int mySectionEndianness;
149 int blockEndianType = 0;
150 unsigned adjustedBlockLength;
151 guint32 blockNumber = 0;
152
153 if (argc != 2) {
154 printf("Usage: ntartest FILENAME\n");
155 exit(1);
156 }
157
158 myFileName = argv[1];
159
160 mySystemEndianness = GetSystemEndianness();
161
162 if ( sizeof(guint32) != 4 ) {
163 printf ("oops: sizeof guint32 (in octets) = %d, which is not 4 (32 bits)!\n", sizeof(guint32));
164 exit(99);
165 }
166
167 myFile = fopen(myFileName, "rb");
168
169 if(myFile == NULL) {
170 printf("Could not open file \"%s\"\n", myFileName);
171 exit(99);
172 }
173
174 printf("+++Working on file: %s\n", myFileName);
175
176 switch(mySystemEndianness)
177 {
178 case ENDIAN_BIG: {
179 printf("This machine is big-endian.\n");
180 break;
181 }
182 case ENDIAN_LITTLE: {
183 printf("This machine is little-endian.\n");
184 break;
185 }
186 case ENDIAN_UNKNOWN: {
187 printf("Endianness for this machine could not be determined.\n");
188 exit(99);
189 break;
190 }
191 default: {
192 printf("Unexpected endianness value %d (%0x).\n", mySystemEndianness, mySystemEndianness);
193 exit(99);
194 break;
195 }
196 }
197
198 while(!feof(myFile)) {
199
200 readLength = ReadBlockHeader(myFile, &myHeader);
201
202 if(readLength < 8) {
203 if(readLength == 0 && feof(myFile))
204 break;
205
206 printf ("Oops: short read, should have read 8 bytes, only read %d, are we reading from a pipe or an open file?.\n", \
207 readLength);
208 exit(99);
209 }
210
211 blockNumber++;
212
213 if (myHeader.blockType == 0x0a0d0d0a) {
214 readLength = fread(myBuffer, 1, 4, myFile);
215
216 if (readLength != 4) {
217 printf ("Oops: short read, should have read %d bytes, only read %d.\n", 4, readLength);
218 exit(99);
219 }
220
221 mySectionEndianness = GetSectionEndianness(myBuffer);
222
223 switch(mySectionEndianness)
224 {
225 case ENDIAN_BIG: {
226 printf("This section is big-endian.\n");
227 break;
228 }
229 case ENDIAN_LITTLE: {
230 printf("This section is little-endian.\n");
231 break;
232 }
233 case ENDIAN_UNKNOWN: {
234 printf("Endianness for this section could not be determined.\n");
235 break;
236 }
237 default: {
238 printf("Unexpected endianness value %d (%0x).\n", mySectionEndianness, mySectionEndianness);
239 break;
240 }
241 }
242 }
243
244 myHeader.blockType = GetDataWord(mySystemEndianness, mySectionEndianness, (unsigned char *)&myHeader.blockType);
245 myHeader.blockLength = GetDataWord(mySystemEndianness, mySectionEndianness, (unsigned char *)&myHeader.blockLength);
246
247 printf("\n");
248
249 switch(myHeader.blockType)
250 {
251 case 0x00000001: /* Interface Description Block */
252 printf("%08x: Block #%u, Type = Interface Description Block (%08x)\n", totalRead, blockNumber, myHeader.blockType);
253 break;
254 case 0x00000002: /* Packet Block */
255 printf("%08x: Block #%u, Type = Packet Block (%08x)\n", totalRead, blockNumber, myHeader.blockType);
256 break;
257 case 0x00000003: /* Simple Packet Block */
258 printf("%08x: Block #%u, Type = Simple Packet Block (%08x)\n", totalRead, blockNumber, myHeader.blockType);
259 break;
260 case 0x00000004: /* Name Resolution Block */
261 printf("%08x: Block #%u, Type = Name Resolution Block (%08x)\n", totalRead, blockNumber, myHeader.blockType);
262 break;
263 case 0x00000005: /* Interface Statistics Block */
264 printf("%08x: Block #%u, Type = Interface Statistics Block (%08x)\n", totalRead, blockNumber, myHeader.blockType);
265 break;
266 case 0x00000006: /* Enhanced Packet Block */
267 printf("%08x: Block #%u, Type = Enhanced Packet Block (%08x)\n", totalRead, blockNumber, myHeader.blockType);
268 break;
269 case 0x0a0d0d0a: /* Section Header Block */
270 printf("%08x: Block #%u, Type = Section Header Block (%08x)\n", totalRead, blockNumber, myHeader.blockType);
271 break;
272 default:
273 if (myHeader.blockType == 0x0a0a0d0a
274 || (myHeader.blockType >= 0x0a0d0a00 && myHeader.blockType <= 0x0a0d0aff)
275 || (myHeader.blockType >= 0x0d0d0a00 && myHeader.blockType <= 0x0d0d0aff)) {
276 printf("%08x: Corrupted Section header. (%08x)\n", totalRead, myHeader.blockType);
277 exit(99);
278 } else {
279 printf("%08x: Unknown Block Type (%08x)\n", totalRead, myHeader.blockType);
280 }
281 }
282
283 totalRead += 4;
284
285 /*
286 * adjusted block length
287 *
288 * A non-obvious characteristic of the current ntar blocklength is
289 * that blocklength MAY not actually indicate the length of the
290 * actual data block (the payload). If the number of payload
291 * octets is NOT evenly divisable by four (4) then the one or
292 * more padding bytes will have been added after the payload and
293 * before the blocktrailer to pad the length of the payload to a
294 * 32 bit boundry. To seek forwards or backwards to the next or
295 * previous block one will need to add from 0 to 3 to the reported
296 * blockLength to detremine where the next block will start.
297 */
298
299 adjustedBlockLength = myHeader.blockLength;
300
301 switch(adjustedBlockLength % 4)
302 {
303 case 0:
304 printf("+++ blockLength %% 4 = 0, no pad bytes\n");
305 break;
306 case 1:
307 printf("+++ blockLength %% 4 = 1, add 3 pad bytes\n");
308 adjustedBlockLength +=3;
309 break;
310 case 2:
311 printf("+++ blockLength %% 4 = 2, add 2 pad bytes\n");
312 adjustedBlockLength +=2;
313 break;
314 case 3:
315 printf("+++ blockLength %% 4 = 3, add 1 pad bytes\n");
316 adjustedBlockLength +=1;
317 break;
318 default:
319 printf("+++ Unexpected remainder: %d\n", adjustedBlockLength % 4);
320 break;
321 }
322
323 printf("%08x: Reported Block Length %u (%08x), Adjusted Block Length %u (%08x), next block at offset %08x\n", \
324 totalRead, \
325 myHeader.blockLength, \
326 myHeader.blockLength, \
327 adjustedBlockLength, \
328 adjustedBlockLength, \
329 totalRead + ( adjustedBlockLength - 4));
330
331 totalRead += 4;
332
333 printf("%08x: Remainder of Block Data (%08x) bytes\n", totalRead, (adjustedBlockLength - 8), (adjustedBlockLength - 8));
334
335 /*
336 * Read in the rest of the block of data.
337 *
338 * Note: The following assumes that the payload will NOT exceed
339 * 4096 bytes. A real program would have read loop that wouldn't
340 * break until the entire block in consumed.
341 */
342
343 if(myHeader.blockType == 0x0a0d0d0a) {
344 readLength = fread(&myBuffer[4], 1, min((adjustedBlockLength - 12), 4096), myFile);
345
346 if (readLength != (adjustedBlockLength - 12)) {
347 printf ("Oops: short read, should have read %d bytes, only read %d.\n",
348 (adjustedBlockLength - 12), readLength);
349 exit(99);
350 }
351
352 } else {
353 readLength = fread(myBuffer, 1, min((adjustedBlockLength - 8), 4096), myFile);
354
355 if (readLength != (adjustedBlockLength - 8)) {
356 printf ("Oops: short read, should have read %d bytes, only read %d.\n",
357 (adjustedBlockLength - 8), readLength);
358 exit(99);
359 }
360 }
361
362 totalRead += readLength;
363 }
364
365 fclose(myFile);
366
367 myFile = NULL;
368
369 return 0;
370 }
371
372 /*
373 * GetDataWord():
374 */
375
376 static guint32 GetDataWord(int mySystemEndianness, int endianType, unsigned char *inData)
377 {
378 union {
379 guint32 l;
380 char c[sizeof(guint32)];
381 } ul;
382
383 if(mySystemEndianness == endianType) {
384 ul.c[0] = inData[0];
385 ul.c[1] = inData[1];
386 ul.c[2] = inData[2];
387 ul.c[3] = inData[3];
388 } else {
389 ul.c[0] = inData[3];
390 ul.c[1] = inData[2];
391 ul.c[2] = inData[1];
392 ul.c[3] = inData[0];
393 }
394
395 return ul.l;
396 }
397
398
399 /*
400 * ReadBlockHeader():
401 */
402
403 static int ReadBlockHeader(FILE *myFile, struct ntarHeader *myHeader)
404 {
405 return fread(myHeader, 1, sizeof(struct ntarHeader), myFile);
406 }
407
408 /*
409 * GetSystemEndianness():
410 *
411 * Adapted from wikipedia:
412 * http://en.wikipedia.org/wiki/Endianness
413 */
414
415 static int GetSystemEndianness(void)
416 {
417 union {
418 short s;
419 char c[sizeof(short)];
420 } un;
421
422 un.s = 0x0102;
423
424 if(sizeof(short) == 2)
425 {
426 if(un.c[0] == 1 && un.c[1] == 2)
427 return ENDIAN_BIG;
428 else if(un.c[0] == 2 && un.c[1] == 1)
429 return ENDIAN_LITTLE;
430 else
431 return ENDIAN_UNKNOWN;
432 }
433 else
434 {
435 return ENDIAN_UNKNOWN;
436 }
437 }
438
439
440 /*
441 * GetSectionEndianness():
442 */
443
444 static int GetSectionEndianness(unsigned char *inData)
445 {
446 if (inData[0] == 0x1a && inData[1] == 0x2b && inData[2] == 0x3c && inData[3] == 0x4d) {
447 return ENDIAN_BIG;
448 } else if (inData[0] == 0x4d && inData[1] == 0x3c && inData[2] == 0x2b && inData[3] == 0x1a) {
449 return ENDIAN_LITTLE;
450 } else {
451 return ENDIAN_UNKNOWN;
452 }
453 }
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.