Ethereal-dev: [Ethereal-dev] VMS TCPIPtrace

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Marc Milgram <marc.milgram@xxxxxxxxxx>
Date: Thu, 18 Oct 2001 15:23:47 -0400
Hello,

This is an implementation of a VMS TCPIPtrace decoder wiretap module.

-Marc Milgram    <mmilgram[AT]arrayinc.com>


Index: AUTHORS
===================================================================
RCS file: /cvsroot/ethereal/AUTHORS,v
retrieving revision 1.336
diff -u -r1.336 AUTHORS
--- AUTHORS    2001/10/18 08:23:24    1.336
+++ AUTHORS    2001/10/18 19:20:17
@@ -829,6 +829,10 @@
    UCP fixes
}

+Marc Milgram <mmilgram[AT]arrayinc.com> {
+    VMS TCPIPtrace wiretap module
+}
+
Alain Magloire <alainm[AT]rcsm.ece.mcgill.ca> was kind enough to
give his permission to use his version of snprintf.c.

Index: doc/editcap.pod
===================================================================
RCS file: /cvsroot/ethereal/doc/editcap.pod,v
retrieving revision 1.10
diff -u -r1.10 doc/editcap.pod
--- doc/editcap.pod    2001/07/12 19:59:40    1.10
+++ doc/editcap.pod    2001/10/18 19:20:17
@@ -26,13 +26,13 @@
B<snoop> (including B<Shomiti>) and B<atmsnoop>, B<LanAlyzer>,
B<Sniffer> (compressed or uncompressed), Microsoft B<Network Monitor>,
AIX's B<iptrace>, B<NetXray>, B<Sniffer Pro>, B<RADCOM>'s WAN/LAN
-analyzer, B<Lucent/Ascend> router debug output, HP-UX's B<nettl>, and
-the dump output from B<Toshiba's> ISDN routers.  There is no need to
-tell B<Editcap> what type of file you are reading; it will determine the
-file type by itself.  B<Editcap> is also capable of reading any of these
-file formats if they are compressed using gzip.  B<Editcap> recognizes
-this directly from the file; the '.gz' extension is not required for
-this purpose.
+analyzer, B<Lucent/Ascend> router debug output, HP-UX's B<nettl>, the
+dump output from B<Toshiba's> ISDN routers, and the output from VMS's
+B<TCPIPtrace> utility.  There is no need to tell B<Editcap> what type
+of file you are reading; it will determine the file type by itself.
+B<Editcap> is also capable of reading any of these file formats if they
+are compressed using gzip.  B<Editcap> recognizes this directly from the
+file; the '.gz' extension is not required for this purpose.

