#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <net/if_utun.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>	// malloc
//#undef ntohs
#include <arpa/inet.h> // inet_ntop
 
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <netinet/in.h>
#include <time.h>
#include "lsock.h"
#include <ncurses.h>

#include <netdb.h> // GAI
int g_debug =0;


int noResolve = 0;
int wantCurses =1;
int wantHumanReadable = 0;
int wantPackets = 0;
int wantOnce = 0;
int wantUDP = 1;
int wantListen = 1;
int wantTCP = 1;
int wantColors =0;
int wantRefresh = 0;

// JDELAY because DELAY is #defined elsewhere..

#define JDELAY		100000// 0.1 second

/**
  * lsock.c : A TCPView inspired netstat(1)-clone (in text mode) for OS X/iOS
  *
  * Coded by Jonathan Levin - http://www.newosxbook.com/
  *
  * Consider this the missing source of nettop (with a few improvements):
  * AAPL uses the com.apple.network.statistics socket through the NetworkStatistics PrivateFramework.
  * This basically shows the same usage, without the dispatch queues and with direct access to the
  * system socket.
  * 
  * No (C). Distribute at will. It would be appreciate if you gave credit, though.
  *
  * SPECIFICALLY those #%$#%ers who rip my sources and put them on GitHUB
  * as if they're their own.
  *
  * If you want to use this commercially, please let me know first.
  *
  * The makefile, universal binary and iOS CLI compilation script can be found in the tar this came in.
  *
  * Note you need "lsock.h" to compile (these are the ntstat headers from kernel mode, which AAPL
  * never bothered to publish). You can find the .h in the same URL you found this, or in the tar file.
  *
  * ----------------------------------------------------------------------------------------------
  * Arguments: "nc" - disable full-screen (will automatically disable curses if piping output)
  *            "once" - one shot - automatically disables curses 
  *            "tcp" - start in TCP-only mode
  *            "udp" - start in UDP-only mode
  *
  * Commands for full screen mode: 'T' - TCP, 'U' - UDP, 'L' - Listeners, 'C' - Colors
  *  'R' - Name Resolution 
  *
  * In CLI/nc mode, delimiters are tabs - this makes it useful to use this command as a continuous
  * source for cut -d'(tab)' and select only the fields which interest you.
  *
  * Possible improvements 
  *
  *    - GUI: Make this more like Windows' TCPView and Procmon
  *    - Remote monitoring
  *  <del>- Periodic polling on sources: Currently only when a source notification is received do
  *      I poll its description</del>
  *    - Add routing provider (prov #1)
  *    - Show provider statistics, too
  *
  * ChangeLog:
  * ----------
  * 02/16/2016 - Almost annual update..
  *
  *              - Updated for iOS 10/OS X 10.12, because AAPL changed format (AGAIN)
  *              - Much better error handling
  *              - added atexit(3) handling to reset term
  *
  * 03/16/2016 - Wow. It's been a while...
  *      
  *              - Updated for iOS 9/OSX 10.11
  *              - Message now at first row, table starts at second. Nicer output this way
  *              - Added "-n" to NOT resolve IPs. Now doing so by default - the Sergio fix.
  *
  *
  * 06/14/2014 - Plenty of improvements, including:
  *              Fixed bug in the public version which prevented binding if any other ntstat
  *               client was registered.. (e.g. UserEventAgent - This one's for you, Jason)
  *
  *              Ordered requests to control socket so that all descriptors are shown, always (no race, etc)
  *
  *              Updated for Yosemite and Mavericks (tested on ML, Yosemite, iOS 7, but not on Mavericks or iOS8)
  *               When the folks @Cupertino share the xnu 27 headers, I'll update again
  *
  *              Added packet/byte counts (makes this tool so much more useful)
  *
  *              Added "once" mode (basically, an improved, colorful netstat(1) - run and exit)
  *
  *		 Added 'K' (display in K/M/G), 'P' (display packets/bytes), and more.
  *
  *
  * TODO: When network activity is high, I poll counts (1004) frequently, which leads to high CPU
  *       utilization (but accurate results :-)
  *
  *       better colors?
  *
  *       use gai to resolve port numbers
  * 
  *       Format IPv6 better (full addresses mess up tabulation)
  *
  *       State of TCP connections (especially in nc mode)
  *
  *       No toggle of udp/tcp/packets for "once" (in fact, just one command line argument)
  *
  *       Assimilate nettop entirely 
  * 
  *  (I'm integrating nettop into procexp, so no promises about maintaining this - but
  *   your emails and comments are always welcome)
  *
  */

int process_nstat_msg (void *msg, int size);

int g_OsVer = 12; // Sierra by default

char *resolveAddr (struct sockaddr *addr, int family)
{

           static char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

           if (getnameinfo(addr, addr->sa_len, hbuf, sizeof(hbuf), sbuf,
               sizeof(sbuf),NI_NAMEREQD  )) {
	
		return NULL;
           }

	   return (hbuf);




} // resolveAddr


