/**************************************************************************** 
** File: ppp.c
**
** Author: Mike Borella
**
** Comments: PPP module.
**
** $Id: ppp.c,v 1.15 2006/11/21 07:47:34 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 "ppp.h"
#include "payload.h"

/*
 * PPP protocol types
 */

#define PPP_PROTOCOL_IP               0x0021
#define PPP_PROTOCOL_IPCP             0x8021
#define PPP_PROTOCOL_IPXCP            0x802b
#define PPP_PROTOCOL_LCP              0xc021
#define PPP_PROTOCOL_PAP              0xc023
#define PPP_PROTOCOL_CHAP             0xc223
#define PPP_PROTOCOL_CCP              0x80fd
#define PPP_PROTOCOL_COMPRESSED_FRAME 0x00fd
#define PPP_PROTOCOL_CBCP             0xc029
#define PPP_PROTOCOL_VJUNCOMPRESSED   0x002f

/*
 * PPP protocol map
 */

strmap_t ppp_protocol_map[] =
{
  { PPP_PROTOCOL_IP,               "IP" },
  { PPP_PROTOCOL_IPCP,             "IPCP" },
  { PPP_PROTOCOL_IPXCP,            "IPXCP" },
  { PPP_PROTOCOL_LCP,              "LCP" },
  { PPP_PROTOCOL_PAP,              "PAP" },
  { PPP_PROTOCOL_CHAP,             "CHAP" },
  { PPP_PROTOCOL_CCP,              "CCP" },
  { PPP_PROTOCOL_COMPRESSED_FRAME, "compressed frame" },
  { PPP_PROTOCOL_CBCP,             "CBCP" },
  { PPP_PROTOCOL_VJUNCOMPRESSED,   "VJ uncompressed" },
  { 0, "" }
};

/*
 * PPP frame format
 */

typedef struct ppp
{
  u_int8_t  address;
  u_int8_t  control;
  u_int16_t protocol;
} ppp_t;

extern struct arg_t * my_args;

/*----------------------------------------------------------------------------
**
** dump_ppp()
**
** Displays PPP packets.
**
**----------------------------------------------------------------------------
*/

void dump_ppp(packet_t * pkt)
{
  ppp_t ppp;
  u_int16_t protocol;
  u_int16_t two_bytes;

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

  /*
   * Look at the first 2 bytes to see if address/control compression is 
   * set or not.
   */

  if (look_packet_bytes((u_int8_t *) &two_bytes, pkt, sizeof(u_int16_t)) == 0)
    return;

  /*
   * If the result is 0xff03, then process the packet normally.  Anything else
   * and we'll assume that compression is being used and that these two
   * bytes are a protocol type.
   */

  if (ntohs(two_bytes) == 0xff03)
    {
      
      /*
       * Get the header
       */
      
      if (get_packet_bytes((u_int8_t *) &ppp, pkt, sizeof(ppp_t)) == 0)
	return;
      
      /*
       * Conversion
       */
      
      protocol = ntohs(ppp.protocol);
      
      /*
       * Dump the header
       */
      
      if (my_args->m)
	{
	  display_minimal_string("| PPP ");
	}
      else
	{
	  display_header_banner("PPP Header");
	  display("Address", &ppp.address, 1, DISP_HEX);
	  display("Control", &ppp.control, 1, DISP_HEX);
	  display_strmap_hex("Protocol type", protocol, 
			     ppp_protocol_map);
	}
    }
  else
    {
      u_int8_t temp_protocol;

      /*
       * Get the protocol.  If the protocol is "0x21" then it is only
       * one byte and compression is suppressing the "0x00" - i.e., its a 
       * damn IP packet.  If is one byte and is "0xfd", its a compressed
       * PPP frame and therefore we cannot decode it.  
       *
       * This is a quick fix = it could be made more robust
       */

      if (look_packet_bytes((u_int8_t *) &temp_protocol, pkt, 1) == 0)
	return;

      if (temp_protocol == PPP_PROTOCOL_IP || 
	  temp_protocol == PPP_PROTOCOL_COMPRESSED_FRAME ||
	  temp_protocol == PPP_PROTOCOL_VJUNCOMPRESSED)
	{
	  if (get_packet_bytes((u_int8_t *) &temp_protocol, pkt, 1) == 0)
	    return;
	  protocol = temp_protocol;
	}
      else
	{
	  if (get_packet_bytes((u_int8_t *) &protocol, pkt, 2) == 0)
	    return;
	  protocol = ntohs(protocol);
	}
      
      /*
       * Dump the header
       */
      
      if (my_args->m)
	{
	  display_minimal_string("PPP ");
	}
      else
	{
	  display_header_banner("PPP Header");
	  display_strmap_hex("Protocol type", protocol, ppp_protocol_map);
	}
    } /* else */

  /* dump the hex buffer */
  hexbuffer_flush();
  
  /*
   * Determine the next layer protocol
   */

  switch(protocol)
    {
    case PPP_PROTOCOL_IP:
    case PPP_PROTOCOL_VJUNCOMPRESSED:
      dump_ip(pkt);
      break;

    case PPP_PROTOCOL_LCP:
      dump_lcp(pkt);
      break;

    case PPP_PROTOCOL_CHAP:
      dump_chap(pkt);
      break;

    case PPP_PROTOCOL_IPCP:
      dump_ipcp(pkt);
      break;

    case PPP_PROTOCOL_CBCP:
      dump_cbcp(pkt);
      break;

    case PPP_PROTOCOL_CCP:
      dump_ccp(pkt);
      break;

    case PPP_PROTOCOL_COMPRESSED_FRAME:
      /* If MPPC was given as the default CCP algorithm, dump it */
      if (!strcmp(my_args->C, "mppc"))
	dump_mppc(pkt);
      break;

    default:
      if (!my_args->m)
	dump_payload(pkt);
      break;
    }

  /* Should be able to display the PPP trailer here.... */
  
}

