1/* netstat.c - Display Linux networking subsystem.
2 *
3 * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
5 *
6 * Not in SUSv4.
7 *
8USE_NETSTAT(NEWTOY(netstat, "pWrxwutneal", TOYFLAG_BIN))
9config NETSTAT
10  bool "netstat"
11  default n
12  help
13    usage: netstat [-pWrxwutneal]
14
15    Display networking information.
16
17    -r  Display routing table.
18    -a  Display all sockets (Default: Connected).
19    -l  Display listening server sockets.
20    -t  Display TCP sockets.
21    -u  Display UDP sockets.
22    -w  Display Raw sockets.
23    -x  Display Unix sockets.
24    -e  Display other/more information.
25    -n  Don't resolve names.
26    -W  Wide Display.
27    -p  Display PID/Program name for sockets.
28*/
29
30#define FOR_netstat
31#include "toys.h"
32#include <net/route.h>
33
34typedef union _iaddr {
35  unsigned u;
36  unsigned char b[4];
37} iaddr;
38
39typedef union _iaddr6 {
40  struct {
41    unsigned a;
42    unsigned b;
43    unsigned c;
44    unsigned d;
45  } u;
46  unsigned char b[16];
47} iaddr6;
48
49#define ADDR_LEN (INET6_ADDRSTRLEN + 1 + 5 + 1) //IPv6 addr len + : + port + '\0'
50
51//For unix states
52enum {
53	SOCK_ACCEPTCON = (1 << 16),  //performed a listen.
54	SOCK_WAIT_DATA = (1 << 17),  //wait data to read.
55	SOCK_NO_SPACE = (1 << 18),  //no space to write.
56};
57
58#define SOCK_NOT_CONNECTED 1
59//For PID/Progrma Name
60#define PROGRAM_NAME "PID/Program Name"
61#define PROGNAME_LEN 21
62
63typedef struct _pidlist {
64  struct _pidlist *next;
65  long inode;
66  char name[PROGNAME_LEN];
67} PID_LIST;
68PID_LIST *pid_list = NULL;
69
70/*
71 * Get base name from the input name.
72 */
73static const char *get_basename(char *name)
74{
75  const char *c = strrchr(name, '/');
76  if (c) return c + 1;
77  return name;
78}
79
80/*
81 * locate character in string.
82 */
83static char *strchr_nul(char *s, int c)
84{
85  while(*s != '\0' && *s != c) s++;
86  return (char*)s;
87}
88
89// Find out if the last character of a string matches with the given one.
90// Don't underrun the buffer if the string length is 0.
91static char *find_last_char(char *str, int c)
92{
93  if (str && *str) {
94    size_t sz = strlen(str) - 1;
95    str += sz;
96    if ( (unsigned char)*str == c) return (char*)str;
97  }
98  return NULL;
99}
100/*
101 * Concat path and the file name.
102 */
103static char *append_pathandfile(char *path, char *fname)
104{
105  char *c;
106  if (!path) path = "";
107  c = find_last_char(path, '/');
108  while (*fname == '/') fname++;
109  return xmprintf("%s%s%s", path, (c)? "" : "/", fname);
110}
111/*
112 * Concat sub-path and the file name.
113 */
114static char *append_subpathandfile(char *path, char *fname)
115{
116#define ISDOTORDOTDOT(s) ((s)[0] == '.' && (!(s)[1] || ((s)[1] == '.' && !(s)[2])))
117  if(!fname) return NULL;
118  if(ISDOTORDOTDOT(fname)) return NULL;
119  return append_pathandfile(path, fname);
120#undef ISDOTORDOTDOT
121}
122/*
123 * used to converts string into int and validate the input str for invalid int value or out-of-range.
124 */
125static unsigned get_strtou(char *str, char **endp, int base)
126{
127  unsigned long uli;
128  char *endptr;
129
130  if (!isalnum(str[0])) {
131    errno = ERANGE;
132    return UINT_MAX;
133  }
134  errno = 0;
135  uli = strtoul(str, &endptr, base);
136  if (uli > UINT_MAX) {
137    errno = ERANGE;
138    return UINT_MAX;
139  }
140
141  if (endp) *endp = endptr;
142  if (endptr[0]) {
143    if (isalnum(endptr[0]) || errno) { //"123abc" or out-of-range
144      errno = ERANGE;
145      return UINT_MAX;
146    }
147    errno = EINVAL;
148  }
149  return uli;
150}
151/*
152 * used to retrive pid name from pid list.
153 */
154static const char *get_pid_name(unsigned long inode)
155{
156  PID_LIST *tmp;
157  for (tmp = pid_list; tmp; tmp = tmp->next)
158    if (tmp->inode == inode) return tmp->name;
159  return "-";
160}
161/*
162 * For TCP/UDP/RAW display data.
163 */
164static void display_data(unsigned rport, char *label, unsigned rxq, unsigned txq, char *lip, char *rip, unsigned state, unsigned long inode)
165{
166  char *ss_state = "UNKNOWN", buf[12];
167  char *state_label[] = {"", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2",
168  		                 "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING", "UNKNOWN"};
169  if (!strcmp(label, "tcp")) {
170    int sz = ARRAY_LEN(state_label);
171    if (!state || state >= sz) state = sz-1;
172    ss_state = state_label[state];
173  }
174  else if (!strcmp(label, "udp")) {
175    if (state == 1) ss_state = state_label[state];
176    else if (state == 7) ss_state = "";
177  }
178  else if (!strcmp(label, "raw")) sprintf(ss_state = buf, "%u", state);
179
180  if ( (toys.optflags & FLAG_W) && (toys.optflags & FLAG_p))
181    xprintf("%3s   %6d %6d %-51s %-51s %-12s%s\n", label, rxq, txq, lip, rip, ss_state, get_pid_name(inode));
182  else if (toys.optflags & FLAG_W)
183    xprintf("%3s   %6d %6d %-51s %-51s %-12s\n", label, rxq, txq, lip, rip, ss_state);
184  else if (toys.optflags & FLAG_p)
185    xprintf("%3s   %6d %6d %-23s %-23s %-12s%s\n", label, rxq, txq, lip, rip, ss_state, get_pid_name(inode));
186  else xprintf("%3s   %6d %6d %-23s %-23s %-12s\n", label, rxq, txq, lip, rip, ss_state);
187}
188/*
189 * For TCP/UDP/RAW show data.
190 */
191static void show_data(unsigned rport, char *label, unsigned rxq, unsigned txq, char *lip, char *rip, unsigned state, unsigned long inode)
192{
193  if (toys.optflags & FLAG_l) {
194    if (!rport && (state && 0xA)) display_data(rport, label, rxq, txq, lip, rip, state, inode);
195  } else if (toys.optflags & FLAG_a) display_data(rport, label, rxq, txq, lip, rip, state, inode);
196  //rport && (TCP | UDP | RAW)
197  else if (rport && (0x10 | 0x20 | 0x40)) display_data(rport, label, rxq, txq, lip, rip, state, inode);
198}
199/*
200 * used to get service name.
201 */
202static char *get_servname(int port, char *label)
203{
204  int lport = htons(port);
205  if (!lport) return xmprintf("%s", "*");
206  struct servent *ser = getservbyport(lport, label);
207  if (ser) return xmprintf("%s", ser->s_name);
208  return xmprintf("%u", (unsigned)ntohs(lport));
209}
210/*
211 * used to convert address into text format.
212 */
213static void addr2str(int af, void *addr, unsigned port, char *buf, char *label)
214{
215  char ip[ADDR_LEN] = {0,};
216  if (!inet_ntop(af, addr, ip, ADDR_LEN)) {
217    *buf = '\0';
218    return;
219  }
220  size_t iplen = strlen(ip);
221  if (!port) {
222    strncat(ip+iplen, ":*", ADDR_LEN-iplen-1);
223    memcpy(buf, ip, ADDR_LEN);
224    return;
225  }
226
227  if (!(toys.optflags & FLAG_n)) {
228    struct addrinfo hints, *result, *rp;
229
230    memset(&hints, 0, sizeof(struct addrinfo));
231    hints.ai_family = af;
232
233    if (!getaddrinfo(ip, NULL, &hints, &result)) {
234      char hbuf[NI_MAXHOST] = {0,}, sbuf[NI_MAXSERV] = {0,};
235      socklen_t sock_len;
236      char *sname = NULL;
237      int plen = 0;
238
239      if (af == AF_INET) sock_len = sizeof(struct sockaddr_in);
240      else sock_len = sizeof(struct sockaddr_in6);
241
242      for (rp = result; rp; rp = rp->ai_next)
243        if (!getnameinfo(rp->ai_addr, sock_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICSERV))
244          break;
245
246      freeaddrinfo(result);
247      sname = get_servname(port, label);
248      plen = strlen(sname);
249      if (*hbuf) {
250        memset(ip, 0, ADDR_LEN);
251        memcpy(ip, hbuf, (ADDR_LEN - plen - 2));
252        iplen = strlen(ip);
253      }
254      snprintf(ip + iplen, ADDR_LEN-iplen, ":%s", sname);
255      free(sname);
256    }
257  }
258  else snprintf(ip+iplen, ADDR_LEN-iplen, ":%d", port);
259  memcpy(buf, ip, ADDR_LEN);
260}
261/*
262 * display ipv4 info for TCP/UDP/RAW.
263 */
264static void show_ipv4(char *fname, char *label)
265{
266  FILE *fp = fopen((char *)fname, "r");
267  if (!fp) {
268     perror_msg("'%s'", fname);
269     return;
270  }
271  fgets(toybuf, sizeof(toybuf), fp); //skip header.
272  while (fgets(toybuf, sizeof(toybuf), fp)) {
273    char lip[ADDR_LEN] = {0,}, rip[ADDR_LEN] = {0,};
274    iaddr laddr, raddr;
275    unsigned lport, rport, state, txq, rxq, num, uid;
276    unsigned long inode;
277
278    int nitems = sscanf(toybuf, " %d: %x:%x %x:%x %x %x:%x %*X:%*X %*X %d %*d %ld",
279        &num, &laddr.u, &lport, &raddr.u, &rport, &state, &txq, &rxq, &uid, &inode);
280    if (nitems == 10) {
281      addr2str(AF_INET, &laddr, lport, lip, label);
282      addr2str(AF_INET, &raddr, rport, rip, label);
283      show_data(rport, label, rxq, txq, lip, rip, state, inode);
284    }
285  }//End of While
286  fclose(fp);
287}
288/*
289 * display ipv6 info for TCP/UDP/RAW.
290 */
291static void show_ipv6(char *fname, char *label)
292{
293  FILE *fp = fopen((char *)fname, "r");
294  if (!fp) {
295    perror_msg("'%s'", fname);
296    return;
297  }
298  fgets(toybuf, sizeof(toybuf), fp); //skip header.
299  while (fgets(toybuf, sizeof(toybuf), fp)) {
300    char lip[ADDR_LEN] = {0,}, rip[ADDR_LEN] = {0,};
301    iaddr6 laddr6, raddr6;
302    unsigned lport, rport, state, txq, rxq, num, uid;
303    unsigned long inode;
304    int nitems = sscanf(toybuf, " %d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x %x:%x %*X:%*X %*X %d %*d %ld",
305        &num, &laddr6.u.a, &laddr6.u.b, &laddr6.u.c, &laddr6.u.d, &lport, &raddr6.u.a, &raddr6.u.b,
306        &raddr6.u.c, &raddr6.u.d, &rport, &state, &txq, &rxq, &uid, &inode);
307    if (nitems == 16) {
308      addr2str(AF_INET6, &laddr6, lport, lip, label);
309      addr2str(AF_INET6, &raddr6, rport, rip, label);
310      show_data(rport, label, rxq, txq, lip, rip, state, inode);
311    }
312  }//End of While
313  fclose(fp);
314}
315/*
316 * display unix socket info.
317 */
318static void show_unix_sockets(char *fname, char *label)
319{
320  FILE *fp = fopen((char *)fname, "r");
321  if (!fp) {
322    perror_msg("'%s'", fname);
323    return;
324  }
325  fgets(toybuf, sizeof(toybuf), fp); //skip header.
326  while (fgets(toybuf, sizeof(toybuf), fp)) {
327    unsigned long int refcount, label, flags, inode;
328    int nitems = 0, path_offset = 0, type, state;
329    char sock_flags[32] = {0,}, *sock_type, *sock_state, *bptr = toybuf;
330
331    if (!toybuf[0]) continue;
332    nitems = sscanf(toybuf, "%*p: %lX %lX %lX %X %X %lu %n",
333        &refcount, &label, &flags, &type, &state, &inode, &path_offset);
334    //for state one less
335    if (nitems < 6) break;
336    if (toys.optflags & FLAG_l) {
337      if ( !((state == SOCK_NOT_CONNECTED) && (flags & SOCK_ACCEPTCON)) ) continue;
338    } else if (!(toys.optflags & FLAG_a)) {
339      if ((state == SOCK_NOT_CONNECTED) && (flags & SOCK_ACCEPTCON)) continue;
340    }
341
342    //prepare socket type, state and flags.
343    {
344      char *ss_type[] = { "", "STREAM", "DGRAM", "RAW", "RDM", "SEQPACKET", "UNKNOWN"};
345      char *ss_state[] = { "FREE", "LISTENING", "CONNECTING", "CONNECTED", "DISCONNECTING", "UNKNOWN"};
346
347      int sz = ARRAY_LEN(ss_type);//sizeof(ss_type)/sizeof(ss_type[0]);
348      if ( (type < SOCK_STREAM) || (type > SOCK_SEQPACKET) ) sock_type = ss_type[sz-1];
349      else sock_type = ss_type[type];
350
351      sz = ARRAY_LEN(ss_state);//sizeof(ss_state)/sizeof(ss_state[0]);
352      if ((state < 0) || (state > sz-2)) sock_state = ss_state[sz-1];
353      else if (state == SOCK_NOT_CONNECTED) {
354        if (flags & SOCK_ACCEPTCON) sock_state = ss_state[state];
355        else sock_state = " ";
356      } else sock_state = ss_state[state];
357
358      strcpy(sock_flags, "[ ");
359      if (flags & SOCK_ACCEPTCON) strcat(sock_flags, "ACC ");
360      if (flags & SOCK_WAIT_DATA) strcat(sock_flags, "W ");
361      if (flags & SOCK_NO_SPACE) strcat(sock_flags, "N ");
362      strcat(sock_flags, "]");
363    }
364    xprintf("%-5s %-6ld %-11s %-10s %-13s %6lu ", (!label ? "unix" : "??"), refcount, sock_flags, sock_type, sock_state, inode);
365    if (toys.optflags & FLAG_p) xprintf("%-20s", get_pid_name(inode));
366
367    bptr += path_offset;
368    *strchr_nul(bptr, '\n') = '\0';
369    xprintf("%s\n", bptr);
370  }//End of while
371  fclose(fp);
372}
373/*
374 * extract inode value from the link.
375 */
376static long ss_inode(char *link)
377{
378  long inode = -1;
379  //"link = socket:[12345]", get "12345" as inode.
380  if (!strncmp(link, "socket:[", sizeof("socket:[")-1)) {
381    inode = get_strtou(link + sizeof("socket:[")-1, (char**)&link, 0);
382    if (*link != ']') inode = -1;
383  }
384  //"link = [0000]:12345", get "12345" as inode.
385  else if (!strncmp(link, "[0000]:", sizeof("[0000]:")-1)) {
386    inode = get_strtou(link + sizeof("[0000]:")-1, NULL, 0);
387    //if not NULL terminated.
388    if (errno) inode = -1;
389  }
390  return inode;
391}
392/*
393 * add inode and progname in the pid list.
394 */
395static void add2list(long inode, char *progname)
396{
397  PID_LIST *node = pid_list;
398  for(; node; node = node->next) {
399    if(node->inode == inode)
400      return;
401  }
402  PID_LIST *new = (PID_LIST *)xzalloc(sizeof(PID_LIST));
403  new->inode = inode;
404  xstrncpy(new->name, progname, PROGNAME_LEN);
405  new->next = pid_list;
406  pid_list = new;
407}
408/*
409 * add pid info in the list.
410 */
411static void extract_inode(char *path, char *progname)
412{
413  DIR *dp;
414  struct dirent *entry;
415
416  if (!(dp = opendir(path))) {
417    if (errno == EACCES) return;
418    else perror_exit("%s", path);
419  }
420  while ((entry = readdir(dp))) {
421    char *link = NULL, *fname = append_subpathandfile(path, entry->d_name);
422    if (!fname) continue;
423    link = xreadlink(fname);
424    if (link) {
425      long inode = ss_inode(link);
426      free(link);
427      if (inode != -1) add2list(inode, progname);
428    }
429    free(fname);
430  }//end of while.
431  closedir(dp);
432}
433/*
434 * prepare the list for all pids in /proc directory.
435 */
436static void get_pid_list(void)
437{
438  DIR *dp;
439  struct dirent *entry;
440  char path[64] = {0,};
441  uid_t uid = geteuid();
442
443  if (!(dp = opendir("/proc"))) perror_exit("opendir");
444
445  while ((entry = readdir(dp))) {
446    int fd, nitems = 0, length = 0;
447    char *pid, *progname;
448
449    if (!isdigit(*entry->d_name)) continue;
450    pid = entry->d_name;
451    length = snprintf(path, sizeof(path), "/proc/%s/cmdline", entry->d_name);
452    if (sizeof(path) <= length) continue;
453
454    fd = xopen(path, O_RDONLY);
455    nitems = readall(fd, toybuf, sizeof(toybuf) - 1);
456    xclose(fd);
457    if (nitems < 1) continue;
458    toybuf[nitems] = '\0';
459    strcpy(path + length - (sizeof("cmdline")-1), "fd");
460    progname = append_pathandfile(pid, (char *)get_basename(toybuf)); //e.g. progname = 2054/gnome-keyring-daemon
461    extract_inode(path, progname);
462    free(progname);
463  }//end of while.
464  closedir(dp);
465
466  if (uid) fprintf(stderr, "(Not all processes could be identified, non-owned process info "
467      "will not be shown, you would have to be root to see it all.)\n");
468}
469/*
470 * Dealloc pid list.
471 */
472static void clean_pid_list(void)
473{
474  PID_LIST *tmp;
475  while (pid_list) {
476    tmp = pid_list->next;
477    free(pid_list);
478    pid_list = tmp;
479  }
480}
481/*
482 * For TCP/UDP/RAW show the header.
483 */
484static void show_header(void)
485{
486  if ((toys.optflags & FLAG_W) && (toys.optflags & FLAG_p))
487    xprintf("\nProto Recv-Q Send-Q %-51s %-51s %-12s%s\n", "Local Address", "Foreign Address", "State", PROGRAM_NAME);
488  else if (toys.optflags & FLAG_p)
489    xprintf("\nProto Recv-Q Send-Q %-23s %-23s %-12s%s\n", "Local Address", "Foreign Address", "State", PROGRAM_NAME);
490  else if (toys.optflags & FLAG_W)
491	  xprintf("\nProto Recv-Q Send-Q %-51s %-51s State     \n", "Local Address", "Foreign Address");
492  else xprintf("\nProto Recv-Q Send-Q %-23s %-23s State     \n", "Local Address", "Foreign Address");
493}
494/*
495 * used to get the flag values for route command.
496 */
497static void get_flag_value(char **flagstr, int flags)
498{
499  int i = 0;
500  char *str = *flagstr;
501  static const char flagchars[] = "GHRDMDAC";
502  static const unsigned flagarray[] = {
503    RTF_GATEWAY,
504    RTF_HOST,
505    RTF_REINSTATE,
506    RTF_DYNAMIC,
507    RTF_MODIFIED,
508    RTF_DEFAULT,
509    RTF_ADDRCONF,
510    RTF_CACHE
511  };
512  *str++ = 'U';
513  while ( (*str = flagchars[i]) ) {
514    if (flags & flagarray[i++]) ++str;
515  }
516}
517/*
518 * extract inet4 route info from /proc/net/route file and display it.
519 */
520static void display_routes(int is_more_info, int notresolve)
521{
522#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
523  unsigned long dest, gate, mask;
524  int flags, ref, use, metric, mss, win, irtt;
525  char iface[64]={0,};
526  char *flag_val = xzalloc(10); //there are 9 flags "UGHRDMDAC" for route.
527
528  FILE *fp = xfopen("/proc/net/route", "r");
529  xprintf("Kernel IP routing table\n"
530                   "Destination     Gateway         Genmask         Flags %s Iface\n",
531  	                        is_more_info ? "  MSS Window  irtt" : "Metric Ref    Use");
532  fgets(toybuf, sizeof(toybuf), fp); //skip 1st line.
533  while (fgets(toybuf, sizeof(toybuf), fp)) {
534     int nitems = 0;
535     char *destip = NULL, *gateip = NULL, *maskip = NULL;
536     memset(flag_val, 0, 10);
537
538     nitems = sscanf(toybuf, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
539                 iface, &dest, &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt);
540     if (nitems != 11) {//EOF with no (nonspace) chars read.
541       if ((nitems < 0) && feof(fp)) break;
542      perror_exit("sscanf");
543    }
544    //skip down interfaces.
545    if (!(flags & RTF_UP)) continue;
546
547    if (dest) {//For Destination
548      if (inet_ntop(AF_INET, &dest, toybuf, sizeof(toybuf)) ) destip = xstrdup(toybuf);
549    } else {
550      if (!notresolve) destip = xstrdup("default");
551      else destip = xstrdup("0.0.0.0");
552    }
553    if (gate) {//For Gateway
554      if (inet_ntop(AF_INET, &gate, toybuf, sizeof(toybuf)) ) gateip = xstrdup(toybuf);
555    } else {
556      if (!notresolve) gateip = xstrdup("*");
557      else gateip = xstrdup("0.0.0.0");
558    }
559    //For Mask
560    if (inet_ntop(AF_INET, &mask, toybuf, sizeof(toybuf)) ) maskip = xstrdup(toybuf);
561
562    //Get flag Values
563    get_flag_value(&flag_val, (flags & IPV4_MASK));
564    if (flags & RTF_REJECT) flag_val[0] = '!';
565    xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val);
566    if (destip) free(destip);
567    if (gateip) free(gateip);
568    if (maskip) free(maskip);
569    if (is_more_info) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface);
570    else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface);
571  }//end of while.
572  fclose(fp);
573  if (flag_val) free(flag_val);
574#undef IPV4_MASK
575  return;
576}
577/*
578 * netstat utily main function.
579 */
580void netstat_main(void)
581{
582#define IS_NETSTAT_PROTO_FLAGS_UP (toys.optflags & (FLAG_t | FLAG_u | FLAG_w | FLAG_x))
583  int flag_listen_and_all = 0;
584  if (!toys.optflags) toys.optflags = FLAG_t | FLAG_u | FLAG_w | FLAG_x;
585
586  //When a is set
587  if (toys.optflags & FLAG_a) flag_listen_and_all = 1;
588  //when a and l both are set
589  if ( (toys.optflags & FLAG_a) && (toys.optflags & FLAG_l) )
590    toys.optflags &= ~FLAG_l;
591  //when only a is set
592  if ( (toys.optflags & FLAG_a) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
593    toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
594  //when only l is set
595  if ( (toys.optflags & FLAG_l) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
596    toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
597  //when only e/n is set
598  if( ((toys.optflags & FLAG_e) || (toys.optflags & FLAG_n)) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
599	  toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
600  //when W is set
601  if ( (toys.optflags & FLAG_W) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
602    toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
603   //when p is set
604  if ( (toys.optflags & FLAG_p) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
605    toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
606
607  //Display routing table.
608  if (toys.optflags & FLAG_r) {
609    display_routes(!(toys.optflags & FLAG_e), (toys.optflags & FLAG_n));
610    return;
611  }
612
613  if (toys.optflags & FLAG_p) get_pid_list();
614
615  //For TCP/UDP/RAW.
616  if ( (toys.optflags & FLAG_t) || (toys.optflags & FLAG_u) || (toys.optflags & FLAG_w) ) {
617    xprintf("Active Internet connections ");
618
619    if (flag_listen_and_all) xprintf("(servers and established)");
620    else if (toys.optflags & FLAG_l) xprintf("(only servers)");
621    else xprintf("(w/o servers)");
622
623    show_header();
624    if (toys.optflags & FLAG_t) {//For TCP
625      show_ipv4("/proc/net/tcp",  "tcp");
626      show_ipv6("/proc/net/tcp6", "tcp");
627    }
628    if (toys.optflags & FLAG_u) {//For UDP
629      show_ipv4("/proc/net/udp",  "udp");
630      show_ipv6("/proc/net/udp6", "udp");
631    }
632    if (toys.optflags & FLAG_w) {//For raw
633      show_ipv4("/proc/net/raw",  "raw");
634      show_ipv6("/proc/net/raw6", "raw");
635    }
636  }
637  if (toys.optflags & FLAG_x) {//For UNIX.
638    xprintf("Active UNIX domain sockets ");
639    if (flag_listen_and_all) xprintf("(servers and established)");
640    else if (toys.optflags & FLAG_l) xprintf("(only servers)");
641    else xprintf("(w/o servers)");
642
643    if (toys.optflags & FLAG_p) xprintf("\nProto RefCnt Flags       Type       State         I-Node %s    Path\n", PROGRAM_NAME);
644    else xprintf("\nProto RefCnt Flags       Type       State         I-Node Path\n");
645    show_unix_sockets("/proc/net/unix", "unix");
646  }
647  if (toys.optflags & FLAG_p) clean_pid_list();
648  if (toys.exitval) toys.exitval = 0;
649#undef IS_NETSTAT_PROTO_FLAGS_UP
650}
651