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/.golangci.yml b/.golangci.yml index 8202fead..775a0789 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -22,6 +22,7 @@ linters: - interfacer - ireturn - lll + - musttag - nestif - nilnil - nlreturn 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..f76604c7 --- /dev/null +++ b/cmds/integrationtest/netstate.go @@ -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) + } +} diff --git a/cmds/portmaster-start/logs.go b/cmds/portmaster-start/logs.go index 09fa01c6..a7ab4d02 100644 --- a/cmds/portmaster-start/logs.go +++ b/cmds/portmaster-start/logs.go @@ -17,7 +17,7 @@ import ( ) 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 { log.Printf("failed to create log file %s: %s\n", logFilePath, err) return nil diff --git a/firewall/api.go b/firewall/api.go index 447c1685..5c2f0eed 100644 --- a/firewall/api.go +++ b/firewall/api.go @@ -149,7 +149,7 @@ func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) (retry bo originalPid = proc.Pid 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 checkLevelsLoop: for i := 0; i < checkLevels+1; i++ { diff --git a/firewall/interception/nfq/nfq.go b/firewall/interception/nfq/nfq.go index f9a688ff..9b382944 100644 --- a/firewall/interception/nfq/nfq.go +++ b/firewall/interception/nfq/nfq.go @@ -90,7 +90,7 @@ func New(qid uint16, v6 bool) (*Queue, error) { //nolint:gocognit 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. // 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 diff --git a/firewall/prompt.go b/firewall/prompt.go index 3ed25403..e1a380d9 100644 --- a/firewall/prompt.go +++ b/firewall/prompt.go @@ -135,7 +135,7 @@ func createPrompt(ctx context.Context, conn *network.Connection) (n *notificatio n.Unlock() // 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 // and now. if state == notifications.Active { diff --git a/intel/geoip/location.go b/intel/geoip/location.go index 1a58077f..585f3e0d 100644 --- a/intel/geoip/location.go +++ b/intel/geoip/location.go @@ -16,7 +16,7 @@ const ( ) // 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 { Continent struct { Code string `maxminddb:"code"` diff --git a/intel/geoip/regions.go b/intel/geoip/regions.go index 0ce18761..cbaa3ee0 100644 --- a/intel/geoip/regions.go +++ b/intel/geoip/regions.go @@ -22,6 +22,7 @@ func (l *Location) IsRegionalNeighbor(other *Location) bool { return false } +// Region defines a geographic region and neighboring regions. type Region struct { ID string Name string diff --git a/netenv/environment.go b/netenv/environment.go index 09fda8a4..fbb91ff3 100644 --- a/netenv/environment.go +++ b/netenv/environment.go @@ -10,7 +10,7 @@ import ( // domain parameter of dhcp // 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! // Nameserver describes a system assigned namserver. diff --git a/netenv/icmp_listener.go b/netenv/icmp_listener.go index 7248469c..ca90b1e4 100644 --- a/netenv/icmp_listener.go +++ b/netenv/icmp_listener.go @@ -19,7 +19,7 @@ delivered correctly, or need special permissions and or sockets to receive 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 -"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. */ diff --git a/netquery/manager.go b/netquery/manager.go index 16dcbe0e..2a7a70a6 100644 --- a/netquery/manager.go +++ b/netquery/manager.go @@ -21,7 +21,7 @@ type ( // It is implemented by the *Database type of this package. ConnectionStore interface { // 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. // The ID of Conn is unique and can be trusted to never collide with other // connections of the save device. diff --git a/network/socket/socket.go b/network/socket/socket.go index 18c5b677..29393de8 100644 --- a/network/socket/socket.go +++ b/network/socket/socket.go @@ -114,5 +114,7 @@ func (i *BindInfo) GetUIDandInode() (int, int) { } // Compile time checks. -var _ Info = new(ConnectionInfo) -var _ Info = new(BindInfo) +var ( + _ Info = new(ConnectionInfo) + _ Info = new(BindInfo) +) 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 } diff --git a/profile/profile.go b/profile/profile.go index d6bc8eaa..1fa12ff8 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -70,7 +70,7 @@ type Profile struct { //nolint:maligned // not worth the effort // WarningLastUpdated holds the timestamp when the Warning field was last // updated. WarningLastUpdated time.Time - // Homepage may refer the the website of the application + // Homepage may refer to the website of the application // vendor. Homepage string // Icon holds the icon of the application. The value diff --git a/resolver/resolver-tcp.go b/resolver/resolver-tcp.go index 4a1c65f8..aed64e2d 100644 --- a/resolver/resolver-tcp.go +++ b/resolver/resolver-tcp.go @@ -149,7 +149,7 @@ func (tr *TCPResolver) getOrCreateResolverConn(ctx context.Context) (*tcpResolve netenv.ReportFailedConnection() 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.