diff --git a/.gitignore b/.gitignore index 7827163e..7332997a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ dnsonly dnsonly.exe main main.exe +integrationtest +integrationtest.exe # Dist dir dist diff --git a/cmds/integrationtest/main.go b/cmds/integrationtest/main.go new file mode 100644 index 00000000..88f2713e --- /dev/null +++ b/cmds/integrationtest/main.go @@ -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) + } +} diff --git a/cmds/integrationtest/netstate.go b/cmds/integrationtest/netstate.go new file mode 100644 index 00000000..ca90cbdc --- /dev/null +++ b/cmds/integrationtest/netstate.go @@ -0,0 +1,112 @@ +package main + +import ( + "fmt" + "time" + + "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 { + fmt.Printf("%s %d\n", c, s.GetPID()) + } else { + fmt.Printf("%s %d (err: %s)\n", c, s.GetPID(), err) + } +} diff --git a/network/state/lookup.go b/network/state/lookup.go index c8235717..03188e8e 100644 --- a/network/state/lookup.go +++ b/network/state/lookup.go @@ -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 socketInfo != nil { - return checkPID(socketInfo, inbound) + return CheckPID(socketInfo, inbound) } // 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 socketInfo != nil { - return checkPID(socketInfo, inbound) + return CheckPID(socketInfo, inbound) } // 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 // that do not want to change direction state. if pktInfo.RemotePort() == 0 { - return checkPID(socketInfo, pktInfo.Inbound) + return CheckPID(socketInfo, pktInfo.Inbound) } // Get (and save) the direction of the connection. connInbound := table.getDirection(socketInfo, pktInfo) // Check we have the PID and return. - return checkPID(socketInfo, connInbound) + return CheckPID(socketInfo, connInbound) } // 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 // that do not want to change direction state. if pktInfo.RemotePort() == 0 { - return checkPID(socketInfo, pktInfo.Inbound) + return CheckPID(socketInfo, pktInfo.Inbound) } // Get (and save) the direction of the connection. connInbound := table.getDirection(socketInfo, pktInfo) // Check we have the PID and return. - return checkPID(socketInfo, connInbound) + return CheckPID(socketInfo, connInbound) } // Search less if we want to be fast. diff --git a/network/state/system_default.go b/network/state/system_default.go index 64cff2fb..4b798996 100644 --- a/network/state/system_default.go +++ b/network/state/system_default.go @@ -1,3 +1,4 @@ +//go:build !windows && !linux // +build !windows,!linux package state @@ -38,6 +39,8 @@ func getUDP6Table() (binds []*socket.BindInfo, err error) { 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 } diff --git a/network/state/system_linux.go b/network/state/system_linux.go index 4f7c4138..f0fe5382 100644 --- a/network/state/system_linux.go +++ b/network/state/system_linux.go @@ -16,7 +16,9 @@ var ( 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++ { // look for PID pid = proc.GetPID(socketInfo) diff --git a/network/state/system_windows.go b/network/state/system_windows.go index 56927366..2a95a01e 100644 --- a/network/state/system_windows.go +++ b/network/state/system_windows.go @@ -12,6 +12,8 @@ var ( 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 }