/*************************************************************************
 *
 *  Copyright (c) 1999 Cornell University
 *  Computer Systems Laboratory
 *  Cornell University, Ithaca, NY 14853
 *  All Rights Reserved
 *
 *  Permission to use, copy, modify, and distribute this software
 *  and its documentation for any purpose and without fee is hereby
 *  granted, provided that the above copyright notice appear in all
 *  copies. Cornell University makes no representations
 *  about the suitability of this software for any purpose. It is
 *  provided "as is" without express or implied warranty. Export of this
 *  software outside of the United States of America may require an
 *  export license.
 *
 *  $Id$
 *
 **************************************************************************
 */

#include <sys/param.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/un.h>
#include <sys/unpcb.h>
#include <sys/sysctl.h>
#include <sys/filedesc.h>
#include <sys/queue.h>
#include <sys/pipe.h>
#define	KERNEL
#include <sys/file.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#undef KERNEL
#include <sys/mount.h>
#include <nfs/nfsproto.h>
#include <nfs/rpcv2.h>
#include <nfs/nfs.h>
#include <nfs/nfsnode.h>

#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>

#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
#include <nlist.h>
#include <paths.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static struct kinfo_proc *p, *plast;
static kvm_t *kd;
static int cnt;

/*
 *------------------------------------------------------------------------
 *
 *  Initialize kvm structures, if necessary
 *
 * Returns: 
 * Side effects: 
 *
 *------------------------------------------------------------------------
 */
void initialize_kernel (void)
{
  char buf[_POSIX2_LINE_MAX];
  
  if ((kd = kvm_open (NULL, NULL, NULL, O_RDONLY, buf)) == NULL) {
    errx (1, "%s", buf);
  }
}

static struct file **ofiles = NULL;/* buffer of pointers to file structures */
static int maxfiles = 0;

#define ALLOC_OFILES(d)	\
	if ((d) > maxfiles) { \
		if (ofiles) free(ofiles); \
		ofiles = malloc((d) * sizeof(struct file *)); \
		if (ofiles == NULL) { \
			err(1, NULL); \
		} \
		maxfiles = (d); \
	}
/*
 *------------------------------------------------------------------------
 *
 *  Determine user id of the person who owns the particular port
 *
 * Returns: uid owning port "query_port"
 * Side effects: none
 *
 *------------------------------------------------------------------------
 */
long gather_proc_info (int query_port)
{
  struct filedesc0 filed0;
  struct proc *gp;
  struct eproc *ep;
  struct kinfo_proc *kp;
  long pid;
  int uid;
  int i;
  struct file file;

  if ((p = kvm_getprocs(kd, KERN_PROC_ALL, 0, &cnt)) == NULL)
    errx(1, "%s", kvm_geterr(kd));

#define KVM_READ(kaddr, paddr, len) \
	(kvm_read(kd, (u_long)(kaddr), (char *)(paddr), (len)) == (len))
#define FPSIZE	(sizeof (struct file *))
#define filed filed0.fd_fd
  kp = p;
  for (plast = &p[cnt]; kp < plast; kp++) {
    
    gp = &kp->kp_proc;
    ep = &kp->kp_eproc;
    pid = gp->p_pid;
    uid = ep->e_ucred.cr_uid;

    if (gp->p_stat == SZOMB) continue;
    if (gp->p_fd == NULL) continue;

    if (!KVM_READ(gp->p_fd, &filed0, sizeof(filed0))) {
      fprintf (stderr, "can't read filedesc at %p (pid=%d)\n",
	       (void*)gp->p_fd, pid);
      continue;
    }
    ALLOC_OFILES(filed.fd_lastfile+1);
    
    if (filed.fd_nfiles > NDFILE) {
      if (!KVM_READ (filed.fd_ofiles, ofiles, (filed.fd_lastfile+1) *
		     FPSIZE)) {
	fprintf (stderr, "can't read file structures at %p (pid=%d)\n",
		 (void*)filed.fd_ofiles, pid);
	return;
      }
    }
    else {
      bcopy (filed0.fd_dfiles, ofiles, (filed.fd_lastfile+1) * FPSIZE);
    }
    for (i=0; i <= filed.fd_lastfile; i++) {
      if (ofiles[i] == NULL)
	continue;
      if (!KVM_READ (ofiles[i], &file, sizeof (struct file))) {
	fprintf (stderr, "can't read file %d at %p (pid=%d)\n",
		 i, (void*)ofiles[i], pid);
	continue;
      }
      if (file.f_type == DTYPE_SOCKET) {
	struct socket *sock = (struct socket *)file.f_data;
	struct socket so;
	struct domain dom;
	struct protosw proto;
	struct inpcb inpcb;

	if (!KVM_READ (sock, &so, sizeof(struct socket))) {
	  fprintf (stderr, "can't read sock at %p\n", (void*)sock);
	  continue;
	}
	if (!KVM_READ (so.so_proto, &proto, sizeof (struct protosw))) {
	  fprintf (stderr, "can't read protosw at %p\n",
		   (void*)so.so_proto);
	  continue;
	}
	if (!KVM_READ (proto.pr_domain, &dom, sizeof (struct domain))) {
	  fprintf (stderr, "can't read domain at %p\n",
		   (void*)proto.pr_domain);
	  continue;
	}
	if (dom.dom_family != AF_INET)
	  /* not ip */
	  continue;
	if (proto.pr_protocol != IPPROTO_TCP)
	  /* not tcp */
	  continue;
	if (!KVM_READ (so.so_pcb, &inpcb, sizeof (struct inpcb))) {
	  fprintf (stderr, "can't read inpcb at %p\n", so.so_pcb);
	  continue;
	}
	if (query_port == htons(inpcb.inp_lport)) {
	  return uid;
	}
      }
    }
  }
  return -1;
}
