blob: 28ae9bc3233787ae8150cfba090c5e2f5d64f69b [file] [log] [blame]
Eric Andersen31a0ece2001-10-31 11:00:46 +00001/* 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>
Eric Andersencd8c4362001-11-10 11:22:46 +000033#include "inet_common.h"
Eric Andersen31a0ece2001-10-31 11:00:46 +000034#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
44int 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
53enum {
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
67static 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
83typedef 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
Eric Andersencd8c4362001-11-10 11:22:46 +000095static char *itoa(unsigned int i)
Eric Andersen31a0ece2001-10-31 11:00:46 +000096{
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
Eric Andersencd8c4362001-11-10 11:22:46 +0000108static char *get_sname(int port, const char *proto, int num)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000109{
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
Eric Andersencd8c4362001-11-10 11:22:46 +0000123static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
Eric Andersen31a0ece2001-10-31 11:00:46 +0000124{
125 char *port_name;
Eric Andersencd8c4362001-11-10 11:22:46 +0000126
127 INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
128 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0),
129 0xffffffff);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000130 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
138static 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
188static 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
252static 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
306static 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
431static 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
461int 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}