void getOsVer()
{ 

	struct utsname name;

	uname (&name);
       // ugly hack, for now.. This works with iOS as well, since the structs are common

	  if (strstr(name.version, "xnu-24")) { g_OsVer = 9; }
	  if (strstr(name.version, "xnu-20")) { g_OsVer = 8; }
	  if (strstr(name.version, "xnu-27")) { g_OsVer = 10; }
	  if (strstr(name.version, "xnu-32")) { g_OsVer = 11; }
	  if (strstr(name.version, "xnu-37")) { g_OsVer = 12; }


}


int fd;
void print_header (WantCurses)
{
	char *header = "Time      PID   Name              \tLocal Addr         \tRemote Addr              \tIf\tState    \tRX\t TX";
	if (WantCurses) {
	 
	attrset(COLOR_PAIR(0));
	 attrset(A_UNDERLINE| A_BOLD);
 	 mvwaddstr(stdscr, 1,0 ,header);
	attroff(A_UNDERLINE); attron(A_NORMAL);
	attrset(COLOR_PAIR(0));
	}
	else printf ("%s\n",  header);

}
int setupSocket(void)
{
        struct sockaddr_ctl sc;
        struct ctl_info ctlInfo;
        int fd;


        memset(&ctlInfo, 0, sizeof(ctlInfo));
        if (strlcpy(ctlInfo.ctl_name, "com.apple.network.statistics", sizeof(ctlInfo.ctl_name)) >=
            sizeof(ctlInfo.ctl_name)) {
                fprintf(stderr,"CONTROL NAME too long");
                return -1;
        }
        if ((fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL)) == -1) {
                fprintf(stderr,"socket(SYSPROTO_CONTROL): %s", strerror(errno));
                return -1;

        }
        if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) {
                fprintf(stderr,"ioctl(CTLIOCGINFO): %s", strerror(errno));
                close(fd);
                return -1;
        }
        sc.sc_id = ctlInfo.ctl_id;
        sc.sc_len = sizeof(sc);
        sc.sc_family = AF_SYSTEM;
        sc.ss_sysaddr = AF_SYS_CONTROL;



/*
	int bufsize = 0x100000;
	int bufsize_len = sizeof(int);

 	int rc =setsockopt (fd, SOL_SOCKET, SO_RCVBUF  , &bufsize, &bufsize_len);
	printf("SET SOCKOPT %d\n", rc);
*/



	// The open source had a bug here, asking for num +1.
 	// which meant that it refused to work unless no other clients were
	// connected.
        sc.sc_unit = 0 ;           /* zero means unspecified */
        if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) {
                fprintf(stderr,"connect(AF_SYS_CONTROL): %s\n", strerror(errno));
		if (errno == EBUSY )
		{
		   printf("Apparently some other process is using the system socket. "
		   "On iOS, this is usually UserEventAgent. Kill it (-9!) and immediately start this program\n");
		}

                close(fd);
                return -1;
        }
         return fd;
}


const char *stateToText(int State)
{

  switch(State)
	{
		case TCPS_CLOSED: return ("CLOSED");
		case TCPS_LISTEN: return ("LISTENING");
		case TCPS_ESTABLISHED: return ("ESTABLISHED");
		case TCPS_CLOSING: return ("CLOSING");
		case TCPS_SYN_SENT: return ("SYN_SENT");
		case TCPS_LAST_ACK: return ("LAST_ACK");
		case TCPS_CLOSE_WAIT: return ("CLOSE_WAIT");
		case TCPS_TIME_WAIT: return ("TIME_WAIT");
		case TCPS_FIN_WAIT_1 : return ("FIN_WAIT_1");
		case TCPS_FIN_WAIT_2 : return ("FIN_WAIT_2");

	
		default:
		  return("?");
	}

} // stateToText

#define MAX_DESC 2000
char *descriptors[MAX_DESC];
nstat_counts descriptorCounts[MAX_DESC];


int maxDesc = 0;


char *message = "                                          ";

void setMessage (char *Message)
{
 	message = Message;
}

