/*  -*- Mode: C;  -*- */

/******************************************************************************
*                                                                             *
*   Copyright 2005 University of Cambridge Computer Laboratory.               *
*                                                                             *
*   This file is part of Nprobe.                                              *
*                                                                             *
*   Nprobe 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.                                       *
*                                                                             *
*   Nprobe 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 Nprobe; if not, write to the Free Software                     *
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
*                                                                             *
******************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/param.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#ifdef __alpha__
#include <sys/mbuf.h>
#endif
#include <net/route.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#undef __STDC__
#include <netinet/ip.h>

#ifdef __alpha__
#include <netinet/ip_var.h>
#endif
#include <netinet/tcp.h>
#include <netinet/if_ether.h>

#include <assert.h>

#include "list.h"
#include "pkt.h"
#include "interface.h"
#include "flows.h"
#include "http.h"
#include "tcp.h"
# include "np_file.h"
#include "udp.h" 
#include "service.h"
#include "tcp_other.h"
#include "udp_other.h"
#include "seq.h"
#include "report.h"
#include "probe_config.h"

#ifdef PRINT_OUT
#include "print_util.h"
#endif

listhdr_t *serv_hashtbl;
/*
 * Initialise tcp services hash table 
 */

listhdr_t *
service_hashtbl_initialise()
{
  int i;
  listhdr_t *sht;;
  

  if ((sht = (listhdr_t *)malloc(N_SERVICE_BUCKETS * sizeof(listhdr_t)))
      == NULL)
    error("service_hashtbl_initialise", "malloc");

  for (i = 0; i < N_SERVICE_BUCKETS; i++)
      L_INIT(&sht[i]);

  fprintf(stderr, "\tTCP service hash table initialised %d slots\n", N_SERVICE_BUCKETS);

  return sht;
}

void 
reg_serv(unsigned short port, unsigned char trans, serv_control_t *tmpl, int dump_set, int dump_clear)
{
  serv_control_t *cp;
  serv_entry_t *ep;
  listhdr_t *lhp = &(serv_hashtbl[port%N_SERVICE_BUCKETS]);

  
  if ((cp = (serv_control_t *)malloc(sizeof(serv_control_t)))
      == NULL)
    error("reg_serv()", "serv_control_t malloc");

  
  if ((ep = (serv_entry_t *)malloc(sizeof(serv_entry_t)))
      == NULL)
    error("reg_serv()", "serv_entry_t malloc");

  if (trans == TRANS_TCP)
    {
      cp->methu.tmeth = *((tcp_serv_methods_t *)(tmpl->methu.methp));
    }
  else if (trans == TRANS_UDP)
    {
      cp->methu.umeth = *((udp_serv_methods_t *)(tmpl->methu.methp));
    }

  cp->rectypes = tmpl->rectypes;
  cp->serv_ctr = tmpl->serv_ctr;
  cp->cflags = (tmpl->cflags | dump_set) & (~dump_clear);
  
  ep->port = port;
  ep->trans = trans;
  ep->control = cp;
  
  L_INS_TAIL(lhp, ep, hlist, serv_entry_t);
  
  return;
  
}


int 
get_serv(unsigned short sport, unsigned short dport, unsigned char trans, serv_control_t **sp) 
{
  int way;
  unsigned int port;
  listhdr_t *slp;
  serv_entry_t *ep;
  for (way = 1, port = sport; way < 3; way++, port = dport)
    {
      slp = &(serv_hashtbl[port%N_SERVICE_BUCKETS]);
      L_WALK(ep, slp, hlist, serv_entry_t)
	if (port == ep->port && trans == ep->trans)
	  {
	    *sp = ep->control;
	    return way;
	  }
    }
  /* Otherwise the default - just count bytes/pkts */
  if (trans == TRANS_TCP)
    *sp = tcp_handler_def;
  else
    *sp = udp_handler_def;

  /* And have a stab at which might be the server end */
  return sport < HIGHEST_WELLKNOWN_PORT ? SERVER : CLIENT;
}

void
services_init(void)
{
  /* service control location hash table */
  serv_hashtbl = service_hashtbl_initialise();
  
/*
 * Initialise default service control structures (these will not necessarily be
 *  initialised by registration
 */
  tcp_handler_def->methu.tmeth = *((tcp_serv_methods_t *)(tcp_handler_def->methu.methp));

  udp_handler_def->methu.umeth = *((udp_serv_methods_t *)(udp_handler_def->methu.methp));
  
/*
 * Dns packet processing can switch to the nodump method suite, which is not
 * registered as it would conflict with the main methods - initialise it here
 */
  extern serv_control_t udp_ns_serv_control_nodump;
  
  udp_ns_serv_control_nodump.methu.umeth = *((udp_serv_methods_t *)(udp_ns_serv_control_nodump.methu.methp));
  
}