/*----------------------------------------------------------------------------
**
** dump_ppp_hdlc()
**
** Scans over the HDLC based PPP frame and translates it into a regular 
** frame that PPP can handle.  Some systems however will fragment the 
** HDLC-ized packets.  We would need memory and state to handle these
** cases, and we're not going to implement that right now.
**
**----------------------------------------------------------------------------
*/

void dump_ppp_hdlc(packet_t * pkt)
{
  packet_t   hdlc_frame;
  int        length;
  char *     holder_before;
  char *     holder_after;
  int        ptr;
  int        i;
  u_int8_t   first_byte;

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

  /* 
   * Look at the first byte.  Bail out if it is not 0x7e.  We may be able
   * to fix this later
   */
  
  if (look_packet_bytes(&first_byte, pkt, 1) == 0)
    return;
  else
    {
      if (first_byte != 0x7e)
	{
	  dump_ppp(pkt);
	  return;
	}
    }
     
  /* Determine the length of the frame */
  length = get_packet_apparentbytesleft(pkt);

  /* Allocate space for this */
  holder_before = my_malloc(length);
  holder_after = my_malloc(length);

  /* Get the packet */
  if (get_packet_bytes(holder_before, pkt, length) == 0)
    return;

  /* Run through the before array moving fixed characters to the after array */
  ptr = 0;
  for (i=0; i<length; i++)
    {
      /* Copy normal bytes */
      if (holder_before[i] != 0x7e && holder_before[i] != 0x7d)
	{
	  holder_after[ptr] = holder_before[i];
	  ptr++;
	  continue;
	}
      
      /* Unescape escaped bytes */
      if (holder_before[i] == 0x7d)
	{
	  if (i+1 >= length)
	    break;
	  i++;
	  holder_after[ptr] = holder_before[i] ^ 0x20;
	  ptr++;
	}
    
      /* Bail out when we see a 0x7e that is not at the beginning */
      if (i>0 && holder_before[i] == 0x7e)
        break;
    }
  
  /* Delete the before data, we no longer need it */
  my_free (holder_before);
 
  /* Create a fake packet to fake out PPP and other modules */
  hdlc_frame.contents = holder_after;
  hdlc_frame.current = &hdlc_frame.contents[0];
  hdlc_frame.end = &hdlc_frame.contents[0] + ptr;
  hdlc_frame.apparent_end = hdlc_frame.end;
  hdlc_frame.media_length = ptr;

  /* Kill the hex buffer here in order to eliminate duplicate processing */
  hexbuffer_kill();

  /* Send this packet through the processing */
  dump_ppp(&hdlc_frame);

  /* Free the memory for the fake packet */
  my_free(hdlc_frame.contents);
}