void print_descriptors()
{

	int i;
	int j = 2;


	if (!wantUDP && !wantTCP) { setMessage ( "Warning: Both TCP and UDP are filtered");}
	if ( wantCurses) {
			attrset(COLOR_PAIR(0) | A_BOLD);
			mvwaddstr(stdscr, 0,0 ,message);

			attroff(A_BOLD);
			clrtoeol();
			print_header(wantCurses);
			}

	if (wantCurses && wantRefresh)
	{
		wantRefresh = 0;erase();
	}

	for (i = 0; i < MAX_DESC; i++)
	{	
		if (descriptors[i] && strcmp(descriptors[i], "reading") !=0)
		{
			// This is a quick and dirty example - So instead of mucking around with
			// descriptor data structures, I look at the text output of each, and figure
 			// out from it whether or not it's UDP, or the TCP state, etc..

			if (strstr(descriptors[i], "N/A")) // UDP have a state of "N/A"
			{
				if ( ! wantUDP) continue;
				if (wantColors) {
				  if (wantCurses) { init_pair(4,COLOR_CYAN, COLOR_BLACK); attrset(COLOR_PAIR(4)); }
				 else
				    printf (GREEN);
				
				}
			}
			else // TCP (not showing route for now)
			{
				if (!wantTCP) continue;
				
				if (wantColors)
					{ 
					if (wantCurses)	attrset(COLOR_PAIR(0)); else printf(NORMAL);
					}
		 		if (strstr(descriptors[i], "ESTABLISHED") )  {
					if (wantColors)
						{
					if (wantCurses) { init_pair(3, COLOR_GREEN, COLOR_BLACK); attrset(COLOR_PAIR(3));}
					else printf(GREEN); 
						}
					} // ESTABLISHED

		 		if (strstr(descriptors[i], "LISTEN") )  {
					if (!wantListen) continue;
					if (wantColors)
					  	{
					if (wantCurses) { init_pair(2, COLOR_WHITE, COLOR_BLACK); attrset(COLOR_PAIR(2));}
					else printf(BLUE); 
						}
					} // ESTABLISHED

		 		if (strstr(descriptors[i], "SYN_SENT") ) {
					if (wantColors) {
					 if ( wantCurses) { init_pair(5, COLOR_RED, COLOR_BLACK); attrset(COLOR_PAIR(5));}
					else printf(RED); 
						}
					} // SYN_SENT


			} // TCP

			if (wantCurses) {
 	 		mvwaddstr(stdscr, j,0 ,descriptors[i]);
			clrtoeol();
			}
			else
			{ 
				printf( "%s\n", descriptors[i]); 

			}
		   
			j++;

					if (wantColors) {
					 if ( wantCurses) { attrset(COLOR_PAIR(0));}
					else printf(NORMAL); 
						}
			}

	}

	clrtobot();
	refresh();
}

char *print_udp_descriptor (char *desc, int prov, int num)
{

   nstat_udp_descriptor *nud = (nstat_udp_descriptor *) desc;

   

  int ipv6 = 0;
  char localAddrPrint[40];
  char remoteAddrPrint[40];
  unsigned short localPort, remotePort;
  char timeBuf[30];
  time_t	now;
  struct tm *n;
  char *returned = (char *) malloc(1024);
  char *resolved;

  returned[0]  ='\0'; // Dont need AF_ because we have this already in the address, right?
  time(&now);
   n = localtime(&now);
   
    memset(timeBuf,'\0', 30);
  strftime(timeBuf, 29, "%H:%M:%S", n);
 // sprintf(timeBuf, "%d......", num);
   //sprintf (returned + strlen(returned), "%-8s ", timeBuf);

	sprintf(returned + strlen(returned), "%d", num);
   sprintf(returned + strlen(returned), "%6d\t", nud->pid);
   if (nud->pname[0]) { sprintf(returned + strlen(returned), "%-16s\t",   nud->pname );}
   else { sprintf(returned + strlen(returned), "                  \t");}
 

   if (nud->local.v4.sin_family == AF_INET6) {
		ipv6++; }
  if (!ipv6)
  {
     localPort = ntohs(nud->local.v4.sin_port);
     remotePort = ntohs(nud->remote.v4.sin_port);
  }
  else {
     localPort = ntohs(nud->local.v6.sin6_port);
     remotePort = ntohs(nud->remote.v6.sin6_port);
  }
  
  if (nud->remote.v6.sin6_family == AF_INET6) {
       inet_ntop(nud->local.v6.sin6_family,&(nud->local.v6.sin6_addr), localAddrPrint, 40);
	inet_ntop(nud->remote.v6.sin6_family,&(nud->remote.v6.sin6_addr), remoteAddrPrint, 40);

	if (!noResolve) resolved = resolveAddr(&(nud->remote.v6), AF_INET6);

	if (noResolve || !resolved) resolved = remoteAddrPrint;
  
 }
  if (nud->remote.v4.sin_family == AF_INET) {
        inet_ntop(nud->local.v4.sin_family,&(nud->local.v4.sin_addr), localAddrPrint, 40);
	inet_ntop(nud->remote.v4.sin_family,&(nud->remote.v4.sin_addr), remoteAddrPrint, 40);

        if (!noResolve) resolved = resolveAddr(&(nud->remote.v4), AF_INET);
	
	if (noResolve || !resolved) resolved = remoteAddrPrint;
			     
  
     }
  
  //if (localAddrPrint)
	 {
	sprintf (localAddrPrint + strlen(localAddrPrint),":%-5hu", (unsigned short) localPort);
	if (remotePort == 0)
	{
        sprintf(returned + strlen(returned),"%-21s\t*.*                    \t        ", localAddrPrint);

	}
 	else
        sprintf(returned + strlen(returned), "%-21s\t%-21s %-5hu\t", localAddrPrint, resolved, (unsigned short) remotePort);
  
     }
      sprintf(returned + strlen(returned), "%d\tN/A         \t", nud->ifindex);

   if (wantPackets)
      sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxpackets, descriptorCounts[num].nstat_txpackets);
   else
      sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxbytes, descriptorCounts[num].nstat_txbytes);


   return (returned);

}

