Merge pull request #1151 from safing/feature/integrationtest-cmd

Add integrationtest utility
This commit is contained in:
Daniel Hovie 2023-04-25 14:41:37 +02:00 committed by GitHub
commit f3dc188f63
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 174 additions and 21 deletions

2
.gitignore vendored
View file

@ -5,6 +5,8 @@ dnsonly
dnsonly.exe dnsonly.exe
main main
main.exe main.exe
integrationtest
integrationtest.exe
# Dist dir # Dist dir
dist dist

View file

@ -22,6 +22,7 @@ linters:
- interfacer - interfacer
- ireturn - ireturn
- lll - lll
- musttag
- nestif - nestif
- nilnil - nilnil
- nlreturn - nlreturn

View 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)
}
}

View 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)
}
}

View file

@ -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

View file

@ -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++ {

View file

@ -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

View file

@ -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 {

View file

@ -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"`

View file

@ -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

View file

@ -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.

View file

@ -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.
*/ */

View file

@ -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.

View file

@ -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)
)

View file

@ -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.

View file

@ -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
} }

View file

@ -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)

View file

@ -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
} }

View file

@ -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

View file

@ -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.