00b58228e5da5a216440a66c3c6b8202d2bffe6a
[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 static 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 * const 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         const char *state_str;
146         char more[512];
147         int num, local_port, rem_port, d, state, timer_run, uid, timeout;
148         struct sockaddr_in localaddr, remaddr;
149         unsigned long rxq, txq, time_len, retr, inode;
150
151         if (lnr == 0)
152                 return;
153
154         more[0] = '\0';
155         num = sscanf(line,
156                                  "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
157                                  &d, local_addr, &local_port,
158                                  rem_addr, &rem_port, &state,
159                                  &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
160
161         if (strlen(local_addr) > 8) {
162         } else {
163                 sscanf(local_addr, "%X",
164                            &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
165                 sscanf(rem_addr, "%X",
166                            &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
167                 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
168                 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
169         }
170
171         if (num < 10) {
172                 error_msg("warning, got bogus tcp line.");
173                 return;
174         }
175         state_str = tcp_state[state];
176         if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
177                 (!rem_port && (flags&NETSTAT_LISTENING)))
178         {
179                 snprint_ip_port(local_addr, sizeof(local_addr),
180                                                 (struct sockaddr *) &localaddr, local_port,
181                                                 "tcp", flags&NETSTAT_NUMERIC);
182                                                 
183                 snprint_ip_port(rem_addr, sizeof(rem_addr),
184                                                 (struct sockaddr *) &remaddr, rem_port,
185                                                 "tcp", flags&NETSTAT_NUMERIC);
186
187                 printf("tcp   %6ld %6ld %-23s %-23s %-12s\n",
188                            rxq, txq, local_addr, rem_addr, state_str);
189
190         }
191 }
192
193 static void udp_do_one(int lnr, const char *line)
194 {
195         char local_addr[64], rem_addr[64];
196         char *state_str, more[512];
197         int num, local_port, rem_port, d, state, timer_run, uid, timeout;
198         struct sockaddr_in localaddr, remaddr;
199         unsigned long rxq, txq, time_len, retr, inode;
200
201         if (lnr == 0)
202                 return;
203
204         more[0] = '\0';
205         num = sscanf(line,
206                                  "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
207                                  &d, local_addr, &local_port,
208                                  rem_addr, &rem_port, &state,
209                                  &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
210
211         if (strlen(local_addr) > 8) {
212         } else {
213                 sscanf(local_addr, "%X",
214                            &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
215                 sscanf(rem_addr, "%X",
216                            &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
217                 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
218                 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
219         }
220
221         if (num < 10) {
222                 error_msg("warning, got bogus udp line.");
223                 return;
224         }
225         switch (state) {
226                 case TCP_ESTABLISHED:
227                         state_str = "ESTABLISHED";
228                         break;
229
230                 case TCP_CLOSE:
231                         state_str = "";
232                         break;
233
234                 default:
235                         state_str = "UNKNOWN";
236                         break;
237         }
238
239 #define notnull(A) (A.sin_addr.s_addr)
240         if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
241                 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
242         {
243                 snprint_ip_port(local_addr, sizeof(local_addr),
244                                                 (struct sockaddr *) &localaddr, local_port,
245                                                 "udp", flags&NETSTAT_NUMERIC);
246                                                 
247                 snprint_ip_port(rem_addr, sizeof(rem_addr),
248                                                 (struct sockaddr *) &remaddr, rem_port,
249                                                 "udp", flags&NETSTAT_NUMERIC);
250
251                 printf("udp   %6ld %6ld %-23s %-23s %-12s\n",
252                            rxq, txq, local_addr, rem_addr, state_str);
253
254         }
255 }
256
257 static void raw_do_one(int lnr, const char *line)
258 {
259         char local_addr[64], rem_addr[64];
260         char *state_str, more[512];
261         int num, local_port, rem_port, d, state, timer_run, uid, timeout;
262         struct sockaddr_in localaddr, remaddr;
263         unsigned long rxq, txq, time_len, retr, inode;
264
265         if (lnr == 0)
266                 return;
267
268         more[0] = '\0';
269         num = sscanf(line,
270                                  "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
271                                  &d, local_addr, &local_port,
272                                  rem_addr, &rem_port, &state,
273                                  &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
274
275         if (strlen(local_addr) > 8) {
276         } else {
277                 sscanf(local_addr, "%X",
278                            &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
279                 sscanf(rem_addr, "%X",
280                            &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
281                 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
282                 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
283         }
284
285         if (num < 10) {
286                 error_msg("warning, got bogus raw line.");
287                 return;
288         }
289         state_str=itoa(state);
290
291 #define notnull(A) (A.sin_addr.s_addr)
292         if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
293                 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
294         {
295                 snprint_ip_port(local_addr, sizeof(local_addr),
296                                                 (struct sockaddr *) &localaddr, local_port,
297                                                 "raw", flags&NETSTAT_NUMERIC);
298                                                 
299                 snprint_ip_port(rem_addr, sizeof(rem_addr),
300                                                 (struct sockaddr *) &remaddr, rem_port,
301                                                 "raw", flags&NETSTAT_NUMERIC);
302
303                 printf("raw   %6ld %6ld %-23s %-23s %-12s\n",
304                            rxq, txq, local_addr, rem_addr, state_str);
305
306         }
307 }
308
309 #define HAS_INODE 1
310
311 static void unix_do_one(int nr, const char *line)
312 {
313         static int has = 0;
314         char path[PATH_MAX], ss_flags[32];
315         char *ss_proto, *ss_state, *ss_type;
316         int num, state, type, inode;
317         void *d;
318         unsigned long refcnt, proto, unix_flags;
319
320         if (nr == 0) {
321                 if (strstr(line, "Inode"))
322                         has |= HAS_INODE;
323                 return;
324         }
325         path[0] = '\0';
326         num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
327                                  &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
328         if (num < 6) {
329                 error_msg("warning, got bogus unix line.");
330                 return;
331         }
332         if (!(has & HAS_INODE))
333                 snprintf(path,sizeof(path),"%d",inode);
334
335         if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
336                 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
337                         if (!(flags&NETSTAT_LISTENING))
338                                 return;
339                 } else {
340                         if (!(flags&NETSTAT_CONNECTED))
341                                 return;
342                 }
343         }
344
345         switch (proto) {
346                 case 0:
347                         ss_proto = "unix";
348                         break;
349
350                 default:
351                         ss_proto = "??";
352         }
353
354         switch (type) {
355                 case SOCK_STREAM:
356                         ss_type = "STREAM";
357                         break;
358
359                 case SOCK_DGRAM:
360                         ss_type = "DGRAM";
361                         break;
362
363                 case SOCK_RAW:
364                         ss_type = "RAW";
365                         break;
366
367                 case SOCK_RDM:
368                         ss_type = "RDM";
369                         break;
370
371                 case SOCK_SEQPACKET:
372                         ss_type = "SEQPACKET";
373                         break;
374
375                 default:
376                         ss_type = "UNKNOWN";
377         }
378
379         switch (state) {
380                 case SS_FREE:
381                         ss_state = "FREE";
382                         break;
383
384                 case SS_UNCONNECTED:
385                         /*
386                          * Unconnected sockets may be listening
387                          * for something.
388                          */
389                         if (unix_flags & SO_ACCEPTCON) {
390                                 ss_state = "LISTENING";
391                         } else {
392                                 ss_state = "";
393                         }
394                         break;
395
396                 case SS_CONNECTING:
397                         ss_state = "CONNECTING";
398                         break;
399
400                 case SS_CONNECTED:
401                         ss_state = "CONNECTED";
402                         break;
403
404                 case SS_DISCONNECTING:
405                         ss_state = "DISCONNECTING";
406                         break;
407
408                 default:
409                         ss_state = "UNKNOWN";
410         }
411
412         strcpy(ss_flags, "[ ");
413         if (unix_flags & SO_ACCEPTCON)
414                 strcat(ss_flags, "ACC ");
415         if (unix_flags & SO_WAITDATA)
416                 strcat(ss_flags, "W ");
417         if (unix_flags & SO_NOSPACE)
418                 strcat(ss_flags, "N ");
419
420         strcat(ss_flags, "]");
421
422         printf("%-5s %-6ld %-11s %-10s %-13s ",
423                    ss_proto, refcnt, ss_flags, ss_type, ss_state);
424         if (has & HAS_INODE)
425                 printf("%-6d ",inode);
426         else
427                 printf("-      ");
428         puts(path);
429 }
430
431 #define _PATH_PROCNET_UDP "/proc/net/udp"
432 #define _PATH_PROCNET_TCP "/proc/net/tcp"
433 #define _PATH_PROCNET_RAW "/proc/net/raw"
434 #define _PATH_PROCNET_UNIX "/proc/net/unix"
435
436 static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
437 {
438         char buffer[8192];
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                 } else {
447                 error_msg("no support for `%s' on this system.", name);
448                 }
449         } else {
450                 do {
451                         if (fgets(buffer, sizeof(buffer), procinfo))
452                                 (proc)(lnr++, buffer);
453                 } while (!feof(procinfo));
454                 fclose(procinfo);
455         }
456 }
457
458 /*
459  * Our main function.
460  */
461
462 int netstat_main(int argc, char **argv)
463 {
464         int opt;
465         int new_flags=0;
466         int showroute = 0, extended = 0; 
467         while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
468                 switch (opt) {
469                 case 'l':
470                         flags &= ~NETSTAT_CONNECTED;
471                         flags |= NETSTAT_LISTENING;
472                         break;
473                 case 'a':
474                         flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
475                         break;
476                 case 'n':
477                         flags |= NETSTAT_NUMERIC;
478                         break;
479                 case 'r':
480                         showroute = 1;
481                         break;
482                 case 'e':
483                         extended = 1;
484                         break;
485                 case 't':
486                         new_flags |= NETSTAT_TCP;
487                         break;
488                 case 'u':
489                         new_flags |= NETSTAT_UDP;
490                         break;
491                 case 'w':
492                         new_flags |= NETSTAT_RAW;
493                         break;
494                 case 'x':
495                         new_flags |= NETSTAT_UNIX;
496                         break;
497                 default:
498                         show_usage();
499                 }
500         if ( showroute ) {
501 #ifdef CONFIG_ROUTE     
502                 displayroutes ( flags & NETSTAT_NUMERIC, !extended );
503                 return 0; 
504 #else
505                 error_msg_and_die( "-r (display routing table) is not compiled in." );
506 #endif
507         }       
508                 
509         if (new_flags) {
510                 flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
511                 flags |= new_flags;
512         }
513         if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
514                 printf("Active Internet connections "); /* xxx */
515
516                 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
517                         printf("(servers and established)");
518                 else {
519                         if (flags&NETSTAT_LISTENING)
520                                 printf("(only servers)");
521                         else
522                                 printf("(w/o servers)");
523                 }
524                 printf("\nProto Recv-Q Send-Q Local Address           Foreign Address         State      \n");
525         }
526         if (flags&NETSTAT_TCP)
527                 do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
528         if (flags&NETSTAT_UDP)
529                 do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
530         if (flags&NETSTAT_RAW)
531                 do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
532         if (flags&NETSTAT_UNIX) {
533                 printf("Active UNIX domain sockets ");
534                 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
535                         printf("(servers and established)");
536                 else {
537                         if (flags&NETSTAT_LISTENING)
538                                 printf("(only servers)");
539                         else
540                                 printf("(w/o servers)");
541                 }
542
543                 printf("\nProto RefCnt Flags       Type       State         I-Node Path\n");
544                 do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);
545         }
546         return 0;
547 }