/**************************************************************************** 
**
** File: dynports.c
**
** Author: Mike Borella
**
** Comments: Support for dynamic port mapping.  This allows a protocol to
** to listened for on a non-standard port.  Each mapping is assigned a 
** duration.  Mappings that are user defined via the command line are 
** considered permanent.  Mappings that are made by protocols will time out 
** in DYNPORTS_DURATION seconds unless they are refreshed.  
**
** $Id: dynports.c,v 1.8 2007/06/25 10:19:34 farooq-i-azam 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 "dynports.h"
#include "error.h"
#include <limits.h>

#define DYNPORTS_NUM      256/* max number of dynamic ports on the system */
#define DYNPORTS_DURATION 5  /* # of seconds that nonpermanent mappings stay */

typedef struct portmap
{
  u_int16_t       port;
  service_func_t  f;
  unsigned long   t;
} portmap_t;

static portmap_t dynport_map[DYNPORTS_NUM];
static int       current_ptr = 0;

/*----------------------------------------------------------------------------
**
** dynports_add()
**
** Adds a dynamic port mapping
** 
**----------------------------------------------------------------------------
*/

void dynports_add(u_int16_t port, service_func_t f, unsigned long t)
{
  if (current_ptr >= DYNPORTS_NUM)
    error_fatal("too many dynamic port mappings");
  
  dynport_map[current_ptr].port = port;
  dynport_map[current_ptr].f = f;
  dynport_map[current_ptr].t = t;

  current_ptr++;
}

/*----------------------------------------------------------------------------
**
** dynports_refresh()
**
** Refreshes dynamic port mapping, by adding the mapping again with the 
** current time.
** 
**----------------------------------------------------------------------------
*/

void dynports_refresh(u_int16_t port, service_func_t f)
{
  struct timeval t;
  int            i;

  /* get the current time */
  gettimeofday(&t, NULL);

  /* refresh the dynamic port mapping with the current time */
  for (i=0; i<current_ptr; i++)
    if (dynport_map[i].port == port)
      {
	dynport_map[i].t = t.tv_sec;
	break;
      }
  
  /* 
   * If the dynamic mapping is not found, we just fall through.  This is done
   * for a reason...if we don't certain mapping in which we try to capture
   * both source and dest ports might break.  See the TFTP module for an
   * example.
   */
}

/*----------------------------------------------------------------------------
**
** dynports_parse()
**
** Parses a string of the form 'protocol=port', such as rtp=12341.
** 
**----------------------------------------------------------------------------
*/

void dynports_parse(char *s)
{
  char           * ptr;
  u_int16_t        port;
  service_func_t   f = NULL;

  /* find = */
  ptr = strchr(s,'=');
  if (ptr == NULL)
    error_system("unable to parse dynamic port assignment %s", s);

  /* determine port # */
  port = (u_int16_t) atoi(ptr+1);
  
  /* determine protocol */
  *ptr = '\0';
  f = NULL;
  if (!strcmp(s, "dhcp"))
    f = dump_dhcp;
  if (!strcmp(s, "dns"))
    f = dump_dns;
  if (!strcmp(s, "http"))
    f = dump_http;
  if (!strcmp(s, "isakmp"))
    f = dump_isakmp;
  if (!strcmp(s, "l2tp"))
    f = dump_l2tp;
  if (!strcmp(s, "mgcp"))
    f = dump_mgcp;
  if (!strcmp(s, "mobileip"))
    f = dump_mobileip;
  if (!strcmp(s, "netbios_ns"))
    f = dump_netbios_ns;
  if (!strcmp(s, "pptp"))
    f = dump_pptp;
  if (!strcmp(s, "rip"))
    f = dump_rip;
  if (!strcmp(s, "ripng"))
    f = dump_ripng;
  if (!strcmp(s, "rtp"))
    f = dump_rtp;
  if (!strcmp(s, "sip"))
    f = dump_sip;
  if (!strcmp(s, "snmp"))
    f = dump_snmp;
  if (!strcmp(s, "slp"))
    f = dump_slp;
  if (!strcmp(s, "ftpctrl"))
    f = dump_ftpctrl;
  if (f==NULL)
    error_fatal("dynamic port assignment to unknown protocol %s", s);

  /* Add the port assignment with an infinite duration */
  dynports_add(port, f, ULONG_MAX);
  
  /* If we're adding an RTP port, add an RTCP port as well. */
  if (!strcmp(s, "rtp"))
    dynports_add(port+1, dump_rtcp, ULONG_MAX);

}

/*----------------------------------------------------------------------------
**
** dynports_find()
**
** Find a protocol function pointer given the port number. Returns the 
** function pointer or null if not found.
** 
**----------------------------------------------------------------------------
*/

service_func_t dynports_find(u_int16_t port)
{
  int i;

  for (i=0; i<current_ptr; i++)
    if (dynport_map[i].port == port)
      return dynport_map[i].f;

  return NULL;
}

/*----------------------------------------------------------------------------
**
** dynports_timeout()
**
** Time out stale dynamic port mappings.  
** 
**----------------------------------------------------------------------------
*/

void dynports_timeout(void)
{
  int i;
  struct timeval t;

  /* get the current time */
  gettimeofday(&t, NULL);

  for (i=0; i<current_ptr; i++)
    if (dynport_map[i].t < ULONG_MAX && 
	dynport_map[i].t + DYNPORTS_DURATION < t.tv_sec)
      {
	dynport_map[i].f = NULL;
	dynport_map[i].t = 0;
      }
}
