Subject: bin/348: print owner of connection in netstat(1)
To: None <gnats-admin>
From: Luke Mewburn <lm@karybdis.cs.rmit.OZ.AU>
List: netbsd-bugs
Date: 07/19/1994 18:05:06
>Number:         348
>Category:       bin
>Synopsis:       a hack to netstat(1) to show the owner of a connection
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    gnats-admin (Utility Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   lm
>Arrival-Date:   Tue Jul 19 18:05:04 1994
>Originator:     Luke Mewburn
>Organization:
"	Technical Services Group, RMIT Computer Science Department"
>Release:        
>Environment:
	
System: NetBSD karybdis 1.0-ALPHA NetBSD 1.0-ALPHA (KARYBDIS) #6: Wed Jul 20 10:35:04 EST 1994 root@karybdis:/Src/src/sys/arch/i386/compile/KARYBDIS i386


>Description:
	Netstat(1) currently does not show the owner of connections.
	This feature would be rather useful to determine the user who
	owns the process tied to a certain outbound socket.
	A while ago I wrote a program which uses the appropriate OS
	module from identd (which, btw, needs to be upgraded on
	netbsd.) to determine who a specific connection belonged to,
	but having the feature in netstat directly is more useful.

>How-To-Repeat:
	run netstat.
	Now, apply this patch, and run netstat with the -U option.
	Effect is more noticable when a non-root process has a telnet
	going out, or has a unix domain server.

>Fix:
	Apply this patch. Note that it's not the most elegant solution
	(especially for inet sockets), and I really should go through
	the open file list once and cache it rather than scan it for
	each connection. But, like ps(1), netstat can't be relied on
	to have _perfect_ information :)


diff -cb /usr/src/usr.bin/netstat/inet.c netstat/inet.c
*** /usr/src/usr.bin/netstat/inet.c	Fri May 13 21:51:48 1994
--- netstat/inet.c	Tue Jul 19 15:07:57 1994
***************
*** 124,134 ****
  			putchar('\n');
  			if (Aflag)
  				printf("%-8.8s ", "PCB");
! 			printf(Aflag ?
! 				"%-5.5s %-6.6s %-6.6s  %-18.18s %-18.18s %s\n" :
! 				"%-5.5s %-6.6s %-6.6s  %-22.22s %-22.22s %s\n",
  				"Proto", "Recv-Q", "Send-Q",
! 				"Local Address", "Foreign Address", "(state)");
  			first = 0;
  		}
  		if (Aflag)
--- 124,137 ----
  			putchar('\n');
  			if (Aflag)
  				printf("%-8.8s ", "PCB");
! 			printf((Aflag | Uflag) ?
! 				"%-5.5s %-6.6s %-6.6s  %-18.18s %-18.18s" :
! 				"%-5.5s %-6.6s %-6.6s  %-22.22s %-22.22s" ,
  				"Proto", "Recv-Q", "Send-Q",
! 				"Local Address", "Foreign Address");
! 			if (Uflag)
! 				printf(" %-8.8s", "User");
! 			printf(" (state)\n");
  			first = 0;
  		}
  		if (Aflag)
***************
*** 140,145 ****
--- 143,152 ----
  			sockb.so_snd.sb_cc);
  		inetprint(&inpcb.inp_laddr, (int)inpcb.inp_lport, name);
  		inetprint(&inpcb.inp_faddr, (int)inpcb.inp_fport, name);
