/* SAMHAIN file system integrity testing                                   */
/* Copyright (C) 1999, 2000 Rainer Wichmann                                */
/*                                                                         */
/*  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., 675 Mass Ave, Cambridge, MA 02139, USA.              */

#include "config_xor.h"


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>


#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif

#define SH_REAL_SET

#include "samhain.h"
#include "sh_error.h"
#include "sh_utils.h"
#include "sh_mem.h"

extern int safe_logger (int signal, int method, char * details);

#undef  FIL__
#define FIL__  _("sh_mem.c")
static int eblock = 0;

#ifdef MEM_DEBUG

#define CHECKBYTE 0x7F

/* Memory alignment; should be 16 bytes on 64 bit machines.
 * -> 32 bytes overhead/allocation 
 */
#define SH_MEMMULT 16


typedef struct mem_struct {
  struct mem_struct *next;        /* link to next struct    */
  char * real_address;            /* address assigned       */
  char * address;                 /* address returned       */
  unsigned long size;             /* size allocated         */
  char file[20];                  /* Allocation file name   */
  int line;                       /* Allocation line number */
} memlist_t;

memlist_t   * memlist       = NULL;

int           Free_Count  = 0, Alloc_Count = 0;
int           Now_Alloc_Count = 0, Max_Alloc_Count = 0;
unsigned long Mem_Current = 0, Mem_Max = 0;

#if 0
#define MEM_DETAILS 
#endif

#ifdef MEM_DETAILS
int           max_upto_032 = 0;
int           max_upto_064 = 0;
int           max_upto_128 = 0;
int           max_upto_256 = 0;
int           max_upto_512 = 0;
int           max_upto_1024 = 0;
int           max_upto_4096 = 0;
int           max_upto_inf = 0;

int           now_upto_032 = 0;
int           now_upto_064 = 0;
int           now_upto_128 = 0;
int           now_upto_256 = 0;
int           now_upto_512 = 0;
int           now_upto_1024 = 0;
int           now_upto_4096 = 0;
int           now_upto_inf = 0;

int           tot_upto_032 = 0;
int           tot_upto_064 = 0;
int           tot_upto_128 = 0;
int           tot_upto_256 = 0;
int           tot_upto_512 = 0;
int           tot_upto_1024 = 0;
int           tot_upto_4096 = 0;
int           tot_upto_inf = 0;
#endif

#ifdef MEM_LOG
void sh_mem_dump ()
{
  memlist_t   * this = memlist;


  FILE * fd = fopen(MEM_LOG, "w");

  while (this != NULL)
    {
      fprintf (fd, "%20s %5d %ld\n",  this->file, this->line, this->size);
      this = this->next;
    }
  fclose(fd);
  return;
}
#else
void sh_mem_dump ()
{
  return;
}
#endif

void sh_mem_stat ()
{
  memlist_t   * this;


  SL_ENTER(_("sh_mem_stat"));

  if (Alloc_Count == Free_Count) 
    {
      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_MSTAMP,
		       Mem_Max, Mem_Current);
      SL_RET0(_("sh_mem_stat"));
    }
    
  sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, 0, MSG_MSTAMP2,
		   Alloc_Count, Free_Count, Max_Alloc_Count);
  sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, 0, MSG_MSTAMP,
		   Mem_Max, Mem_Current);

#ifdef MEM_DETAILS
  fprintf(stderr, "\n");
  fprintf(stderr, "__SIZE_____TOTAL___MAXIMUM___\n");
  fprintf(stderr, "    32    %6d    %6d\n", tot_upto_032, max_upto_032);
  fprintf(stderr, "    64    %6d    %6d\n", tot_upto_064, max_upto_064);
  fprintf(stderr, "   128    %6d    %6d\n", tot_upto_128, max_upto_128);
  fprintf(stderr, "   256    %6d    %6d\n", tot_upto_256, max_upto_256);
  fprintf(stderr, "   512    %6d    %6d\n", tot_upto_512, max_upto_512);
  fprintf(stderr, "  1024    %6d    %6d\n", tot_upto_1024, max_upto_1024);
  fprintf(stderr, "  4096    %6d    %6d\n", tot_upto_4096, max_upto_4096);
  fprintf(stderr, "   inf    %6d    %6d\n", tot_upto_inf, max_upto_inf);
  fprintf(stderr, "\n");
