/**************************************************************************** 
** File: pptp.c
**
** Author: Mike Borella
**
** Comments: Dump PPTP information
**
** $Id: pptp.c,v 1.12 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 "global.h"
#include "ip_protocols.h"
#include "pptp.h"

extern struct arg_t *my_args;

/*
 * PPTP message type map
 */

strmap_t pptp_msgtype_map[] =
{
  { PPTP_MSGTYPE_CONTROL,    "control" },
  { PPTP_MSGTYPE_MANAGEMENT, "management" }
};

#define PPTP_MSGTYPE_CONTROL    1
#define PPTP_MSGTYPE_MANAGEMENT 2

/*
 * PPTP control message types
 */

strmap_t pptp_cntlmsgtype_map[] =
{
  { PPTP_CNTLMSGTYPE_STARTCCREQ,    "Start-Control-Connection-Request" },
  { PPTP_CNTLMSGTYPE_STARTCCREP,    "Start-Control-Connection-Reply" },
  { PPTP_CNTLMSGTYPE_STOPCCREQ,     "Stop-Control-Connection-Request" },
  { PPTP_CNTLMSGTYPE_STOPCCREP,     "Stop-Control-Connection-Reply" },
  { PPTP_CNTLMSGTYPE_ECHOREQ,       "Echo-Request" },
  { PPTP_CNTLMSGTYPE_ECHOREP,       "Echo-Reply" },
  { PPTP_CNTLMSGTYPE_OUTGOINGREQ,   "Outgoing-Call-Request" },
  { PPTP_CNTLMSGTYPE_OUTGOINGREP,   "Outgoing-Call-Reply" },
  { PPTP_CNTLMSGTYPE_INCOMINGREQ,   "Incoming-Call-Request" },
  { PPTP_CNTLMSGTYPE_INCOMINGREP,   "Incoming-Call-Reply" },
  { PPTP_CNTLMSGTYPE_INCOMINGCONN,  "Incoming-Call-Connected" },
  { PPTP_CNTLMSGTYPE_CALLCLEARREQ,  "Call-Clear-Request" },
  { PPTP_CNTLMSGTYPE_CALLDISCNTFY,  "Call-Disconnect-Notify" },
  { PPTP_CNTLMSGTYPE_WANERRORNTFY,  "WAN-Error-Notify" },
  { PPTP_CNTLMSGTYPE_SETLINKINFO,   "Set-Link-Info" },
  { 0, "" }
};

/*----------------------------------------------------------------------------
**
** dump_pptp_startccreq()
**
** Parse PPTP Start-Control-Connection-Request
**
**----------------------------------------------------------------------------
*/

void dump_pptp_startccreq(packet_t *pkt)
{
  pptp_startccreq_t hdr;

  /*
   * Get the header
   */
  
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(pptp_startccreq_t))
      == 0)
    return;

  /* 
   * Conversions
   */

  hdr.version = ntohs(hdr.version);
  hdr.reserved1 = 
    ntohs(hdr.reserved1);
  hdr.framing_cap = ntohl(hdr.framing_cap);
  hdr.bearer_cap = ntohl(hdr.bearer_cap);
  hdr.max_channels = ntohs(hdr.max_channels);
  hdr.firmware_rev = ntohs(hdr.firmware_rev);
  hdr.hostname[PPTP_HOSTNAME_LEN-1] = '\0';
  hdr.vendor[PPTP_VENDOR_LEN-1] = '\0';

  /*
   * Display
   */

  if (my_args->m)
    {
      display_minimal_string(hdr.hostname);
      display_minimal_string(" ");
    }
  else 
    {
      display("Version", (u_int8_t *) &hdr.version, 2, DISP_DEC);
      display("Reserved", (u_int8_t *) &hdr.reserved1, 2, DISP_DEC);
      display("Framing capabilities", (u_int8_t *) &hdr.framing_cap, 4, 
	      DISP_DEC);
      display("Bearer capabilities", (u_int8_t *) &hdr.bearer_cap, 4, 
	      DISP_DEC);
      display("Max channels", (u_int8_t *) &hdr.max_channels, 2, DISP_DEC);
      display("Firmware revision", (u_int8_t *) &hdr.firmware_rev, 2, 
	      DISP_DEC);
      display_string("Host name", hdr.hostname);
      display_string("Vendor", hdr.vendor);
    }
}

/*----------------------------------------------------------------------------
**
** dump_pptp_startccrep()
**
** Parse PPTP Start-Control-Connection-Reply
**
**----------------------------------------------------------------------------
*/

