diff --git a/portlist/portlist_linux.go b/portlist/portlist_linux.go index cb1a9480a..ee413ca40 100644 --- a/portlist/portlist_linux.go +++ b/portlist/portlist_linux.go @@ -81,7 +81,7 @@ func parsePorts(r *bufio.Reader, fileBase string) ([]Port, error) { var ret []Port // skip header row - _, err := r.ReadString('\n') + _, err := r.ReadSlice('\n') if err != nil { return nil, err } @@ -93,7 +93,20 @@ func parsePorts(r *bufio.Reader, fileBase string) ([]Port, error) { wantRemote = mem.S(v6Any) } - var inoBuf []byte + // remoteIndex is the index within a line to the remote address field. + // -1 means not yet found. + remoteIndex := -1 + + // Add an upper bound on how many rows we'll attempt to read just + // to make sure this doesn't consume too much of their CPU. + // TODO(bradfitz,crawshaw): adaptively adjust polling interval as function + // of open sockets. + const maxRows = 1e6 + rows := 0 + + // Scratch buffer for making inode strings. + inoBuf := make([]byte, 0, 50) + for err == nil { line, err := r.ReadSlice('\n') if err == io.EOF { @@ -102,9 +115,25 @@ func parsePorts(r *bufio.Reader, fileBase string) ([]Port, error) { if err != nil { return nil, err } + rows++ + if rows >= maxRows { + break + } + if len(line) == 0 { + continue + } + + // On the first row of output, find the index of the 3rd field (index 2), + // the remote address. All the rows are aligned, at least until 4 billion open + // TCP connections, per the Linux get_tcp4_sock's "%4d: " on an int i. + if remoteIndex == -1 { + remoteIndex = fieldIndex(line, 2) + if remoteIndex == -1 { + break + } + } - if i := fieldIndex(line, 2); i == -1 || - !mem.HasPrefix(mem.B(line).SliceFrom(i), wantRemote) { + if len(line) < remoteIndex || !mem.HasPrefix(mem.B(line).SliceFrom(remoteIndex), wantRemote) { // Fast path for not being a listener port. continue }