void refreshSrc (int Prov, int Num)
{
	int rc ;
	nstat_msg_get_src_description gsdreq;

        gsdreq.hdr.type= 1005; //NSTAT_MSG_TYPE_GET_SRC_DESC
        gsdreq.srcref=Num;
        gsdreq.hdr.context = 2410; ;//;nmsa->provider;
        rc = write (fd, &gsdreq, sizeof(gsdreq));

	// Get reply
	char c[2048];
	rc = read(fd,c,2048);
	nstat_msg_hdr *ns = (nstat_msg_hdr *) c;

	process_nstat_msg (ns, rc);


}

// @TODO: Merge common into print_descriptor, then udp/tcp specifics in each of these
//        functions..

char *print_tcp_descriptor (char *desc, int prov, int num)
{

#ifdef PRE_37xx
   nstat_tcp_descriptor *ntd = (nstat_tcp_descriptor *) desc;
#else
   nstat_tcp_descriptor_10_12 *ntd = (nstat_tcp_descriptor_10_12 *) desc;
#endif

   

#if 0
  int i = 0;
  fprintf(stderr, "\nDESC:\n 0: ");
  for (i = 0; i < 160; i++)
	{
		unsigned char *debug = (char *) ntd;
		fprintf (stderr, "%x ", debug[i]);
		if (i %16 == 15) { fprintf(stderr, "\n%d: ", i+1);}
	}

#endif


  int ipv6 = 0;
  char localAddrPrint[40];
  char remoteAddrPrint[40];
  unsigned short localPort, remotePort;
  char timeBuf[30];
  time_t	now;
  struct tm *n;

  char *returned = (char *) malloc(1024);

  returned[0]  ='\0'; // Dont need AF_ because we have this already in the address, right?
  time(&now);
   n = localtime(&now); 
  strftime(timeBuf, 29, "%H:%M:%S", n);

 //sprintf(timeBuf, "%d......", num);
 //  sprintf (returned + strlen(returned), "%-8s ", timeBuf);

	sprintf(returned + strlen(returned), "%d", num);


   // This was changed in ML - this might change again..
   char *pname;
   int  pid;
   char *resolved =NULL;

   switch (g_OsVer)
	{
		case 7:
			pid = ntd->pid;
			pname = (char *)&ntd->pname;
			break;
		case 8: case 9:
			pid = ((nstat_tcp_descriptor_10_8 *) ntd)->pid;
			pname = ((nstat_tcp_descriptor_10_8 *) ntd)->pname;
			break;
		case 10:
			pid = ((nstat_tcp_descriptor_10_10 *) ntd)->pid;
			pname = ((nstat_tcp_descriptor_10_10 *) ntd)->pname;
			break;
		case 11: case 12:
			pid = ((nstat_tcp_descriptor_10_11 *) ntd)->pid;
			pname = ((nstat_tcp_descriptor_10_11 *) ntd)->pname;
			break;
		
			
		default: // now changed to 10.12..
			// When we get to 10.12, we'll deal with it.. for now , stay silent
			pname = NULL;
			pid = -1;
			break;

       }

   sprintf(returned + strlen(returned), "%6d\t", pid);

   if (pname[0]) { sprintf(returned + strlen(returned), "%-16s\t",  pname );}
   else { sprintf(returned + strlen(returned), "                 \t");}


// sprintf(returned+strlen(returned), "%d\t", ntd->sndbufused);

   if (ntd->local.v4.sin_family == AF_INET6) {
		//printf("IPv6\t"); 
		ipv6++; }
  if (!ipv6)
  {
     localPort = ntohs(ntd->local.v4.sin_port);
     remotePort = ntohs(ntd->remote.v4.sin_port);
  }
  else {
     localPort = ntohs(ntd->local.v6.sin6_port);
     remotePort = ntohs(ntd->remote.v6.sin6_port);
  }
  
  if (ntd->remote.v6.sin6_family == AF_INET6) {
        inet_ntop(ntd->local.v6.sin6_family,&(ntd->local.v6.sin6_addr), localAddrPrint, 40);
	inet_ntop(ntd->remote.v6.sin6_family,&(ntd->remote.v6.sin6_addr), remoteAddrPrint, 40);
	if (!noResolve) { resolved = resolveAddr(&(ntd->remote.v6), AF_INET6); }
	if (noResolve || !resolved)  resolved = remoteAddrPrint;
	
  
 } // AF_INET 6

  if (ntd->remote.v4.sin_family == AF_INET) {
        inet_ntop(ntd->local.v4.sin_family,&(ntd->local.v4.sin_addr), localAddrPrint, 40);
	inet_ntop(ntd->remote.v4.sin_family,&(ntd->remote.v4.sin_addr), remoteAddrPrint, 40);

	if (!noResolve) { resolved = resolveAddr(&(ntd->remote.v4), AF_INET); }
	if (noResolve || !resolved)  resolved = remoteAddrPrint;
  
 }
  
  if (resolved[0])
	{
	   sprintf (remoteAddrPrint + strlen(remoteAddrPrint),":%-5hu", (unsigned short) remotePort);

	}
	else sprintf (remoteAddrPrint, "*.*");

 //  if (localAddrPrint) 
	{
	 sprintf (localAddrPrint + strlen(localAddrPrint),":%-5hu", (unsigned short) localPort);
	if (remotePort == 0)
	{
        sprintf(returned + strlen(returned),"%-21s\t*.*                    \t        ", localAddrPrint);

	}
 	else
        sprintf(returned + strlen(returned), "%-21s\t%-25s\t", localAddrPrint,  resolved);
  
     }

   sprintf(returned + strlen(returned), "%d\t%-10s\t", ntd->ifindex,  stateToText(ntd->state));

   if (wantPackets)
      sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxpackets, descriptorCounts[num].nstat_txpackets);
   else
      sprintf(returned+strlen(returned), "%lld\t%lld", descriptorCounts[num].nstat_rxbytes, descriptorCounts[num].nstat_txbytes);
 
   if ( strstr(returned, "CLOSED") && strstr(returned,"("))
	{
		refreshSrc(prov,num);

	}
   return (returned);

}

