/**
 * A client-side 802.1x implementation supporting EAP/TLS
 *
 * This code is released under both the GPL version 2 and BSD licenses.
 * Either license may be used.  The respective licenses are found below.
 *
 * Copyright (C) 2002 Bryan D. Payne & Nick L. Petroni Jr.
 * All Rights Reserved
 *
 * --- GPL Version 2 License ---
 * 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 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.
 *
 * --- BSD License ---
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  - All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *       This product includes software developed by the University of
 *       Maryland at College Park and its contributors.
 *  - Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*******************************************************************
 * The driver function for a Linux application layer EAPOL 
 * implementation
 * File: xsup_debug.c
 *
 * Authors: Chris.Hessing@utah.edu
 *
 * $Id: xsup_debug.c,v 1.14 2004/08/11 04:38:12 chessing Exp $
 * $Date: 2004/08/11 04:38:12 $
 * $Log: xsup_debug.c,v $
 * Revision 1.14  2004/08/11 04:38:12  chessing
 *
 * Fixes to the new debug code.  (DEBUG_NORMAL stuff wasn't be displayed when it should have.)
 *
 * Revision 1.13  2004/08/11 04:01:17  chessing
 *
 * The -d option now supports letter designations for each debug level, in addition to 0..7.  (However, both letters and numbers can't be used at the same time!)  Also changed some formatting for the SNMP scoreboard.
 *
 * Revision 1.12  2004/06/15 03:22:29  chessing
 *
 * XSupplicant Release 1.0
 *
 *
 *******************************************************************/

#include <stdio.h>
#include <stdarg.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "xsup_debug.h"
#include "config.h"

unsigned char debug_level = 0;     // By default just show the "normal" stuff.
int isdaemon = 0;
FILE *logfile = NULL;

/***************************************************
 *
 * Remove an old logfile, and create a new one.
 *
 ***************************************************/
void logfile_setup(char *logfilename)
{
  if (logfilename != NULL)
    {
      if (isdaemon != 2)
	{
	  unlink(logfilename);

	  logfile = fopen(logfilename, "w+");
	}
    }
}

/**************************************************
 *
 * Clean up our old logfile.
 *
 **************************************************/
void logfile_cleanup()
{
  if (logfile != NULL)
    {
      fclose(logfile);
    }
}

/*************************************************
 *
 * Set flags based on the numeric value that was passed in.
 *
 *************************************************/
void debug_set_flags(int new_flags)
{
  if (new_flags >= 1) debug_level |= DEBUG_CONFIG;
  if (new_flags >= 2) debug_level |= DEBUG_STATE;
  if (new_flags >= 3) debug_level |= DEBUG_AUTHTYPES;
  if (new_flags >= 4) debug_level |= DEBUG_INT;
  if (new_flags >= 5) debug_level |= DEBUG_SNMP;
  if (new_flags >= 6) debug_level |= DEBUG_EVERYTHING;
  if (new_flags >= 7) debug_level |= DEBUG_EXCESSIVE;
}

/*************************************************
 *
 * Set flags based on an ASCII string that was passed in.
 *
 *************************************************/
void debug_alpha_set_flags(char *new_flags)
{
  int i;

  debug_level = 0;

  for (i=0;i<strlen(new_flags);i++)
    {
      switch (new_flags[i])
	{
	case 'c':
	  debug_level |= DEBUG_CONFIG;
	  break;

	case 's':
	  debug_level |= DEBUG_STATE;
	  break;

	case 'a':
	  debug_level |= DEBUG_AUTHTYPES;
	  break;

	case 'i':
	  debug_level |= DEBUG_INT;
	  break;

	case 'n':
	  debug_level |= DEBUG_SNMP;
	  break;

	case 'e':
	  debug_level |= DEBUG_EVERYTHING;
	  break;

	case 'x':
	  debug_level |= DEBUG_EXCESSIVE;
	  break;

	case 'A':
	  debug_level |= 0xff;   // Set all flags.
	  break;
	}
    }
}

/*************************************************
 *
 * Depending on the value of fh, we will either print to the screen, or
 * a log file.
 *
 *************************************************/
void ufprintf(FILE *fh, char *instr)
{
  if ((isdaemon == 2) || (fh == NULL))
    {
      printf("%s", instr);
    } else {
      fprintf(fh, "%s", instr);
      fflush(fh);
    }
}

/*************************************************
 *
 * Set the debug level.  This is a global value, and shouldn't be set per
 * interface.
 *
 *************************************************/
void debug_setdaemon(int xdaemon)
{
  isdaemon = xdaemon;

  if (xdaemon == TRUE)
    {
      close(0);
      close(1);
      close(2);
    }
}

/*************************************************
 *
 * Get the debug level for debug situations where we can't use debug_printf
 * easily.
 *
 *************************************************/
int debug_getlevel()
{
  return debug_level;
}