By default, it writes the capture file in B<libpcap> format, and writes
all of the packets in the capture file to the output file.  The B<-F>
Index: doc/ethereal.pod.template
===================================================================
RCS file: /cvsroot/ethereal/doc/ethereal.pod.template,v
retrieving revision 1.216
diff -u -r1.216 doc/ethereal.pod.template
--- doc/ethereal.pod.template    2001/10/15 03:54:05    1.216
+++ doc/ethereal.pod.template    2001/10/18 19:20:18
@@ -40,9 +40,10 @@
Microsoft B<Network Monitor>, AIX's B<iptrace>, B<NetXray>, B<Sniffer
Pro>, B<Etherpeek>, B<RADCOM>'s WAN/LAN analyzer, B<Lucent/Ascend>
router debug output, HP-UX's B<nettl>, the dump output from B<Toshiba's>
-ISDN routers, the output from B<i4btrace> from the ISDN4BSD project, the
-output in B<IPLog> format from the Cisco Secure Intrusion Detection
-System, and B<pppd logs> (pppdump format).  There is no need to tell
+ISDN routers, the output from VMS's B<TCPIPtrace> utility, the output
+from B<i4btrace> from the ISDN4BSD project, the output in B<IPLog>
+format from the Cisco Secure Intrusion Detection System, and
+B<pppd logs> (pppdump format).  There is no need to tell
B<Ethereal> what type of file you are reading; it will determine the
file type by itself.  B<Ethereal> is also capable of reading any of
these file formats if they are compressed using gzip.  B<Ethereal>
Index: doc/mergecap.pod
===================================================================
RCS file: /cvsroot/ethereal/doc/mergecap.pod,v
retrieving revision 1.3
diff -u -r1.3 doc/mergecap.pod
--- doc/mergecap.pod    2001/07/14 19:28:11    1.3
+++ doc/mergecap.pod    2001/10/18 19:20:18
@@ -23,13 +23,13 @@
B<Shomiti>) and B<atmsnoop>, B<LanAlyzer>, B<Sniffer> (compressed or
uncompressed), Microsoft B<Network Monitor>, AIX's B<iptrace>,
B<NetXray>, B<Sniffer Pro>, B<RADCOM>'s WAN/LAN analyzer,
-B<Lucent/Ascend> router debug output, HP-UX's B<nettl>, and the dump
-output from B<Toshiba's> ISDN routers.  There is no need to tell
-B<Mergecap> what type of file you are reading; it will determine the
-file type by itself.  B<Mergecap> is also capable of reading any of
-these file formats if they are compressed using gzip.  B<Mergecap>
-recognizes this directly from the file; the '.gz' extension is not
-required for this purpose.
+B<Lucent/Ascend> router debug output, HP-UX's B<nettl>, the dump output
+from B<Toshiba's> ISDN routers, and the output from VMS's B<TCPIPtrace>
+utility.  There is no need to tell B<Mergecap> what type of file you are
+reading; it will determine the file type by itself.  B<Mergecap> is also
+capable of reading any of these file formats if they are compressed using
+gzip.  B<Mergecap> recognizes this directly from the file; the '.gz'
+extension is not required for this purpose.

By default, it writes the capture file in B<libpcap> format, and writes
all of the packets in both input capture files to the output file.  The
Index: doc/tethereal.pod.template
===================================================================
RCS file: /cvsroot/ethereal/doc/tethereal.pod.template,v
retrieving revision 1.32
diff -u -r1.32 doc/tethereal.pod.template
--- doc/tethereal.pod.template    2001/10/11 01:21:50    1.32
+++ doc/tethereal.pod.template    2001/10/18 19:20:18
@@ -39,13 +39,14 @@
uncompressed), Microsoft B<Network Monitor>, AIX's B<iptrace>,
B<NetXray>, B<Sniffer Pro>, B<Etherpeek>, B<RADCOM>'s WAN/LAN analyzer,
B<Lucent/Ascend> router debug output, HP-UX's B<nettl>, the dump output
-from B<Toshiba's> ISDN routers, the output from B<i4btrace> from the
-ISDN4BSD project, and output in IPLog format from the Cisco Secure
-Intrusion Detection System.  There is no need to tell B<Tethereal> what
-type of file you are reading; it will determine the file type by itself.
-B<Tethereal> is also capable of reading any of these file formats if
-they are compressed using gzip.  B<Tethereal> recognizes this directly
-from the file; the '.gz' extension is not required for this purpose.
+from B<Toshiba's> ISDN routers, the output from VMS's B<TCPIPtrace>
+utility, the output from B<i4btrace> from the ISDN4BSD project, and
+output in IPLog format from the Cisco Secure Intrusion Detection System.
+There is no need to tell B<Tethereal> what type of file you are reading;
+it will determine the file type by itself.  B<Tethereal> is also capable
+of reading any of these file formats if they are compressed using gzip.
+B<Tethereal> recognizes this directly from the file; the '.gz' extension
+is not required for this purpose.

If the B<-w> flag is not specified, B<Tethereal> prints a decoded form
of the packets it captures or reads; otherwise, it writes those packets
Index: wiretap/AUTHORS
===================================================================
RCS file: /cvsroot/ethereal/wiretap/AUTHORS,v
retrieving revision 1.14
diff -u -r1.14 wiretap/AUTHORS
--- wiretap/AUTHORS    2001/04/17 00:46:06    1.14
+++ wiretap/AUTHORS    2001/10/18 19:20:18
@@ -10,3 +10,4 @@
Mike Hall        <mlh@xxxxxx>
Daniel Thompson        <daniel.thompson@xxxxxx>
Chris Jepeway        <thai-dragon@xxxxxxxxxxxx>
+Marc Milgram        <mmilgram@xxxxxxxxxxxx>
Index: wiretap/Makefile.am
===================================================================
RCS file: /cvsroot/ethereal/wiretap/Makefile.am,v
retrieving revision 1.33
diff -u -r1.33 wiretap/Makefile.am
--- wiretap/Makefile.am    2001/04/18 21:34:22    1.33
+++ wiretap/Makefile.am    2001/10/18 19:20:18
@@ -72,6 +72,8 @@
    snoop.h            \
    toshiba.c        \
    toshiba.h        \
