/**************************************************************************** 
** File: cbcp.c
**
** Author: Mike Borella
**
** Comments: CBCP module. This is Microsoft's Callback Control Protocol.
**
** $Id: cbcp.c,v 1.1 2001/10/02 18:37:43 mborella Exp $
**
** 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 Library 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.
**
*****************************************************************************/

#include "cbcp.h"

#define CBCP_OPTION_LEN 64

/*
 * CBCP codes
 */

#define CBCP_CODE_CBREQ        1
#define CBCP_CODE_CBRES        2
#define CBCP_CODE_CBACK        3

/*
 * CBCP code map
 */

strmap_t cbcp_code_map[] =
{
  { CBCP_CODE_CBREQ,        "Callback-Request" },
  { CBCP_CODE_CBRES,        "Callback-Response" },
  { CBCP_CODE_CBACK,        "Callback-Ack" },
  { 0, ""}
};

/*
 * CBCP options
 */

#define CBCP_OPTION_NOCALLBACK            1
#define CBCP_OPTION_CALLBACKTOUSERNUMBER  2
#define CBCP_OPTION_CALLBACKTOADMINNUMBER 3
#define CBCP_OPTION_CALLBACKTOANYLIST     4

/*
 * CBCP option map
 */

strmap_t cbcp_option_map[] = 
{
  { CBCP_OPTION_NOCALLBACK,            "no callback" },
  { CBCP_OPTION_CALLBACKTOUSERNUMBER,  "callback to user defined number" },
  { CBCP_OPTION_CALLBACKTOADMINNUMBER, "callback to admin defined number" },
  { CBCP_OPTION_CALLBACKTOANYLIST,     "callback to any list of numbers" },
  { 0, "" } 
};

/*
 * CBCP frame format
 */

typedef struct cbcp
{
  u_int8_t  code;
  u_int8_t  identifier;
  u_int16_t length;
} cbcp_t;

extern struct arg_t * my_args;

/*----------------------------------------------------------------------------
**
** dump_cbcp()
**
** Displays CBCP packets.
**
**----------------------------------------------------------------------------
*/

void dump_cbcp(packet_t *pkt)
{
  cbcp_t cbcp;

  /* Set the layer */
  set_layer(LAYER_DATALINK);

  /*
   * Get the header
   */

  if (get_packet_bytes((u_int8_t *) &cbcp, pkt, sizeof(cbcp_t)) == 0)
    return;

  /*
   * Conversion
   */

  cbcp.length = ntohs(cbcp.length);

  /*
   * Dump the header
   */

  if (my_args->m)
    {
      display_minimal_string("| CBCP ");
      display_minimal_string(map2str(cbcp_code_map, cbcp.code));
      display_minimal_string(" ");
    }
  else
    {
      display_header_banner("CBCP Header");
      display_strmap("Code", cbcp.code, cbcp_code_map);
      display("Identifier", &cbcp.identifier, 1, DISP_DEC);
      display("Length", (u_int8_t *) &cbcp.length, 2, DISP_DEC);
    }

  /*
   * Grab the payload data
   */

  if (cbcp.length > sizeof(cbcp_t))
    {
      switch(cbcp.code)
	{
	case CBCP_CODE_CBREQ:
	case CBCP_CODE_CBRES:
	case CBCP_CODE_CBACK:
	  {
	    u_int8_t bytes_read = 0;
	    u_int8_t type;
	    u_int8_t length;
	    u_int8_t value [CBCP_OPTION_LEN];
	    int comma = 0;
	    
	    /*
	     * CBCP options appear in Type-Length-Value format 
	     */
	    
	    while (bytes_read < cbcp.length - sizeof(cbcp_t))
	      {
		/*
		 * Get type
		 */
		
		if (get_packet_bytes((u_int8_t *) &type, pkt, 1)  == 0)
		  break;
		bytes_read ++;


		/*
		 * Get length
		 */
		
		if (get_packet_bytes((u_int8_t *) &length, pkt, 1)  == 0)
		  break;
		bytes_read ++;

		/* 
		 * In minimal mode we start all CBCP options with an open paren
		 * and put a comma before all but the first
		 */

		if (my_args->m)
		  {
		    if (comma)
		      display_minimal_string(", ");
		    else
		      display_minimal_string("(");
		  }
		comma = 1;

		/*
		 * Display the type and length
		 */
		
		if (my_args->m)
		  {
		    display_minimal_string(map2str(cbcp_option_map, type));
		  }
		else
		  {
		    display_strmap("Option", type, cbcp_option_map);
		    display("  Length", &length, 1, DISP_DEC);
		  }
		
		/*
		 * Figure out if we need to get a value
		 */

		if (length > 2)
		  {
		    if (get_packet_bytes((u_int8_t *) value, pkt, length-2)  
			== 0)
		      break;
		    bytes_read = bytes_read + length - 2;
		    
		    /*
		     * Display the value
		     */

		    if (my_args->m)
		      {
			display_minimal_string(" ");
			display_minimal(value, length-2, DISP_HEX);
		      }
		    else
		      {
			display("  Value", (u_int8_t *) value, length-2,
				DISP_HEX);
		      }
		  } /* if */
		
	      } /* while */
	    
	    /* 
	     * Insert end paren in minimal mode
	     */

	    if (my_args->m)
	      display_minimal_string(")");

	  }
	  break;
	  
	default:
	  break;
	}
    }

  /* dump the hex buffer */
  if (!my_args->l)
    hexbuffer_flush();
}