+ 		if (Uflag)
+ 			printf(" %-8.8s",
+ 			    ucred_to_name(
+ 				    (socket_ucred((void *)inpcb.inp_socket))));
  		if (istcp) {
  			if (tcpcb.t_state < 0 || tcpcb.t_state >= TCP_NSTATES)
  				printf(" %d", tcpcb.t_state);
***************
*** 425,431 ****
  	char line[80], *cp;
  	int width;
  
! 	sprintf(line, "%.*s.", (Aflag && !nflag) ? 12 : 16, inetname(in));
  	cp = index(line, '\0');
  	if (!nflag && port)
  		sp = getservbyport((int)port, proto);
--- 432,438 ----
  	char line[80], *cp;
  	int width;
  
! 	sprintf(line, "%.*s.", ((Aflag || Uflag) && !nflag) ? 12 : 16, inetname(in));
  	cp = index(line, '\0');
  	if (!nflag && port)
  		sp = getservbyport((int)port, proto);
***************
*** 433,439 ****
  		sprintf(cp, "%.8s", sp ? sp->s_name : "*");
  	else
  		sprintf(cp, "%d", ntohs((u_short)port));
! 	width = Aflag ? 18 : 22;
  	printf(" %-*.*s", width, width, line);
  }
  
--- 440,446 ----
  		sprintf(cp, "%.8s", sp ? sp->s_name : "*");
  	else
  		sprintf(cp, "%d", ntohs((u_short)port));
! 	width = (Aflag | Uflag) ? 18 : 22;
  	printf(" %-*.*s", width, width, line);
  }
  
diff -cb /usr/src/usr.bin/netstat/main.c netstat/main.c
*** /usr/src/usr.bin/netstat/main.c	Fri May 13 21:51:50 1994
--- netstat/main.c	Tue Jul 19 15:17:54 1994
***************
*** 46,51 ****
--- 46,52 ----
  #include <sys/file.h>
  #include <sys/protosw.h>
  #include <sys/socket.h>
+ #include <sys/ucred.h>
  
  #include <netinet/in.h>
  
