Subj : [fwd] binkptrace.c
To   : All
From : andrew clarke
Date : Sat Jun 28 2003 12:08 pm

* Forwarded (from: ftsc.wg.b) by andrew clarke using timEd/FreeBSD 1.11.b1.
* Originally from andrew clarke (3:633/267) to All.
* Original dated: 2003-06-28 11:07:54

Hi All,

I'm posting C code for a program that displays the output of data captured
during a BinkP session in human-readable text.  I was wondering if people
familiar with BinkP internals could verify it is correct so far, and for the
people unfamilar with BinkP, whether it makes some sense.

Obviously as it deals with data "after the fact" it doesn't handle the various
states during the session.  This is where the real guts of the BinkP spec need
to be double checked before it can be called a standard.  I'm tossing up the
idea of writing a very basic BinkP client to do that, then make corrections to
the doc.  Hmm, I guess I'd have to write some sort of server too.

Regards
Andrew

/*
*  binkptrace.c
*
*  BinkP protocol tracer.
*
*  Written in 2003 by Andrew Clarke and released to the public domain.
*
*  Usage: binkptrace <file>
*
*  Where <file> is the raw data obtained from a host during a BinkP
*  session.  You could use tcpflow to capture it on a UNIX-style system.
*/

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

#define PROGNAME "binkptrace"
#define AUTHOR "Andrew Clarke"
#define YEAR "2003"

static FILE *fp;
static char *fn;

#define M_NUL  0
#define M_ADR  1
#define M_PWD  2
#define M_FILE 3
#define M_OK   4
#define M_EOB  5
#define M_GOT  6
#define M_ERR  7
#define M_BSY  8
#define M_GET  9
#define M_SKIP 10

static struct command_t
{
   char *name;
   int id;
}
command[] =
{
   { "M_NUL", M_NUL },
   { "M_ADR", M_ADR },
   { "M_PWD", M_PWD },
   { "M_FILE", M_FILE },
   { "M_OK", M_OK },
   { "M_EOB", M_EOB },
   { "M_GOT", M_GOT },
   { "M_ERR", M_ERR },
   { "M_BSY", M_BSY },
   { "M_GET", M_GET },
   { "M_SKIP", M_SKIP },
   { NULL, 0 }
};

static char *command_name(int id)
{
   struct command_t *cmd;

   cmd = command;

   while (cmd->name != NULL)
   {
       if (id == cmd->id)
       {
           return cmd->name;
       }

       cmd++;
   }

   /* fell through, unknown protocol command */

   return "M_UNK";
}

static int decode_frame(const char *data)
{
   printf("%s(id=%d) \"%s\"\n", command_name(data[0]), (int) data[0],
     data + 1);

   return EXIT_SUCCESS;
}

static int loop(void)
{
   while (1)
   {
       int ch, ch2, size;

       /* read the high byte of the frame header */

       ch = getc(fp);

       if (ch == EOF)
       {
           break;
       }

       /* read the low byte of the frame header */

       ch2 = getc(fp);

       if (ch2 == EOF)
       {
           /* we got the high byte but no low byte */

           fprintf(stderr, PROGNAME ": Premature EOF in file '%s'\n", fn);
           return EXIT_FAILURE;
       }

       /* the size of the frame data is a 15-bit value, so calculate it */

       size = (int) ((unsigned char) (ch & 0x7f) << 8);  /* high byte */
       size |= (int) ((unsigned char) ch2);              /* low byte */

       if (size != 0)
       {
           char *ptr;
           size_t ofs;

           /* let's read the frame data */

           ptr = malloc(size + 1);

           if (ptr == NULL)
           {
               /*
                *  size can't exceed 32K bytes so this is unlikely, but you
                *  never know...
                */

               fprintf(stderr, PROGNAME
                 ": Out of memory (%d bytes required)\n", (int) size + 1);

               return EXIT_FAILURE;
           }

           /*
            *  Get the current offset into the file.  This is just to
            *  display to the user in case fread() fails...
            */

           ofs = ftell(fp);

           if (ofs == -1L)
           {
               /* ftell() failed for some reason */

               fprintf(stderr, PROGNAME ": ftell() failed in file '%s'\n",
fn);
               return EXIT_FAILURE;
           }

           if (fread(ptr, size, 1, fp) != 1)
           {
               /* fread() failed for some reason */

               fprintf(
                 stderr,
                 PROGNAME
                 ": fread() failed (size=%d, offset=%d) in file '%s'\n",
                 size, ofs, fn
               );

               free(ptr);

               return EXIT_FAILURE;
           }

           /*
            *  All OK so far.  Now we determine if this is a command frame
            *  or a data frame.  We display command frames to the user.
            */

           if ((ch & 0x80) == 0x80)
           {
               /* this is a command frame */

               /*
                *  nul-terminate the tail of command frames so we can
                *  display them.
                */

               ptr[size] = '\0';

               if (decode_frame(ptr) != EXIT_SUCCESS)
               {
                   free(ptr);
                   return EXIT_FAILURE;
               }
           }
           else
           {
               /*
                *  This is a data frame, so just display the size of the
                *  frame data and continue on to the next frame.
                */

               printf("DATA(size=%d)\n", (int) size);
           }

           free(ptr);
       }
   }

   /*
    *  We fell out of the loop, which may or may not be a premature EOF,
    *  depending on what state we are up to during the session.  We're
    *  not a very smart program so we'll just assume it's OK and leave
    *  it up to the reader to decide.
    */

   puts("EOF");

   return EXIT_SUCCESS;
}

int main(int argc, char **argv)
{
   int rc;

   /*
    *  argv[0] == "binkptrace"
    *  argv[1] == <file>
    *  argv[2] == NULL
    */

   if (argc != 2)
   {
       fputs(
         PROGNAME ": BinkP protocol tracer" "\n"
         "Written by " AUTHOR " in " YEAR
         " and released to the public domain." "\n"
         "\n"
         "Usage: " PROGNAME " file" "\n",
         stderr
       );

       return EXIT_FAILURE;
   }

   fn = argv[1];

   fp = fopen(fn, "rb");

   if (fp == NULL)
   {
       fprintf(stderr, PROGNAME ": Can't open %s: %s", fn, strerror(errno));

       return EXIT_FAILURE;
   }

   rc = loop();

   fclose(fp);

   return rc;
}

-- [email protected]

-+- timEd/FreeBSD 1.11.b1
+ Origin: Blizzard of Ozz, Mt Eliza, Melbourne, Australia (3:633/267)

--- timEd/FreeBSD 1.11.b1
* Origin: Blizzard of Ozz, Mt Eliza, Melbourne, Australia (3:633/267)