/**************************************************************************** 
** File: chap.c
**
** Author: Mike Borella
**
** Comments: CHAP decoding.
**
** $Id: chap.c,v 1.8 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 "chap.h"

/*
 * CHAP header
 */

typedef struct chap
{
  u_int8_t  code;
  u_int8_t  identifier;
  u_int16_t length;
} chap_t;

/*
 * CHAP codes
 */

#define CHAP_CODE_CHALLENGE   1
#define CHAP_CODE_RESPONSE    2
#define CHAP_CODE_SUCCESS     3
#define CHAP_CODE_FAILURE     4

/*
 * CHAP code map
 */

strmap_t chap_code_map[] =
{
  { CHAP_CODE_CHALLENGE,      "challenge" },
  { CHAP_CODE_RESPONSE,       "response" },
  { CHAP_CODE_SUCCESS,        "success" },
  { CHAP_CODE_FAILURE,        "failure" },
  { 0, "" }
};

extern struct arg_t * my_args;

/*----------------------------------------------------------------------------
**
** dump_chap()
**
** Dump CHAP headers and payload.
**
**----------------------------------------------------------------------------
*/

void dump_chap(packet_t *pkt)
{
  chap_t chap;
  
  /* Set the layer */
  set_layer(LAYER_DATALINK);

  /*
   * Get the header
   */
  
  if (get_packet_bytes((u_int8_t *) &chap, pkt, sizeof(chap_t)) == 0)
    return;
      
  /*
   * Conversion
   */
  
  chap.length = ntohs(chap.length);
      
  /*
   * Dump the header
   */
  
  if (my_args->m)
    {
      display_minimal_string("| CHAP ");
    }
  else
    {
      display_header_banner("CHAP Header");
      display_strmap("Code", chap.code, chap_code_map);
      display("Identifier", &chap.identifier, 1, DISP_DEC);
      display("Length", (u_int8_t *) &chap.length, 2, DISP_DEC);
    }

  /*
   * Get the CHAP payload 
   */

  switch(chap.code)
    {
    case CHAP_CODE_CHALLENGE:
    case CHAP_CODE_RESPONSE:
      {
	u_int8_t * value;
	u_int8_t   value_size;
	u_int8_t * name;
	u_int16_t  name_size;

	/* Get the value size */
	if (get_packet_bytes(&value_size, pkt, 1) == 0)
	  return;
	
	/* Get the value */
	value = (u_int8_t *) my_malloc(value_size);
	if (get_packet_bytes(value, pkt, value_size) == 0)
	  {
	    my_free(value);
	    return;
	  }

	/* Figure out the size of the name and get it */
	name_size = chap.length - sizeof(chap_t) - value_size - 1;
	name = (u_int8_t *) my_malloc(name_size+1);
	if (get_packet_bytes(name, pkt, name_size) == 0)
	  {
	    my_free(value);
	    my_free(name);
	    return;
	  }
	name[name_size] = '\0';

	/* display everything */
	if (my_args->m)
	  {
	    if (chap.code == CHAP_CODE_CHALLENGE)
	      display_minimal_string("challenge ");
	    if (chap.code == CHAP_CODE_RESPONSE)
	      display_minimal_string("response ");
	    display_minimal_string("(");
	    display_minimal(value, value_size, DISP_HEX);
	    display_minimal_string(", ");
	    display_minimal_string(name);
	    display_minimal_string(")");
	  }
	else
	  {
	    display("Value size", &value_size, 1, DISP_DEC);
	    display("Value", value, value_size, DISP_HEX_MULTILINE);
	    display_string("Name", name);
	  }
	
	/* free memory */
	my_free(value);
	my_free(name);
      }
      break;

    case CHAP_CODE_SUCCESS:
    case CHAP_CODE_FAILURE:
      {
	u_int8_t * message;
	u_int8_t   message_len;

	/* figure out length of the message and then grab it */
	message_len = chap.length - sizeof(chap_t);
	message = (u_int8_t *) my_malloc (message_len + 1);
	if (get_packet_bytes(message, pkt, message_len) == 0)
	  {
	    my_free(message);
	    return;
	  }
	message[message_len] = '\0';

	/* display it */
	if (my_args->m)
	  {
	    if (chap.code == CHAP_CODE_SUCCESS)
	      display_minimal_string("success ");
	    if (chap.code == CHAP_CODE_FAILURE)
	      display_minimal_string("failure ");
	    display_minimal_string("(");
	    display_minimal_string(message);
	    display_minimal_string(")");
	  }
	else
	  {
	    display_string("Message", message);
	  }

	/* free memory */
	my_free(message);
      }
      break;

    }

}