void dump_pptp_startccrep(packet_t *pkt)
{
  pptp_startccrep_t hdr;

  /*
   * Get the header
   */
  
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(pptp_startccrep_t))
      == 0)
    return;

  /* 
   * Conversions
   */

  hdr.version = ntohs(hdr.version);
  hdr.framing_cap = ntohl(hdr.framing_cap);
  hdr.bearer_cap = ntohl(hdr.bearer_cap);
  hdr.max_channels = ntohs(hdr.max_channels);
  hdr.firmware_rev = ntohs(hdr.firmware_rev);
  hdr.hostname[PPTP_HOSTNAME_LEN-1] = '\0';
  hdr.vendor[PPTP_VENDOR_LEN-1] = '\0';

  /*
   * Display
   */

  if (my_args->m)
    {
      display_minimal_string(hdr.hostname);
      display_minimal_string(" ");
    }
  else 
    {
      display("Version", (u_int8_t *) &hdr.version, 2, DISP_DEC);
      display("Result code", (u_int8_t *) &hdr.result_code, 1, DISP_DEC);
      display("Error code", (u_int8_t *) &hdr.error_code, 1, DISP_DEC);
      display("Framing capabilities", (u_int8_t *) &hdr.framing_cap, 4, 
	      DISP_DEC);
      display("Bearer capabilities", (u_int8_t *) &hdr.bearer_cap, 4, 
	      DISP_DEC);
      display("Max channels", (u_int8_t *) &hdr.max_channels, 2, DISP_DEC);
      display("Firmware revision", (u_int8_t *) &hdr.firmware_rev, 2, 
	      DISP_DEC);
      display_string("Host name", hdr.hostname);
      display_string("Vendor", hdr.vendor);
    }
}

/*----------------------------------------------------------------------------
**
** dump_pptp_outgoingreq()
**
** Parse PPTP Outgoing-Call-Request
**
**----------------------------------------------------------------------------
*/

void dump_pptp_outgoingreq(packet_t *pkt)
{
  pptp_outgoingreq_t hdr;

  /*
   * Get the header
   */
  
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(pptp_outgoingreq_t))
      == 0)
    return;

  /* 
   * Conversions
   */

  hdr.call_id = ntohs(hdr.call_id);
  hdr.call_sn = ntohs(hdr.call_sn);
  hdr.min_bps = ntohl(hdr.min_bps);
  hdr.max_bps = ntohl(hdr.max_bps);
  hdr.bearer_type = ntohl(hdr.bearer_type);
  hdr.framing_type = ntohl(hdr.framing_type);
  hdr.recv_window_size = ntohs(hdr.recv_window_size);
  hdr.packet_proc_delay = ntohs(hdr.packet_proc_delay);
  hdr.phone_num_len = ntohs(hdr.phone_num_len);
  hdr.reserved1 = ntohs(hdr.reserved1);
  hdr.phone_num[PPTP_PHONENUM_LEN-1] = '\0';
  hdr.phone_num[hdr.phone_num_len] = '\0';
  hdr.subaddress[PPTP_SUBADDRESS_LEN-1] = '\0';

  /*
   * Display
   */

  if (my_args->m)
    {
      display_minimal_string(hdr.phone_num);
      display_minimal_string(" ");
    }
  else 
    {
      display("Call ID", (u_int8_t *) &hdr.call_id, 2, DISP_DEC);
      display("Call serial number", (u_int8_t *) &hdr.call_sn, 2, DISP_DEC);
      display("Minimum BPS", (u_int8_t *) &hdr.min_bps, 4, DISP_DEC);
      display("Maximum BPS", (u_int8_t *) &hdr.max_bps, 4, DISP_DEC);
      display("Bearer type", (u_int8_t *) &hdr.bearer_type, 4, DISP_DEC);
      display("Framing type", (u_int8_t *) &hdr.framing_type, 4, DISP_DEC);
      display("Receive window size", (u_int8_t *) &hdr.recv_window_size, 2, 
	      DISP_DEC);
      display("Packet processing delay", (u_int8_t *) &hdr.packet_proc_delay, 
	      2, DISP_DEC);
      display("Phone number length", (u_int8_t *) &hdr.phone_num_len, 2, 
	      DISP_DEC);
      display("Reserved", (u_int8_t *) &hdr.reserved1, 2, DISP_DEC);
      display_string("Phone number", hdr.phone_num);
      display_string("Subaddress", hdr.subaddress);
    }
}

