cmd/tswrap,portlist: switch tswrap to portlist, add pid to portlist

Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:
James Tucker 2022-11-04 22:52:55 -07:00 committed by James Tucker
parent 2a619d3bcf
commit 1621f3aa6c
No known key found for this signature in database
6 changed files with 38 additions and 37 deletions

View File

@ -2,15 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux
// The tswrap binary runs a child process and makes it accessible over
// Tailscale.
package main
import (
"bufio"
"bytes"
"context"
"errors"
"flag"
@ -18,19 +14,18 @@ import (
"io"
"log"
"net"
"net/netip"
"os"
"os/exec"
"os/signal"
"sort"
"strconv"
"strings"
"syscall"
"time"
"golang.org/x/sys/unix"
"tailscale.com/client/tailscale"
"tailscale.com/ipn/ipnstate"
"tailscale.com/ipn/store/mem"
"tailscale.com/portlist"
"tailscale.com/syncs"
"tailscale.com/tsnet"
"tailscale.com/types/logger"
@ -43,7 +38,7 @@ var (
func main() {
sigch := make(chan os.Signal, 1)
signal.Notify(sigch, os.Interrupt, unix.SIGTERM)
signal.Notify(sigch, os.Interrupt, syscall.SIGTERM)
flag.Parse()
@ -188,7 +183,7 @@ func (p *proxy) Stop() {
func (p *proxy) Wait() {
<-p.shutdownCtx.Done()
p.cmd.Process.Signal(unix.SIGTERM)
p.cmd.Process.Signal(syscall.SIGTERM)
p.ln.Close()
if p.srv.Ephemeral {
p.client.Logout(context.Background())
@ -290,37 +285,30 @@ func portsOfCmd(cmd *exec.Cmd) (ports []int, err error) {
return nil, errors.New("no process")
}
pid := cmd.Process.Pid
wantSub := fmt.Sprintf(" %d/", pid)
ns := exec.Command("netstat", "-p", "--inet", "-l", "-n")
out, err := ns.Output()
poller, err := portlist.NewPoller()
if err != nil {
return nil, err
return nil, fmt.Errorf("creating port poller: %w", err)
}
bs := bufio.NewScanner(bytes.NewReader(out))
for bs.Scan() {
line := bs.Text()
if !strings.HasPrefix(line, "tcp") ||
!strings.Contains(line, "LISTEN") ||
!strings.Contains(line, wantSub) {
continue
defer poller.Close()
// TODO(raggi): timeout?
go poller.Run(context.Background())
c := poller.Updates()
for list := range c {
for _, p := range list {
if p.Pid == pid {
ports = append(ports, int(p.Port))
}
}
f := strings.Fields(line)
if len(f) < 4 {
continue
if len(ports) > 0 {
break
}
ipp, err := netip.ParseAddrPort(f[3])
if err == nil {
ports = append(ports, int(ipp.Port()))
continue
}
}
if err := bs.Err(); err != nil {
return nil, err
}
if len(ports) == 0 {
return nil, errors.New("no listening ports found")
}
sort.Ints(ports)
return ports, nil
}

View File

@ -54,12 +54,12 @@ udp46 0 0 *.146 *.*
func TestParsePortsNetstat(t *testing.T) {
want := List{
Port{"tcp", 23, ""},
Port{"tcp", 24, ""},
Port{"udp", 104, ""},
Port{"udp", 106, ""},
Port{"udp", 146, ""},
Port{"tcp", 8185, ""}, // but not 8186, 8187, 8188 on localhost
Port{"tcp", 23, "", 0},
Port{"tcp", 24, "", 0},
Port{"udp", 104, "", 0},
Port{"udp", 106, "", 0},
Port{"udp", 146, "", 0},
Port{"tcp", 8185, "", 0}, // but not 8186, 8187, 8188 on localhost
}
pl, err := appendParsePortsNetstat(nil, bufio.NewReader(strings.NewReader(netstatOutput)))

View File

@ -19,6 +19,7 @@ type Port struct {
Proto string // "tcp" or "udp"
Port uint16 // port number
Process string // optional process name, if found
Pid int // process id, if known
}
// List is a list of Ports.

View File

@ -40,6 +40,7 @@ type linuxImpl struct {
type portMeta struct {
port Port
pid int
keep bool
needsProcName bool
}
@ -326,6 +327,9 @@ func (li *linuxImpl) findProcessNames(need map[string]*portMeta) error {
}
argv := strings.Split(strings.TrimSuffix(string(bs), "\x00"), "\x00")
if p, err := strconv.Atoi(pid); err == nil {
pe.pid = p
}
pe.port.Process = argvSubject(argv...)
pe.needsProcName = false
delete(need, string(targetBuf[:n]))

View File

@ -12,6 +12,7 @@ import (
"fmt"
"log"
"os/exec"
"strconv"
"strings"
"sync/atomic"
"time"
@ -162,6 +163,7 @@ func (im *macOSImpl) addProcesses() error {
im.br.Reset(outPipe)
var cmd, proto string
var pid int
for {
line, err := im.br.ReadBytes('\n')
if err != nil {
@ -176,6 +178,10 @@ func (im *macOSImpl) addProcesses() error {
// starting a new process
cmd = ""
proto = ""
pid = 0
if p, err := strconv.Atoi(string(val)); err == nil {
pid = p
}
case 'c':
cmd = string(val) // TODO(bradfitz): avoid garbage; cache process names between runs?
case 'P':
@ -194,6 +200,7 @@ func (im *macOSImpl) addProcesses() error {
switch {
case m != nil:
m.port.Process = cmd
m.port.Pid = pid
default:
// ignore: processes and ports come and go
}

View File

@ -82,6 +82,7 @@ func (im *windowsImpl) AppendListeningPorts(base []Port) ([]Port, error) {
Proto: "tcp",
Port: e.Local.Port(),
Process: procNameOfPid(e.Pid),
Pid: e.Pid,
},
}
im.known[fp] = pm