void humanReadable (uint64_t	number, char *output)
{
	char unit = 'B';
	
  	float num = (float) number;
	if (num < 1024) {}
	else if ( num < 1024*1024) { unit='K'; num /=1024;}
	else if ( num < 1024*1024*1024) { unit='M'; num /=(1024*1024);}
	else if ( num < 1024UL*1024UL*1024UL*1024UL) { unit='G'; num /=(1024*1024 *1024);}
	else { // let's not get carried away here... 
	     }
	
	sprintf (output, "%5.2f%c", num, unit);


}

void update_provider_statistics (int prov)
{
	// the descriptors array holds a textual printable output. I'm piggybacking on that fact
	// and doing minor string parsing, rather than keep all the src_description structs in 
  	// memory. Let's not forget this is PoC code

	char *desc = descriptors[prov];
	
	if (desc)
	{
		// get last tab..
		char *col = strrchr (desc,'\t');
		// get penultimate tab
		if (col)
		{
		   *col ='\0';
		   col = strrchr (desc,'\t');
		   if (col)
		   {
		   *(col) = '\0';
	
	char humanReadableRX [15];
	char humanReadableTX [15];

   if (wantPackets)
      sprintf(desc + strlen(desc), "\t%7lld\t%7lld", descriptorCounts[prov].nstat_rxpackets, descriptorCounts[prov].nstat_txpackets);
   else
      if (wantHumanReadable)
	{
	  humanReadable(descriptorCounts[prov].nstat_rxbytes, humanReadableRX);
	  humanReadable(descriptorCounts[prov].nstat_txbytes, humanReadableTX);
	  sprintf(desc + strlen(desc), "\t%s\t%s", humanReadableRX, humanReadableTX);

	}
	else
	{
      sprintf(desc + strlen(desc), "\t%lld\t%lld", descriptorCounts[prov].nstat_rxbytes, descriptorCounts[prov].nstat_txbytes);
	}

		   }
		}

	}
}