/*************************************************
 *
 * Dump hex values, without the ascii versions.
 *
 *************************************************/
void debug_hex_printf(unsigned char level, u_char *hextodump, int size)
{
  int i;
  char chrstr[1024];

  if ((!(debug_level & level)) && (level != 0)) return;

  if (hextodump == NULL) return;

  for (i=0;i<size;i++)
    {
      bzero((char *)&chrstr, 1024);
      sprintf((char *)&chrstr, "%02X ", hextodump[i]);
      ufprintf(logfile, (char *)&chrstr);
    }
  ufprintf(logfile, "\n");
}

/*************************************************
 *
 * dump some hex values -- also
 * show the ascii version of the dump.
 *
 *************************************************/
void debug_hex_dump(unsigned char level, u_char *hextodump, int size)
{
  int i,x,s,t;
  char chrstr[1024];

  if ((!(debug_level & level)) && (level != 0)) return;

  if (hextodump == NULL) return;

  s=0;
  for (i=0;i<size;i++)
    {
      bzero((char *)&chrstr, 1024);
      sprintf((char *)&chrstr, "%02X ",hextodump[i]);
      ufprintf(logfile, (char *)&chrstr);

      if ((i>0) && (((i+1) % 8) == 0) && (((i+1) % 16) != 0)) 
	{
	  ufprintf(logfile, "- ");
	}

      if ((i>0) && (((i+1) % 16) == 0))
	{
	  if (i<17) 
	    {
	      t=i+1;
	    } else {
	      t=i+1;
	      s=s+1;
	    }

	  for (x=s;x<t;x++)
	    {
	      if ((hextodump[x] < 0x21) || (hextodump[x] > 0x7e))
		{
		  ufprintf(logfile, ".");
		} else {
		  bzero((char *)&chrstr, 1024);
		  sprintf((char *)&chrstr, "%c", hextodump[x]);
		  ufprintf(logfile, (char *)&chrstr);
		}
	    }

	  s = i;
	  ufprintf(logfile, "\n");
	}
    }

  if ((size % 16) > 0)
    {
      i = (16 - (size % 16));

      if (i>8) ufprintf(logfile, "  ");

      for (x=0;x<i;x++)
	{
	  ufprintf(logfile, "   ");
	}
      
      for (x=(s+1);x<size;x++)
	{
	  if ((hextodump[x] < 0x21) || (hextodump[x] > 0x7e))
	    {
	      ufprintf(logfile, ".");
	    } else {
	      bzero((char *)&chrstr, 1024);
	      sprintf((char *)&chrstr, "%c", hextodump[x]);
	      ufprintf(logfile, (char *)&chrstr);
	    }
	}
    }
  ufprintf(logfile, "\n");
}

/*************************************************
 *
 * Display some information.  But only if we are at a debug level that
 * should display it.
 *
 *************************************************/
void debug_printf(unsigned char level, char *fmt, ...)
{
  char dumpstr[2048], temp[2048];

  if (((level & debug_level) || (level == 0)) && (fmt != NULL))
    {
      va_list ap;
      va_start(ap, fmt);

      bzero((char *)&dumpstr, 2048);
      bzero((char *)&temp, 2048);

      // Print out a tag that identifies the type of debug message being used.
      switch (level)
	{
	case DEBUG_NORMAL:
	  break;   
	  
	case DEBUG_CONFIG:
	  strcpy((char *)&dumpstr, "[CONFIG] ");
	  break;

	case DEBUG_STATE:
	  strcpy((char *)&dumpstr, "[STATE] ");
	  break;

	case DEBUG_AUTHTYPES:
	  strcpy((char *)&dumpstr, "[AUTH TYPE] ");
	  break;
	  
	case DEBUG_INT:
	  strcpy((char *)&dumpstr, "[INT] ");
	  break;

	case DEBUG_EVERYTHING:
	  strcpy((char *)&dumpstr, "[ALL] ");
	  break;
	}

      vsnprintf((char *)&temp, 2048, fmt, ap);
      
      strcat((char *)&dumpstr, (char *)&temp);

      if ((isdaemon == 2) || (logfile == NULL))
	{
	  printf("%s", dumpstr);
	} else {
	  fprintf(logfile, "%s", dumpstr);
	  fflush(logfile);
	}      

      va_end(ap);
    }
}

/*************************************************
 *
 * Display some information.  But only if we are at a debug level that
 * should display it.
 *
 *************************************************/
void debug_printf_nl(unsigned char level, char *fmt, ...)
{
  if ((((level & debug_level) != 0x00) || (level == 0)) && (fmt != NULL))
    {
      va_list ap;
      va_start(ap, fmt);

      if ((isdaemon == 2) || (logfile == NULL))
	{ 
	  vprintf(fmt, ap);
	} else {
	  vfprintf(logfile, fmt, ap);
	  fflush(logfile);
	}
	  
      va_end(ap);
    }
}