+    vms.c            \
+    vms.h            \
    wtap.c            \
    wtap.h            \
    wtap-int.h
Index: wiretap/Makefile.nmake
===================================================================
RCS file: /cvsroot/ethereal/wiretap/Makefile.nmake,v
retrieving revision 1.20
diff -u -r1.20 wiretap/Makefile.nmake
--- wiretap/Makefile.nmake    2001/04/12 18:07:22    1.20
+++ wiretap/Makefile.nmake    2001/10/18 19:20:18
@@ -32,6 +32,7 @@
    pppdump.obj \
    snoop.obj \
    toshiba.obj \
+    vms.obj \
    wtap.obj


Index: wiretap/README
===================================================================
RCS file: /cvsroot/ethereal/wiretap/README,v
retrieving revision 1.26
diff -u -r1.26 wiretap/README
--- wiretap/README    2000/11/22 04:07:04    1.26
+++ wiretap/README    2001/10/18 19:20:19
@@ -139,6 +139,20 @@
--------------------------------
Gilbert

+VMS TCPTRACE
+------------
+Compaq VMS's TCPIPTRACE format is supported.  This is the capture program
+that comes with TCP/IP or UCX as supplied by Compaq or Digital Equipment
+Corporation.

+Under UCX 4.x, it is invoked as TCPIPTRACE.  Under TCPIP 5.x, it is invoked
+as TCPTRACE.
+
+TCPTRACE produces an ascii text based format, that has changed slightly over
+time.
+
+
Gilbert Ramirez <gram@xxxxxxxxxx>
Guy Harris <guy@xxxxxxxxxxxx>
+
+
Index: wiretap/file.c
===================================================================
RCS file: /cvsroot/ethereal/wiretap/file.c,v
retrieving revision 1.69
diff -u -r1.69 wiretap/file.c
--- wiretap/file.c    2001/10/16 04:58:24    1.69
+++ wiretap/file.c    2001/10/18 19:20:19
@@ -60,6 +60,7 @@
#include "csids.h"
#include "pppdump.h"
#include "etherpeek.h"
+#include "vms.h"

/* The open_file_* routines should return:
 *
@@ -107,6 +108,7 @@
    toshiba_open,
    i4btrace_open,
    csids_open,
+    vms_open,
};

#define    N_FILE_TYPES    (sizeof open_routines / sizeof open_routines[0])
@@ -365,6 +367,10 @@
    /* WTAP_FILE_ETHERPEEK_MAC_V7 */
    { "Etherpeek trace (Macintosh V7)", NULL,
      NULL, NULL },
+
+    /* WTAP_FILE_VMS */
+    { "TCPIPtrace (VMS)", NULL,
+      NULL, NULL},
};

/* Name that should be somewhat descriptive. */
Index: wiretap/wtap.h
===================================================================
RCS file: /cvsroot/ethereal/wiretap/wtap.h,v
retrieving revision 1.89
diff -u -r1.89 wiretap/wtap.h
--- wiretap/wtap.h    2001/10/04 08:30:36    1.89
+++ wiretap/wtap.h    2001/10/18 19:20:19
@@ -132,9 +132,10 @@
#define WTAP_FILE_PPPDUMP            24
#define WTAP_FILE_ETHERPEEK_MAC_V56        25
#define WTAP_FILE_ETHERPEEK_MAC_V7        26
+#define WTAP_FILE_VMS                27

/* last WTAP_FILE_ value + 1 */
-#define WTAP_NUM_FILE_TYPES            27
+#define WTAP_NUM_FILE_TYPES            28