#endif

  this = memlist;

  while (this != NULL) 
    {
      sh_error_handle (SH_ERR_WARN, FIL__, __LINE__, 0, MSG_E_NOTFREE,
		       this->size, this->file, this->line);
      this = this->next;
    }

  SL_RET0(_("sh_mem_stat"));
}

void sh_mem_check ()
{
  memlist_t * this;
  long        nerr = 0;

  SL_ENTER(_("sh_mem_check"));

  sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_MSTAMP,
		   Mem_Max, Mem_Current);

  this = memlist;

  while (this != NULL) 
    {
      if ( this->address == NULL )
	{
	  sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MNULL);
	  ++nerr;
	}
      else
	{
	  if ( this->address[this->size]        != CHECKBYTE )
	    {
	      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MOVER,
			       this->file, this->line, FIL__, __LINE__);
	      ++nerr;
	    }
	  if ( this->real_address[SH_MEMMULT-1] != CHECKBYTE )
	    {
	      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MUNDER,
			       this->file, this->line, FIL__, __LINE__);
	      ++nerr;
	    }
	}
      this = this->next;
    }

  /* if (nerr > 0) abort(); */

  SL_RET0(_("sh_mem_check"));
}

void * sh_mem_malloc (size_t size, char * file, int line)
{
  void      * the_realAddress;
  void      * theAddress;
  memlist_t * this;

  SL_ENTER(_("sh_mem_malloc"));

  the_realAddress = malloc(size + 2 * SH_MEMMULT);
  
  if ( the_realAddress  == NULL ) 
    {
      if (eblock == 0)
	{
	  eblock = 1;
	  (void) safe_logger (0, 0, NULL);
	  /*
	  sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MMEM,
			   file, line);
	  */
	  eblock = 0;
	}
      /* use _exit() rather than exit() - we malloc() in atexit() functions 
       */
      _exit (42);
    }
  
  /* --- Set check bytes. --- 
   */
  theAddress = ((char *) the_realAddress + SH_MEMMULT);

  memset(the_realAddress, CHECKBYTE, SH_MEMMULT);
  memset(theAddress,      CHECKBYTE, size + 1);
  memset(theAddress,      0,         1);

  ++Alloc_Count;
  ++Now_Alloc_Count;

  if (Max_Alloc_Count < Now_Alloc_Count)
    Max_Alloc_Count = Now_Alloc_Count;

#ifdef MEM_DETAILS
  if (size <= 32)
    { 
      ++now_upto_032;
      ++tot_upto_032;
      if (now_upto_032 > max_upto_032) max_upto_032 = now_upto_032;
    }
  else if  (size <= 64)
    { 
      ++now_upto_064;
      ++tot_upto_064;
      if (now_upto_064 > max_upto_064) max_upto_064 = now_upto_064;
    }
  else if  (size <= 128)
    { 
      ++now_upto_128;
      ++tot_upto_128;
      if (now_upto_128 > max_upto_128) max_upto_128 = now_upto_128;
    }
  else if  (size <= 256)
    { 
      ++now_upto_256;
      ++tot_upto_256;
      if (now_upto_256 > max_upto_256) max_upto_256 = now_upto_256;
    }
  else if  (size <= 512)
    { 
      ++now_upto_512;
      ++tot_upto_512;
      if (now_upto_512 > max_upto_512) max_upto_512 = now_upto_512;
    }
  else if  (size <= 1024)
    { 
      ++now_upto_1024;
      ++tot_upto_1024;
      if (now_upto_1024 > max_upto_1024) max_upto_1024 = now_upto_1024;
    }
  else if  (size <= 4096)
    { 
      ++now_upto_4096;
      ++tot_upto_4096;
      if (now_upto_4096 > max_upto_4096) max_upto_4096 = now_upto_4096;
    }
  else 
    { 
      ++now_upto_inf;
      ++tot_upto_inf;
      if (now_upto_inf > max_upto_inf) max_upto_inf = now_upto_inf;

      fprintf(stderr, "\n___BIGSIZE___");
      fprintf(stderr, "   %6d  -> %16s  %10d \n", size, file, line);
      fprintf(stderr, "\n");

    }
#endif

  Mem_Current += size;
  Mem_Max = ( (Mem_Current > Mem_Max) ? Mem_Current : Mem_Max);

  this = (memlist_t *) malloc (sizeof(memlist_t));

  if ( this == NULL) 
    {
      if (eblock == 0)
	{
	  eblock = 1;
	  (void) safe_logger(0, 0, NULL);
	  /*
	  sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MMEM,
			   file, line);
	  */
	  eblock = 0;
	}
      SL_RETURN( the_realAddress, _("sh_mem_malloc"));
    }

  /* make list entry */

  this->real_address = the_realAddress;
  this->address      = theAddress;
  this->size         = size;
  this->line         = line;
  sl_strlcpy(this->file, file, 20);

  this->next = memlist;
  memlist = this;

  SL_RETURN( theAddress, _("sh_mem_malloc"));
}


