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