From f8d67bb591c1e8162e3fc6d048929215565796cc Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 17 Mar 2020 20:19:39 -0700 Subject: [PATCH] portlist: ignore ports bound to localhost Signed-off-by: Brad Fitzpatrick --- portlist/netstat.go | 16 ++++++++----- portlist/netstat_test.go | 4 ++-- portlist/portlist_darwin.go | 48 ++++++++++++++++++------------------- portlist/portlist_linux.go | 4 ++++ portlist/portlist_test.go | 24 ++++++++++++++++++- 5 files changed, 63 insertions(+), 33 deletions(-) diff --git a/portlist/netstat.go b/portlist/netstat.go index 9ebdc678c..48e8c75ec 100644 --- a/portlist/netstat.go +++ b/portlist/netstat.go @@ -75,6 +75,10 @@ func parsePortsNetstat(output string) List { // not interested in non-listener sockets continue } + if strings.HasPrefix(laddr, "127.0.0.1:") || strings.HasPrefix(laddr, "127.0.0.1.") { + // not interested in loopback-bound listeners + continue + } } else if strings.HasPrefix(protos, "udp") { if len(cols) < 3 { continue @@ -82,6 +86,10 @@ func parsePortsNetstat(output string) List { proto = "udp" laddr = cols[len(cols)-2] raddr = cols[len(cols)-1] + if strings.HasPrefix(laddr, "127.0.0.1:") || strings.HasPrefix(laddr, "127.0.0.1.") { + // not interested in loopback-bound listeners + continue + } } else if protos[0] == '[' && len(trimline) > 2 { // Windows: with netstat -nab, appends a line like: // [description] @@ -134,16 +142,12 @@ func parsePortsNetstat(output string) List { //lint:ignore U1000 function is only used on !linux, but we want the // unit test to run on linux, so we don't build-tag it away. -func listPortsNetstat(args string) (List, error) { +func listPortsNetstat(arg string) (List, error) { exe, err := exec.LookPath("netstat") if err != nil { return nil, fmt.Errorf("netstat: lookup: %v", err) } - c := exec.Cmd{ - Path: exe, - Args: []string{exe, args}, - } - output, err := c.Output() + output, err := exec.Command(exe, arg).Output() if err != nil { xe, ok := err.(*exec.ExitError) stderr := "" diff --git a/portlist/netstat_test.go b/portlist/netstat_test.go index 3125e360b..8f1d33abd 100644 --- a/portlist/netstat_test.go +++ b/portlist/netstat_test.go @@ -31,7 +31,7 @@ func TestParsePort(t *testing.T) { } } -var netstat_output = ` +const netstatOutput = ` // linux tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN udp 0 0 0.0.0.0:5353 0.0.0.0:* @@ -76,7 +76,7 @@ func TestParsePortsNetstat(t *testing.T) { Port{"udp", 9353, "iTunes", ""}, } - pl := parsePortsNetstat(netstat_output) + pl := parsePortsNetstat(netstatOutput) for i := range pl { if pl[i] != want[i] { t.Errorf("row#%d\n got: %#v\n\nwant: %#v\n", diff --git a/portlist/portlist_darwin.go b/portlist/portlist_darwin.go index e1e385d0c..08bb514cc 100644 --- a/portlist/portlist_darwin.go +++ b/portlist/portlist_darwin.go @@ -37,11 +37,7 @@ func addProcesses(pl []Port) ([]Port, error) { if err != nil { return nil, fmt.Errorf("lsof: lookup: %v", err) } - c := exec.Cmd{ - Path: exe, - Args: []string{exe, "-F", "-n", "-P", "-O", "-S2", "-T", "-i4", "-i6"}, - } - output, err := c.Output() + output, err := exec.Command(exe, "-F", "-n", "-P", "-O", "-S2", "-T", "-i4", "-i6").Output() if err != nil { xe, ok := err.(*exec.ExitError) stderr := "" @@ -69,28 +65,32 @@ func addProcesses(pl []Port) ([]Port, error) { var cmd, proto string for scanner.Scan() { line := scanner.Text() - if line[0] == 'p' { + if line == "" { + continue + } + field, val := line[0], line[1:] + switch field { + case 'p': // starting a new process cmd = "" proto = "" - } else if line[0] == 'c' { - cmd = line[1:len(line)] - } else if line[0] == 'P' { - proto = strings.ToLower(line[1:len(line)]) - } else if line[0] == 'n' { - rest := line[1:len(line)] - i := strings.Index(rest, "->") - if i < 0 { - // a listening port - port := parsePort(rest) - if port > 0 { - pp := ProtoPort{proto, uint16(port)} - p := m[pp] - if p != nil { - p.Process = cmd - } else { - fmt.Fprintf(os.Stderr, "weird: missing %v\n", pp) - } + case 'c': + cmd = val + case 'P': + proto = strings.ToLower(val) + case 'n': + if strings.Contains(val, "->") { + continue + } + // a listening port + port := parsePort(val) + if port > 0 { + pp := ProtoPort{proto, uint16(port)} + p := m[pp] + if p != nil { + p.Process = cmd + } else { + fmt.Fprintf(os.Stderr, "weird: missing %v\n", pp) } } } diff --git a/portlist/portlist_linux.go b/portlist/portlist_linux.go index 959a65ed9..26cf52fe4 100644 --- a/portlist/portlist_linux.go +++ b/portlist/portlist_linux.go @@ -59,6 +59,10 @@ func listPorts() (List, error) { rem := words[2] inode := words[9] + // If a port is bound to 127.0.0.1, ignore it. + if strings.HasPrefix(local, "0100007F:") { + continue + } if rem != "00000000:0000" { // not a "listener" port continue diff --git a/portlist/portlist_test.go b/portlist/portlist_test.go index 4b95f8d6f..2e1f4a88a 100644 --- a/portlist/portlist_test.go +++ b/portlist/portlist_test.go @@ -4,7 +4,10 @@ package portlist -import "testing" +import ( + "net" + "testing" +) func TestGetList(t *testing.T) { pl, err := GetList(nil) @@ -17,6 +20,25 @@ func TestGetList(t *testing.T) { t.Logf("As String: %v", pl.String()) } +func TestIgnoreLocallyBoundPorts(t *testing.T) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Skipf("failed to bind: %v", err) + } + defer ln.Close() + ta := ln.Addr().(*net.TCPAddr) + port := ta.Port + pl, err := GetList(nil) + if err != nil { + t.Fatal(err) + } + for _, p := range pl { + if p.Proto == "tcp" && int(p.Port) == port { + t.Fatal("didn't expect to find test's localhost ephemeral port") + } + } +} + func BenchmarkGetList(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ {