#define KMEM "/dev/kmem"
#define VMUNIX "/vmunix"
#define NETSTAT "/usr/ucb/netstat -n -A -f inet"
#define NETSTATBUF 200 /* 80 would suffice */
#define NETSTATREMOTE 49
#define NETSTATWIDTH 17
#define REMOTESIZE 100 /* guaranteed to be enough */

#include <stdio.h>
#include <ctype.h>
#ifdef USE_SYSLOG
#include <syslog.h>
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/user.h>
#ifdef convex
#include <sys/ucred.h> /* dorks */
#define FCRED_ISNOT_POINTER
#endif
#define KERNEL
#include <sys/file.h>
#undef KERNEL
#ifdef BSD
#include <limits.h>
#endif
#include <nlist.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#ifdef convex
#include <sys/mbuf.h>
#endif
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <arpa/inet.h>
#include <pwd.h>
extern char *calloc();
extern long lseek();

#define ZAP(x,err) if (x) if (flagauthd) \
{printf("%s, %s: ERROR: UNKNOWN-ERROR\r\n",localport,remoteport);exit(37);} \
else { fprintf(stderr,"%s: fatal: %s\n",argv[0],err); exit(37); }
/* Reporting errors honestly to a remote host could damage security. */

static struct nlist nl[] = { { "_file" }, { "_nfile" }, { "" } };
#define SFIL 0
#define SNFILE 1

static char s[NETSTATBUF];
static char remote[REMOTESIZE];

