mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
Merge pull request #1151 from safing/feature/integrationtest-cmd
Add integrationtest utility
This commit is contained in:
commit
f3dc188f63
20 changed files with 174 additions and 21 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -5,6 +5,8 @@ dnsonly
|
||||||
dnsonly.exe
|
dnsonly.exe
|
||||||
main
|
main
|
||||||
main.exe
|
main.exe
|
||||||
|
integrationtest
|
||||||
|
integrationtest.exe
|
||||||
|
|
||||||
# Dist dir
|
# Dist dir
|
||||||
dist
|
dist
|
||||||
|
|
|
@ -22,6 +22,7 @@ linters:
|
||||||
- interfacer
|
- interfacer
|
||||||
- ireturn
|
- ireturn
|
||||||
- lll
|
- lll
|
||||||
|
- musttag
|
||||||
- nestif
|
- nestif
|
||||||
- nilnil
|
- nilnil
|
||||||
- nlreturn
|
- nlreturn
|
||||||
|
|
18
cmds/integrationtest/main.go
Normal file
18
cmds/integrationtest/main.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "integrationtest",
|
||||||
|
Short: "A simple tool to test system integrations",
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
122
cmds/integrationtest/netstate.go
Normal file
122
cmds/integrationtest/netstate.go
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
processInfo "github.com/shirou/gopsutil/process"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/safing/portmaster/network/packet"
|
||||||
|
"github.com/safing/portmaster/network/socket"
|
||||||
|
"github.com/safing/portmaster/network/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(netStateCmd)
|
||||||
|
netStateCmd.AddCommand(netStateMonitorCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
netStateCmd = &cobra.Command{
|
||||||
|
Use: "netstate",
|
||||||
|
Short: "Print current network state as received from the system",
|
||||||
|
RunE: netState,
|
||||||
|
}
|
||||||
|
netStateMonitorCmd = &cobra.Command{
|
||||||
|
Use: "monitor",
|
||||||
|
Short: "Monitor the network state and print any new connections",
|
||||||
|
RunE: netStateMonitor,
|
||||||
|
}
|
||||||
|
|
||||||
|
seen = make(map[string]bool)
|
||||||
|
)
|
||||||
|
|
||||||
|
func netState(cmd *cobra.Command, args []string) error {
|
||||||
|
tables := state.GetInfo()
|
||||||
|
|
||||||
|
for _, s := range tables.TCP4Connections {
|
||||||
|
checkAndPrintConnectionInfoIfNew(packet.IPv4, packet.TCP, s)
|
||||||
|
}
|
||||||
|
for _, s := range tables.TCP4Listeners {
|
||||||
|
checkAndPrintBindInfoIfNew(packet.IPv4, packet.TCP, s)
|
||||||
|
}
|
||||||
|
for _, s := range tables.TCP6Connections {
|
||||||
|
checkAndPrintConnectionInfoIfNew(packet.IPv6, packet.TCP, s)
|
||||||
|
}
|
||||||
|
for _, s := range tables.TCP6Listeners {
|
||||||
|
checkAndPrintBindInfoIfNew(packet.IPv6, packet.TCP, s)
|
||||||
|
}
|
||||||
|
for _, s := range tables.UDP4Binds {
|
||||||
|
checkAndPrintBindInfoIfNew(packet.IPv6, packet.UDP, s)
|
||||||
|
}
|
||||||
|
for _, s := range tables.UDP6Binds {
|
||||||
|
checkAndPrintBindInfoIfNew(packet.IPv6, packet.UDP, s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func netStateMonitor(cmd *cobra.Command, args []string) error {
|
||||||
|
for {
|
||||||
|
err := netState(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAndPrintConnectionInfoIfNew(ipv packet.IPVersion, p packet.IPProtocol, s *socket.ConnectionInfo) {
|
||||||
|
// Build connection string.
|
||||||
|
c := fmt.Sprintf(
|
||||||
|
"%s %s %s:%d <-> %s:%d",
|
||||||
|
ipv, p,
|
||||||
|
s.Local.IP,
|
||||||
|
s.Local.Port,
|
||||||
|
s.Remote.IP,
|
||||||
|
s.Remote.Port,
|
||||||
|
)
|
||||||
|
|
||||||
|
checkAndPrintSocketInfoIfNew(c, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAndPrintBindInfoIfNew(ipv packet.IPVersion, p packet.IPProtocol, s *socket.BindInfo) {
|
||||||
|
// Build connection string.
|
||||||
|
c := fmt.Sprintf(
|
||||||
|
"%s %s bind %s:%d",
|
||||||
|
ipv, p,
|
||||||
|
s.Local.IP,
|
||||||
|
s.Local.Port,
|
||||||
|
)
|
||||||
|
|
||||||
|
checkAndPrintSocketInfoIfNew(c, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAndPrintSocketInfoIfNew(c string, s socket.Info) {
|
||||||
|
// Return if connection was already seen.
|
||||||
|
if _, ok := seen[c]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Otherwise, add as seen.
|
||||||
|
seen[c] = true
|
||||||
|
|
||||||
|
// Check if we have the PID.
|
||||||
|
_, _, err := state.CheckPID(s, false)
|
||||||
|
|
||||||
|
// Print result.
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
pInfo, err := processInfo.NewProcess(int32(s.GetPID()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s %d no binary: %s\n", c, s.GetPID(), err)
|
||||||
|
} else {
|
||||||
|
exe, _ := pInfo.Exe()
|
||||||
|
fmt.Printf("%s %d %s\n", c, s.GetPID(), exe)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s %d (err: %s)\n", c, s.GetPID(), err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func initializeLogFile(logFilePath string, identifier string, version string) *os.File {
|
func initializeLogFile(logFilePath string, identifier string, version string) *os.File {
|
||||||
logFile, err := os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE, 0o0440)
|
logFile, err := os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE, 0o0440) //nolint:gosec // As desired.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to create log file %s: %s\n", logFilePath, err)
|
log.Printf("failed to create log file %s: %s\n", logFilePath, err)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -149,7 +149,7 @@ func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) (retry bo
|
||||||
originalPid = proc.Pid
|
originalPid = proc.Pid
|
||||||
var previousPid int
|
var previousPid int
|
||||||
|
|
||||||
// Go up up to two levels, if we don't match the path.
|
// Find parent for up to two levels, if we don't match the path.
|
||||||
checkLevels := 2
|
checkLevels := 2
|
||||||
checkLevelsLoop:
|
checkLevelsLoop:
|
||||||
for i := 0; i < checkLevels+1; i++ {
|
for i := 0; i < checkLevels+1; i++ {
|
||||||
|
|
|
@ -90,7 +90,7 @@ func New(qid uint16, v6 bool) (*Queue, error) { //nolint:gocognit
|
||||||
return q, nil
|
return q, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// open opens a new netlink socket and and creates a new nfqueue.
|
// open opens a new netlink socket and creates a new nfqueue.
|
||||||
// Upon success, the new nfqueue is atomically stored in Queue.nf.
|
// Upon success, the new nfqueue is atomically stored in Queue.nf.
|
||||||
// Users must use Queue.getNfq to access it. open does not care about
|
// Users must use Queue.getNfq to access it. open does not care about
|
||||||
// any other value or queue that might be stored in Queue.nf at
|
// any other value or queue that might be stored in Queue.nf at
|
||||||
|
|
|
@ -135,7 +135,7 @@ func createPrompt(ctx context.Context, conn *network.Connection) (n *notificatio
|
||||||
n.Unlock()
|
n.Unlock()
|
||||||
|
|
||||||
// If the notification is still active, extend and return.
|
// If the notification is still active, extend and return.
|
||||||
// This can can happen because user input (prompts changing the endpoint
|
// This can happen because user input (prompts changing the endpoint
|
||||||
// lists) can happen any time - also between checking the endpoint lists
|
// lists) can happen any time - also between checking the endpoint lists
|
||||||
// and now.
|
// and now.
|
||||||
if state == notifications.Active {
|
if state == notifications.Active {
|
||||||
|
|
|
@ -16,7 +16,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Location holds information regarding the geographical and network location of an IP address.
|
// Location holds information regarding the geographical and network location of an IP address.
|
||||||
// TODO: We are currently re-using the Continent-Code for the region. Update this and and all dependencies.
|
// TODO: We are currently re-using the Continent-Code for the region. Update this and all dependencies.
|
||||||
type Location struct {
|
type Location struct {
|
||||||
Continent struct {
|
Continent struct {
|
||||||
Code string `maxminddb:"code"`
|
Code string `maxminddb:"code"`
|
||||||
|
|
|
@ -22,6 +22,7 @@ func (l *Location) IsRegionalNeighbor(other *Location) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Region defines a geographic region and neighboring regions.
|
||||||
type Region struct {
|
type Region struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
// domain parameter of dhcp
|
// domain parameter of dhcp
|
||||||
|
|
||||||
// TODO: get dhcp servers on windows:
|
// TODO: get dhcp servers on windows:
|
||||||
// windows: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365917
|
// doc: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365917
|
||||||
// this info might already be included in the interfaces api provided by golang!
|
// this info might already be included in the interfaces api provided by golang!
|
||||||
|
|
||||||
// Nameserver describes a system assigned namserver.
|
// Nameserver describes a system assigned namserver.
|
||||||
|
|
|
@ -19,7 +19,7 @@ delivered correctly, or need special permissions and or sockets to receive
|
||||||
them. This is the case when doing a traceroute.
|
them. This is the case when doing a traceroute.
|
||||||
|
|
||||||
In order to keep it simple, the system is only designed to be used by one
|
In order to keep it simple, the system is only designed to be used by one
|
||||||
"user" at at time. Further calls to ListenToICMP will wait for the previous
|
"user" at a time. Further calls to ListenToICMP will wait for the previous
|
||||||
operation to complete.
|
operation to complete.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ type (
|
||||||
// It is implemented by the *Database type of this package.
|
// It is implemented by the *Database type of this package.
|
||||||
ConnectionStore interface {
|
ConnectionStore interface {
|
||||||
// Save is called to perists the new or updated connection. If required,
|
// Save is called to perists the new or updated connection. If required,
|
||||||
// It's up the the implementation to figure out if the operation is an
|
// It's up to the implementation to figure out if the operation is an
|
||||||
// insert or an update.
|
// insert or an update.
|
||||||
// The ID of Conn is unique and can be trusted to never collide with other
|
// The ID of Conn is unique and can be trusted to never collide with other
|
||||||
// connections of the save device.
|
// connections of the save device.
|
||||||
|
|
|
@ -114,5 +114,7 @@ func (i *BindInfo) GetUIDandInode() (int, int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile time checks.
|
// Compile time checks.
|
||||||
var _ Info = new(ConnectionInfo)
|
var (
|
||||||
var _ Info = new(BindInfo)
|
_ Info = new(ConnectionInfo)
|
||||||
|
_ Info = new(BindInfo)
|
||||||
|
)
|
||||||
|
|
|
@ -92,7 +92,7 @@ func (table *tcpTable) lookup(pktInfo *packet.Info, fast bool) (
|
||||||
|
|
||||||
// If there's a match, check if we have the PID and return.
|
// If there's a match, check if we have the PID and return.
|
||||||
if socketInfo != nil {
|
if socketInfo != nil {
|
||||||
return checkPID(socketInfo, inbound)
|
return CheckPID(socketInfo, inbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DUAL-STACK
|
// DUAL-STACK
|
||||||
|
@ -114,7 +114,7 @@ func (table *tcpTable) lookup(pktInfo *packet.Info, fast bool) (
|
||||||
|
|
||||||
// If there's a match, check if we have the PID and return.
|
// If there's a match, check if we have the PID and return.
|
||||||
if socketInfo != nil {
|
if socketInfo != nil {
|
||||||
return checkPID(socketInfo, inbound)
|
return CheckPID(socketInfo, inbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search less if we want to be fast.
|
// Search less if we want to be fast.
|
||||||
|
@ -199,14 +199,14 @@ func (table *udpTable) lookup(pktInfo *packet.Info, fast bool) (
|
||||||
// connection. This will be the case for pure checking functions
|
// connection. This will be the case for pure checking functions
|
||||||
// that do not want to change direction state.
|
// that do not want to change direction state.
|
||||||
if pktInfo.RemotePort() == 0 {
|
if pktInfo.RemotePort() == 0 {
|
||||||
return checkPID(socketInfo, pktInfo.Inbound)
|
return CheckPID(socketInfo, pktInfo.Inbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get (and save) the direction of the connection.
|
// Get (and save) the direction of the connection.
|
||||||
connInbound := table.getDirection(socketInfo, pktInfo)
|
connInbound := table.getDirection(socketInfo, pktInfo)
|
||||||
|
|
||||||
// Check we have the PID and return.
|
// Check we have the PID and return.
|
||||||
return checkPID(socketInfo, connInbound)
|
return CheckPID(socketInfo, connInbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DUAL-STACK
|
// DUAL-STACK
|
||||||
|
@ -232,14 +232,14 @@ func (table *udpTable) lookup(pktInfo *packet.Info, fast bool) (
|
||||||
// connection. This will be the case for pure checking functions
|
// connection. This will be the case for pure checking functions
|
||||||
// that do not want to change direction state.
|
// that do not want to change direction state.
|
||||||
if pktInfo.RemotePort() == 0 {
|
if pktInfo.RemotePort() == 0 {
|
||||||
return checkPID(socketInfo, pktInfo.Inbound)
|
return CheckPID(socketInfo, pktInfo.Inbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get (and save) the direction of the connection.
|
// Get (and save) the direction of the connection.
|
||||||
connInbound := table.getDirection(socketInfo, pktInfo)
|
connInbound := table.getDirection(socketInfo, pktInfo)
|
||||||
|
|
||||||
// Check we have the PID and return.
|
// Check we have the PID and return.
|
||||||
return checkPID(socketInfo, connInbound)
|
return CheckPID(socketInfo, connInbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search less if we want to be fast.
|
// Search less if we want to be fast.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !windows && !linux
|
||||||
// +build !windows,!linux
|
// +build !windows,!linux
|
||||||
|
|
||||||
package state
|
package state
|
||||||
|
@ -38,6 +39,8 @@ func getUDP6Table() (binds []*socket.BindInfo, err error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) {
|
// CheckPID checks the if socket info already has a PID and if not, tries to find it.
|
||||||
|
// Depending on the OS, this might be a no-op.
|
||||||
|
func CheckPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) {
|
||||||
return socketInfo.GetPID(), connInbound, nil
|
return socketInfo.GetPID(), connInbound, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@ var (
|
||||||
|
|
||||||
var baseWaitTime = 3 * time.Millisecond
|
var baseWaitTime = 3 * time.Millisecond
|
||||||
|
|
||||||
func checkPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) {
|
// CheckPID checks the if socket info already has a PID and if not, tries to find it.
|
||||||
|
// Depending on the OS, this might be a no-op.
|
||||||
|
func CheckPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) {
|
||||||
for i := 1; i <= lookupTries; i++ {
|
for i := 1; i <= lookupTries; i++ {
|
||||||
// look for PID
|
// look for PID
|
||||||
pid = proc.GetPID(socketInfo)
|
pid = proc.GetPID(socketInfo)
|
||||||
|
|
|
@ -12,6 +12,8 @@ var (
|
||||||
getUDP6Table = iphelper.GetUDP6Table
|
getUDP6Table = iphelper.GetUDP6Table
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) {
|
// CheckPID checks the if socket info already has a PID and if not, tries to find it.
|
||||||
|
// Depending on the OS, this might be a no-op.
|
||||||
|
func CheckPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) {
|
||||||
return socketInfo.GetPID(), connInbound, nil
|
return socketInfo.GetPID(), connInbound, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ type Profile struct { //nolint:maligned // not worth the effort
|
||||||
// WarningLastUpdated holds the timestamp when the Warning field was last
|
// WarningLastUpdated holds the timestamp when the Warning field was last
|
||||||
// updated.
|
// updated.
|
||||||
WarningLastUpdated time.Time
|
WarningLastUpdated time.Time
|
||||||
// Homepage may refer the the website of the application
|
// Homepage may refer to the website of the application
|
||||||
// vendor.
|
// vendor.
|
||||||
Homepage string
|
Homepage string
|
||||||
// Icon holds the icon of the application. The value
|
// Icon holds the icon of the application. The value
|
||||||
|
|
|
@ -149,7 +149,7 @@ func (tr *TCPResolver) getOrCreateResolverConn(ctx context.Context) (*tcpResolve
|
||||||
netenv.ReportFailedConnection()
|
netenv.ReportFailedConnection()
|
||||||
|
|
||||||
log.Debugf("resolver: failed to connect to %s: %s", tr.resolver.Info.DescriptiveName(), err)
|
log.Debugf("resolver: failed to connect to %s: %s", tr.resolver.Info.DescriptiveName(), err)
|
||||||
return nil, fmt.Errorf("%w: failed to connect to %s: %s", ErrFailure, tr.resolver.Info.DescriptiveName(), err)
|
return nil, fmt.Errorf("%w: failed to connect to %s: %w", ErrFailure, tr.resolver.Info.DescriptiveName(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hint network environment at successful connection.
|
// Hint network environment at successful connection.
|
||||||
|
|
Loading…
Add table
Reference in a new issue