void sh_mem_free (void * a, char * file, int line)
{
  memlist_t * this   = memlist;
  memlist_t * before = memlist;
  unsigned long size = 0;

  SL_ENTER(_("sh_mem_free"));

  if ( a == NULL ) 
    {
      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MNULL,
		       file, line);
      SL_RET0(_("sh_mem_free"));
    }
    
  /* -- Find record. -- 
   */
  while (this != NULL) 
    {
      if (this->address == a) 
	break;
      before = this;
      this   = this->next;
    }

  if (this == NULL) 
    {
      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MREC,
		       file, line);
      SL_RET0(_("sh_mem_free"));
    } 
  else 
    {
      a = this->real_address;

      if ( this->address[this->size]        != CHECKBYTE )
	{
	  sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MOVER,
			   this->file, this->line, file, line);
	}
      if ( this->real_address[SH_MEMMULT-1] != CHECKBYTE )
	sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MUNDER,
			 this->file, this->line, file, line);

      size = this->size;

      if (this == memlist) 
	memlist = this->next;
      else 
	before->next = this->next;
    }

  free(a);
  if (this)
    free(this);
  ++Free_Count;
  --Now_Alloc_Count;

#ifdef MEM_DETAILS
  if (size <= 32)
    --now_upto_032;
  else if  (size <= 64)
    --now_upto_064;
  else if  (size <= 128)
    --now_upto_128;
  else if  (size <= 256)
    --now_upto_256;
  else if  (size <= 512)
    --now_upto_512;
  else if  (size <= 1024)
    --now_upto_1024;
  else if  (size <= 4096)
    --now_upto_4096;
  else 
    --now_upto_inf;
#endif

  Mem_Current -= size;
  SL_RET0(_("sh_mem_free"));
}

#else

void sh_mem_free (void * a)
{
  SL_ENTER(_("sh_mem_free"));

  if (a)
    {
      free(a);
    }
  else
    {
      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MNULL);
    }
  SL_RET0(_("sh_mem_free"));
}

void * sh_mem_malloc (size_t size)
{
  void * theAddress;

  SL_ENTER(_("sh_mem_malloc"));

  theAddress = malloc(size);

  if ( theAddress == NULL ) 
    {
      if (eblock == 0)
	{
	  eblock = 1;
	  (void) safe_logger(0, 0, NULL);
	  /*
	  sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_MMEM);
	  */
	  eblock = 0;
	}
      /* use _exit() rather than exit() - we malloc() in atexit()  
       */
      _exit (42);
    }
  /* memset (theAddress, 0, 1); *//* needs testing */

  SL_RETURN( theAddress, _("sh_mem_malloc"));
}
#endif
