accidentally applied wrong (old) patch, fixing up...
[oweals/busybox.git] / networking / netstat.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini netstat implementation(s) for busybox
4  * based in part on the netstat implementation from net-tools.
5  *
6  * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
7  *
8  * 2002-04-20
9  * IPV6 support added by Bart Visscher <magick@linux-fan.com>
10  *
11  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
12  */
13
14 #include "busybox.h"
15 #include "inet_common.h"
16
17 #define NETSTAT_CONNECTED 0x01
18 #define NETSTAT_LISTENING 0x02
19 #define NETSTAT_NUMERIC   0x04
20 /* Must match getopt32 option string */
21 #define NETSTAT_TCP       0x10
22 #define NETSTAT_UDP       0x20
23 #define NETSTAT_RAW       0x40
24 #define NETSTAT_UNIX      0x80
25 #define NETSTAT_ALLPROTO  (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX)
26
27 static int flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO;
28
29 enum {
30         TCP_ESTABLISHED = 1,
31         TCP_SYN_SENT,
32         TCP_SYN_RECV,
33         TCP_FIN_WAIT1,
34         TCP_FIN_WAIT2,
35         TCP_TIME_WAIT,
36         TCP_CLOSE,
37         TCP_CLOSE_WAIT,
38         TCP_LAST_ACK,
39         TCP_LISTEN,
40         TCP_CLOSING /* now a valid state */
41 };
42
43 static const char * const tcp_state[] =
44 {
45         "",
46         "ESTABLISHED",
47         "SYN_SENT",
48         "SYN_RECV",
49         "FIN_WAIT1",
50         "FIN_WAIT2",
51         "TIME_WAIT",
52         "CLOSE",
53         "CLOSE_WAIT",
54         "LAST_ACK",
55         "LISTEN",
56         "CLOSING"
57 };
58
59 typedef enum {
60         SS_FREE = 0,     /* not allocated                */
61         SS_UNCONNECTED,  /* unconnected to any socket    */
62         SS_CONNECTING,   /* in process of connecting     */
63         SS_CONNECTED,    /* connected to socket          */
64         SS_DISCONNECTING /* in process of disconnecting  */
65 } socket_state;
66
67 #define SO_ACCEPTCON (1<<16)    /* performed a listen           */
68 #define SO_WAITDATA  (1<<17)    /* wait data to read            */
69 #define SO_NOSPACE   (1<<18)    /* no space to write            */
70
71 static char *get_sname(int port, const char *proto, int num)
72 {
73         char *str = itoa(ntohs(port));
74         if (!num) {
75                 struct servent *se = getservbyport(port, proto);
76                 if (se)
77                         str = se->s_name;
78         }
79         if (!port) {
80                 str = "*";
81         }
82         return str;
83 }
84
85 static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
86 {
87         char *port_name;
88
89 #if ENABLE_FEATURE_IPV6
90         if (addr->sa_family == AF_INET6) {
91                 INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr,
92                         (numeric & NETSTAT_NUMERIC) ? 0x0fff : 0);
93         } else
94 #endif
95         {
96                 INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
97                         0x4000 | ((numeric & NETSTAT_NUMERIC) ? 0x0fff : 0),
98                         0xffffffff);
99         }
100         port_name = get_sname(htons(port), proto, numeric);
101         if ((strlen(ip_port) + strlen(port_name)) > 22)
102                 ip_port[22 - strlen(port_name)] = '\0';
103         ip_port += strlen(ip_port);
104         strcat(ip_port, ":");
105         strcat(ip_port, port_name);
106 }
107
108 static void tcp_do_one(int lnr, const char *line)
109 {
110         char local_addr[64], rem_addr[64];
111         const char *state_str;
112         char more[512];
113         int num, local_port, rem_port, d, state, timer_run, uid, timeout;
114 #if ENABLE_FEATURE_IPV6
115         struct sockaddr_in6 localaddr, remaddr;
116         char addr6[INET6_ADDRSTRLEN];
117         struct in6_addr in6;
118 #else
119         struct sockaddr_in localaddr, remaddr;
120 #endif
121         unsigned long rxq, txq, time_len, retr, inode;
122
123         if (lnr == 0)
124                 return;
125
126         more[0] = '\0';
127         num = sscanf(line,
128                                  "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
129                                  &d, local_addr, &local_port,
130                                  rem_addr, &rem_port, &state,
131                                  &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
132
133         if (strlen(local_addr) > 8) {
134 #if ENABLE_FEATURE_IPV6
135                 sscanf(local_addr, "%08X%08X%08X%08X",
136                            &in6.s6_addr32[0], &in6.s6_addr32[1],
137                            &in6.s6_addr32[2], &in6.s6_addr32[3]);
138                 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
139                 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
140                 sscanf(rem_addr, "%08X%08X%08X%08X",
141                            &in6.s6_addr32[0], &in6.s6_addr32[1],
142                            &in6.s6_addr32[2], &in6.s6_addr32[3]);
143                 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
144                 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
145                 localaddr.sin6_family = AF_INET6;
146                 remaddr.sin6_family = AF_INET6;
147 #endif
148         } else {
149                 sscanf(local_addr, "%X",
150                            &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
151                 sscanf(rem_addr, "%X",
152                            &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
153                 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
154                 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
155         }
156
157         if (num < 10) {
158                 bb_error_msg("warning, got bogus tcp line");
159                 return;
160         }
161         state_str = tcp_state[state];
162         if ((rem_port && (flags & NETSTAT_CONNECTED))
163          || (!rem_port && (flags & NETSTAT_LISTENING))
164         ) {
165                 snprint_ip_port(local_addr, sizeof(local_addr),
166                                                 (struct sockaddr *) &localaddr, local_port,
167                                                 "tcp", flags & NETSTAT_NUMERIC);
168
169                 snprint_ip_port(rem_addr, sizeof(rem_addr),
170                                                 (struct sockaddr *) &remaddr, rem_port,
171                                                 "tcp", flags & NETSTAT_NUMERIC);
172
173                 printf("tcp   %6ld %6ld %-23s %-23s %-12s\n",
174                            rxq, txq, local_addr, rem_addr, state_str);
175         }
176 }
177
178 static void udp_do_one(int lnr, const char *line)
179 {
180         char local_addr[64], rem_addr[64];
181         char *state_str, more[512];
182         int num, local_port, rem_port, d, state, timer_run, uid, timeout;
183 #if ENABLE_FEATURE_IPV6
184         struct sockaddr_in6 localaddr, remaddr;
185         char addr6[INET6_ADDRSTRLEN];
186         struct in6_addr in6;
187 #else
188         struct sockaddr_in localaddr, remaddr;
189 #endif
190         unsigned long rxq, txq, time_len, retr, inode;
191
192         if (lnr == 0)
193                 return;
194
195         more[0] = '\0';
196         num = sscanf(line,
197                                  "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
198                                  &d, local_addr, &local_port,
199                                  rem_addr, &rem_port, &state,
200                                  &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
201
202         if (strlen(local_addr) > 8) {
203 #if ENABLE_FEATURE_IPV6
204         /* Demangle what the kernel gives us */
205                 sscanf(local_addr, "%08X%08X%08X%08X",
206                            &in6.s6_addr32[0], &in6.s6_addr32[1],
207                            &in6.s6_addr32[2], &in6.s6_addr32[3]);
208                 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
209                 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
210                 sscanf(rem_addr, "%08X%08X%08X%08X",
211                            &in6.s6_addr32[0], &in6.s6_addr32[1],
212                            &in6.s6_addr32[2], &in6.s6_addr32[3]);
213                 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
214                 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
215                 localaddr.sin6_family = AF_INET6;
216                 remaddr.sin6_family = AF_INET6;
217 #endif
218         } else {
219                 sscanf(local_addr, "%X",
220                            &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
221                 sscanf(rem_addr, "%X",
222                            &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
223                 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
224                 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
225         }
226
227         if (num < 10) {
228                 bb_error_msg("warning, got bogus udp line");
229                 return;
230         }
231         switch (state) {
232                 case TCP_ESTABLISHED:
233                         state_str = "ESTABLISHED";
234                         break;
235
236                 case TCP_CLOSE:
237                         state_str = "";
238                         break;
239
240                 default:
241                         state_str = "UNKNOWN";
242                         break;
243         }
244
245 #if ENABLE_FEATURE_IPV6
246 # define notnull(A) (((A.sin6_family == AF_INET6) &&            \
247                                          ((A.sin6_addr.s6_addr32[0]) ||            \
248                                           (A.sin6_addr.s6_addr32[1]) ||            \
249                                           (A.sin6_addr.s6_addr32[2]) ||            \
250                                           (A.sin6_addr.s6_addr32[3]))) ||          \
251                                         ((A.sin6_family == AF_INET) &&             \
252                                          ((struct sockaddr_in *) &A)->sin_addr.s_addr))
253 #else
254 # define notnull(A) (A.sin_addr.s_addr)
255 #endif
256         if ((notnull(remaddr) && (flags & NETSTAT_CONNECTED))
257          || (!notnull(remaddr) && (flags & NETSTAT_LISTENING))
258         ) {
259                 snprint_ip_port(local_addr, sizeof(local_addr),
260                                                 (struct sockaddr *) &localaddr, local_port,
261                                                 "udp", flags & NETSTAT_NUMERIC);
262
263                 snprint_ip_port(rem_addr, sizeof(rem_addr),
264                                                 (struct sockaddr *) &remaddr, rem_port,
265                                                 "udp", flags & NETSTAT_NUMERIC);
266
267                 printf("udp   %6ld %6ld %-23s %-23s %-12s\n",
268                            rxq, txq, local_addr, rem_addr, state_str);
269         }
270 }
271
272 static void raw_do_one(int lnr, const char *line)
273 {
274         char local_addr[64], rem_addr[64];
275         char *state_str, more[512];
276         int num, local_port, rem_port, d, state, timer_run, uid, timeout;
277 #if ENABLE_FEATURE_IPV6
278         struct sockaddr_in6 localaddr, remaddr;
279         char addr6[INET6_ADDRSTRLEN];
280         struct in6_addr in6;
281 #else
282         struct sockaddr_in localaddr, remaddr;
283 #endif
284         unsigned long rxq, txq, time_len, retr, inode;
285
286         if (lnr == 0)
287                 return;
288
289         more[0] = '\0';
290         num = sscanf(line,
291                                  "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
292                                  &d, local_addr, &local_port,
293                                  rem_addr, &rem_port, &state,
294                                  &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
295
296         if (strlen(local_addr) > 8) {
297 #if ENABLE_FEATURE_IPV6
298                 sscanf(local_addr, "%08X%08X%08X%08X",
299                            &in6.s6_addr32[0], &in6.s6_addr32[1],
300                            &in6.s6_addr32[2], &in6.s6_addr32[3]);
301                 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
302                 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
303                 sscanf(rem_addr, "%08X%08X%08X%08X",
304                            &in6.s6_addr32[0], &in6.s6_addr32[1],
305                            &in6.s6_addr32[2], &in6.s6_addr32[3]);
306                 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
307                 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
308                 localaddr.sin6_family = AF_INET6;
309                 remaddr.sin6_family = AF_INET6;
310 #endif
311         } else {
312                 sscanf(local_addr, "%X",
313                            &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
314                 sscanf(rem_addr, "%X",
315                            &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
316                 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
317                 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
318         }
319
320         if (num < 10) {
321                 bb_error_msg("warning, got bogus raw line");
322                 return;
323         }
324         state_str = itoa(state);
325
326 #if ENABLE_FEATURE_IPV6
327 # define notnull(A) (((A.sin6_family == AF_INET6) &&            \
328                                          ((A.sin6_addr.s6_addr32[0]) ||            \
329                                           (A.sin6_addr.s6_addr32[1]) ||            \
330                                           (A.sin6_addr.s6_addr32[2]) ||            \
331                                           (A.sin6_addr.s6_addr32[3]))) ||          \
332                                         ((A.sin6_family == AF_INET) &&             \
333                                          ((struct sockaddr_in *) &A)->sin_addr.s_addr))
334 #else
335 # define notnull(A) (A.sin_addr.s_addr)
336 #endif
337         if ((notnull(remaddr) && (flags & NETSTAT_CONNECTED))
338          || (!notnull(remaddr) && (flags & NETSTAT_LISTENING))
339         ) {
340                 snprint_ip_port(local_addr, sizeof(local_addr),
341                                                 (struct sockaddr *) &localaddr, local_port,
342                                                 "raw", flags & NETSTAT_NUMERIC);
343
344                 snprint_ip_port(rem_addr, sizeof(rem_addr),
345                                                 (struct sockaddr *) &remaddr, rem_port,
346                                                 "raw", flags & NETSTAT_NUMERIC);
347
348                 printf("raw   %6ld %6ld %-23s %-23s %-12s\n",
349                            rxq, txq, local_addr, rem_addr, state_str);
350         }
351 }
352
353 #define HAS_INODE 1
354
355 static void unix_do_one(int nr, const char *line)
356 {
357         static int has = 0;
358         char path[PATH_MAX], ss_flags[32];
359         char *ss_proto, *ss_state, *ss_type;
360         int num, state, type, inode;
361         void *d;
362         unsigned long refcnt, proto, unix_flags;
363
364         if (nr == 0) {
365                 if (strstr(line, "Inode"))
366                         has |= HAS_INODE;
367                 return;
368         }
369         path[0] = '\0';
370         num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
371                                  &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
372         if (num < 6) {
373                 bb_error_msg("warning, got bogus unix line");
374                 return;
375         }
376         if (!(has & HAS_INODE))
377                 snprintf(path,sizeof(path),"%d",inode);
378
379         if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) != (NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
380                 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
381                         if (!(flags & NETSTAT_LISTENING))
382                                 return;
383                 } else {
384                         if (!(flags & NETSTAT_CONNECTED))
385                                 return;
386                 }
387         }
388
389         switch (proto) {
390                 case 0:
391                         ss_proto = "unix";
392                         break;
393
394                 default:
395                         ss_proto = "??";
396         }
397
398         switch (type) {
399                 case SOCK_STREAM:
400                         ss_type = "STREAM";
401                         break;
402
403                 case SOCK_DGRAM:
404                         ss_type = "DGRAM";
405                         break;
406
407                 case SOCK_RAW:
408                         ss_type = "RAW";
409                         break;
410
411                 case SOCK_RDM:
412                         ss_type = "RDM";
413                         break;
414
415                 case SOCK_SEQPACKET:
416                         ss_type = "SEQPACKET";
417                         break;
418
419                 default:
420                         ss_type = "UNKNOWN";
421         }
422
423         switch (state) {
424                 case SS_FREE:
425                         ss_state = "FREE";
426                         break;
427
428                 case SS_UNCONNECTED:
429                         /*
430                          * Unconnected sockets may be listening
431                          * for something.
432                          */
433                         if (unix_flags & SO_ACCEPTCON) {
434                                 ss_state = "LISTENING";
435                         } else {
436                                 ss_state = "";
437                         }
438                         break;
439
440                 case SS_CONNECTING:
441                         ss_state = "CONNECTING";
442                         break;
443
444                 case SS_CONNECTED:
445                         ss_state = "CONNECTED";
446                         break;
447
448                 case SS_DISCONNECTING:
449                         ss_state = "DISCONNECTING";
450                         break;
451
452                 default:
453                         ss_state = "UNKNOWN";
454         }
455
456         strcpy(ss_flags, "[ ");
457         if (unix_flags & SO_ACCEPTCON)
458                 strcat(ss_flags, "ACC ");
459         if (unix_flags & SO_WAITDATA)
460                 strcat(ss_flags, "W ");
461         if (unix_flags & SO_NOSPACE)
462                 strcat(ss_flags, "N ");
463
464         strcat(ss_flags, "]");
465
466         printf("%-5s %-6ld %-11s %-10s %-13s ",
467                    ss_proto, refcnt, ss_flags, ss_type, ss_state);
468         if (has & HAS_INODE)
469                 printf("%-6d ",inode);
470         else
471                 printf("-      ");
472         puts(path);
473 }
474
475 #define _PATH_PROCNET_UDP "/proc/net/udp"
476 #define _PATH_PROCNET_UDP6 "/proc/net/udp6"
477 #define _PATH_PROCNET_TCP "/proc/net/tcp"
478 #define _PATH_PROCNET_TCP6 "/proc/net/tcp6"
479 #define _PATH_PROCNET_RAW "/proc/net/raw"
480 #define _PATH_PROCNET_RAW6 "/proc/net/raw6"
481 #define _PATH_PROCNET_UNIX "/proc/net/unix"
482
483 static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
484 {
485         int lnr = 0;
486         FILE *procinfo;
487
488         procinfo = fopen(file, "r");
489         if (procinfo == NULL) {
490                 if (errno != ENOENT) {
491                         bb_perror_msg("%s", file);
492                 } else {
493                         bb_error_msg("no support for '%s' on this system", name);
494                 }
495                 return;
496         }
497         do {
498                 char *buffer = xmalloc_fgets(procinfo);
499                 if (buffer) {
500                         (proc)(lnr++, buffer);
501                         free(buffer);
502                 }
503         } while (!feof(procinfo));
504         fclose(procinfo);
505 }
506
507 /*
508  * Our main function.
509  */
510
511 int netstat_main(int argc, char **argv)
512 {
513         enum {
514                 OPT_extended = 0x4,
515                 OPT_showroute = 0x100,
516         };
517         unsigned opt;
518 #if ENABLE_FEATURE_IPV6
519         int inet = 1;
520         int inet6 = 1;
521 #else
522         enum { inet = 1, inet6 = 0 };
523 #endif
524
525         /* Option string must match NETSTAT_xxx constants */
526         opt = getopt32(argc, argv, "laentuwxr");
527         if (opt & 0x1) { // -l
528                 flags &= ~NETSTAT_CONNECTED;
529                 flags |= NETSTAT_LISTENING;
530         }
531         if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
532         //if (opt & 0x4) // -e
533         if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n
534         //if (opt & 0x10) // -t: NETSTAT_TCP
535         //if (opt & 0x20) // -u: NETSTAT_UDP
536         //if (opt & 0x40) // -w: NETSTAT_RAW
537         //if (opt & 0x80) // -x: NETSTAT_UNIX
538         if (opt & OPT_showroute) { // -r
539 #if ENABLE_ROUTE
540                 bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended));
541                 return 0;
542 #else
543                 bb_error_msg_and_die("-r (display routing table) is not compiled in");
544 #endif
545         }
546
547         opt &= NETSTAT_ALLPROTO;
548         if (opt) {
549                 flags &= ~NETSTAT_ALLPROTO;
550                 flags |= opt;
551         }
552         if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
553                 printf("Active Internet connections "); /* xxx */
554
555                 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
556                         printf("(servers and established)");
557                 else if (flags & NETSTAT_LISTENING)
558                         printf("(only servers)");
559                 else
560                         printf("(w/o servers)");
561                 printf("\nProto Recv-Q Send-Q Local Address           Foreign Address         State\n");
562         }
563         if (inet && flags & NETSTAT_TCP)
564                 do_info(_PATH_PROCNET_TCP, "AF INET (tcp)", tcp_do_one);
565 #if ENABLE_FEATURE_IPV6
566         if (inet6 && flags & NETSTAT_TCP)
567                 do_info(_PATH_PROCNET_TCP6, "AF INET6 (tcp)", tcp_do_one);
568 #endif
569         if (inet && flags & NETSTAT_UDP)
570                 do_info(_PATH_PROCNET_UDP, "AF INET (udp)", udp_do_one);
571 #if ENABLE_FEATURE_IPV6
572         if (inet6 && flags & NETSTAT_UDP)
573                 do_info(_PATH_PROCNET_UDP6, "AF INET6 (udp)", udp_do_one);
574 #endif
575         if (inet && flags & NETSTAT_RAW)
576                 do_info(_PATH_PROCNET_RAW, "AF INET (raw)", raw_do_one);
577 #if ENABLE_FEATURE_IPV6
578         if (inet6 && flags & NETSTAT_RAW)
579                 do_info(_PATH_PROCNET_RAW6, "AF INET6 (raw)", raw_do_one);
580 #endif
581         if (flags & NETSTAT_UNIX) {
582                 printf("Active UNIX domain sockets ");
583                 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
584                         printf("(servers and established)");
585                 else if (flags & NETSTAT_LISTENING)
586                         printf("(only servers)");
587                 else
588                         printf("(w/o servers)");
589                 printf("\nProto RefCnt Flags       Type       State         I-Node Path\n");
590                 do_info(_PATH_PROCNET_UNIX, "AF UNIX", unix_do_one);
591         }
592         return 0;
593 }