main(argc,argv)
int argc;
char *argv[];
{
 register struct file *xfile;
 register struct file *fp;
 register char *sockloc;
 int nfile;
 struct tcpcb tcp;
 struct inpcb inp;
 struct socket sock;
 struct ucred uc;
 struct file *ftp;
 FILE *fi;
 int fd;
 unsigned long pcbl;
 int pcb;
 int r1; int r2; int r3; int r4; int rp;
 int l1; int l2; int l3; int l4; int lp;
 int uid;
 struct passwd *pw;
 int flagpwnam = 0;
 int flagauthd = 0;
 char localport[10];
 char remoteport[10];
 
 if ((!strcmp(argv[0],"authd"))
  || ((strlen(argv[0]) >= 6)
   && (!strcmp(argv[0] + strlen(argv[0]) - 6,"/authd"))))
   flagauthd = flagpwnam = 1;
 else
   if ((!strcmp(argv[0],"tcpuname"))
    || ((strlen(argv[0]) >= 9)
     && (!strcmp(argv[0] + strlen(argv[0]) - 9,"/tcpuname"))))
     flagpwnam = 1;

 if (flagauthd)
  {
   int ch;
   int localportlen;
   int remoteportlen;
   int loop;
   struct sockaddr_in sa;
   int salen;

   localportlen = remoteportlen = loop = 0;

   while ((ch = getchar()) != ',')
    {
     if (isascii(ch) && isdigit(ch)) localport[localportlen++] = ch;
     if (localportlen == 6) /* tough luck! */ exit(2);
     if ((++loop) > 1000) /* tough luck! */ exit(3);
    }
   while ((ch = getchar()) != '\n')
    {
     if (isascii(ch) && isdigit(ch)) remoteport[remoteportlen++] = ch;
     if (remoteportlen == 6) /* tough luck! */ exit(4);
     if ((++loop) > 1000) /* tough luck! */ exit(5);
    }

   localport[localportlen] = remoteport[remoteportlen] = 0;
   lp = atoi(localport);
   rp = atoi(remoteport);

   salen = sizeof(sa);
   if (getpeername(0,&sa,&salen) == -1)
    {
     printf("%s, %s: ERROR: UNKNOWN-ERROR\r\n",localport,remoteport);
     exit(6);
    }
   r1 = (int) (unsigned int) ((unsigned char *) &sa.sin_addr)[0];
   r2 = (int) (unsigned int) ((unsigned char *) &sa.sin_addr)[1];
   r3 = (int) (unsigned int) ((unsigned char *) &sa.sin_addr)[2];
   r4 = (int) (unsigned int) ((unsigned char *) &sa.sin_addr)[3];

   salen = sizeof(sa);
   if (getsockname(0,&sa,&salen) == -1)
    {
     printf("%s, %s: ERROR: UNKNOWN-ERROR\r\n",localport,remoteport);
     exit(6);
    }
   l1 = (int) (unsigned int) ((unsigned char *) &sa.sin_addr)[0];
   l2 = (int) (unsigned int) ((unsigned char *) &sa.sin_addr)[1];
   l3 = (int) (unsigned int) ((unsigned char *) &sa.sin_addr)[2];
   l4 = (int) (unsigned int) ((unsigned char *) &sa.sin_addr)[3];
  }
 else
  {
   ZAP(argc < 4,"need four arguments")
   ZAP(sscanf(argv[1],"%d.%d.%d.%d",&r1,&r2,&r3,&r4)<4,"arg 1 must be a.b.c.d")
   ZAP(sscanf(argv[2],"%d",&rp) < 1,"arg 2 must be integer")
   ZAP(sscanf(argv[3],"%d.%d.%d.%d",&l1,&l2,&l3,&l4)<4,"arg 3 must be a.b.c.d")
   ZAP(sscanf(argv[4],"%d",&lp) < 1,"arg 4 must be integer")
  }

 (void) sprintf(remote,"%d.%d.%d.%d.%d                    ",r1,r2,r3,r4,rp);

#ifdef USE_SYSLOG
 /* This isn't worth the time for the procedure call, but if you want... */
 syslog(LOG_DEBUG,"authd: checking up on %d.%d.%d.%d %d %d.%d.%d.%d %d\n",
   r1,r2,r3,r4,rp,l1,l2,l3,l4,lp);
#endif

 nlist(VMUNIX,nl);
 ZAP(!(nl[SNFILE].n_value),"cannot nlist _nfile")
 ZAP(!(nl[SFIL].n_value),"cannot nlist _file")
 fd = open(KMEM,O_RDONLY);
 ZAP(fd == -1,"cannot open kernel memory")
 ZAP(lseek(fd,(long) (nl[SNFILE].n_value),0) == -1,"cannot seek to _nfile")
 ZAP(read(fd,(char *) &nfile,sizeof(nfile)) < sizeof(nfile),"cannot read _nfile")

 ZAP(lseek(fd,(long) nl[SFIL].n_value,0) == -1,"cannot seek to _file")
 ZAP(read(fd,(char *) &ftp,sizeof(ftp)) < sizeof(ftp),"cannot read _file")
 ZAP(lseek(fd,(long) ftp,0) == -1,"cannot lseek to file table")
 xfile = (struct file *) calloc((unsigned) nfile,sizeof(struct file));
 ZAP(!xfile,"cannot allocate space for copy of file table")
 ZAP(read(fd,(char *) xfile,nfile * sizeof(struct file)) < nfile * sizeof(struct file),"cannot read file table")

 fi = popen(NETSTAT,"r");
 ZAP(!fi,"cannot execute netstat")
 ZAP(!fgets(s,sizeof(s),fi),"cannot read netstat header line 1")
 ZAP(!fgets(s,sizeof(s),fi),"cannot read netstat header line 2")
 while (fgets(s,sizeof(s),fi))
  {
   ZAP(sscanf(s,"%8x",&pcb) != 1,"cannot scan netstat pcb address")
   pcbl = pcb;
   if (!strncmp(s + NETSTATREMOTE,remote,NETSTATWIDTH))
    {
     ZAP(lseek(fd,(long) pcbl,0) == -1,"cannot seek to pcb address")
     ZAP(read(fd,(char *) &tcp,sizeof(tcp)) < sizeof(tcp),"cannot read tcp buffer")
     ZAP(!tcp.t_inpcb,"tcp buffer unlinked")
     ZAP(lseek(fd,(long) tcp.t_inpcb,0) == -1,"cannot seek to inpcb address")
     ZAP(read(fd,(char *) &inp,sizeof(inp)) < sizeof(inp),"cannot read inpcb buffer")
     ZAP(inp.inp_ppcb != (char *) pcbl,"inpcb not linked to pcb")

/* Cursed be Convex and the other manufacturers who make this code */
/* nearly impossible to write with any pretense of portability. */
     if((((char *) &inp.inp_faddr)[0] == (char) r1)
      &&(((char *) &inp.inp_faddr)[1] == (char) r2)
      &&(((char *) &inp.inp_faddr)[2] == (char) r3)
      &&(((char *) &inp.inp_faddr)[3] == (char) r4)
      &&(inp.inp_fport == htons((unsigned short) rp))
      &&(((char *) &inp.inp_laddr)[0] == (char) l1)
      &&(((char *) &inp.inp_laddr)[1] == (char) l2)
      &&(((char *) &inp.inp_laddr)[2] == (char) l3)
      &&(((char *) &inp.inp_laddr)[3] == (char) l4)
      &&(inp.inp_lport == htons((unsigned short) lp))
     )
      {
       sockloc = (char *) inp.inp_socket;
       ZAP(!sockloc,"inp not linked to socket")
       ZAP(lseek(fd,(long) sockloc,0) == -1,"cannot seek to socket address")
       ZAP(read(fd,(char *) &sock,sizeof(sock)) < sizeof(sock),"cannot read socket buffer")
       ZAP(sock.so_pcb != (char *) tcp.t_inpcb,"socket not linked to inpcb")
       for (fp = xfile;fp < xfile + nfile;++fp)
	 if (fp->f_count && (fp->f_type == DTYPE_SOCKET))
	   if (fp->f_data == sockloc)
	     break;
       if (fp != xfile + nfile)
	{
#ifdef FCRED_ISNOT_POINTER
         uid = (int) fp->f_cred.cr_ruid;
#else
         ZAP(lseek(fd,(long) (fp->f_cred),0) == -1,"cannot seek to credentials")
         ZAP(read(fd,(char *) &uc,sizeof(uc)) < sizeof(uc),"cannot read credentials")
         uid = (int) uc.cr_ruid;
#endif
	 if (flagpwnam)
	  {
	   pw = getpwuid(uid);
	   ZAP(!pw,"cannot get password entry")
	   if (flagauthd)
	     /* UNIX is a trademark of AT&T. :-) */
             printf("%s, %s: USERID: UNIX: %s\r\n"
		    ,localport,remoteport,pw->pw_name);
	   else
	     printf("%s\n",pw->pw_name);
	  }
	 else
	   printf("%d\n",uid);
         exit(0);
	}
      }
    }
  }
 ZAP(1,"no such TCP connection")
 exit(1);
}