/*----------------------------------------------------------------------------
**
** dump_pptp_outgoingrep()
**
** Parse PPTP Outgoing-Call-Reply
**
**----------------------------------------------------------------------------
*/

void dump_pptp_outgoingrep(packet_t *pkt)
{
  pptp_outgoingrep_t hdr;

  /*
   * Get the header
   */
  
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(pptp_outgoingrep_t))
      == 0)
    return;

  /* 
   * Conversions
   */

  hdr.call_id = ntohs(hdr.call_id);
  hdr.peer_call_id = ntohs(hdr.peer_call_id);
  hdr.cause_code = ntohs(hdr.cause_code);
  hdr.connect_speed = ntohl(hdr.connect_speed);
  hdr.recv_window_size = ntohs(hdr.recv_window_size);
  hdr.packet_proc_delay = ntohs(hdr.packet_proc_delay);
  hdr.phy_channel_id = ntohl(hdr.phy_channel_id);

  /*
   * Display
   */

  if (my_args->m)
    {
      /* XXX fix me ! */
      display_minimal_string(" ");
    }
  else 
    {
      display("Call ID", (u_int8_t *) &hdr.call_id, 2, DISP_DEC);
      display("Peer call ID", (u_int8_t *) &hdr.peer_call_id, 2, DISP_DEC);
      display("Result code", (u_int8_t *) &hdr.result_code, 1, DISP_DEC);
      display("Error code", (u_int8_t *) &hdr.error_code, 1, DISP_DEC);
      display("Cause code", (u_int8_t *) &hdr.cause_code, 2, DISP_DEC);
      display("Connect speed", (u_int8_t *) &hdr.connect_speed, 4, DISP_DEC);
      display("Receive window size", (u_int8_t *) &hdr.recv_window_size, 2, 
	      DISP_DEC);
      display("Packet processing delay", (u_int8_t *) &hdr.packet_proc_delay, 
	      2, DISP_DEC);
      display("Physical channel ID", (u_int8_t *) &hdr.phy_channel_id, 4, 
	      DISP_DEC);
    }
}

/*----------------------------------------------------------------------------
**
** dump_pptp_setlinkinfo()
**
** Parse PPTP Set-Link-Info
**
**----------------------------------------------------------------------------
*/

void dump_pptp_setlinkinfo(packet_t *pkt)
{
  pptp_setlinkinfo_t hdr;

  /*
   * Get the header
   */
  
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(pptp_setlinkinfo_t))
      == 0)
    return;

  /* 
   * Conversions
   */

  hdr.peer_call_id = ntohs(hdr.peer_call_id);
  hdr.reserved1 = ntohs(hdr.reserved1);
  hdr.send_accm = ntohl(hdr.send_accm);
  hdr.receive_accm = ntohl(hdr.receive_accm);

  /*
   * Display
   */

  if (my_args->m)
    {
      /* XXX fix me ! */
      display_minimal_string(" ");
    }
  else 
    {
      display("Peer call ID", (u_int8_t *) &hdr.peer_call_id, 2, DISP_DEC);
      display("Reserved", (u_int8_t *) &hdr.reserved1, 2, DISP_DEC);
      display("Send ACCM", (u_int8_t *) &hdr.send_accm, 4, DISP_HEX);
      display("Receive ACCM", (u_int8_t *) &hdr.receive_accm, 4, DISP_HEX);
    }
}


/*----------------------------------------------------------------------------
**
** dump_pptp_callclearreq()
**
** Parse PPTP Call-Clear-Request
**
**----------------------------------------------------------------------------
*/

void dump_pptp_callclearreq(packet_t *pkt)
{
  pptp_callclearreq_t hdr;

  /*
   * Get the header
   */
  
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(pptp_callclearreq_t))
      == 0)
    return;

  /* 
   * Conversions
   */

  hdr.call_id = ntohs(hdr.call_id);
  hdr.reserved = ntohs(hdr.reserved);

  /*
   * Display
   */

  if (my_args->m)
    {
      /* XXX fix me ! */
      display_minimal_string(" ");
    }
  else 
    {
      display("Call ID", (u_int8_t *) &hdr.call_id, 2, DISP_DEC);
      display("Reserved", (u_int8_t *) &hdr.reserved, 2, DISP_DEC);
    }
}

/*----------------------------------------------------------------------------
**
** dump_pptp_calldiscntfy()
**
** Parse PPTP Call-Disconnect-Notify
**
**----------------------------------------------------------------------------
*/

