diff options
author | Elliott Hughes <enh@google.com> | 2021-02-06 12:47:59 -0800 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2021-02-06 19:32:03 -0600 |
commit | 859764984485fd0d7f92b0f49430832c3d95888e (patch) | |
tree | c94effc530cd743785a413fc03acc351cd39f976 | |
parent | 7ed7ff8386cc982ec9e334332f853e9e1f0bcfc6 (diff) | |
download | toybox-859764984485fd0d7f92b0f49430832c3d95888e.tar.gz |
netstat: various fixes.
Avoid fgets() into toybuf other than to skip header lines, preferring
fscanf() or xgetline() instead.
Remove the early returns if we fail to skip a header line (fixes #62).
Fix `netstat -xl` which was previously broken (because only -a would
include listening sockets).
Fix a variety of formatting issues, bringing the diff between toybox
netstat and net-tools netstat down (which was what I used for testing).
Use FLAG() more consistently.
-rw-r--r-- | toys/net/netstat.c | 143 |
1 files changed, 62 insertions, 81 deletions
diff --git a/toys/net/netstat.c b/toys/net/netstat.c index 23cd9f6d..24a2ceed 100644 --- a/toys/net/netstat.c +++ b/toys/net/netstat.c @@ -36,40 +36,25 @@ GLOBALS( int wpad; ) -// convert address into text format. static void addr2str(int af, void *addr, unsigned port, char *buf, int len, char *proto) { + char pres[INET6_ADDRSTRLEN]; + struct servent *se = 0; int pos, count; - struct servent *ser = 0; -// TODO lib/net.c has ntop() - // Convert to numeric address - if (!inet_ntop(af, addr, buf, 256)) { - *buf = 0; + if (!inet_ntop(af, addr, pres, sizeof(pres))) perror_exit("inet_ntop"); - return; - } - buf[len] = 0; - pos = strlen(buf); - - // If there's no port number, it's a local :* binding, nothing to look up. - if (!port) { - if (len-pos<2) pos = len-2; - strcpy(buf+pos, ":*"); - - return; - } - -// TODO xgetaddrinfo() - if (!FLAG(n)) { + if (FLAG(n) || !port) { + strcpy(buf, pres); + } else { struct addrinfo hints, *result, *rp; char cut[4]; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = af; - if (!getaddrinfo(buf, NULL, &hints, &result)) { + if (!getaddrinfo(pres, NULL, &hints, &result)) { socklen_t sock_len = (af == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); @@ -78,28 +63,31 @@ static void addr2str(int af, void *addr, unsigned port, char *buf, int len, if (!getnameinfo(rp->ai_addr, sock_len, buf, 256, 0, 0, 0)) break; freeaddrinfo(result); buf[len] = 0; - pos = strlen(buf); } - // Doesn't understand proto "tcp6", so truncate + // getservbyport() doesn't understand proto "tcp6", so truncate memcpy(cut, proto, 3); cut[3] = 0; - ser = getservbyport(htons(port), cut); + se = getservbyport(htons(port), cut); } - // Append :service - count = snprintf(0, 0, ":%u", port); - if (ser) { - count = snprintf(0, 0, ":%s", ser->s_name); - // sheer paranoia + if (!strcmp(buf, "::")) strcpy(buf, "[::]"); + + // Append :service or :* if port == 0. + if (se) { + count = snprintf(0, 0, ":%s", se->s_name); + // NI_MAXSERV == 32, which is greater than our minimum field width. + // (Although the longest service name on Debian in 2021 is only 16 bytes.) if (count>=len) { count = len-1; - ser->s_name[count] = 0; + se->s_name[count] = 0; } - } + } else count = port ? snprintf(0, 0, ":%u", port) : 2; + // We always show the port, even if that means clobbering the end of the host. + pos = strlen(buf); if (len-pos<count) pos = len-count; - if (ser) sprintf(buf+pos, ":%s", ser->s_name); - else sprintf(buf+pos, ":%u", port); + if (se) sprintf(buf+pos, ":%s", se->s_name); + else sprintf(buf+pos, port ? ":%u" : ":*", port); } // Display info for tcp/udp/raw @@ -109,10 +97,10 @@ static void show_ip(char *fname) char *state_label[] = {"", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING", "UNKNOWN"}; - FILE *fp = fopen(fname, "r"); + FILE *fp = xfopen(fname, "r"); - if (!fp) return perror_msg("'%s'", fname); - if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header. + // Skip header. + fgets(toybuf, sizeof(toybuf), fp); while (fgets(toybuf, sizeof(toybuf), fp)) { char lip[256], rip[256]; @@ -120,7 +108,7 @@ static void show_ip(char *fname) struct {unsigned u; unsigned char b[4];} i4; struct {struct {unsigned a, b, c, d;} u; unsigned char b[16];} i6; } laddr, raddr; - unsigned lport, rport, state, txq, rxq, num, uid, nitems; + unsigned lport, rport, state, txq, rxq, num, uid, af = AF_INET6; unsigned long inode; // Try ipv6, then try ipv4 @@ -131,19 +119,19 @@ static void show_ip(char *fname) &raddr.i6.u.c, &raddr.i6.u.d, &rport, &state, &txq, &rxq, &uid, &inode)) { - nitems = AF_INET; + af = AF_INET; if (10 != sscanf(toybuf, " %d: %x:%x %x:%x %x %x:%x %*X:%*X %*X %d %*d %ld", &num, &laddr.i4.u, &lport, &raddr.i4.u, &rport, &state, &txq, &rxq, &uid, &inode)) continue; - } else nitems = AF_INET6; + } // Should we display this? (listening or all or TCP/UDP/RAW) if (!(FLAG(l) && (!rport && (state&0xA))) && !FLAG(a) && !(rport&0x70)) continue; - addr2str(nitems, &laddr, lport, lip, TT.wpad, label); - addr2str(nitems, &raddr, rport, rip, TT.wpad, label); + addr2str(af, &laddr, lport, lip, TT.wpad, label); + addr2str(af, &raddr, rport, rip, TT.wpad, label); // Display data s = label; @@ -177,46 +165,43 @@ static void show_unix_sockets(void) { char *types[] = {"","STREAM","DGRAM","RAW","RDM","SEQPACKET","DCCP","PACKET"}, *states[] = {"","LISTENING","CONNECTING","CONNECTED","DISCONNECTING"}, - *s, *ss; + *filename = 0; unsigned long refcount, flags, type, state, inode; FILE *fp = xfopen("/proc/net/unix", "r"); - if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header. - - while (fgets(toybuf, sizeof(toybuf), fp)) { - unsigned offset = 0; - - // count = 6 or 7 (first field ignored, sockets don't always have filenames) - if (6<sscanf(toybuf, "%*p: %lX %*X %lX %lX %lX %lu %n", - &refcount, &flags, &type, &state, &inode, &offset)) - continue; + // Skip header. + fgets(toybuf, sizeof(toybuf), fp); + while (fscanf(fp, "%*p: %lX %*X %lX %lX %lX %lu%m[^\n]", &refcount, &flags, + &type, &state, &inode, &filename) >= 5) { // Linux exports only SO_ACCEPTCON since 2.3.15pre3 in 1999, but let's // filter in case they add more someday. flags &= 1<<16; - // Only show unconnected listening sockets with -a - if (state==1 && flags && !(toys.optflags&FLAG_a)) continue; + // Only show unconnected listening sockets with -a or -l. + if (state==1 && flags && !(FLAG(a) || FLAG(l))) continue; if (type==10) type = 7; // move SOCK_PACKET into line if (type>ARRAY_LEN(types)) type = 0; if (state>ARRAY_LEN(states) || (state==1 && !flags)) state = 0; + + if (state!=1 && FLAG(l)) continue; + sprintf(toybuf, "[ %s]", flags ? "ACC " : ""); - printf("unix %-6ld %-11s %-10s %-13s %8lu ", + printf("unix %-6ld %-11s %-10s %-13s %-8lu ", refcount, toybuf, types[type], states[state], inode); if (FLAG(p)) { struct num_cache *nc = get_num_cache(TT.inodes, inode); - printf("%-19.19s", nc ? nc->data : "-"); + printf("%-19.19s ", nc ? nc->data : "-"); } - if (offset) { - if ((ss = strrchr(s = toybuf+offset, '\n'))) *ss = 0; - printf("%s", s); - } - xputc('\n'); + if (filename) { + printf("%s\n", filename+!FLAG(p)); + free(filename); + filename = 0; + } else xputc('\n'); } - fclose(fp); } @@ -265,18 +250,16 @@ static void display_routes(void) char iface[64]={0}; FILE *fp = xfopen("/proc/net/route", "r"); - if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header. + // Skip header. + fgets(toybuf, sizeof(toybuf), fp); printf("Kernel IP routing table\n" "Destination\tGateway \tGenmask \tFlags %s Iface\n", - !(toys.optflags&FLAG_e) ? " MSS Window irtt" : "Metric Ref Use"); - - while (fgets(toybuf, sizeof(toybuf), fp)) { - char *destip = 0, *gateip = 0, *maskip = 0; + !FLAG(e) ? " MSS Window irtt" : "Metric Ref Use"); - if (11 != sscanf(toybuf, "%63s%x%x%X%d%d%d%x%d%d%d", iface, &dest, - &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt)) - break; + while (fscanf(fp, "%63s%x%x%X%d%d%d%x%d%d%d", iface, &dest, &gate, &flags, + &ref, &use, &metric, &mask, &mss, &win, &irtt) == 11) { + char *destip = 0, *gateip = 0, *maskip = 0; // skip down interfaces. if (!(flags & RTF_UP)) continue; @@ -285,12 +268,12 @@ static void display_routes(void) if (dest) { if (inet_ntop(AF_INET, &dest, out, 16)) destip = out; - } else destip = (toys.optflags&FLAG_n) ? "0.0.0.0" : "default"; + } else destip = FLAG(n) ? "0.0.0.0" : "default"; out += 16; if (gate) { if (inet_ntop(AF_INET, &gate, out, 16)) gateip = out; - } else gateip = (toys.optflags&FLAG_n) ? "0.0.0.0" : "*"; + } else gateip = FLAG(n) ? "0.0.0.0" : "*"; out += 16; // TODO /24 @@ -311,27 +294,26 @@ static void display_routes(void) if (!FLAG(e)) printf("%5d %-5d %6d %s\n", mss, win, irtt, iface); else printf("%-6d %-2d %7d %s\n", metric, ref, use, iface); } - fclose(fp); } void netstat_main(void) { int tuwx = FLAG_t|FLAG_u|FLAG_w|FLAG_x; - char *type = "w/o"; + char *type = "w/o servers"; TT.wpad = FLAG(W) ? 51 : 23; if (!(toys.optflags&(FLAG_r|tuwx))) toys.optflags |= tuwx; if (FLAG(r)) display_routes(); if (!(toys.optflags&tuwx)) return; - if (FLAG(a)) type = "established and"; - else if (FLAG(l)) type = "only"; + if (FLAG(a)) type = "servers and established"; + else if (FLAG(l)) type = "only servers"; if (FLAG(p)) dirtree_read("/proc", scan_pids); if (toys.optflags&(FLAG_t|FLAG_u|FLAG_w)) { - printf("Active %s (%s servers)\n", "Internet connections", type); + printf("Active Internet connections (%s)\n", type); printf("Proto Recv-Q Send-Q %*s %*s State ", -TT.wpad, "Local Address", -TT.wpad, "Foreign Address"); if (FLAG(e)) printf(" User Inode "); @@ -353,10 +335,9 @@ void netstat_main(void) } if (FLAG(x)) { - printf("Active %s (%s servers)\n", "UNIX domain sockets", type); - - printf("Proto RefCnt Flags\t Type\t State\t %s Path\n", - FLAG(p) ? "PID/Program Name" : "I-Node"); + printf("Active UNIX domain sockets (%s)\n", type); + printf("Proto RefCnt Flags Type State I-Node%sPath\n", + FLAG(p) ? " PID/Program Name " : " "); show_unix_sockets(); } |