/*
 * Maximum packet size we'll support.


/* vms.c
*
* $Id$
*
* Wiretap Library
* Copyright (c) 2001 by Marc Milgram <mmilgram@xxxxxxxxxxxx>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "wtap-int.h"
#include "buffer.h"
#include "vms.h"
#include "file_wrappers.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* This module reads the output of the 'TCPIPTRACE' command in VMS
* It was initially based on toshiba.c.
*/

/*
  Example 'TCPIPTRACE' output data:
  TCPIPtrace full display RCV packet 8 at 10-JUL-2001 14:54:19.56

  IP Version = 4,  IHL = 5,  TOS = 00,   Total Length = 84 = ^x0054
  IP Identifier  = ^x178F,  Flags (0=0,DF=0,MF=0),
        Fragment Offset = 0 = ^x0000,   Calculated Offset = 0 = ^x0000
  IP TTL = 64 = ^x40,  Protocol = 17 = ^x11,  Header Checksum = ^x4C71
  IP Source Address      = 10.12.1.80
  IP Destination Address = 10.12.1.50

  UDP Source Port = 731,   UDP Destination Port = 111
  UDP Header and Datagram Length = 64 = ^x0040,   Checksum = ^xB6C0

  50010C0A   714C1140   00008F17   54000045    0000    E..T....@.Lq...P
  27E54C3C | C0B64000   6F00DB02 | 32010C0A    0010    ...2...o.@..<L.'
  02000000   A0860100   02000000   00000000    0020    ................
  00000000   00000000   00000000   03000000    0030    ................
  06000000   01000000   A5860100   00000000    0040    ................
                                   00000000    0050    ....

--------------------------------------------------------------------------------

*/

/* Magic text to check for VMS-ness of file */
static const char vms_hdr_magic[]  =
{ ' ', ' ', ' ', 'T', 'C', 'P', 'I', 'P', 't', 'r', 'a', 'c', 'e', ' '};
#define VMS_HDR_MAGIC_SIZE (sizeof vms_hdr_magic / sizeof vms_hdr_magic[0])

/* Magic text for start of packet */
#define vms_rec_magic vms_hdr_magic
#define VMS_REC_MAGIC_SIZE (sizeof vms_rec_magic / sizeof vms_rec_magic[0])

static gboolean vms_read(wtap *wth, int *err, int *data_offset);
static int vms_seek_read(wtap *wth, int seek_off,
   union wtap_pseudo_header *pseudo_header, guint8 *pd, int len);
static gboolean parse_single_hex_dump_line(char* rec, guint8 *buf, int byte_offset, int remaining_bytes); static int parse_vms_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err);
static int parse_vms_rec_hdr(wtap *wth, FILE_T fh, int *err);


/* Seeks to the beginning of the next packet, and returns the
  byte offset.  Returns -1 on failure. */
/* XXX - Handle I/O errors. */
static int vms_seek_next_packet(wtap *wth)
{
 int byte;
 unsigned int level = 0;

 while ((byte = file_getc(wth->fh)) != EOF) {
   if (byte == vms_rec_magic[level]) {
     level++;
     if (level >= VMS_REC_MAGIC_SIZE) {
/* note: we're leaving file pointer right after the magic characters */
       return file_tell(wth->fh) + 1;
     }
   } else {
     level = 0;
   }
 }
 return -1;
}

#define VMS_HEADER_LINES_TO_CHECK    200
#define VMS_LINE_LENGTH        240

/* Look through the first part of a file to see if this is
* a VMS trace file.
*
* Returns TRUE if it is, FALSE if it isn't.
*
* Leaves file handle at begining of line that contains the VMS Magic
* identifier.
*/
static gboolean vms_check_file_type(wtap *wth)
{
   char    buf[VMS_LINE_LENGTH];
   int    line, byte;
   unsigned int reclen, i, level;
   long mpos;
buf[VMS_LINE_LENGTH-1] = 0;

   for (line = 0; line < VMS_HEADER_LINES_TO_CHECK; line++) {
       mpos = file_tell(wth->fh);
       if (file_gets(buf, VMS_LINE_LENGTH, wth->fh) != NULL) {

           reclen = strlen(buf);
           if (reclen < VMS_HDR_MAGIC_SIZE)
               continue;

           level = 0;
           for (i = 0; i < reclen; i++) {
               byte = buf[i];
               if (byte == vms_hdr_magic[level]) {
                   level++;
                   if (level >= VMS_HDR_MAGIC_SIZE) {
                       file_seek(wth->fh, mpos, SEEK_SET);
                       return TRUE;
                   }
               }
               else
                   level = 0;
           }
       }
       else
           return FALSE;
   }
   return FALSE;
}


