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