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