/* XXX - return -1 on I/O error and actually do something with 'err'. */
int vms_open(wtap *wth, int *err)
{
   /* Look for VMS header */
   if (!vms_check_file_type(wth)) {
       return 0;
   }

   wth->data_offset = 0;
   wth->file_encap = WTAP_ENCAP_PER_PACKET;
   wth->file_type = WTAP_FILE_VMS;
   wth->snapshot_length = 16384; /* just guessing */
   wth->subtype_read = vms_read;
   wth->subtype_seek_read = vms_seek_read;

   return 1;
}

/* Find the next packet and parse it; called from wtap_loop(). */
static gboolean vms_read(wtap *wth, int *err, int *data_offset)
{
   int    offset = 0;
   guint8    *buf;
   int    pkt_len;

   /* Find the next packet */
   offset = vms_seek_next_packet(wth);
   if (offset < 1) {
       *err = 0;    /* XXX - assume, for now, that it's an EOF */
       return FALSE;
   }

   /* Parse the header */
   pkt_len = parse_vms_rec_hdr(wth, wth->fh, err);

   /* Make sure we have enough room for the packet */
   buffer_assure_space(wth->frame_buffer, wth->snapshot_length);
   buf = buffer_start_ptr(wth->frame_buffer);

   /* Convert the ASCII hex dump to binary data */
   parse_vms_hex_dump(wth->fh, pkt_len, buf, err);

   wth->data_offset = offset;
   *data_offset = offset;
   return TRUE;
}

/* Used to read packets in random-access fashion */
static int
vms_seek_read (wtap *wth, int seek_off, union wtap_pseudo_header *pseudo_header,
   guint8 *pd, int len)
{
   int    pkt_len;
   int    err;

   file_seek(wth->random_fh, seek_off - 1, SEEK_SET);

   pkt_len = parse_vms_rec_hdr(NULL, wth->random_fh, &err);

   if (pkt_len != len) {
       return -1;
   }

   parse_vms_hex_dump(wth->random_fh, pkt_len, pd, &err);

   return 0;
}

/* isdumpline assumes that dump lines start with some spaces followed by a
* hex number.
*/
static int
isdumpline( char *line )
{
   int i = 0;

   while (i<VMS_LINE_LENGTH && !isalnum(line[i]))
       i++;

   if (! isxdigit(line[i]))
       return 0;

   while (i<VMS_LINE_LENGTH && isxdigit(line[i]))
       i++;

   return isspace(line[i]);
}

