/*-
 * Copyright (c) 2001-2005 Christian S.J. Peron
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#ifndef lint
static const char rcsid[] =
        "@(#) $Header: /usr/cvs/ipex/proc_kvm.c,v 1.4 2005/02/25 08:46:11 modulus Exp $";
#endif

#include "ipex_includes.h"

#if !defined __POWERPC__ && !defined linux

int count = 0;

/*
 * should we run acorss multiple socket
 * sets per process in /dev/mem. We 
 * will queue them in this list, so 
 * that we might generate a functional
 * pcap logic string for the capturing of
 * the packets.
 */
void 
queue_logic(char *pcap_logic)
{
   struct connections *p;
   if ((p = calloc(1, sizeof(*p))) == 0) {
      printf("fatal: calloc: unable to allocate memory\n");
      exit(EXIT_FAILURE);
   }
   p->pcap_logic = pcap_logic;
   assert(p->pcap_logic);
   SLIST_INSERT_HEAD(&list, p, next);
   count++;
   return;
}

/*
 * after we are done, no need to have the memory
 * allocated for the process. Free the list and
 * carry on.
 */
void
flush_logic(void)
{
    struct connections *p;
   while (!SLIST_EMPTY(&list)) {
      p = SLIST_FIRST(&list);
      assert(p);
      SLIST_REMOVE_HEAD(&list, next);
      free(p);
   }
   return;
}

/*
 * now that we have our list built will all the proper
 * connections, we will setup a function to take
 * the list and generate the ruleset that pcap 
 * will use to capture the packets.
 */
char *
compile_logic_list(void)
{
   static char logic[1024];
   int off = 0;
   struct connections *p;
   
   if (count == 0) {
      printf("fatal: no PF_INET sockets located in process.\n");
      exit(1);
   }
   if ((p = calloc(1, sizeof(*p))) == 0) {
      printf("fatal: calloc: unable to allocate memory\n");
      exit(EXIT_FAILURE);
   }
   memset(&logic, 0, sizeof(logic));
   SLIST_FOREACH(p, &list, next) {
        assert(p->pcap_logic);
	strncat(logic, p->pcap_logic, strlen(p->pcap_logic));
	off++;
	if (off < count) {
		strncat(logic, " or ", 4);
	}
   }
   flush_logic();	/* cleanup memory */
   return(logic);
}

/*
 * if autoconf discovered that struct kproc_info had
 * a ki_addr member, we are dealing with the -CURRENT
 * branch of BSD, so we will adjust the process status,
 * process pointer and process open files strucuts as
 * required
 */

#if (!defined(HAVE_KI_ADDR))	/* NOT FREEBSD 5.0 -CURRENT? */
#define	P_ADDR		kp_eproc.e_paddr
#define P_FD		kp_proc.p_fd
#define P_STAT		kp_proc.p_stat
#endif	/* ! HAVE_KI_ADDR ! */
#if (defined(HAVE_KI_ADDR))	/* FREEBSD 5.0 -CURRENT */
#define P_ADDR		ki_paddr
#define P_FD		ki_fd   
#define P_STAT		ki_stat
#endif	/* HAVE_KI_ADDR */

/*
 * global kernel descriptor
 */

kvm_t *kd;	/* kernel descriptor */

void
scan_descriptor_tables(char *buf)
{
   struct file *Cfp;
   struct kinfo_proc *p, *plast;
   struct filedesc fd;
   static struct file **ofb = NULL;
   static int ofbb = 0;
    int i, nf;
   int cnt = 0;
   size_t nb;
   struct core *uap;

   if ((uap = proc_args(buf)) == NULL) {
      printf("fatal: invalid arguments and/or operation for -P\n");
      exit(1);
   }
   if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL) {
      printf("fatal: mem: could not open core\n");
      exit(1);
   }
   if ((p = kvm_getprocs(kd, opts.op, opts.arg, &cnt)) == NULL) {
      printf("fatal: kvm_getprocs: could not retrieve process pointers\n");
      exit(1);
   }

   free(uap);
   for (plast = &p[cnt]; p < plast; ++p) {
   
      if (p->P_STAT == SZOMB) {
         continue;
      }
      if (!p->P_FD || kread((kvm_t *)p->P_FD, (char *)&fd, sizeof(fd))) {
         printf("fatal: kvm_read: could not locate process descriptors\n");
         exit(1);
      }
      if (!fd.fd_ofiles || (nf = fd.fd_nfiles) <= 0) {
          printf("fatal: no descriptors located in process\n");
          exit(1);
      }
      nb = (size_t)(sizeof(struct file *) * nf);
      if (nb > ofbb) {
         if (!ofb)
            ofb = (struct file **)memalloc(nb);
         else
            ofb = (struct file **)memrealloc((void *)ofb, nb);
         ofbb = nb;
      }
      kread((kvm_t *)fd.fd_ofiles, (char *)ofb, nb);
	/*
	 * process descriptor tables so we can 
	 * grab the PF_INET sockets from them and
	 * generate a pcap logic string for the process's.
	 */
	for (i = 0; i < nf; i++) {
           if (ofb[i]) {
		Cfp = ofb[i];
		process_descriptors((void *)Cfp);
           }
	}
   }
        return;
}

