6e7cc5e7bfda1dd8cc9f6e225dcfe0475c5c8483
[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) 2001 by Bart Visscher <magick@linux-fan.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <signal.h>
28 #include <errno.h>
29 #include <sys/stat.h>
30 #include <dirent.h>
31 #include <unistd.h>
32 #include "inet_common.h"
33 #include "busybox.h"
34 #include "pwd.h"
35
36 #ifdef CONFIG_ROUTE
37 extern void displayroutes(int noresolve, int netstatfmt);
38 #endif
39
40 #define NETSTAT_CONNECTED       0x01
41 #define NETSTAT_LISTENING       0x02
42 #define NETSTAT_NUMERIC         0x04
43 #define NETSTAT_TCP                     0x10
44 #define NETSTAT_UDP                     0x20
45 #define NETSTAT_RAW                     0x40
46 #define NETSTAT_UNIX            0x80
47
48 int flags = NETSTAT_CONNECTED | 
49                         NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX;
50
51 #define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH)
52 #define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s)
53 #define PROGNAME_WIDTH2(s) #s
54
55 #define PRG_HASH_SIZE 211
56
57 enum {
58     TCP_ESTABLISHED = 1,
59     TCP_SYN_SENT,
60     TCP_SYN_RECV,
61     TCP_FIN_WAIT1,
62     TCP_FIN_WAIT2,
63     TCP_TIME_WAIT,
64     TCP_CLOSE,
65     TCP_CLOSE_WAIT,
66     TCP_LAST_ACK,
67     TCP_LISTEN,
68     TCP_CLOSING                 /* now a valid state */
69 };
70
71 static const char *tcp_state[] =
72 {
73     "",
74     "ESTABLISHED",
75     "SYN_SENT",
76     "SYN_RECV",
77     "FIN_WAIT1",
78     "FIN_WAIT2",
79     "TIME_WAIT",
80     "CLOSE",
81     "CLOSE_WAIT",
82     "LAST_ACK",
83     "LISTEN",
84     "CLOSING"
85 };
86
87 typedef enum {
88     SS_FREE = 0,                /* not allocated                */
89     SS_UNCONNECTED,             /* unconnected to any socket    */
90     SS_CONNECTING,              /* in process of connecting     */
91     SS_CONNECTED,               /* connected to socket          */
92     SS_DISCONNECTING            /* in process of disconnecting  */
93 } socket_state;
94
95 #define SO_ACCEPTCON    (1<<16) /* performed a listen           */
96 #define SO_WAITDATA     (1<<17) /* wait data to read            */
97 #define SO_NOSPACE      (1<<18) /* no space to write            */
98
99 static char *itoa(unsigned int i)
100 {
101         /* 21 digits plus null terminator, good for 64-bit or smaller ints */
102         static char local[22];
103         char *p = &local[21];
104         *p-- = '\0';
105         do {
106                 *p-- = '0' + i % 10;
107                 i /= 10;
108         } while (i > 0);
109         return p + 1;
110 }
111
112 static char *get_sname(int port, const char *proto, int num)
113 {
114         char *str=itoa(ntohs(port));
115         if (num) {
116         } else {
117                 struct servent *se=getservbyport(port,proto);
118                 if (se)
119                         str=se->s_name;
120         }
121         if (!port) {
122                 str="*";
123         }
124         return str;
125 }
126
127 static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
128 {
129         char *port_name;
130
131         INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
132                 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0),
133                 0xffffffff);
134         port_name=get_sname(htons(port), proto, numeric);
135         if ((strlen(ip_port) + strlen(port_name)) > 22)
136                 ip_port[22 - strlen(port_name)] = '\0';
137         ip_port+=strlen(ip_port);
138         strcat(ip_port, ":");
139         strcat(ip_port, port_name);
140 }
141
142 static void tcp_do_one(int lnr, const char *line)
143 {
144         char local_addr[64], rem_addr[64];
145         char *state_str, more[512];
146         int num, local_port, rem_port, d, state, timer_run, uid, timeout;
147         struct sockaddr_in localaddr, remaddr;
148         unsigned long rxq, txq, time_len, retr, inode;
149
150         if (lnr == 0)
151                 return;
152
153         more[0] = '\0';
154         num = sscanf(line,
155                                  "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
156                                  &d, local_addr, &local_port,
157                                  rem_addr, &rem_port, &state,
158                                  &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
159
160         if (strlen(local_addr) > 8) {
161         } else {
162                 sscanf(local_addr, "%X",
163                            &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
164                 sscanf(rem_addr, "%X",
165                            &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
166                 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
167                 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
168         }
169
170         if (num < 10) {
171                 error_msg("warning, got bogus tcp line.\n");
172                 return;
173         }
174         state_str=(char*)tcp_state[state];
175         if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
176                 (!rem_port && (flags&NETSTAT_LISTENING)))
177         {
178                 snprint_ip_port(local_addr, sizeof(local_addr),
179                                                 (struct sockaddr *) &localaddr, local_port,
180                                                 "tcp", flags&NETSTAT_NUMERIC);
181                                                 
182                 snprint_ip_port(rem_addr, sizeof(rem_addr),
183                                                 (struct sockaddr *) &remaddr, rem_port,
184                                                 "tcp", flags&NETSTAT_NUMERIC);
185
186                 printf("tcp   %6ld %6ld %-23s %-23s %-12s\n",
187                            rxq, txq, local_addr, rem_addr, state_str);
188
189         }
190 }
191
192 static void udp_do_one(int lnr, const char *line)
193 {
194         char local_addr[64], rem_addr[64];
195         char *state_str, more[512];
196         int num, local_port, rem_port, d, state, timer_run, uid, timeout;
197         struct sockaddr_in localaddr, remaddr;
198         unsigned long rxq, txq, time_len, retr, inode;
199
200         if (lnr == 0)
201                 return;
202
203         more[0] = '\0';
204         num = sscanf(line,
205                                  "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
206                                  &d, local_addr, &local_port,
207                                  rem_addr, &rem_port, &state,
208                                  &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
209
210         if (strlen(local_addr) > 8) {
211         } else {
212                 sscanf(local_addr, "%X",
213                            &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
214                 sscanf(rem_addr, "%X",
215                            &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
216                 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
217                 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
218         }
219
220         if (num < 10) {
221                 error_msg("warning, got bogus udp line.\n");
222                 return;
223         }
224         switch (state) {
225                 case TCP_ESTABLISHED:
226                         state_str = "ESTABLISHED";
227                         break;
228
229                 case TCP_CLOSE:
230                         state_str = "";
231                         break;
232
233                 default:
234                         state_str = "UNKNOWN";
235                         break;
236         }
237
238 #define notnull(A) (A.sin_addr.s_addr)
239         if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
240                 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
241         {
242                 snprint_ip_port(local_addr, sizeof(local_addr),
243                                                 (struct sockaddr *) &localaddr, local_port,
244                                                 "udp", flags&NETSTAT_NUMERIC);
245                                                 
246                 snprint_ip_port(rem_addr, sizeof(rem_addr),
247                                                 (struct sockaddr *) &remaddr, rem_port,
248                                                 "udp", flags&NETSTAT_NUMERIC);
249
250                 printf("udp   %6ld %6ld %-23s %-23s %-12s\n",
251                            rxq, txq, local_addr, rem_addr, state_str);
252
253         }
254 }
255
256 static void raw_do_one(int lnr, const char *line)
257 {
258         char local_addr[64], rem_addr[64];
259         char *state_str, more[512];
260         int num, local_port, rem_port, d, state, timer_run, uid, timeout;
261         struct sockaddr_in localaddr, remaddr;
262         unsigned long rxq, txq, time_len, retr, inode;
263
264         if (lnr == 0)
265                 return;
266
267         more[0] = '\0';
268         num = sscanf(line,
269                                  "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
270                                  &d, local_addr, &local_port,
271                                  rem_addr, &rem_port, &state,
272                                  &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
273
274         if (strlen(local_addr) > 8) {
275         } else {
276                 sscanf(local_addr, "%X",
277                            &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
278                 sscanf(rem_addr, "%X",
279                            &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
280                 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
281                 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
282         }
283
284         if (num < 10) {
285                 error_msg("warning, got bogus raw line.\n");
286                 return;
287         }
288         state_str=itoa(state);
289
290 #define notnull(A) (A.sin_addr.s_addr)
291         if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
292                 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
293         {
294                 snprint_ip_port(local_addr, sizeof(local_addr),
295                                                 (struct sockaddr *) &localaddr, local_port,
296                                                 "raw", flags&NETSTAT_NUMERIC);
297                                                 
298                 snprint_ip_port(rem_addr, sizeof(rem_addr),
299                                                 (struct sockaddr *) &remaddr, rem_port,
300                                                 "raw", flags&NETSTAT_NUMERIC);
301
302                 printf("raw   %6ld %6ld %-23s %-23s %-12s\n",
303                            rxq, txq, local_addr, rem_addr, state_str);
304
305         }
306 }
307
308 #define HAS_INODE 1
309
310 static void unix_do_one(int nr, const char *line)
311 {
312         static int has = 0;
313         char path[PATH_MAX], ss_flags[32];
314         char *ss_proto, *ss_state, *ss_type;
315         int num, state, type, inode;
316         void *d;
317         unsigned long refcnt, proto, unix_flags;
318
319         if (nr == 0) {
320                 if (strstr(line, "Inode"))
321                         has |= HAS_INODE;
322                 return;
323         }
324         path[0] = '\0';
325         num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
326                                  &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
327         if (num < 6) {
328                 error_msg("warning, got bogus unix line.\n");
329                 return;
330         }
331         if (!(has & HAS_INODE))
332                 snprintf(path,sizeof(path),"%d",inode);
333
334         if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
335                 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
336                         if (!(flags&NETSTAT_LISTENING))
337                                 return;
338                 } else {
339                         if (!(flags&NETSTAT_CONNECTED))
340                                 return;
341                 }
342         }
343
344         switch (proto) {
345                 case 0:
346                         ss_proto = "unix";
347                         break;
348
349                 default:
350                         ss_proto = "??";
351         }
352
353         switch (type) {
354                 case SOCK_STREAM:
355                         ss_type = "STREAM";
356                         break;
357
358                 case SOCK_DGRAM:
359                         ss_type = "DGRAM";
360                         break;
361
362                 case SOCK_RAW:
363                         ss_type = "RAW";
364                         break;
365
366                 case SOCK_RDM:
367                         ss_type = "RDM";
368                         break;
369
370                 case SOCK_SEQPACKET:
371                         ss_type = "SEQPACKET";
372                         break;
373
374                 default:
375                         ss_type = "UNKNOWN";
376         }
377
378         switch (state) {
379                 case SS_FREE:
380                         ss_state = "FREE";
381                         break;
382
383                 case SS_UNCONNECTED:
384                         /*
385                          * Unconnected sockets may be listening
386                          * for something.
387                          */
388                         if (unix_flags & SO_ACCEPTCON) {
389                                 ss_state = "LISTENING";
390                         } else {
391                                 ss_state = "";
392                         }
393                         break;
394
395                 case SS_CONNECTING:
396                         ss_state = "CONNECTING";
397                         break;
398
399                 case SS_CONNECTED:
400                         ss_state = "CONNECTED";
401                         break;
402
403                 case SS_DISCONNECTING:
404                         ss_state = "DISCONNECTING";
405                         break;
406
407                 default:
408                         ss_state = "UNKNOWN";
409         }
410
411         strcpy(ss_flags, "[ ");
412         if (unix_flags & SO_ACCEPTCON)
413                 strcat(ss_flags, "ACC ");
414         if (unix_flags & SO_WAITDATA)
415                 strcat(ss_flags, "W ");
416         if (unix_flags & SO_NOSPACE)
417                 strcat(ss_flags, "N ");
418
419         strcat(ss_flags, "]");
420
421         printf("%-5s %-6ld %-11s %-10s %-13s ",
422                    ss_proto, refcnt, ss_flags, ss_type, ss_state);
423         if (has & HAS_INODE)
424                 printf("%-6d ",inode);
425         else
426                 printf("-      ");
427         puts(path);
428 }
429
430 #define _PATH_PROCNET_UDP "/proc/net/udp"
431 #define _PATH_PROCNET_TCP "/proc/net/tcp"
432 #define _PATH_PROCNET_RAW "/proc/net/raw"
433 #define _PATH_PROCNET_UNIX "/proc/net/unix"
434
435 static int do_info(char *file, char *name, void (*proc)(int, const char *))
436 {
437         char buffer[8192];
438         int rc = 0;
439         int lnr = 0;
440         FILE *procinfo;
441
442         procinfo = fopen((file), "r");
443         if (procinfo == NULL) {
444                 if (errno != ENOENT) {
445                         perror((file));
446                         return -1;
447                 }
448                 error_msg("%s: no support for `%s' on this system.\n",
449                                 "netstat", (name));
450                 rc = 1;
451         } else {
452                 do {
453                         if (fgets(buffer, sizeof(buffer), procinfo))
454                                 (proc)(lnr++, buffer);
455                 } while (!feof(procinfo));
456                 fclose(procinfo);
457         }
458         return rc;
459 }
460
461 /*
462  * Our main function.
463  */
464
465 int netstat_main(int argc, char **argv)
466 {
467         int opt;
468         int new_flags=0;
469         int showroute = 0, extended = 0; 
470         while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
471                 switch (opt) {
472                 case 'l':
473                         flags &= ~NETSTAT_CONNECTED;
474                         flags |= NETSTAT_LISTENING;
475                         break;
476                 case 'a':
477                         flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
478                         break;
479                 case 'n':
480                         flags |= NETSTAT_NUMERIC;
481                         break;
482                 case 'r':
483                         showroute = 1;
484                         break;
485                 case 'e':
486                         extended = 1;
487                         break;
488                 case 't':
489                         new_flags |= NETSTAT_TCP;
490                         break;
491                 case 'u':
492                         new_flags |= NETSTAT_UDP;
493                         break;
494                 case 'w':
495                         new_flags |= NETSTAT_RAW;
496                         break;
497                 case 'x':
498                         new_flags |= NETSTAT_UNIX;
499                         break;
500                 default:
501                         show_usage();
502                 }
503         if ( showroute ) {
504 #ifdef CONFIG_ROUTE     
505                 displayroutes ( flags & NETSTAT_NUMERIC, !extended );
506                 return 0; 
507 #else
508                 printf( "-r (display routing table) is not compiled in.\n" );
509                 return 1;
510 #endif
511         }       
512                 
513         if (new_flags) {
514                 flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
515                 flags |= new_flags;
516         }
517         if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
518                 printf("Active Internet connections "); /* xxx */
519
520                 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
521                         printf("(servers and established)");
522                 else {
523                         if (flags&NETSTAT_LISTENING)
524                                 printf("(only servers)");
525                         else
526                                 printf("(w/o servers)");
527                 }
528                 printf("\nProto Recv-Q Send-Q Local Address           Foreign Address         State      \n");
529         }
530         if (flags&NETSTAT_TCP)
531                 do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
532         if (flags&NETSTAT_UDP)
533                 do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
534         if (flags&NETSTAT_RAW)
535                 do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
536         if (flags&NETSTAT_UNIX) {
537                 printf("Active UNIX domain sockets ");
538                 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
539                         printf("(servers and established)");
540                 else {
541                         if (flags&NETSTAT_LISTENING)
542                                 printf("(only servers)");
543                         else
544                                 printf("(w/o servers)");
545                 }
546
547                 printf("\nProto RefCnt Flags       Type       State         I-Node Path\n");
548                 do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);
549         }
550         return 0;
551 }