int process_nstat_msg (void *msg, int size)
{

  nstat_msg_hdr *ns = (nstat_msg_hdr *) msg;
  
  nstat_msg_get_src_description gsdreq;
  int rc = 0;
  switch (ns->type)
	{


	case NSTAT_MSG_TYPE_SRC_ADDED : // 10001:
	  {
  	   nstat_msg_src_added *nmsa = (nstat_msg_src_added *) (ns);
	  uint64_t i = nmsa->srcref;
		// Initialize counts for a new source
	 memset(&descriptorCounts[i], '\0', sizeof(nstat_counts));
 if (g_debug) { fprintf(stderr,"added src %llx\n", nmsa->srcref); }
	   descriptors[i] = "reading"; //nmsa->srcref] = "reading";

	   gsdreq.hdr.type= 1005; //NSTAT_MSG_TYPE_GET_SRC_DESC
	   gsdreq.srcref=(int)nmsa->srcref;
	   gsdreq.hdr.context = 2411; // This way I can tell if errors get returned for dead sources
	   rc = write (fd, &gsdreq, sizeof(gsdreq));

	   break;
	  }
	case NSTAT_MSG_TYPE_SRC_REMOVED: //             = 10002
	  {
		//struct nstat_msg_src_removed *rem;

  	   nstat_msg_src_removed *nmsr = (nstat_msg_src_removed *) (ns);
		if (g_debug) { fprintf(stderr,"removed src %llx\n", nmsr->srcref); }
	   if (descriptors[nmsr->srcref]) 
		{
	   	free(descriptors[nmsr->srcref] ); // = NULL; // @TODO:  free..
	   	descriptors[nmsr->srcref]  = NULL; // @TODO:  free..

		 // force a refresh
		 if (wantCurses)
			{
			  print_descriptors();
			}
		}
	

	   
	   break;
	  }


           case NSTAT_MSG_TYPE_SRC_COUNTS: //              = 10004
		{
		nstat_msg_src_counts *nmsc = (nstat_msg_src_counts *) (ns );
		memcpy (&(descriptorCounts[nmsc->srcref]), &(nmsc->counts), sizeof (nstat_counts));
		update_provider_statistics (nmsc->srcref);
		} 

		break;

	   case NSTAT_MSG_TYPE_SRC_DESC: //                = 10003
	     {
#ifdef PRE_37xx
	        nstat_msg_src_description *nmsd = (nstat_msg_src_description *) (ns );
#else
	        nstat_msg_src_description *nmsd = (nstat_msg_src_description *) (ns );
#endif
		

		switch (nmsd->provider)
		{
			case NSTAT_PROVIDER_TCP_KERNEL:
			case NSTAT_PROVIDER_TCP_USERLAND:

				descriptors[nmsd->srcref]  = print_tcp_descriptor((char *)nmsd->data, nmsd->provider,nmsd->srcref);
				break;
			case NSTAT_PROVIDER_UDP_KERNEL:
			case NSTAT_PROVIDER_UDP_USERLAND:
         			descriptors[nmsd->srcref] = print_udp_descriptor((char *) nmsd->data, nmsd->provider, nmsd->srcref);
				break;
	
		}

		if (g_debug) { fprintf(stderr,"Description for src %llx (provider %d) - %s:\n", nmsd->srcref, nmsd->provider, descriptors[nmsd->srcref]); }

	     }
	      break;

	
	    case 1: 
		{
			if (ns->context < MAX_DESC && !descriptors[ns->context]) break;
        	nstat_msg_error *err = (nstat_msg_error *) ns;
       // printf("Error: %s (%d) for context %d\n", strerror(err->error),err->error, ns->context); return rc; 

	 break; 
		}
	    case 0:  printf("Success for context %x\n", ns->context); break;
		break;
	   default:
		printf("Type : %d\n", ns->type);
	}
	fflush(NULL);
return (ns->type) ;
}


int addAll(int fd, int provider)
{
	int rc;

// #@$@#%$#% AAPL can't keep anything internal stable for more than one generation

// 02/15/2017
 if (g_OsVer > 11) {
        struct nstat_msg_add_all_srcs_as_of_xnu_37xx aasreq;
	bzero( &aasreq, sizeof(aasreq));
	
        aasreq.provider = provider ;
        aasreq.hdr.type = NSTAT_MSG_TYPE_ADD_ALL_SRCS;
        aasreq.hdr.context = 3;
        rc = write (fd, &aasreq, sizeof(aasreq));

	}
 else
 if (g_OsVer > 10) {
        nstat_msg_add_all_srcs_as_of_10_11 aasreq;
        aasreq.provider = provider ;
        aasreq.hdr.type = NSTAT_MSG_TYPE_ADD_ALL_SRCS;
        aasreq.hdr.context = 3;
       rc = write (fd, &aasreq, sizeof(aasreq));
        
    }
  else 
	{

		nstat_msg_add_all_srcs aasreq;
		aasreq.provider = provider ;
		aasreq.hdr.type = NSTAT_MSG_TYPE_ADD_ALL_SRCS;
		aasreq.hdr.context = 3;
		rc = write (fd, &aasreq, sizeof(aasreq));
	}

	if (rc < 0) 
	{
		fprintf(stderr,"addAll: Error during write(2) operation : %s\n", strerror(errno));
		return (rc);
	     }

	// no process reply

	for (;;) // we'll exit on error on success anyway
	{
	char c[20480];
	rc = read(fd,c,20480);
	
	if (rc < 0)
	{
		fprintf(stderr,"addAll: Error during read(2) of reply : %s\n", strerror(errno));

	      return (rc);
	}
	
	nstat_msg_hdr *ns = (nstat_msg_hdr *) c;
	if (ns->type == 1)
	{
        nstat_msg_error *err = (nstat_msg_error *) ns;
        printf("Error: %s (%d) for context %d\n", strerror(err->error),err->error, ns->context);  ///exit(err->error);
	
	}
	
	else if (ns->type == 0) { 
		if (g_debug)fprintf(stderr,"Source added successfully\n");
		break; /* OK */ }
	else  { //printf("RC: %d, type: %d\n", rc, ns->type); 
			process_nstat_msg(ns,rc);}// { process_nstat_msg(ns, rc);} // }fprintf(stderr,"Unrecognized reply: %d\n", ns->type);}
	}

	// now refresh


	int src = 0;
	for (src = 0; src < MAX_DESC; src++)
	{
		if ((descriptors[src])) //  && (strcmp(descriptors[src], "reading") == 0))
		{
		fprintf(stderr,"REFRESHING %d\n", src);
			refreshSrc(provider, src);
		}
	}

	return 0;
}


