/**************************************************************************** 
** File: ccp.c
**
** Author: Mike Borella
**
** Comments: CCP module.
**
** $Id: ccp.c,v 1.2 2007/06/25 11:52:45 farooq-i-azam 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 "ccp.h"

#define CCP_OPTION_LEN 64

/*
 * CCP codes
 */

#define CCP_CODE_CONFREQ        1
#define CCP_CODE_CONFACK        2
#define CCP_CODE_CONFNAK        3
#define CCP_CODE_CONFREJ        4
#define CCP_CODE_TERMREQ        5
#define CCP_CODE_TERMACK        6
#define CCP_CODE_CODEREJ        7
#define CCP_CODE_RESETREQ       14
#define CCP_CODE_RESETREP       15

/*
 * CCP code map
 */

strmap_t ccp_code_map[] =
{
  { CCP_CODE_CONFREQ,        "Configure-Request" },
  { CCP_CODE_CONFACK,        "Configure-Ack" },
  { CCP_CODE_CONFNAK,        "Configure-Nak" },
  { CCP_CODE_CONFREJ,        "Configure-Reject" },
  { CCP_CODE_TERMREQ,        "Terminate-Request" }, 
  { CCP_CODE_TERMACK,        "Terminate-Ack" }, 
  { CCP_CODE_CODEREJ,        "Code-Rej" },
  { CCP_CODE_RESETREQ,       "Reset-Request" },
  { CCP_CODE_RESETREP,       "Reset-Replay" },
  { 0, ""}
};

/*
 * CCP options
 */

#define CCP_OPTION_OUI                   0
#define CCP_OPTION_PREDICTORTYPE1        1
#define CCP_OPTION_PREDICTORTYPE2        2
#define CCP_OPTION_PUDDLEJUMPER          3
#define CCP_OPTION_HPPPC                 16
#define CCP_OPTION_STACLZS               17
#define CCP_OPTION_MSPPC                 18
#define CCP_OPTION_GANDALFFZA            19
#define CCP_OPTION_V42BIS                20
#define CCP_OPTION_BSDCOMPRESS           21
#define CCP_OPTION_LZSDCP                23
#define CCP_OPTION_MVRCAMAGNALINK        24
#define CCP_OPTION_DEFLATE               26

/*
 * CCP option map
 */

strmap_t ccp_option_map[] = 
{
  { CCP_OPTION_OUI,                   "OUI" },
  { CCP_OPTION_PREDICTORTYPE1,        "predictor type 1" },
  { CCP_OPTION_PREDICTORTYPE2,        "predictor type 2" },
  { CCP_OPTION_PUDDLEJUMPER,          "puddlejumper" },
  { CCP_OPTION_HPPPC,                 "Hewlett-Packard PPC" },
  { CCP_OPTION_STACLZS,               "STAC LZS" },
  { CCP_OPTION_MSPPC,                 "Microsoft PPC" },
  { CCP_OPTION_GANDALFFZA,            "Gandalf FZA" },
  { CCP_OPTION_V42BIS,                "V42BIS" },
  { CCP_OPTION_BSDCOMPRESS,           "BSD compress" },
  { CCP_OPTION_LZSDCP,                "LZS DCP" },
  { CCP_OPTION_MVRCAMAGNALINK,        "MVRCA/Magnalink" },
  { CCP_OPTION_DEFLATE,               "deflate" },
  { 0, "" } 
};

/*
 * CCP frame format
 */

typedef struct ccp
{
  u_int8_t  code;
  u_int8_t  identifier;
  u_int16_t length;
} ccp_t;

extern struct arg_t * my_args;

/*----------------------------------------------------------------------------
**
** dump_ccp()
**
** Displays CCP packets.
**
**----------------------------------------------------------------------------
*/

void dump_ccp(packet_t *pkt)
{
  ccp_t ccp;

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

  /*
   * Get the header
   */

  if (get_packet_bytes((u_int8_t *) &ccp, pkt, sizeof(ccp_t)) == 0)
    return;

  /*
   * Conversion
   */

  ccp.length = ntohs(ccp.length);

  /*
   * Dump the header
   */

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

  /*
   * Grab the payload data
   */

  if (ccp.length > sizeof(ccp_t))
    {
      switch(ccp.code)
	{
	case CCP_CODE_CONFREQ:
	case CCP_CODE_CONFACK:
	case CCP_CODE_CONFNAK:
	case CCP_CODE_CONFREJ:
	  {
	    u_int8_t bytes_read = 0;
	    u_int8_t type;
	    u_int8_t length;
	    u_int8_t value [CCP_OPTION_LEN];
	    int comma = 0;
	    
	    /*
	     * CCP options appear in Type-Length-Value format 
	     */
	    
	    while (bytes_read < ccp.length - sizeof(ccp_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 CCP 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(ccp_option_map, type));
		  }
		else
		  {
		    display_strmap("Option", type, ccp_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;
	  
	case CCP_CODE_TERMREQ:
	case CCP_CODE_TERMACK:
	  {
	    u_int8_t * data;
	    u_int8_t data_len;

	    /*
	     * Read the data, if any
	     */

	    data_len = ccp.length - sizeof(ccp_t);
	    if (data_len > 0)
	      {
		/* allocate memory for the data */
		data = (u_int8_t *) my_malloc(data_len);

		/* grab the data field */
		if (get_packet_bytes(data, pkt, data_len) == 0)
		  { 
		    my_free(data);
		    return;
		  }
	
		/* dump it out as hex, but don't dump in minimal mode */
		if (!my_args->m)
		  {
		    display("Data", data, data_len, DISP_HEX);
		  }
		
		/* free that darn mem! */
		my_free(data);
	      }
	  }
	  break;

	case CCP_CODE_CODEREJ:
	  dump_ccp(pkt);
	  break;

	case CCP_CODE_RESETREQ:
	case CCP_CODE_RESETREP:

	  {
	    u_int8_t	* data;
	    u_int8_t	data_len;

	    data_len = ccp.length - sizeof(ccp_t);    

	    if(data_len > 0)
	      {	
	   	data = (u_int8_t *) my_malloc(data_len);
  		if (get_packet_bytes(data, pkt, data_len) == 0)
		  {
		    my_free(data);
		    return;
	  	  }	

		if(!my_args->m)
		  {
		    display("Data", data, data_len, DISP_HEX);
		  }
		my_free(data);
	      }	
	  }
	  break;

	default:
	  break;
	}
    }

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