Refactor Wiretap
[metze/wireshark/wip.git] / wiretap / dbs-etherwatch.c
1 /* dbs-etherwatch.c
2  *
3  * Wiretap Library
4  * Copyright (c) 2001 by Marc Milgram <ethereal@mmilgram.NOSPAMmail.net>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "config.h"
22 #include "wftap-int.h"
23 #include "wtap-int.h"
24 #include "buffer.h"
25 #include "dbs-etherwatch.h"
26 #include "file_wrappers.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 /* This module reads the text output of the 'DBS-ETHERTRACE' command in VMS
34  * It was initially based on vms.c.
35  */
36
37 /*
38    Example 'ETHERWATCH' output data (with "printable" characters in the
39    "printable characters" section of the output replaced by "." if they have
40    the 8th bit set, so as not to upset compilers that are expecting text
41    in comments to be in a particular character encoding that can't handle
42    those values):
43 ETHERWATCH  X5-008
44 42 names and addresses were loaded
45 Reading recorded data from PERSISTENCE
46 ------------------------------------------------------------------------------
47 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
48 Protocol 08-00 00 00-00-00-00-00,   60 byte buffer at 10-OCT-2001 10:20:45.16
49   [E..<8...........]-    0-[45 00 00 3C 38 93 00 00 1D 06 D2 12 80 93 11 1A]
50   [.........(......]-   16-[80 93 80 D6 02 D2 02 03 00 28 A4 90 00 00 00 00]
51   [................]-   32-[A0 02 FF FF 95 BD 00 00 02 04 05 B4 03 03 04 01]
52   [............    ]-   48-[01 01 08 0A 90 90 E5 14 00 00 00 00]
53 ------------------------------------------------------------------------------
54 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
55 Protocol 08-00 00 00-00-00-00-00,   50 byte buffer at 10-OCT-2001 10:20:45.17
56   [E..(8......%....]-    0-[45 00 00 28 38 94 00 00 1D 06 D2 25 80 93 11 1A]
57   [.........(..Z.4w]-   16-[80 93 80 D6 02 D2 02 03 00 28 A4 91 5A 1C 34 77]
58   [P.#(.s..........]-   32-[50 10 23 28 C1 73 00 00 02 04 05 B4 03 03 00 00]
59   [..              ]-   48-[02 04]
60
61
62 Alternative HEX only output, slightly more efficient and all wireshark needs:
63 ------------------------------------------------------------------------------
64 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
65 Protocol 08-00 00 00-00-00-00-00,   50 byte buffer at 10-OCT-2001 10:20:45.17
66      0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A 80 93 80 D6]
67     20-[02 D2 02 03 00 28 A4 BF 5A 1C 34 79 50 10 23 28 C1 43 00 00]
68     40-[03 30 30 30 30 30 00 00 03 30]
69  */
70
71 /* Magic text to check for DBS-ETHERWATCH-ness of file */
72 static const char dbs_etherwatch_hdr_magic[]  =
73 { 'E', 'T', 'H', 'E', 'R', 'W', 'A', 'T', 'C', 'H', ' ', ' '};
74 #define DBS_ETHERWATCH_HDR_MAGIC_SIZE  \
75         (sizeof dbs_etherwatch_hdr_magic  / sizeof dbs_etherwatch_hdr_magic[0])
76
77 /* Magic text for start of packet */
78 static const char dbs_etherwatch_rec_magic[]  =
79 {'F', 'r', 'o', 'm', ' '};
80 #define DBS_ETHERWATCH_REC_MAGIC_SIZE \
81         (sizeof dbs_etherwatch_rec_magic  / sizeof dbs_etherwatch_rec_magic[0])
82
83 /*
84  * XXX - is this the biggest packet we can get?
85  */
86 #define DBS_ETHERWATCH_MAX_PACKET_LEN   16384
87
88 static gboolean dbs_etherwatch_read(wftap *wfth, int *err, gchar **err_info,
89         gint64 *data_offset);
90 static gboolean dbs_etherwatch_seek_read(wftap *wth, gint64 seek_off,
91         void* header, Buffer *buf, int *err, gchar **err_info);
92 static gboolean parse_dbs_etherwatch_packet(struct wtap_pkthdr *phdr, FILE_T fh,
93         Buffer* buf, int *err, gchar **err_info);
94 static guint parse_single_hex_dump_line(char* rec, guint8 *buf,
95         int byte_offset);
96 static guint parse_hex_dump(char* dump, guint8 *buf, char seperator, char end);
97
98 /* Seeks to the beginning of the next packet, and returns the
99    byte offset.  Returns -1 on failure, and sets "*err" to the error
100    and "*err_info" to null or an additional error string. */
101 static gint64 dbs_etherwatch_seek_next_packet(wftap *wfth, int *err,
102     gchar **err_info)
103 {
104   int byte;
105   unsigned int level = 0;
106   gint64 cur_off;
107
108   while ((byte = file_getc(wfth->fh)) != EOF) {
109     if (byte == dbs_etherwatch_rec_magic[level]) {
110       level++;
111       if (level >= DBS_ETHERWATCH_REC_MAGIC_SIZE) {
112         /* note: we're leaving file pointer right after the magic characters */
113         cur_off = file_tell(wfth->fh);
114         if (cur_off == -1) {
115           /* Error. */
116           *err = file_error(wfth->fh, err_info);
117           return -1;
118         }
119         return cur_off + 1;
120       }
121     } else {
122       level = 0;
123     }
124   }
125   /* EOF or error. */
126   *err = file_error(wfth->fh, err_info);
127   return -1;
128 }
129
130 #define DBS_ETHERWATCH_HEADER_LINES_TO_CHECK    200
131 #define DBS_ETHERWATCH_LINE_LENGTH              240
132
133 /* Look through the first part of a file to see if this is
134  * a DBS Ethertrace text trace file.
135  *
136  * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
137  * if we get an I/O error, "*err" will be set to a non-zero value and
138  * "*err_info" will be set to null or an error string.
139  */
140 static gboolean dbs_etherwatch_check_file_type(wftap *wfth, int *err,
141     gchar **err_info)
142 {
143         char    buf[DBS_ETHERWATCH_LINE_LENGTH];
144         int     line, byte;
145         gsize   reclen;
146         unsigned int i, level;
147
148         buf[DBS_ETHERWATCH_LINE_LENGTH-1] = 0;
149
150         for (line = 0; line < DBS_ETHERWATCH_HEADER_LINES_TO_CHECK; line++) {
151                 if (file_gets(buf, DBS_ETHERWATCH_LINE_LENGTH, wfth->fh) == NULL) {
152                         /* EOF or error. */
153                         *err = file_error(wfth->fh, err_info);
154                         return FALSE;
155                 }
156
157                 reclen = strlen(buf);
158                 if (reclen < DBS_ETHERWATCH_HDR_MAGIC_SIZE)
159                         continue;
160
161                 level = 0;
162                 for (i = 0; i < reclen; i++) {
163                         byte = buf[i];
164                         if (byte == dbs_etherwatch_hdr_magic[level]) {
165                                 level++;
166                                 if (level >=
167                                       DBS_ETHERWATCH_HDR_MAGIC_SIZE) {
168                                         return TRUE;
169                                 }
170                         }
171                         else
172                                 level = 0;
173                 }
174         }
175         *err = 0;
176         return FALSE;
177 }
178
179
180 int dbs_etherwatch_open(wftap *wfth, int *err, gchar **err_info)
181 {
182         /* Look for DBS ETHERWATCH header */
183         if (!dbs_etherwatch_check_file_type(wfth, err, err_info)) {
184                 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
185                         return -1;
186                 return 0;
187         }
188
189         wfth->file_encap = WTAP_ENCAP_ETHERNET;
190         wfth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_DBS_ETHERWATCH;
191         wfth->snapshot_length = 0;      /* not known */
192         wfth->subtype_read = dbs_etherwatch_read;
193         wfth->subtype_seek_read = dbs_etherwatch_seek_read;
194         wfth->tsprecision = WTAP_FILE_TSPREC_CSEC;
195
196         return 1;
197 }
198
199 /* Find the next packet and parse it; called from wtap_read(). */
200 static gboolean dbs_etherwatch_read(wftap *wfth, int *err, gchar **err_info,
201     gint64 *data_offset)
202 {
203         gint64  offset;
204         wtap* wth = (wtap*)wfth->tap_specific_data;
205
206         /* Find the next packet */
207         offset = dbs_etherwatch_seek_next_packet(wfth, err, err_info);
208         if (offset < 1)
209                 return FALSE;
210         *data_offset = offset;
211
212         /* Parse the packet */
213         return parse_dbs_etherwatch_packet(&wth->phdr, wfth->fh,
214              wfth->frame_buffer, err, err_info);
215 }
216
217 /* Used to read packets in random-access fashion */
218 static gboolean
219 dbs_etherwatch_seek_read(wftap *wfth, gint64 seek_off,
220         void* header, Buffer *buf, int *err, gchar **err_info)
221 {
222         struct wtap_pkthdr *phdr = (struct wtap_pkthdr*)header;
223         if (file_seek(wfth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
224                 return FALSE;
225
226         return parse_dbs_etherwatch_packet(phdr, wfth->random_fh, buf, err,
227             err_info);
228 }
229
230 /* Parse a packet */
231 /*
232 Packet header:
233           1         2         3         4
234 0123456789012345678901234567890123456789012345
235 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
236 Protocol 08-00 00 00-00-00-00-00,   50 byte buffer at 10-OCT-2001 10:20:45.17
237 */
238 #define MAC_ADDR_LENGTH         6                       /* Length MAC address */
239 #define DEST_MAC_PREFIX         "] to "         /* Prefix to the dest. MAC address */
240 #define PROTOCOL_LENGTH         2                       /* Length protocol */
241 #define PROTOCOL_POS            9                       /* Position protocol */
242 #define SAP_LENGTH                      2                       /* Length DSAP+SSAP */
243 #define SAP_POS                         9                       /* Position DSAP+SSAP */
244 #define CTL_UNNUMB_LENGTH       1                       /* Length unnumbered control field */
245 #define CTL_NUMB_LENGTH         2                       /* Length numbered control field */
246 #define CTL_POS                         15                      /* Position control field */
247 #define PID_LENGTH                      5                       /* Length PID */
248 #define PID_POS                         18                      /* Position PID */
249 #define LENGTH_POS                      33                      /* Position length */
250 #define HEX_HDR_SPR                     '-'                     /* Seperator char header hex values */
251 #define HEX_HDR_END                     ' '                     /* End char hdr. hex val. except PID */
252 #define HEX_PID_END                     ','                     /* End char PID hex value */
253 #define IEEE802_LEN_LEN         2                       /* Length of the IEEE 802 len. field */
254 /*
255 To check whether it is Ethernet II or IEEE 802 we check the values of the
256 control field and PID, when they are all 0's we assume it is Ethernet II
257 else IEEE 802. In IEEE 802 the DSAP and SSAP are behind protocol, the
258 length in the IEEE data we have to construct.
259 */
260 #define ETH_II_CHECK_POS    15
261 #define ETH_II_CHECK_STR    "00 00-00-00-00-00,"
262 /*
263 To check whether it IEEE 802.3 with SNAP we check that both the DSAP & SSAP
264 values are 0xAA and the control field 0x03.
265 */
266 #define SNAP_CHECK_POS          9
267 #define SNAP_CHECK_STR          "AA-AA 03"
268 /*
269 To check whether the control field is 1 or two octets we check if it is
270 unnumbered. Unnumbered has length 1, numbered 2.
271 */
272 #define CTL_UNNUMB_MASK         0x03
273 #define CTL_UNNUMB_VALUE        0x03
274 static gboolean
275 parse_dbs_etherwatch_packet(struct wtap_pkthdr *phdr, FILE_T fh, Buffer* buf,
276     int *err, gchar **err_info)
277 {
278         guint8 *pd;
279         char    line[DBS_ETHERWATCH_LINE_LENGTH];
280         int     num_items_scanned;
281         int     eth_hdr_len, pkt_len, csec;
282         int length_pos, length_from, length;
283         struct tm tm;
284         char mon[4] = "xxx";
285         gchar *p;
286         static const gchar months[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
287         int     count, line_count;
288
289         /* Make sure we have enough room for the packet */
290         buffer_assure_space(buf, DBS_ETHERWATCH_MAX_PACKET_LEN);
291         pd = buffer_start_ptr(buf);
292
293         eth_hdr_len = 0;
294         memset(&tm, 0, sizeof(tm));
295         /* Our file pointer should be on the first line containing the
296          * summary information for a packet. Read in that line and
297          * extract the useful information
298          */
299         if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
300                 *err = file_error(fh, err_info);
301                 if (*err == 0) {
302                         *err = WTAP_ERR_SHORT_READ;
303                 }
304                 return FALSE;
305         }
306
307         /* Get the destination address */
308         p = strstr(line, DEST_MAC_PREFIX);
309         if(!p) {
310                 *err = WTAP_ERR_BAD_FILE;
311                 *err_info = g_strdup("dbs_etherwatch: destination address not found");
312                 return FALSE;
313         }
314         p += strlen(DEST_MAC_PREFIX);
315         if(parse_hex_dump(p, &pd[eth_hdr_len], HEX_HDR_SPR, HEX_HDR_END)
316                                 != MAC_ADDR_LENGTH) {
317                 *err = WTAP_ERR_BAD_FILE;
318                 *err_info = g_strdup("dbs_etherwatch: destination address not valid");
319                 return FALSE;
320         }
321         eth_hdr_len += MAC_ADDR_LENGTH;
322
323         /* Get the source address */
324         /*
325          * Since the first part of the line is already skipped in order to find
326          * the start of the record we cannot index, just look for the first
327          * 'HEX' character
328          */
329         p = line;
330         while(!isxdigit((guchar)*p)) {
331                 p++;
332         }
333         if(parse_hex_dump(p, &pd[eth_hdr_len], HEX_HDR_SPR,
334                 HEX_HDR_END) != MAC_ADDR_LENGTH) {
335                 *err = WTAP_ERR_BAD_FILE;
336                 *err_info = g_strdup("dbs_etherwatch: source address not valid");
337                 return FALSE;
338         }
339         eth_hdr_len += MAC_ADDR_LENGTH;
340
341         /* Read the next line of the record header */
342         if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
343                 *err = file_error(fh, err_info);
344                 if (*err == 0) {
345                         *err = WTAP_ERR_SHORT_READ;
346                 }
347                 return FALSE;
348         }
349
350         /* Check the lines is as least as long as the length position */
351         if(strlen(line) < LENGTH_POS) {
352                 *err = WTAP_ERR_BAD_FILE;
353                 *err_info = g_strdup("dbs_etherwatch: line too short");
354                 return FALSE;
355         }
356
357         num_items_scanned = sscanf(line + LENGTH_POS,
358                                 "%9d byte buffer at %2d-%3s-%4d %2d:%2d:%2d.%9d",
359                                 &pkt_len,
360                                 &tm.tm_mday, mon,
361                                 &tm.tm_year, &tm.tm_hour, &tm.tm_min,
362                                 &tm.tm_sec, &csec);
363
364         if (num_items_scanned != 8) {
365                 *err = WTAP_ERR_BAD_FILE;
366                 *err_info = g_strdup("dbs_etherwatch: header line not valid");
367                 return FALSE;
368         }
369
370         /* Determine whether it is Ethernet II or IEEE 802 */
371         if(strncmp(&line[ETH_II_CHECK_POS], ETH_II_CHECK_STR,
372                 strlen(ETH_II_CHECK_STR)) == 0) {
373                 /* Ethernet II */
374                 /* Get the Protocol */
375                 if(parse_hex_dump(&line[PROTOCOL_POS], &pd[eth_hdr_len], HEX_HDR_SPR,
376                                         HEX_HDR_END) != PROTOCOL_LENGTH) {
377                         *err = WTAP_ERR_BAD_FILE;
378                         *err_info = g_strdup("dbs_etherwatch: Ethernet II protocol value not valid");
379                         return FALSE;
380                 }
381                 eth_hdr_len += PROTOCOL_LENGTH;
382         } else {
383                 /* IEEE 802 */
384                 /* Remember where to put the length in the header */
385                 length_pos = eth_hdr_len;
386                 /* Leave room in the header for the length */
387                 eth_hdr_len += IEEE802_LEN_LEN;
388                 /* Remember how much of the header should not be added to the length */
389                 length_from = eth_hdr_len;
390                 /* Get the DSAP + SSAP */
391                 if(parse_hex_dump(&line[SAP_POS], &pd[eth_hdr_len], HEX_HDR_SPR,
392                                         HEX_HDR_END) != SAP_LENGTH) {
393                         *err = WTAP_ERR_BAD_FILE;
394                         *err_info = g_strdup("dbs_etherwatch: 802.2 DSAP+SSAP value not valid");
395                         return FALSE;
396                 }
397                 eth_hdr_len += SAP_LENGTH;
398                 /* Get the (first part of the) control field */
399                 if(parse_hex_dump(&line[CTL_POS], &pd[eth_hdr_len], HEX_HDR_SPR,
400                                         HEX_HDR_END) != CTL_UNNUMB_LENGTH) {
401                         *err = WTAP_ERR_BAD_FILE;
402                         *err_info = g_strdup("dbs_etherwatch: 802.2 control field first part not valid");
403                         return FALSE;
404                 }
405                 /* Determine whether the control is numbered, and thus longer */
406                 if((pd[eth_hdr_len] & CTL_UNNUMB_MASK) != CTL_UNNUMB_VALUE) {
407                         /* Get the rest of the control field, the first octet in the PID */
408                         if(parse_hex_dump(&line[PID_POS],
409                                                 &pd[eth_hdr_len + CTL_UNNUMB_LENGTH], HEX_HDR_END,
410                                                 HEX_HDR_SPR) != CTL_NUMB_LENGTH - CTL_UNNUMB_LENGTH) {
411                                 *err = WTAP_ERR_BAD_FILE;
412                                 *err_info = g_strdup("dbs_etherwatch: 802.2 control field second part value not valid");
413                                 return FALSE;
414                         }
415                         eth_hdr_len += CTL_NUMB_LENGTH;
416                 } else {
417                         eth_hdr_len += CTL_UNNUMB_LENGTH;
418                 }
419                 /* Determine whether it is SNAP */
420                 if(strncmp(&line[SNAP_CHECK_POS], SNAP_CHECK_STR,
421                                 strlen(SNAP_CHECK_STR)) == 0) {
422                         /* Get the PID */
423                         if(parse_hex_dump(&line[PID_POS], &pd[eth_hdr_len], HEX_HDR_SPR,
424                                                 HEX_PID_END) != PID_LENGTH) {
425                                 *err = WTAP_ERR_BAD_FILE;
426                                 *err_info = g_strdup("dbs_etherwatch: 802.2 PID value not valid");
427                                 return FALSE;
428                         }
429                         eth_hdr_len += PID_LENGTH;
430                 }
431                 /* Write the length in the header */
432                 length = eth_hdr_len - length_from + pkt_len;
433                 pd[length_pos] = (length) >> 8;
434                 pd[length_pos+1] = (length) & 0xFF;
435         }
436
437         phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
438
439         p = strstr(months, mon);
440         if (p)
441                 tm.tm_mon = (int)(p - months) / 3;
442         tm.tm_year -= 1900;
443
444         tm.tm_isdst = -1;
445         phdr->ts.secs = mktime(&tm);
446         phdr->ts.nsecs = csec * 10000000;
447         phdr->caplen = eth_hdr_len + pkt_len;
448         phdr->len = eth_hdr_len + pkt_len;
449
450         /*
451          * We don't have an FCS in this frame.
452          */
453         phdr->pseudo_header.eth.fcs_len = 0;
454
455         /* Parse the hex dump */
456         count = 0;
457         while (count < pkt_len) {
458                 if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
459                         *err = file_error(fh, err_info);
460                         if (*err == 0) {
461                                 *err = WTAP_ERR_SHORT_READ;
462                         }
463                         return FALSE;
464                 }
465                 if (!(line_count = parse_single_hex_dump_line(line,
466                                 &pd[eth_hdr_len + count], count))) {
467                         *err = WTAP_ERR_BAD_FILE;
468                         *err_info = g_strdup("dbs_etherwatch: packet data value not valid");
469                         return FALSE;
470                 }
471                 count += line_count;
472                 if (count > pkt_len) {
473                         *err = WTAP_ERR_BAD_FILE;
474                         *err_info = g_strdup("dbs_etherwatch: packet data value has too many bytes");
475                         return FALSE;
476                 }
477         }
478         return TRUE;
479 }
480
481 /* Parse a hex dump line */
482 /*
483 /DISPLAY=BOTH output:
484
485           1         2         3         4
486 0123456789012345678901234567890123456789012345
487   [E..(8...........]-    0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A]
488   [.........(..Z.4y]-   16-[80 93 80 D6 02 D2 02 03 00 28 A4 BF 5A 1C 34 79]
489   [P.#(.C...00000..]-   32-[50 10 23 28 C1 43 00 00 03 30 30 30 30 30 00 00]
490   [.0              ]-   48-[03 30]
491
492 /DISPLAY=HEXADECIMAL output:
493
494           1         2         3         4
495 0123456789012345678901234567890123456789012345
496      0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A 80 93 80 D6]
497     20-[02 D2 02 03 00 28 A4 BF 5A 1C 34 79 50 10 23 28 C1 43 00 00]
498     40-[03 30 30 30 30 30 00 00 03 30]
499
500 */
501
502 #define TYPE_CHECK_POS          2       /* Position to check the type of hex dump */
503 #define TYPE_CHECK_BOTH         '['     /* Value at pos. that indicates BOTH type */
504 #define COUNT_POS_BOTH          21      /* Count position BOTH type */
505 #define COUNT_POS_HEX           1       /* Count position HEX type */
506 #define COUNT_SIZE              5       /* Length counter */
507 #define HEX_DUMP_START          '['     /* Start char */
508 #define HEX_DUMP_SPR            ' ' /* Seperator char */
509 #define HEX_DUMP_END            ']' /* End char */
510
511 /* Take a string representing one line from a hex dump and converts the
512  * text to binary data. We check the printed offset with the offset
513  * we are passed to validate the record. We place the bytes in the buffer
514  * at the specified offset.
515  *
516  * Returns length parsed if a good hex dump, 0 if bad.
517  */
518 static guint
519 parse_single_hex_dump_line(char* rec, guint8 *buf, int byte_offset) {
520
521         int             pos, i;
522         int             value;
523
524
525         /* Check that the record is as least as long as the check offset */
526         for(i = 0; i < TYPE_CHECK_POS; i++)
527         {
528                 if(rec[i] == '\0') {
529                         return 0;
530                 }
531         }
532         /* determine the format and thus the counter offset and hex dump length */
533         if(rec[TYPE_CHECK_POS] == TYPE_CHECK_BOTH)
534         {
535                 pos = COUNT_POS_BOTH;
536         }
537         else
538         {
539                 pos = COUNT_POS_HEX;
540         }
541
542         /* Check that the record is as least as long as the start position */
543         while(i < pos)
544         {
545                 if(rec[i] == '\0') {
546                         return 0;
547                 }
548                 i++;
549         }
550
551         /* Get the byte_offset directly from the record */
552         value = 0;
553         for(i = 0; i < COUNT_SIZE; i++) {
554                 if(!isspace((guchar)rec[pos])) {
555                         if(isdigit((guchar)rec[pos])) {
556                                 value *= 10;
557                                 value += rec[pos] - '0';
558                         } else {
559                                 return 0;
560                         }
561                 }
562                 pos++;
563         }
564
565         if (value != byte_offset) {
566                 return 0;
567         }
568
569         /* find the start of the hex dump */
570         while(rec[pos] != HEX_DUMP_START) {
571                 if(rec[pos] == '\0') {
572                         return 0;
573                 }
574                 pos++;
575         }
576         pos++;
577         return parse_hex_dump(&rec[pos], buf, HEX_DUMP_SPR, HEX_DUMP_END);
578 }
579
580 /* Parse a hex dump */
581 static guint
582 parse_hex_dump(char* dump, guint8 *buf, char seperator, char end) {
583         int             pos, count;
584
585         /* Parse the hex dump */
586         pos = 0;
587         count = 0;
588         while(dump[pos] != end) {
589                 /* Check the hex value */
590                 if(!(isxdigit((guchar)dump[pos]) &&
591                     isxdigit((guchar)dump[pos + 1]))) {
592                         return 0;
593                 }
594                 /* Get the hex value value */
595                 if(isdigit((guchar)dump[pos])) {
596                         buf[count] = (dump[pos] - '0') << 4;
597                 } else {
598                         buf[count] = (toupper(dump[pos]) - 'A' + 10) << 4;
599                 }
600                 pos++;
601                 if(isdigit((guchar)dump[pos])) {
602                         buf[count] += dump[pos] - '0';
603                 } else {
604                         buf[count] += toupper(dump[pos]) - 'A' + 10;
605                 }
606                 pos++;
607                 count++;
608                 /* Skip the seperator characters */
609                 while(dump[pos] == seperator) {
610                         pos++;
611                 }
612         }
613         return count;
614 }