void printHelp()
{
   fprintf (stderr,"Usage:\n");
   fprintf (stderr,"\tlsock       \tRun in curses (full screen) mode\n");
   fprintf (stderr,"\tlsock tcp   \tRun in curses mode, but filter on TCP only (can change with 'T')\n");
   fprintf (stderr,"\tlsock udp   \tRun in curses mode, but filter on UDP only (can change with 'U')\n");
   fprintf (stderr,"\tlsock nc    \tRun in filter mode, ongoing\n");
   fprintf (stderr,"\tlsock once  \tRun in filter mode, and exit\n");

   fprintf (stderr,"\tNote ncurses is broken on iOS - either install from cydia, or use supplied 78/x256-color file (TERMINFO=.)\n");

   fprintf (stderr,"\nIn curses mode, you can try the following keys:\n");
   fprintf (stderr,"\tC\tToggle colors on/off (recommended: on)\n");
   fprintf (stderr,"\tK\tDisplay byte counts in K/M/G\n");
   fprintf (stderr,"\tP\tToggle byte or packet counts\n");
   fprintf (stderr,"\tT\tToggle TCP on/off\n");
   fprintf (stderr,"\tU\tToggle UDP on/off\n");

}


int querySources(int fd, int ref)
{
	int rc;
	nstat_msg_query_src_req qsreq = { 0};
	qsreq.hdr.type = ref; //NSTAT_MSG_TYPE_QUERY_SRC   ; // 1004

	qsreq.srcref= NSTAT_SRC_REF_ALL;
	qsreq.hdr.context = 1025; // This way I can tell if errors get returned for dead sources

	
	rc = write (fd, &qsreq, sizeof(qsreq));


	char buf[20480];
	errno = 0;
	rc = read (fd, buf, 20480);
	//endwin();
	errno = 0;
	nstat_msg_hdr *ns = (nstat_msg_hdr *) buf;

	if (ns->type ==1)
	{
		
		//printf("ERROR: %d on context %d\n", ((nstat_msg_error *) ns)->error, ns->context);
		//exit(0);
	}
	else { 
		// printf("GOT %d bytes - %d, TYPE: %d\n", rc, ns->context, ns->type);
		process_nstat_msg (buf,rc);
	}

   

}



void resetTerm(void)
{
	if (wantCurses) { endwin();}

}