/*
 * all we want are the descriptors which have x.f_type
 * DTYPE_SOCKET, we are not interested in any other
 * descriptor types for the purpose of the function.
 * If we find a descriptor which is DTYPE_SOCKET lets
 * process it.
 */
void
process_descriptors(kvm_t *fp)
{
   struct file f;

   if (kread((kvm_t *)fp, (char *)&f, sizeof(f))) {
      printf("fatal: cant read file structure\n");
      exit(1);
   }
   if (f.f_type == DTYPE_SOCKET) {
      process_socket(f.f_data);
   }
   return;
}
	
int 
kread(kvm_t *addr, char *buf, size_t len)
{
   int br;
   br = kvm_read(kd, (u_long) addr, buf, len);
   return((br == len) ? 0 : 1);
}

/*
 * for each DTYPE_SOCKET descriptor, that is NOT
 * just a listening descriptor and has a PF_INET domain
 * should have some connection stats we can use
 * to generate a null terminated and add the connection
 * stats string to our pcap logic queue.
 */
void
process_socket(void *data)
{
   struct connections *uap;
   char *expr;
   struct inpcb inp;
   struct domain d;
   struct socket s;
   struct protosw p;
   char tmp[200];
   
   if (!data) {
      (void) fprintf(stderr,"fatal: socket has no address?\n");
      return;
   }
   if (kread((kvm_t *)data, (char *)&s, sizeof(s))) {
      (void) fprintf(stderr,"diagnostic: kread: cannot read in socket structure. skipping...\n");
      return;
   }
   if (!s.so_type) {
      (void) fprintf(stderr,"diagnostic: socket has no type. skipping...\n");
      return;
   }
   if (!s.so_proto || kread((kvm_t *)s.so_proto, (char *)&p, sizeof(p))) {
      (void) fprintf(stderr,"diagnostic: kread: cant read socket protocol switch. skipping...\n");
   }
   if (!p.pr_domain || kread((kvm_t *)p.pr_domain, (char *)&d, sizeof(d))) {
      (void) fprintf(stderr,"diagnostic: kread: cant read domain structure. skipping...\n");
      return;
   }
   if (d.dom_family == AF_INET) {
      if (!s.so_pcb || kread((kvm_t *)s.so_pcb, (char *)&inp, sizeof(inp))) {
	;
      }
      if ((inp.inp_faddr.s_addr != INADDR_ANY) || (inp.inp_fport &&  inp.inp_lport)) {
         if ((inp.inp_fport == 0) || (inp.inp_lport == 0)) {
            return;
         }
         snprintf(tmp, sizeof(tmp),"(port %u and %u)",
            (int)ntohs(inp.inp_lport),
            (int)ntohs(inp.inp_fport));

         if ((expr = strdup(tmp)) == NULL) {
            return;
         }
         SLIST_FOREACH(uap, &list, next) {
            if (strncmp(expr, opts.pcap_logic, 20) == 0) {
               (void) fprintf(stderr,"diagnostic: duplicate entry. removing dupes... \n");
               return;
            }
         }
         queue_logic(expr);
      }
   }
   return;
}
#else
#define NO_PROC_KVM_DOT_H_ 1
#endif
/* EOF PROC_KVM_C_ */


syntax highlighted by Code2HTML, v. 0.9.1