void dump_pptp_calldiscntfy(packet_t *pkt)
{
  pptp_calldiscntfy_t hdr;

  /*
   * Get the header
   */
  
  if (get_packet_bytes((u_int8_t *) &hdr, pkt, sizeof(pptp_calldiscntfy_t))
      == 0)
    return;

  /* 
   * Conversions
   */

  hdr.call_id = ntohs(hdr.call_id);
  hdr.cause_code = ntohs(hdr.cause_code);
  hdr.reserved = ntohs(hdr.reserved);
  
  /*
   * Display
   */

  if (my_args->m)
    {
      /* XXX fix me ! */
      display_minimal_string(" ");
    }
  else 
    {
      display("Call ID", (u_int8_t *) &hdr.call_id, 2, DISP_DEC);
      display("Result code", (u_int8_t *) &hdr.result_code, 1, DISP_DEC);
      display("Error code", (u_int8_t *) &hdr.error_code, 1, DISP_DEC);
      display("Cause code", (u_int8_t *) &hdr.cause_code, 2, DISP_DEC);
      display("Reserved", (u_int8_t *) &hdr.reserved, 2, DISP_DEC);
      display("Call statistics", (u_int8_t *) &hdr.call_stats, 128, 
	      DISP_HEX_MULTILINE);
    }
}

/*----------------------------------------------------------------------------
**
** dump_pptp()
**
** Parse PPTP header and dump fields
**
**----------------------------------------------------------------------------
*/

void dump_pptp(packet_t *pkt)
{
  pptp_generic_header_t pptp;
  /* char holder[64]; */

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

  /*
   * Get the generic header
   */

  if (get_packet_bytes((u_int8_t *) &pptp, pkt, sizeof(pptp_generic_header_t))
      == 0)
    return;

  /* 
   * Conversions
   */

  pptp.length = ntohs(pptp.length);
  pptp.pptp_msg_type = ntohs(pptp.pptp_msg_type);
  pptp.cookie = ntohl(pptp.cookie);
  pptp.control_msg_type = ntohs(pptp.control_msg_type);
  pptp.reserved0 = ntohs(pptp.reserved0);

  /*
   * Minimal mode
   */

  if (my_args->m)
    {
      display_minimal_string("| PPTP ");
      display_minimal_string(map2str(pptp_msgtype_map, 
				     pptp.pptp_msg_type));
      display_minimal_string(" ");
      display_minimal_string(map2str(pptp_cntlmsgtype_map, 
				     pptp.control_msg_type));
      display_minimal_string(" ");
    }
  else 
    {  
      /* announcement */
      display_header_banner("PPTP Header");
      
      /* dump common fields */
      display("Length", (u_int8_t *) &pptp.length, 2, DISP_DEC);
      display_strmap("PPTP message type", pptp.pptp_msg_type, 
		     pptp_msgtype_map);
      display("Magic cookie", (u_int8_t *) &pptp.cookie, 4, DISP_HEX);
      display_strmap("Control message type", pptp.control_msg_type,
		     pptp_cntlmsgtype_map);
      display("Reserved", (u_int8_t *) &pptp.reserved0, 2, DISP_HEX);
    }

  /*
   * Now decide what to do based on the control message type.
   * Note - management messages are currently undefined so we shouldn't
   * have to worry about them for a while.
   */

  if (pptp.pptp_msg_type == PPTP_MSGTYPE_CONTROL)
    {
      switch(pptp.control_msg_type)
	{
	case PPTP_CNTLMSGTYPE_STARTCCREQ:
	  dump_pptp_startccreq(pkt);
	  break;

	case PPTP_CNTLMSGTYPE_STARTCCREP:
	  dump_pptp_startccrep(pkt);
	  break;

	case PPTP_CNTLMSGTYPE_OUTGOINGREQ:
	  dump_pptp_outgoingreq(pkt);
	  break;

	case PPTP_CNTLMSGTYPE_OUTGOINGREP:
	  dump_pptp_outgoingrep(pkt);
	  break;
	
	case PPTP_CNTLMSGTYPE_SETLINKINFO:
	  dump_pptp_setlinkinfo(pkt);
	  break;

	case PPTP_CNTLMSGTYPE_CALLCLEARREQ:
	  dump_pptp_callclearreq(pkt);
	  break;

	case PPTP_CNTLMSGTYPE_CALLDISCNTFY:
	  dump_pptp_calldiscntfy(pkt);
	  break;
	}
    }

  /* dump the hex buffer */
  hexbuffer_flush();

  return;
}
