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