From 5e305032a953258fc47b56dc0462ac652c9c78db Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 1 Apr 2025 04:01:00 -0700 Subject: [PATCH] portlist: add Plan 9 support Updates #5794 Change-Id: I77df1eb9bea9f079a25337cb7bbd498cf8a19135 Signed-off-by: Brad Fitzpatrick --- portlist/portlist_plan9.go | 122 +++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 portlist/portlist_plan9.go diff --git a/portlist/portlist_plan9.go b/portlist/portlist_plan9.go new file mode 100644 index 000000000..77f8619f9 --- /dev/null +++ b/portlist/portlist_plan9.go @@ -0,0 +1,122 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package portlist + +import ( + "bufio" + "bytes" + "os" + "strconv" + "strings" + "time" +) + +func init() { + newOSImpl = newPlan9Impl + + pollInterval = 5 * time.Second +} + +type plan9Impl struct { + known map[protoPort]*portMeta // inode string => metadata + + br *bufio.Reader // reused + portsBuf []Port + includeLocalhost bool +} + +type protoPort struct { + proto string + port uint16 +} + +type portMeta struct { + port Port + keep bool +} + +func newPlan9Impl(includeLocalhost bool) osImpl { + return &plan9Impl{ + known: map[protoPort]*portMeta{}, + br: bufio.NewReader(bytes.NewReader(nil)), + includeLocalhost: includeLocalhost, + } +} + +func (*plan9Impl) Close() error { return nil } + +func (im *plan9Impl) AppendListeningPorts(base []Port) ([]Port, error) { + ret := base + + des, err := os.ReadDir("/proc") + if err != nil { + return nil, err + } + for _, de := range des { + if !de.IsDir() { + continue + } + pidStr := de.Name() + pid, err := strconv.Atoi(pidStr) + if err != nil { + continue + } + st, _ := os.ReadFile("/proc/" + pidStr + "/fd") + if !bytes.Contains(st, []byte("/net/tcp/clone")) { + continue + } + args, _ := os.ReadFile("/proc/" + pidStr + "/args") + procName := string(bytes.TrimSpace(args)) + // term% cat /proc/417/fd + // /usr/glenda + // 0 r M 35 (0000000000000001 0 00) 16384 260 /dev/cons + // 1 w c 0 (000000000000000a 0 00) 0 471 /dev/null + // 2 w M 35 (0000000000000001 0 00) 16384 108 /dev/cons + // 3 rw I 0 (000000000000002c 0 00) 0 14 /net/tcp/clone + for line := range bytes.Lines(st) { + if !bytes.Contains(line, []byte("/net/tcp/clone")) { + continue + } + f := strings.Fields(string(line)) + if len(f) < 10 { + continue + } + if f[9] != "/net/tcp/clone" { + continue + } + qid, err := strconv.ParseUint(strings.TrimPrefix(f[4], "("), 16, 64) + if err != nil { + continue + } + tcpN := (qid >> 5) & (1<<12 - 1) + tcpNStr := strconv.FormatUint(tcpN, 10) + st, _ := os.ReadFile("/net/tcp/" + tcpNStr + "/status") + if !bytes.Contains(st, []byte("Listen ")) { + // Unexpected. Or a race. + continue + } + bl, _ := os.ReadFile("/net/tcp/" + tcpNStr + "/local") + i := bytes.LastIndexByte(bl, '!') + if i == -1 { + continue + } + if bytes.HasPrefix(bl, []byte("127.0.0.1!")) && !im.includeLocalhost { + continue + } + portStr := strings.TrimSpace(string(bl[i+1:])) + port, _ := strconv.Atoi(portStr) + if port == 0 { + continue + } + ret = append(ret, Port{ + Proto: "tcp", + Port: uint16(port), + Process: procName, + Pid: pid, + }) + } + } + + return sortAndDedup(ret), nil +}