mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-01 22:15:51 +00:00
a3c5de641b
These "weird" port lines show up in logs frequently. They're the result of uninteresting races, and they're not actionable. Remove the noise. Remove the isLoopbackAddr case to placate staticcheck. Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
110 lines
2.6 KiB
Go
110 lines
2.6 KiB
Go
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build darwin && !ios
|
|
// +build darwin,!ios
|
|
|
|
package portlist
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"os/exec"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// We have to run netstat, which is a bit expensive, so don't do it too often.
|
|
const pollInterval = 5 * time.Second
|
|
|
|
func listPorts() (List, error) {
|
|
return listPortsNetstat("-na")
|
|
}
|
|
|
|
var lsofFailed int64 // atomic bool
|
|
|
|
// In theory, lsof could replace the function of both listPorts() and
|
|
// addProcesses(), since it provides a superset of the netstat output.
|
|
// However, "netstat -na" runs ~100x faster than lsof on my machine, so
|
|
// we should do it only if the list of open ports has actually changed.
|
|
//
|
|
// This fails in a macOS sandbox (i.e. in the Mac App Store or System
|
|
// Extension GUI build), but does at least work in the
|
|
// tailscaled-on-macos mode.
|
|
func addProcesses(pl []Port) ([]Port, error) {
|
|
if atomic.LoadInt64(&lsofFailed) != 0 {
|
|
// This previously failed in the macOS sandbox, so don't try again.
|
|
return pl, nil
|
|
}
|
|
exe, err := exec.LookPath("lsof")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("lsof: lookup: %v", err)
|
|
}
|
|
output, err := exec.Command(exe, "-F", "-n", "-P", "-O", "-S2", "-T", "-i4", "-i6").Output()
|
|
if err != nil {
|
|
var stderr []byte
|
|
if xe, ok := err.(*exec.ExitError); ok {
|
|
stderr = xe.Stderr
|
|
}
|
|
// fails when run in a macOS sandbox, so make this non-fatal.
|
|
if atomic.CompareAndSwapInt64(&lsofFailed, 0, 1) {
|
|
log.Printf("portlist: can't run lsof in Mac sandbox; omitting process names from service list. Error details: %v, %s", err, bytes.TrimSpace(stderr))
|
|
}
|
|
return pl, nil
|
|
}
|
|
|
|
type ProtoPort struct {
|
|
proto string
|
|
port uint16
|
|
}
|
|
m := map[ProtoPort]*Port{}
|
|
for i := range pl {
|
|
pp := ProtoPort{pl[i].Proto, pl[i].Port}
|
|
m[pp] = &pl[i]
|
|
}
|
|
|
|
r := bytes.NewReader(output)
|
|
scanner := bufio.NewScanner(r)
|
|
|
|
var cmd, proto string
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if line == "" {
|
|
continue
|
|
}
|
|
field, val := line[0], line[1:]
|
|
switch field {
|
|
case 'p':
|
|
// starting a new process
|
|
cmd = ""
|
|
proto = ""
|
|
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]
|
|
switch {
|
|
case p != nil:
|
|
p.Process = cmd
|
|
default:
|
|
// ignore: processes and ports come and go
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return pl, nil
|
|
}
|