mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 00:01:40 +01:00 
			
		
		
		
	Updates #5794 Change-Id: I77df1eb9bea9f079a25337cb7bbd498cf8a19135 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			123 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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
 | |
| }
 |