***************
*** 60,65 ****
--- 61,67 ----
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
+ #include <pwd.h>
  #include "netstat.h"
  
  struct nlist nl[] = {
***************
*** 203,209 ****
  		prog = argv[0];
  	af = AF_UNSPEC;
  
! 	while ((ch = getopt(argc, argv, "Aadf:ghI:iM:mN:np:rstuw:")) != EOF)
  		switch(ch) {
  		case 'A':
  			Aflag = 1;
--- 205,211 ----
  		prog = argv[0];
  	af = AF_UNSPEC;
  
! 	while ((ch = getopt(argc, argv, "AUadf:ghI:iM:mN:np:rstuw:")) != EOF)
  		switch(ch) {
  		case 'A':
  			Aflag = 1;
***************
*** 279,284 ****
--- 281,289 ----
  		case 'u':
  			af = AF_UNIX;
  			break;
+ 		case 'U':
+ 			Uflag = 1;
+ 			break;
  		case 'w':
  			interval = atoi(optarg);
  			iflag = 1;
***************
*** 494,504 ****
  	return (NULL);
  }
  
  static void
  usage()
  {
  	(void)fprintf(stderr,
! "usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n", prog);
  	(void)fprintf(stderr,
  "       %s [-ghimnrs] [-f address_family] [-M core] [-N system]\n", prog);
  	(void)fprintf(stderr,
--- 499,533 ----
  	return (NULL);
  }
  
+ /*
+  * Get the username from the ucred struct, and return a pointer
+  * to the static data structure.
+  */
+ char *
+ ucred_to_name(creds)
+ 	void *creds;	/* actually a (struct ucred *) */
+ {
+ 	static char res[9];
+ 	struct ucred ucd, *ucp = &ucd;
+ 	struct passwd *pwent;
+ 
+ 	res[0] = '\0';
+ 	if (creds == NULL)
+ 		return res;
+ 	if (kread((u_long)creds, (char *)ucp, sizeof(*ucp)))
+ 		return res;
+ 	if ((pwent = getpwuid(ucp->cr_uid)) != NULL)
+ 		sprintf(res, "%-8.8s", pwent->pw_name);
+ 	else
+ 		sprintf(res, "%-8d", ucp->cr_uid);
+ 	return res;
+ }
+ 
  static void
  usage()
  {
  	(void)fprintf(stderr,
! "usage: %s [-AanU] [-f address_family] [-M core] [-N system]\n", prog);
  	(void)fprintf(stderr,
  "       %s [-ghimnrs] [-f address_family] [-M core] [-N system]\n", prog);
  	(void)fprintf(stderr,
diff -cb /usr/src/usr.bin/netstat/netstat.1 netstat/netstat.1
*** /usr/src/usr.bin/netstat/netstat.1	Fri May 13 21:51:53 1994
--- netstat/netstat.1	Wed Jul 20 10:42:56 1994
***************
*** 40,46 ****
  .Nd show network status
  .Sh SYNOPSIS
  .Nm netstat
! .Op Fl Aan
  .Op Fl f Ar address_family
  .Op Fl M Ar core
  .Op Fl N Ar system
--- 40,46 ----
  .Nd show network status
  .Sh SYNOPSIS
  .Nm netstat
! .Op Fl AanU
  .Op Fl f Ar address_family
  .Op Fl M Ar core
  .Op Fl N Ar system
***************
*** 172,177 ****
--- 172,180 ----
  When
  .Fl s
  is also present, show routing statistics instead.
+ .It Fl U
+ Show the username of the owner of the connection. (Implemented only for inet
+ and unix domain connections at this time.)
  .It Fl w Ar wait
  Show network interface statistics at intervals of
  .Ar wait
diff -cb /usr/src/usr.bin/netstat/netstat.h netstat/netstat.h
*** /usr/src/usr.bin/netstat/netstat.h	Fri May 13 18:08:20 1994
--- netstat/netstat.h	Tue Jul 19 15:05:55 1994
***************
*** 37,42 ****
--- 37,43 ----
  #include <sys/cdefs.h>
  
  int	Aflag;		/* show addresses of protocol control block */
+ int	Uflag;		/* show username of connection owner */
  int	aflag;		/* show all sockets (including servers) */
  int	dflag;		/* show i/f dropped packets */
  int	gflag;		/* show group (multicast) routing or stats */
***************
*** 108,110 ****
--- 109,114 ----
  
  void	mroutepr __P((u_long, u_long, u_long));
  void	mrt_stats __P((u_long, u_long));
+ 
+ char   *ucred_to_name __P((void *));
+ void   *socket_ucred __P((void *));
Common subdirectories: /usr/src/usr.bin/netstat/obj and netstat/obj
diff -cb /usr/src/usr.bin/netstat/unix.c netstat/unix.c
*** /usr/src/usr.bin/netstat/unix.c	Fri May 13 21:51:55 1994
--- netstat/unix.c	Tue Jul 19 15:06:11 1994
***************
*** 59,65 ****
  #include <stdlib.h>
  #include "netstat.h"
  
! static	void unixdomainpr __P((struct socket *, caddr_t));
  
  static struct	file *file, *fileNFILE;
  static int	nfiles;
--- 59,65 ----
  #include <stdlib.h>
  #include "netstat.h"
  
! static	void unixdomainpr __P((struct socket *, struct file *));
  
  static struct	file *file, *fileNFILE;
  static int	nfiles;
***************
*** 89,95 ****
  		/* kludge */
  		if (so->so_proto >= unixsw && so->so_proto <= unixsw + 2)
  			if (so->so_pcb)
! 				unixdomainpr(so, fp->f_data);
  	}
  }
  
--- 89,95 ----
  		/* kludge */
  		if (so->so_proto >= unixsw && so->so_proto <= unixsw + 2)
  			if (so->so_pcb)
! 				unixdomainpr(so, fp);
  	}
  }
  
***************
*** 97,110 ****
      { "#0", "stream", "dgram", "raw", "rdm", "seqpacket" };
  
  static void
! unixdomainpr(so, soaddr)
  	register struct socket *so;
! 	caddr_t soaddr;
  {
  	struct unpcb unpcb, *unp = &unpcb;
  	struct mbuf mbuf, *m;
  	struct sockaddr_un *sa;
  	static int first = 1;
  
  	if (kread((u_long)so->so_pcb, (char *)unp, sizeof (*unp)))
  		return;
--- 97,111 ----
      { "#0", "stream", "dgram", "raw", "rdm", "seqpacket" };
  
  static void
! unixdomainpr(so, fp)
  	register struct socket *so;
! 	register struct file *fp;
  {
  	struct unpcb unpcb, *unp = &unpcb;
  	struct mbuf mbuf, *m;
  	struct sockaddr_un *sa;
  	static int first = 1;
+ 	caddr_t soaddr = fp->f_data;
  
  	if (kread((u_long)so->so_pcb, (char *)unp, sizeof (*unp)))
  		return;
***************
*** 118,135 ****
  	if (first) {
  		printf("Active UNIX domain sockets\n");
  		printf(
! "%-8.8s %-6.6s %-6.6s %-6.6s %8.8s %8.8s %8.8s %8.8s Addr\n",
  		    "Address", "Type", "Recv-Q", "Send-Q",
  		    "Inode", "Conn", "Refs", "Nextref");
  		first = 0;
  	}
  	printf("%8x %-6.6s %6d %6d %8x %8x %8x %8x",
  	    soaddr, socktype[so->so_type], so->so_rcv.sb_cc, so->so_snd.sb_cc,
  	    unp->unp_vnode, unp->unp_conn,
  	    unp->unp_refs, unp->unp_nextref);
  	if (m)
  		printf(" %.*s",
  		    m->m_len - (int)(sizeof(*sa) - sizeof(sa->sun_path)),
  		    sa->sun_path);
  	putchar('\n');
  }
--- 119,167 ----
  	if (first) {
  		printf("Active UNIX domain sockets\n");
  		printf(
! 		    "%-8.8s %-6.6s %-6.6s %-6.6s %8.8s %8.8s %8.8s %8.8s",
  		    "Address", "Type", "Recv-Q", "Send-Q",
  		    "Inode", "Conn", "Refs", "Nextref");
+ 		if (Uflag)
+ 			printf(" %-8.8s", "User");
+ 		printf(" Addr\n");
  		first = 0;
  	}
  	printf("%8x %-6.6s %6d %6d %8x %8x %8x %8x",
  	    soaddr, socktype[so->so_type], so->so_rcv.sb_cc, so->so_snd.sb_cc,
  	    unp->unp_vnode, unp->unp_conn,
  	    unp->unp_refs, unp->unp_nextref);
+ 	if (Uflag)
+ 		printf(" %-8.8s", ucred_to_name((void *)fp->f_cred));
  	if (m)
  		printf(" %.*s",
  		    m->m_len - (int)(sizeof(*sa) - sizeof(sa->sun_path)),
  		    sa->sun_path);
  	putchar('\n');
+ }
+ 
+ void *
+ socket_ucred(the_sock)
+ 	void *the_sock;
+ {
+ 	register struct file *fp;
+ 	struct socket sock, *so = &sock;
+ 	char *filebuf;
+ 
+ 	filebuf = (char *)kvm_getfiles(kvmd, KERN_FILE, 0, &nfiles);
+ 	if (filebuf == 0) {
+ 		printf("Out of memory (file table).\n");
+ 		return NULL;
+ 	}
+ 	file = (struct file *)(filebuf + sizeof(fp));
+ 	fileNFILE = file + nfiles;
+ 	for (fp = file; fp < fileNFILE; fp++) {
+ 		if (fp->f_count == 0 || fp->f_type != DTYPE_SOCKET)
+ 			continue;
+ 		if (kread((u_long)fp->f_data, (char *)so, sizeof (*so)))
+ 			continue;
+ 		if ((void *)fp->f_data == the_sock)
+ 			return (void *)fp->f_cred;
+ 	}
+ 	return NULL;
  }
>Audit-Trail:
>Unformatted:


------------------------------------------------------------------------------