int main (int argc, char **argv)
{

  atexit(resetTerm);

 if (getenv("JDEBUG")) g_debug++;
 
 int rc = 0;
 char c[20480];


 getOsVer();
 memset (&descriptors, '\0', sizeof(char *) * MAX_DESC);
  fd =setupSocket ();

 if (fd == -1)
	{
		fprintf(stderr,"Unable to continue without socket\n"); exit(1);
	}
 struct stat stbuf;
 fstat(1, &stbuf);
 
 if (stbuf.st_mode & S_IFCHR) { wantColors = 1; wantCurses = 1;} else {wantColors = 0; wantCurses = 0;}
 

 int arg = 1;
 for (arg = 1; arg < argc; arg++)
      {
  if (strcmp(argv[arg],"-n") == 0) noResolve = 1;
else
 if (strcmp(argv[arg], "nc") == 0) wantCurses = 0;
 else if (strcmp(argv[arg], "once") == 0) {wantCurses = 0; wantOnce = 1; wantColors = 0;}
 else if (strcmp(argv[arg], "udp") == 0) { wantUDP = 1; wantTCP = 0;}
 else if (strcmp(argv[arg], "tcp") == 0)  { wantTCP = 1; wantUDP = 0;}
 else { printHelp(); exit(2);}
	}

// wantOnce = 1; wantCurses = 0 ;wantOnce= 1; wantColors=0;
if (getenv("JCOLOR")) wantColors =1;
else wantColors = 0;



nstat_msg_query_src_req qsreq;


char ch;



// Want both - we'll just filter

int udpAdded = 0;
int tcpAdded = 0 ;
int gettingCounts = 0 ;
int gotCounts = 0;

if (wantTCP)
	{
 	rc = addAll (fd, NSTAT_PROVIDER_TCP_KERNEL);
 	if (rc !=0){ fprintf(stderr,"Unable to add TCP kernel provider - exiting\n"); exit(1);}
 	rc = addAll (fd, NSTAT_PROVIDER_TCP_USERLAND);
 	if (rc !=0){ fprintf(stderr,"Unable to add TCP userland provider - exiting\n"); exit(1);}
	}

if (wantUDP)
	{
 	rc = addAll (fd, NSTAT_PROVIDER_UDP_KERNEL);
 	if (rc !=0){ fprintf(stderr,"Unable to add UDP kernel provider - exiting\n"); exit(1);}
 	rc = addAll (fd, NSTAT_PROVIDER_UDP_USERLAND);
 	if (rc !=0){ fprintf(stderr,"Unable to add UDP userland provider - exiting\n"); exit(1);}

	}


 if (wantCurses)
	{
  initscr();  cbreak(); 
 noecho();
  start_color();
 
 nodelay (stdscr,1);

	}


   print_header(wantCurses);

   while (1) { 

   if (wantCurses || wantOnce)
	{
		fd_set	fds;
		struct timeval to;
		to.tv_sec = 0;
		to.tv_usec =  JDELAY;
		FD_ZERO (&fds);
		FD_SET (fd, &fds);
		// select on socket, rather than read..
		rc = select(fd + 1, &fds, NULL, NULL, &to);

		if (rc > 0) {
			rc = read(fd,c,20480);
			}
		else
		{
		// Timed out on select: now we can print
		if (wantOnce) { print_descriptors(); exit(0);}
		else {

			print_descriptors();
			querySources(fd, 0);
		     }



		}

	}
  else
     rc = read (fd, c, 2048);

  // check rc. Meh.

  if (rc > 0)
   {

     nstat_msg_hdr *ns = (nstat_msg_hdr *) c;


  switch (ns->type)
   {
	
	case 10001: case 10002: case 10003: case 10004:
 		 rc = process_nstat_msg(c,rc);
		break;

	case 0: 
	   // Got all sources, or all counts
	   // if we were getting sources, ask now for all descriptions

	   if (!tcpAdded) 
		{ tcpAdded++;

		   addAll (fd, NSTAT_PROVIDER_TCP_USERLAND);
		   addAll (fd, NSTAT_PROVIDER_TCP_KERNEL);

		}


	   else { 
		if (!udpAdded) udpAdded++; 

		   addAll (fd, NSTAT_PROVIDER_UDP_KERNEL);
		   addAll (fd, NSTAT_PROVIDER_UDP_USERLAND);
		}

	   if (tcpAdded && udpAdded )
	    {
		if (!gettingCounts)
		{
	    		qsreq.hdr.type= NSTAT_MSG_TYPE_QUERY_SRC   ; // 1004
                        qsreq.srcref= NSTAT_SRC_REF_ALL; //0xffffffff; //NSTAT_SRC_REF_ALL;
                        qsreq.hdr.context = 1005; // This way I can tell if errors get returned for dead sources


                        rc = write (fd, &qsreq, sizeof(qsreq));
			gettingCounts++;
		}
	  	else  gotCounts++;
		
	    }


	break;

	case 1:
	    //Error message - these are usually for dead sources (context will be 1005)
	{
	nstat_msg_error *err = (nstat_msg_error *) ns;
	printf("Error: %s (%d)\n", strerror(err->error),err->error); 


		break;
	}
	
	default:

	break;
	
   }
  
	}

 if (wantCurses)
  {
        ch = getch(); // could do lowercase instead of 'H'/'h', etc...

	if (ch != ERR) {
	if (ch == 'H' || ch =='h') printf("Too late now :-)\n");
	if (ch == 'U' || ch =='u') {wantUDP = !wantUDP; setMessage(wantUDP ? "Showing UDP Sockets" :"Not showing UDP sockets"); }
	if (ch == 'T' || ch == 't') { wantTCP = !wantTCP; setMessage(wantTCP ? "Showing TCP Sockets" : "Not showing TCP sockets"); }
	if (ch == 'L' || ch == 'l') { wantListen= !wantListen; setMessage("Toggling TCP listeners"); }

	if (ch == 'P' || ch == 'p') { wantPackets = !wantPackets; setMessage(wantPackets ? "Showing Packets" : "Showing Bytes"); }
	if (ch == 'C' || ch == 'c') { wantColors = !wantColors; setMessage("Toggling color display"); }
	if (ch == 'K' || ch == 'k') { wantHumanReadable = !wantHumanReadable; }
	if (ch == 'R' || ch == 'r') { noResolve = !noResolve; setMessage(noResolve ? "Not resolving IPs from now on" : "Resolving IPs from now on"); }
	if (ch =='Q' || ch == 'q') {  if (wantCurses) {endwin();}exit(0);}
	}
  }


  if (wantOnce > 1) exit(24);
  
  if (!wantOnce && gotCounts) { 
	   print_descriptors();
	   if (!wantCurses) memset (&descriptors, '\0', sizeof(char *) * MAX_DESC); 
	}



} // end while


return (0);

}