/*
** kernel/sunos5.c                 SunOS 5 kernel access functions
**
** This program is in the public domain and may be used freely by anyone
** who wants to. 
**
** Author: Casper Dik <casper@fwi.uva.nl>
**
** Last update: 21 Apr 1993
**
** Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se>
*/


#define _KMEMUSER
#define _KERNEL

/* some definition conflicts. but we must define _KERNEL */

#define exit 		kernel_exit
#define strsignal	kernel_strsignal

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <netinet/in.h>

#include <stdio.h>
#include <kvm.h>
#include <nlist.h>
#include <math.h>
#include <sys/fcntl.h>
#include <sys/cred.h>
#include <sys/file.h>
#include <sys/stream.h>
#include <inet/common.h>
#include <inet/ip.h>

#undef exit
#undef strsignal

#include <unistd.h>
#include <string.h>
#include <stddef.h>

#include "identd.h"
#include "error.h"

#define N_FANOUT 0
#define N_FILE	 1

struct nlist nl[] = {
	{ "ipc_tcp_fanout" },
	{ "file" },
	{ 0 },
};


static kvm_t *kd;


int k_open()
{
  /*
  ** Open the kernel memory device
  */
  if (!(kd = kvm_open(path_unix, path_kmem, NULL, O_RDONLY, NULL)))
    ERROR("main: kvm_open");
  
  /*
  ** Extract offsets to the needed variables in the kernel
  */
  if (kvm_nlist(kd, nl) != 0)
    ERROR("main: kvm_nlist");

  return 0;
}


/*
** Get a piece of kernel memory with error handling.
** Returns 1 if call succeeded, else 0 (zero).
*/
static int getbuf(addr, buf, len, what)
  long addr;
  char *buf;
  int len;
  char *what;
{

  if (kvm_read(kd, addr, buf, len) < 0)
  {
    if (syslog_flag)
      syslog(LOG_ERR, "getbuf: kvm_read(%08x, %d) - %s : %m",
	     addr, len, what);

    return 0;
  }
  
  return 1;
}


/*
** Return the user number for the connection owner
*/
int k_getuid(faddr, fport, laddr, lport, uid)
  struct in_addr *faddr;
  int fport;
  struct in_addr *laddr;
  int lport;
  int *uid;
{
    queue_t *qp, *pq, sqr;
    ipc_t ic, *icp;
    unsigned short uslp, usfp;
    unsigned int offset;
    unsigned long fp;
    file_t tf;
    unsigned long zero = 0;
    
    usfp = fport;
    uslp = lport;

    offset = usfp ^ uslp;
    offset ^= (unsigned) faddr->S_un.S_un_b.s_b4 ^ (offset >> 8);
    offset %= 256;
    
    if (!getbuf(nl[N_FANOUT].n_value + sizeof(ipc_t *) * offset,
		(char *) &icp,
		sizeof(ipc_t *),
		"ipc_tcp_fanout[offset]"))
	return -1;
    
    if (icp == 0) {
	syslog(LOG_INFO, "k_getuid: Hash miss");
	return -1;
    }
    
    while (icp) {
	if (!getbuf((unsigned long) icp,
		    (char *) &ic,
		    sizeof(ic),
		    "hash entry"))
	    return -1;

	if (usfp == ic.ipc_tcp_addr[4] && /* remote port */
	    uslp == ic.ipc_tcp_addr[5] && /* local port */
#if 0
	    memcmp(&laddr->s_addr, &ic.ipc_tcp_addr[0], 4) == 0 && /* local */
#else
 	    (memcmp(&laddr->s_addr, &ic.ipc_tcp_addr[0], 4) == 0 ||
 	    /* In SunOS 5.3, the local part can be all zeros */
 	     memcmp(&zero, &ic.ipc_tcp_addr[0], 4) == 0) /* local */ &&
#endif
	    memcmp(&faddr->s_addr, &ic.ipc_tcp_addr[2], 4) == 0)
		break;
	icp = ic.ipc_hash_next;
    }
    if (!icp) {
	syslog(LOG_INFO, "k_getuid: Port not found");
	return -1;
    }
    
    if (!getbuf((unsigned long) ic.ipc_rq+offsetof(queue_t, q_stream),
		(char *) &sqr.q_stream,
		sizeof(sqr.q_stream),
		"queue.q_stream"))
	return -1;

    /* at this point sqr.q_stream holds the pointer to the stream we're
       interested in. Now we're going to find the file pointer
       that refers to the vnode that refers to this stream stream */

    fp = nl[N_FILE].n_value;
    for (;fp;fp = (unsigned long) tf.f_next) {
	vnode_t vp;

	if (!getbuf(fp, (char *) &tf, sizeof(file_t),"file pointer"))
	    return -1;

	if (!tf.f_vnode)
	    continue;

	if (!getbuf((unsigned long) tf.f_vnode + offsetof(vnode_t,v_stream),
			(char *) &vp.v_stream,
			sizeof(vp.v_stream),"vnode.v_stream"))
	    return -1;

	if (vp.v_stream == sqr.q_stream) {
	    cred_t cr;
	    if (!getbuf((unsigned long) tf.f_cred + offsetof(cred_t, cr_ruid),
			(char *) &cr.cr_ruid,
			sizeof(cr.cr_ruid),
			"cred.cr_ruid"))
		return -1;
	    *uid = cr.cr_ruid;
	    return 0;
	}
    }
    return -1;
}
