/*
 * leases.c -- tools to manage DHCP leases
 * Russ Dill <Russ.Dill@asu.edu> July 2001
 */

#include <time.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include "dhcpd.h"
#include "files.h"
#include "options.h"
#include "leases.h"
#include "arpping.h"
#include "common.h"

#include "static_leases.h"


uint8_t blank_chaddr[] = {[0 ... 15] = 0};

/* clear every lease out that chaddr OR yiaddr matches and is nonzero */
void clear_lease(uint8_t *chaddr, uint32_t yiaddr)
{
  unsigned int i, j;

  for (j = 0; j < 16 && !chaddr[j]; j++);

  for (i = 0; i < server_config.max_leases; i++)
    if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) ||
        (yiaddr && leases[i].yiaddr == yiaddr)) {
      memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr));
    }
}


/* add a lease into the table, clearing out any old ones */
struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease, uint8_t *hname, uint8_t opt_len)
{
  struct dhcpOfferedAddr *oldest;

  /* clean out any old ones */
  clear_lease(chaddr, yiaddr);

  oldest = oldest_expired_lease();

  if (oldest) {
    memcpy(oldest->chaddr, chaddr, 16);
    oldest->yiaddr = yiaddr;
    oldest->expires = time(0) + lease;
    
    if((hname != NULL) && (opt_len != 0)){
    memcpy(oldest->hname, hname, opt_len);
    oldest->hname[opt_len]='\0';
    oldest->hname_len = opt_len;
    DEBUG(LOG_INFO,"Host == >%s(len:%d)\n",oldest->hname, opt_len);
  }
}
  return oldest;
}


/* true if a lease has expired */
int lease_expired(struct dhcpOfferedAddr *lease)
{
  return (lease->expires < (unsigned long) time(0));
}


/* Find the oldest expired lease, NULL if there are no expired leases */
struct dhcpOfferedAddr *oldest_expired_lease(void)
{
  struct dhcpOfferedAddr *oldest = NULL;
  unsigned long oldest_lease = time(0);
  unsigned int i;


  for (i = 0; i < server_config.max_leases; i++)
    if (oldest_lease > leases[i].expires) {
      oldest_lease = leases[i].expires;
      oldest = &(leases[i]);
    }
  return oldest;

}


/* Find the first lease that matches chaddr, NULL if no match */
struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr)
{
  unsigned int i;

  for (i = 0; i < server_config.max_leases; i++)
    if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]);

  return NULL;
}


/* Find the first lease that matches yiaddr, NULL is no match */
struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr)
{
  unsigned int i;

  for (i = 0; i < server_config.max_leases; i++)
    if (leases[i].yiaddr == yiaddr) return &(leases[i]);

  return NULL;
}


/* check is an IP is taken, if it is, add it to the lease table */
static int check_ip(uint32_t addr)
{
  struct in_addr temp;
  unsigned char opt_len=0;

  if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) {
    temp.s_addr = addr;
    LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds",
      inet_ntoa(temp), server_config.conflict_time);
    //add_lease(blank_chaddr, addr, server_config.conflict_time, NULL );
    add_lease(blank_chaddr, addr, server_config.conflict_time, NULL, opt_len );
    return 1;
  } else return 0;
}


/* find an assignable address, it check_expired is true, we check all the expired leases as well.
 * Maybe this should try expired leases by age... */
uint32_t find_address(int check_expired)
{
  uint32_t addr, ret;
  struct dhcpOfferedAddr *lease = NULL;

  addr = ntohl(server_config.start); /* addr is in host order here */
  for (;addr <= ntohl(server_config.end); addr++) {

    /* ie, 192.168.55.0 */
    if (!(addr & 0xFF)) continue;

    /* ie, 192.168.55.255 */
    if ((addr & 0xFF) == 0xFF) continue;
  // Leo Lin 2010/8/12 modify start
  // no need to get static lease here again
        //uci_read_static_leases();  // Bryan add
  // Leo Lin 2010/8/12 modify end
    /* Only do if it isn't an assigned as a static lease */
    if(!reservedIp(server_config.static_leases, htonl(addr)))
    {

    /* lease is not taken */
    ret = htonl(addr);
    if ((!(lease = find_lease_by_yiaddr(ret)) ||

         /* or it expired and we are checking for expired leases */
         (check_expired  && lease_expired(lease))) &&

         /* and it isn't on the network */
         !check_ip(ret)) {
      return ret;
      break;
    }
  }
  }
  return 0;
}


#define HOST_FILE "/etc/hosts"

void write_hosts()
{
  FILE *fop;
  unsigned long i,len, total=0;
  char buf[64];
  char *ipstr;
  struct in_addr addr;
  uint32_t ip, startaddr, endaddr;
  
  
  DEBUG(LOG_INFO, "%s::Write leased ip-hostname pairs to /etc/hosts",__FUNCTION__);
  fop = fopen(HOST_FILE, "r");
  if(!fop)
    LOG(LOG_ERR, "Can't open /etc/hosts for operation");
  
  startaddr = ntohl(server_config.start);
  endaddr   = ntohl(server_config.end);
  while(!feof(fop)){
    fgets(buf, 64, fop);
    
    len = strlen(buf);
    total += len;
    ipstr = strtok(buf, "\t");
    if(ipstr)
    {
      inet_aton(ipstr, &addr);
      ip = ntohl(addr.s_addr);
      if(ip >= startaddr && ip <= endaddr){
        total -= len;
        
        break;
      }
    }
    memset(buf, 0, 64);
  }
  fclose(fop);
  DEBUG(LOG_INFO,"Total read bytes %d", total);
  truncate(HOST_FILE, total);
  
  fop=fopen(HOST_FILE, "r+");
  fseek(fop, total, SEEK_SET);
  for(i=0;i<server_config.max_leases;i++)
  {
    int expires;
    
    expires = leases[i].expires - time(0);
    DEBUG(LOG_INFO,"%s:: Lease time %d",__FUNCTION__,expires);
    if(leases[i].hname_len > 0 && expires > 0)
    {
      addr.s_addr=leases[i].yiaddr;
      sprintf(buf,"%s   %s\n",inet_ntoa(addr), leases[i].hname);
      fputs(buf, fop);
    }
  }
  
  fclose(fop);
}
