/**************************************************
OpengateM - a MAC address authentication system
 module to control cache for MAC address DB

  the cache holds the temporary copy of DB to speedup checking.
  Only the allowable MAC addresses are hold.

  As ip address check by database is time consuming procedure,
  the recently checked mac addresses are cached.
  Implemented with HashTable.
  HashTable:
    Key= MAC Address
    Val= userId, extraId and cache time.
    If MAC address is found in table and time is new, skip DB access.
    If time is old, remove the cached item.

Copyright (C) 2012 Opengate Project Team
Written by Yoshiaki Watanabe

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.

Email: watanaby@is.saga-u.ac.jp
**************************************************/
#include "opengatemd.h"

/* HashTable to store MacAddress->userId,extraId,Time */
static DB* macHashDb;

/* Cache Timeout(seconds) (read from conf file) */
static int macCacheTimeout;

void dumpHashTable(DB* table);

/****************************************
add item to Mac cache(hash table:macHashDb)
 key=macaddress, value="<found>SPACE<unixtime>SPACE<userId>SPACE<extraId>"
 <found>: TRUE=found in DB, FALSE=not found in DB
****************************************/
int addMacCacheItem(char* macAddress, char* userId, char* extraId, int found) {

  DBT hashKey;
  DBT hashVal;
  char hashValueStr[BUFFMAXLN];

  /* check address format */
  if(isNull(macAddress)) return FALSE;
  if(!ReFormatMacAddr(macAddress)) return FALSE;

  /** setup hash key **/
  /* hash key : string of mac address  */  
  hashKey.data = macAddress;
  hashKey.size = strlen(macAddress) + 1;

  /** setup hash value **/
  /* hash value : string "<found>SPACE<unixtime>SPACE<userId>SPACE<extraId>" */
  snprintf(hashValueStr,BUFFMAXLN,"%d %ld %s %s",
	   found,time(NULL),userId,extraId);
  hashVal.data = hashValueStr;
  hashVal.size = strlen(hashValueStr) + 1;    
  if(macHashDb->put(macHashDb, &hashKey, &hashVal, 0) == -1) {
    err_msg("ERR at %s#%d: fail to put into hash table",__FILE__,__LINE__);
    return FALSE;
  }

  return TRUE;
}

/****************************************
query userid and extraid for mac address
if the address is in cache, return TRUE(found in DB) or FALSE(not found).
if not in cache, or too old, or error, return ERROR.
****************************************/
int queryMacFromMacCache(char* macAddress, char* userId, char* extraId){

  DBT hashKey;
  DBT hashVal;
  time_t entryTime;
  int ret;
  int found;

  /* set default */
  entryTime=0;
  userId[0]=extraId[0]='\0';

  /* if null or illegal form, return */
  if(isNull(macAddress)) return ERROR;
  if(!ReFormatMacAddr(macAddress)) return ERROR;

  /***** get hashed item matched to the indicated mac */
  hashKey.data = macAddress;
  hashKey.size = strlen(macAddress) + 1;
  memset(&hashVal, 0, sizeof(DBT));
  ret=macHashDb->get(macHashDb, &hashKey, &hashVal, 0);

  /* get is failed, return false */
  if(ret!=0) return ERROR;

  /* get is successed */
  /* pick up the hash values */
  ret=sscanf(hashVal.data,"%d %ld %s %s",&found,&entryTime,userId,extraId);

  /* found, entryTime should be obtained */
  if(ret<=1) return ERROR;

  /* if entry time is older than timeout, return false */
  if( entryTime + macCacheTimeout < time(NULL) ) return ERROR;

  /* return the mac db access result (found/not found) */
  return found;
}

/****************************************
initialize Mac Cache
****************************************/
void initMacCache(void) {

  /* prepare hash table */
  if((macHashDb = dbopen(NULL, O_CREAT | O_RDWR, 0644, DB_HASH, NULL)) == NULL) {
    err_msg("ERR at %s#%d: fail to open mac hash table",__FILE__,__LINE__);
    terminateProg(0);
  }

  /* set counter and timeout parameter */
  if(isNull(GetConfValue("MacCacheTimeout"))){
    err_msg("ERR at %s#%d: cannot get MacCacheTimeout from conf file",__FILE__,__LINE__);
    macCacheTimeout=0;
  }else{
    macCacheTimeout=atoi(GetConfValue("MacCacheTimeout"));
  }  
}

/****************************************
Memory free for Mac Cache
****************************************/
void freeMacCache(void) {

  macHashDb->close(macHashDb);
}

/****************************************
delete item from Mac cache matched to the mac address
****************************************/
int delMacCacheItem(char* macAddress) {

  DBT hashKey;
  
  /* if null or illegal form, return */
  if(isNull(macAddress)) return FALSE;
  if(!ReFormatMacAddr(macAddress)) return FALSE;

  /* delete the item from Hash Table */
  hashKey.data = macAddress;
  hashKey.size = strlen(macAddress) + 1;
  macHashDb->del(macHashDb, &hashKey, 0);

  return TRUE;
}

/************************************
dump print of hash table to syslog
(debug routine for hash table)
************************************/
void dumpHashTable(DB* table){
  DBT hashKey;
  DBT hashVal;
  int ret;

  memset(&hashKey, 0, sizeof(DBT));
  memset(&hashVal, 0, sizeof(DBT));
  ret=table->seq(table, &hashKey, &hashVal, R_FIRST);
  while(ret==0){

    err_msg("%s:%s", (char*)hashKey.data, (char*)hashVal.data);

    /* get next entry */
    ret=table->seq(table, &hashKey, &hashVal, R_NEXT);
  }
}

/****************************************************
 routines for debugging putput
 ***************************************************/

void InitMacCache(void) {
  if(debug>1) err_msg("DEBUG:=>initmacCache( )");
  initMacCache();
  if(debug>1) err_msg("DEBUG:<=initMacCache( )");
}

void FreeMacCache(void) {
  if(debug>1) err_msg("DEBUG:=>freemacCache()");
  freeMacCache();
  if(debug>1) err_msg("DEBUG:<=freeMacCache()");
}

int AddMacCacheItem(char* macAddress, char* userId, char* extraId, int found) {
  int ret;
  if(debug>1) err_msg("DEBUG:=>addMacCacheItem(%s,%s,%s,%d)", macAddress,userId,extraId,found);
  ret = addMacCacheItem(macAddress,userId,extraId,found);
  if(debug>1) err_msg("DEBUG:(%d)<=addMacCacheItem( )",ret);
  return ret;
}

int QueryMacFromMacCache(char* macAddress, char* userId, char* extraId){
  int ret;
  if(debug>1) err_msg("DEBUG:=>queryMacFromMacCache(%s)", macAddress);
  ret = queryMacFromMacCache(macAddress, userId, extraId);
  if(debug>1) err_msg("DEBUG:(%d)<=queryMacFromMacCache(,%s,%s)",ret,userId,extraId);
  return ret;
}

int DelMacCacheItem(char* macAddress) {
  int ret;
  if(debug>1) err_msg("DEBUG:=>delMacCacheItem(%s)", macAddress);
  ret = delMacCacheItem(macAddress);
  if(debug>1) err_msg("DEBUG:(%d)<=delMacCacheItem( )",ret);
  return ret;
}

