portlist: ignore ports bound to localhost

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
reviewable/pr192/r1
Brad Fitzpatrick 4 years ago committed by Brad Fitzpatrick
parent c706731dc7
commit f8d67bb591

@ -75,6 +75,10 @@ func parsePortsNetstat(output string) List {
// not interested in non-listener sockets // not interested in non-listener sockets
continue 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") { } else if strings.HasPrefix(protos, "udp") {
if len(cols) < 3 { if len(cols) < 3 {
continue continue
@ -82,6 +86,10 @@ func parsePortsNetstat(output string) List {
proto = "udp" proto = "udp"
laddr = cols[len(cols)-2] laddr = cols[len(cols)-2]
raddr = cols[len(cols)-1] 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 { } else if protos[0] == '[' && len(trimline) > 2 {
// Windows: with netstat -nab, appends a line like: // Windows: with netstat -nab, appends a line like:
// [description] // [description]
@ -134,16 +142,12 @@ func parsePortsNetstat(output string) List {
//lint:ignore U1000 function is only used on !linux, but we want the //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. // 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") exe, err := exec.LookPath("netstat")
if err != nil { if err != nil {
return nil, fmt.Errorf("netstat: lookup: %v", err) return nil, fmt.Errorf("netstat: lookup: %v", err)
} }
c := exec.Cmd{ output, err := exec.Command(exe, arg).Output()
Path: exe,
Args: []string{exe, args},
}
output, err := c.Output()
if err != nil { if err != nil {
xe, ok := err.(*exec.ExitError) xe, ok := err.(*exec.ExitError)
stderr := "" stderr := ""

@ -31,7 +31,7 @@ func TestParsePort(t *testing.T) {
} }
} }
var netstat_output = ` const netstatOutput = `
// linux // linux
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 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:* 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", ""}, Port{"udp", 9353, "iTunes", ""},
} }
pl := parsePortsNetstat(netstat_output) pl := parsePortsNetstat(netstatOutput)
for i := range pl { for i := range pl {
if pl[i] != want[i] { if pl[i] != want[i] {
t.Errorf("row#%d\n got: %#v\n\nwant: %#v\n", t.Errorf("row#%d\n got: %#v\n\nwant: %#v\n",

@ -37,11 +37,7 @@ func addProcesses(pl []Port) ([]Port, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("lsof: lookup: %v", err) return nil, fmt.Errorf("lsof: lookup: %v", err)
} }
c := exec.Cmd{ output, err := exec.Command(exe, "-F", "-n", "-P", "-O", "-S2", "-T", "-i4", "-i6").Output()
Path: exe,
Args: []string{exe, "-F", "-n", "-P", "-O", "-S2", "-T", "-i4", "-i6"},
}
output, err := c.Output()
if err != nil { if err != nil {
xe, ok := err.(*exec.ExitError) xe, ok := err.(*exec.ExitError)
stderr := "" stderr := ""
@ -69,28 +65,32 @@ func addProcesses(pl []Port) ([]Port, error) {
var cmd, proto string var cmd, proto string
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
if line[0] == 'p' { if line == "" {
continue
}
field, val := line[0], line[1:]
switch field {
case 'p':
// starting a new process // starting a new process
cmd = "" cmd = ""
proto = "" proto = ""
} else if line[0] == 'c' { case 'c':
cmd = line[1:len(line)] cmd = val
} else if line[0] == 'P' { case 'P':
proto = strings.ToLower(line[1:len(line)]) proto = strings.ToLower(val)
} else if line[0] == 'n' { case 'n':
rest := line[1:len(line)] if strings.Contains(val, "->") {
i := strings.Index(rest, "->") continue
if i < 0 { }
// a listening port // a listening port
port := parsePort(rest) port := parsePort(val)
if port > 0 { if port > 0 {
pp := ProtoPort{proto, uint16(port)} pp := ProtoPort{proto, uint16(port)}
p := m[pp] p := m[pp]
if p != nil { if p != nil {
p.Process = cmd p.Process = cmd
} else { } else {
fmt.Fprintf(os.Stderr, "weird: missing %v\n", pp) fmt.Fprintf(os.Stderr, "weird: missing %v\n", pp)
}
} }
} }
} }

@ -59,6 +59,10 @@ func listPorts() (List, error) {
rem := words[2] rem := words[2]
inode := words[9] 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" { if rem != "00000000:0000" {
// not a "listener" port // not a "listener" port
continue continue

@ -4,7 +4,10 @@
package portlist package portlist
import "testing" import (
"net"
"testing"
)
func TestGetList(t *testing.T) { func TestGetList(t *testing.T) {
pl, err := GetList(nil) pl, err := GetList(nil)
@ -17,6 +20,25 @@ func TestGetList(t *testing.T) {
t.Logf("As String: %v", pl.String()) 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) { func BenchmarkGetList(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

Loading…
Cancel
Save