mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-09 08:01:31 +00:00
Move Linux client & common packages into a public repo.
This commit is contained in:
155
portlist/portlist_linux.go
Normal file
155
portlist/portlist_linux.go
Normal file
@@ -0,0 +1,155 @@
|
||||
// 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.
|
||||
|
||||
package portlist
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Reading the sockfiles on Linux is very fast, so we can do it often.
|
||||
const POLL_SECONDS = 1
|
||||
|
||||
// TODO(apenwarr): Include IPv6 ports eventually.
|
||||
// Right now we don't route IPv6 anyway so it's better to exclude them.
|
||||
var sockfiles = []string{"/proc/net/tcp", "/proc/net/udp"}
|
||||
|
||||
func listPorts() (List, error) {
|
||||
l := []Port{}
|
||||
|
||||
for pi, fname := range sockfiles {
|
||||
proto := protos[pi]
|
||||
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", fname, err)
|
||||
}
|
||||
defer f.Close()
|
||||
r := bufio.NewReader(f)
|
||||
|
||||
// skip header row
|
||||
_, err = r.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for err == nil {
|
||||
line, err := r.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// sl local rem ... inode
|
||||
words := strings.Fields(line)
|
||||
local := words[1]
|
||||
rem := words[2]
|
||||
inode := words[9]
|
||||
|
||||
if rem != "00000000:0000" {
|
||||
// not a "listener" port
|
||||
continue
|
||||
}
|
||||
|
||||
portv, err := strconv.ParseUint(local[9:], 16, 16)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%#v: %s", local[9:], err)
|
||||
}
|
||||
inodev := fmt.Sprintf("socket:[%s]", inode)
|
||||
l = append(l, Port{
|
||||
Proto: proto,
|
||||
Port: uint16(portv),
|
||||
inode: inodev,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(l, func(i, j int) bool {
|
||||
return (&l[i]).lessThan(&l[j])
|
||||
})
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func addProcesses(pl []Port) ([]Port, error) {
|
||||
pm := map[string]*Port{}
|
||||
for k := range pl {
|
||||
pm[pl[k].inode] = &pl[k]
|
||||
}
|
||||
|
||||
pdir, err := os.Open("/proc")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("/proc: %s", err)
|
||||
}
|
||||
defer pdir.Close()
|
||||
|
||||
for {
|
||||
pids, err := pdir.Readdirnames(100)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("/proc: %s", err)
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
_, err := strconv.ParseInt(pid, 10, 64)
|
||||
if err != nil {
|
||||
// not a pid, ignore it.
|
||||
// /proc has lots of non-pid stuff in it.
|
||||
continue
|
||||
}
|
||||
fddir, err := os.Open(fmt.Sprintf("/proc/%s/fd", pid))
|
||||
if err != nil {
|
||||
// Can't open fd list for this pid. Maybe
|
||||
// don't have access. Ignore it.
|
||||
continue
|
||||
}
|
||||
defer fddir.Close()
|
||||
|
||||
for {
|
||||
fds, err := fddir.Readdirnames(100)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("readdir: %s", err)
|
||||
}
|
||||
for _, fd := range fds {
|
||||
target, err := os.Readlink(fmt.Sprintf("/proc/%s/fd/%s", pid, fd))
|
||||
if err != nil {
|
||||
// Not a symlink or no permission.
|
||||
// Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO(apenwarr): use /proc/*/cmdline instead of /comm?
|
||||
// Unsure right now whether users will want the extra detail
|
||||
// or not.
|
||||
pe := pm[target]
|
||||
if pe != nil {
|
||||
comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%s/comm", pid))
|
||||
if err != nil {
|
||||
// Usually shouldn't happen. One possibility is
|
||||
// the process has gone away, so let's skip it.
|
||||
continue
|
||||
}
|
||||
pe.Process = strings.TrimSpace(string(comm))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pl, nil
|
||||
}
|
Reference in New Issue
Block a user