-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathports_supported.go
More file actions
88 lines (73 loc) · 2.29 KB
/
Copy pathports_supported.go
File metadata and controls
88 lines (73 loc) · 2.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//go:build linux || (windows && amd64)
package agent
import (
"sync"
"time"
"github.com/cakturk/go-netstat/netstat"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/codersdk"
)
type osListeningPortsGetter struct {
cacheDuration time.Duration
mut sync.Mutex
ports []codersdk.WorkspaceAgentListeningPort
mtime time.Time
}
func (lp *osListeningPortsGetter) GetListeningPorts() ([]codersdk.WorkspaceAgentListeningPort, error) {
lp.mut.Lock()
defer lp.mut.Unlock()
if time.Since(lp.mtime) < lp.cacheDuration {
// copy
ports := make([]codersdk.WorkspaceAgentListeningPort, len(lp.ports))
copy(ports, lp.ports)
return ports, nil
}
acceptListening := func(s *netstat.SockTabEntry) bool {
return s.State == netstat.Listen
}
tabs, err := netstat.TCPSocks(acceptListening)
if err != nil {
return nil, xerrors.Errorf("scan listening ports: %w", err)
}
// Include IPv6 listeners too. Many dev servers (e.g. Next.js, Node's
// default http.Server) bind to the IPv6 wildcard address "::", which is
// a dual-stack socket that also accepts IPv4 connections, but only shows
// up in the IPv6 socket table (/proc/net/tcp6), not /proc/net/tcp.
//
// The IPv6 scan fails on systems with IPv6 disabled (e.g. missing
// /proc/net/tcp6 on Linux). Fall back to the IPv4 results instead of
// failing the whole scan, otherwise the ports UI breaks entirely on
// such systems.
tabs6, err := netstat.TCP6Socks(acceptListening)
if err == nil {
tabs = append(tabs, tabs6...)
}
seen := make(map[uint16]struct{}, len(tabs))
ports := []codersdk.WorkspaceAgentListeningPort{}
for _, tab := range tabs {
if tab.LocalAddr == nil {
continue
}
// Don't include ports that we've already seen. This can happen on
// Windows, and maybe on Linux if you're using a shared listener socket.
if _, ok := seen[tab.LocalAddr.Port]; ok {
continue
}
seen[tab.LocalAddr.Port] = struct{}{}
procName := ""
if tab.Process != nil {
procName = tab.Process.Name
}
ports = append(ports, codersdk.WorkspaceAgentListeningPort{
ProcessName: procName,
Network: "tcp",
Port: tab.LocalAddr.Port,
})
}
lp.ports = ports
lp.mtime = time.Now()
// copy
ports = make([]codersdk.WorkspaceAgentListeningPort, len(lp.ports))
copy(ports, lp.ports)
return ports, nil
}