/* Parses a packet record header. */
static int
parse_vms_rec_hdr(wtap *wth, FILE_T fh, int *err)
{
   char    line[VMS_LINE_LENGTH];
   int    num_items_scanned;
   int    pkt_len, pktnum, csec;
   struct tm time;
   char mon[4];
   char *p;
   long mpos;
   static char months[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";

   pkt_len = 0;

   /* Our file pointer should be on the first line containing the
    * summary information for a packet. Read in that line and
    * extract the useful information
    */
   if (file_gets(line, VMS_LINE_LENGTH, fh) == NULL) {
       *err = file_error(fh);
       if (*err == 0) {
           *err = WTAP_ERR_SHORT_READ;
       }
       return -1;
   }

   p = strstr(line, "packet ");
   if ( !p ) {
       *err = WTAP_ERR_BAD_RECORD;
       return 01;
   }
/* Find text in line starting with "packet ". */
   num_items_scanned = sscanf(p,
                  "packet %d at %d-%3s-%d %d:%d:%d.%d",
                  &pktnum, &time.tm_mday, mon,
                  &time.tm_year, &time.tm_hour, &time.tm_min,
                  &time.tm_sec, &csec);

   if (num_items_scanned != 8) {
       *err = WTAP_ERR_BAD_RECORD;
       return -1;
   }

   /* Skip lines until one starts with a hex number */
   do {
       mpos = file_tell(fh);
       if (file_gets(line, VMS_LINE_LENGTH, fh) == NULL) {
           *err = file_error(fh);
           if (*err == 0) {
               *err = WTAP_ERR_SHORT_READ;
           }
           return -1;
       }
       if ( (! pkt_len) && (p = strstr(line, "Length"))) {
           p += sizeof("Length ");
           while (*p && ! isdigit(*p))
               p++;

           if ( !*p ) {
               *err = WTAP_ERR_BAD_RECORD;
               return -1;
           }

           pkt_len = atoi(p);
       }
   } while (! isdumpline(line));

   file_seek(fh, mpos, SEEK_SET);
if (wth) {
       p = strstr(months, mon);
       if (p)
           time.tm_mon = (p - months) / 3;
       time.tm_year -= 1900;

       wth->phdr.ts.tv_sec = mktime(&time);

       wth->phdr.ts.tv_usec = csec * 10000;
       wth->phdr.caplen = pkt_len;
       wth->phdr.len = pkt_len;
       wth->phdr.pkt_encap = WTAP_ENCAP_RAW_IP;
   }

   return pkt_len;
}

/* Converts ASCII hex dump to binary data */
static int
parse_vms_hex_dump(FILE_T fh, int pkt_len, guint8* buf, int *err)
{
   char    line[VMS_LINE_LENGTH];
   int    i, hex_lines;
   int    offset = 0;

   /* Calculate the number of hex dump lines, each
    * containing 16 bytes of data */
   hex_lines = pkt_len / 16 + ((pkt_len % 16) ? 1 : 0);

   for (i = 0; i < hex_lines; i++) {
       if (file_gets(line, VMS_LINE_LENGTH, fh) == NULL) {
           *err = file_error(fh);
           if (*err == 0) {
               *err = WTAP_ERR_SHORT_READ;
           }
           return -1;
       }
       if (i == 0)
           while (line[offset] && !isxdigit(line[offset]))
               offset++;

       if (!parse_single_hex_dump_line(line, buf, i * 16,
                       offset)) {
           *err = WTAP_ERR_BAD_RECORD;
           return -1;
       }
   }
   return 0;
}

/*
         1         2         3         4
0123456789012345678901234567890123456789012345
  50010C0A   A34C0640   00009017   2C000045    0000    E..,....@.L....P
  00000000   14945E52   0A00DC02 | 32010C0A    0010    ...2....R^......
      0000 | B4050402   00003496   00020260    0020    `....4........
*/

#define START_POS    7
#define HEX_LENGTH ((8 * 4) + 7) /* eight clumps of 4 bytes with 7 inner spaces */
/* Take a string representing one line from a hex dump and converts the
* text to binary data. We check the printed offset with the offset
* we are passed to validate the record. We place the bytes in the buffer
* at the specified offset.
*
* In the process, we're going to write all over the string.
*
* Returns TRUE if good hex dump, FALSE if bad.
*/
static gboolean
parse_single_hex_dump_line(char* rec, guint8 *buf, int byte_offset,
              int in_off) {

   int        i;
   char        *s;
   int        value;
   static int offsets[16] = {39,37,35,33,28,26,24,22,17,15,13,11,6,4,2,0};
   char lbuf[3] = {0,0,0};
   /* Get the byte_offset directly from the record */
   s = rec;
   value = strtoul(s + 45 + in_off, NULL, 16);
if (value != byte_offset) {
       return FALSE;
   }

   /* Read the octets right to left, as that is how they are displayed
    * in VMS.
    */

   for (i = 0; i < 16; i++) {
       lbuf[0] = rec[offsets[i] + in_off];
       lbuf[1] = rec[offsets[i] + 1 + in_off];

       buf[byte_offset + i] = (guint8) strtoul(lbuf, NULL, 16);
   }

   return TRUE;
}


/* vms.h
*
* $Id$
*
* Wiretap Library
* Copyright (c) 2001 by Marc Milgram <mmilgram@xxxxxxxxxxxx>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/

#ifndef __W_VMS_H__
#define __W_VMS_H__

int vms_open(wtap *wth, int *err);

#endif