mirror of
https://github.com/safing/portmaster
synced 2025-09-02 10:39:22 +00:00
Add support for the new kext
This commit is contained in:
parent
e9940d77a0
commit
1f2f0e5213
10 changed files with 765 additions and 3 deletions
|
@ -5,7 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/safing/portmaster/firewall/interception/windowskext"
|
"github.com/safing/portmaster/firewall/interception/windowskext2"
|
||||||
"github.com/safing/portmaster/network"
|
"github.com/safing/portmaster/network"
|
||||||
"github.com/safing/portmaster/network/packet"
|
"github.com/safing/portmaster/network/packet"
|
||||||
"github.com/safing/portmaster/updates"
|
"github.com/safing/portmaster/updates"
|
||||||
|
@ -35,8 +35,29 @@ func startInterception(packets chan packet.Packet) error {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Start bandwidth stats monitor.
|
// Start bandwidth stats monitor.
|
||||||
module.StartServiceWorker("kext bandwidth stats monitor", 0, func(ctx context.Context) error {
|
// module.StartServiceWorker("kext bandwidth stats monitor", 0, func(ctx context.Context) error {
|
||||||
return windowskext.BandwidthStatsWorker(ctx, 1*time.Second, BandwidthUpdates)
|
// return windowskext.BandwidthStatsWorker(ctx, 1*time.Second, BandwidthUpdates)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// Start kext logging. The worker will periodically send request to the kext to send logs.
|
||||||
|
module.StartServiceWorker("kext log request worker", 0, func(ctx context.Context) error {
|
||||||
|
timer := time.NewTimer(time.Second)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
{
|
||||||
|
err := windowskext.SendLogRequest()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
{
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
132
firewall/interception/windowskext2/bandwidth_stats.go
Normal file
132
firewall/interception/windowskext2/bandwidth_stats.go
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windowskext
|
||||||
|
|
||||||
|
// This file contains example code how to read bandwidth stats from the kext. Its not ment to be used in production.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/safing/portbase/log"
|
||||||
|
"github.com/safing/portmaster/network/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Rxtxdata struct {
|
||||||
|
rx uint64
|
||||||
|
tx uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Key struct {
|
||||||
|
localIP [4]uint32
|
||||||
|
remoteIP [4]uint32
|
||||||
|
localPort uint16
|
||||||
|
remotePort uint16
|
||||||
|
ipv6 bool
|
||||||
|
protocol uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
var m = make(map[Key]Rxtxdata)
|
||||||
|
|
||||||
|
func BandwidthStatsWorker(ctx context.Context, collectInterval time.Duration, bandwidthUpdates chan *packet.BandwidthUpdate) error {
|
||||||
|
// Setup ticker.
|
||||||
|
ticker := time.NewTicker(collectInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
// Collect bandwidth at every tick.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
err := reportBandwidth(ctx, bandwidthUpdates)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reportBandwidth(ctx context.Context, bandwidthUpdates chan *packet.BandwidthUpdate) error {
|
||||||
|
stats, err := GetConnectionsStats()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report all statistics.
|
||||||
|
for i, stat := range stats {
|
||||||
|
connID := packet.CreateConnectionID(
|
||||||
|
packet.IPProtocol(stat.protocol),
|
||||||
|
convertArrayToIP(stat.localIP, stat.ipV6 == 1), stat.localPort,
|
||||||
|
convertArrayToIP(stat.remoteIP, stat.ipV6 == 1), stat.remotePort,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
update := &packet.BandwidthUpdate{
|
||||||
|
ConnID: connID,
|
||||||
|
BytesReceived: stat.receivedBytes,
|
||||||
|
BytesSent: stat.transmittedBytes,
|
||||||
|
Method: packet.Additive,
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case bandwidthUpdates <- update:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
log.Warningf("kext: bandwidth update queue is full, skipping rest of batch (%d entries)", len(stats)-i)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartBandwithConsoleLogger() {
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(2 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for range ticker.C {
|
||||||
|
conns, err := GetConnectionsStats()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, conn := range conns {
|
||||||
|
if conn.receivedBytes == 0 && conn.transmittedBytes == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := Key{
|
||||||
|
localIP: conn.localIP,
|
||||||
|
remoteIP: conn.remoteIP,
|
||||||
|
localPort: conn.localPort,
|
||||||
|
remotePort: conn.remotePort,
|
||||||
|
ipv6: conn.ipV6 == 1,
|
||||||
|
protocol: conn.protocol,
|
||||||
|
}
|
||||||
|
|
||||||
|
// First we get a "copy" of the entry
|
||||||
|
if entry, ok := m[key]; ok {
|
||||||
|
// Then we modify the copy
|
||||||
|
entry.rx += conn.receivedBytes
|
||||||
|
entry.tx += conn.transmittedBytes
|
||||||
|
|
||||||
|
// Then we reassign map entry
|
||||||
|
m[key] = entry
|
||||||
|
} else {
|
||||||
|
m[key] = Rxtxdata{
|
||||||
|
rx: conn.receivedBytes,
|
||||||
|
tx: conn.transmittedBytes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debug("----------------------------------")
|
||||||
|
for key, value := range m {
|
||||||
|
log.Debugf(
|
||||||
|
"Conn: %d %s:%d %s:%d rx:%d tx:%d", key.protocol,
|
||||||
|
convertArrayToIP(key.localIP, key.ipv6), key.localPort,
|
||||||
|
convertArrayToIP(key.remoteIP, key.ipv6), key.remotePort,
|
||||||
|
value.rx, value.tx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
4
firewall/interception/windowskext2/doc.go
Normal file
4
firewall/interception/windowskext2/doc.go
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package windowskext provides network interception capabilities on windows via the Portmaster Kernel Extension.
|
||||||
|
package windowskext
|
181
firewall/interception/windowskext2/handler.go
Normal file
181
firewall/interception/windowskext2/handler.go
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windowskext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/safing/portmaster/process"
|
||||||
|
|
||||||
|
"github.com/tevino/abool"
|
||||||
|
|
||||||
|
"github.com/safing/portbase/log"
|
||||||
|
"github.com/safing/portmaster/network/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// VerdictRequestFlagFastTrackPermitted is set on packets that have been
|
||||||
|
// already permitted by the kernel extension and the verdict request is only
|
||||||
|
// informational.
|
||||||
|
VerdictRequestFlagFastTrackPermitted = 1
|
||||||
|
|
||||||
|
// VerdictRequestFlagSocketAuth indicates that the verdict request is for a
|
||||||
|
// connection that was intercepted on an ALE layer instead of in the network
|
||||||
|
// stack itself. Thus, no packet data is available.
|
||||||
|
VerdictRequestFlagSocketAuth = 2
|
||||||
|
|
||||||
|
// VerdictRequestFlagExpectSocketAuth indicates that the next verdict
|
||||||
|
// requests is expected to be an informational socket auth request from
|
||||||
|
// the ALE layer.
|
||||||
|
VerdictRequestFlagExpectSocketAuth = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnectionStat struct {
|
||||||
|
localIP [4]uint32 //Source Address, only srcIP[0] if IPv4
|
||||||
|
remoteIP [4]uint32 //Destination Address
|
||||||
|
localPort uint16 //Source Port
|
||||||
|
remotePort uint16 //Destination port
|
||||||
|
receivedBytes uint64 //Number of bytes recived on this connection
|
||||||
|
transmittedBytes uint64 //Number of bytes transsmited from this connection
|
||||||
|
ipV6 uint8 //True: IPv6, False: IPv4
|
||||||
|
protocol uint8 //Protocol (UDP, TCP, ...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type VersionInfo struct {
|
||||||
|
major uint8
|
||||||
|
minor uint8
|
||||||
|
revision uint8
|
||||||
|
build uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *VersionInfo) String() string {
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d", v.major, v.minor, v.revision, v.build)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler transforms received packets to the Packet interface.
|
||||||
|
func Handler(ctx context.Context, packets chan packet.Packet) {
|
||||||
|
for {
|
||||||
|
packetInfo, err := RecvVerdictRequest()
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to get packet from windows kext: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if packetInfo.Connection != nil {
|
||||||
|
log.Tracef("packet: %+v", packetInfo.Connection)
|
||||||
|
conn := packetInfo.Connection
|
||||||
|
// New Packet
|
||||||
|
new := &Packet{
|
||||||
|
verdictRequest: conn.Id,
|
||||||
|
verdictSet: abool.NewBool(false),
|
||||||
|
}
|
||||||
|
info := new.Info()
|
||||||
|
info.Inbound = conn.Direction > 0
|
||||||
|
info.InTunnel = false
|
||||||
|
info.Protocol = packet.IPProtocol(conn.Protocol)
|
||||||
|
info.PID = int(*conn.ProcessId)
|
||||||
|
info.SeenAt = time.Now()
|
||||||
|
|
||||||
|
// Check PID
|
||||||
|
if info.PID == 0 {
|
||||||
|
// Windows does not have zero PIDs.
|
||||||
|
// Set to UndefinedProcessID.
|
||||||
|
info.PID = process.UndefinedProcessID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set IP version
|
||||||
|
if conn.IpV6 {
|
||||||
|
info.Version = packet.IPv6
|
||||||
|
} else {
|
||||||
|
info.Version = packet.IPv4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set IPs
|
||||||
|
if info.Inbound {
|
||||||
|
// Inbound
|
||||||
|
info.Src = net.IP(conn.RemoteIp)
|
||||||
|
info.Dst = net.IP(conn.LocalIp)
|
||||||
|
} else {
|
||||||
|
// Outbound
|
||||||
|
info.Src = net.IP(conn.LocalIp)
|
||||||
|
info.Dst = net.IP(conn.RemoteIp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Ports
|
||||||
|
if info.Inbound {
|
||||||
|
// Inbound
|
||||||
|
info.SrcPort = conn.RemotePort
|
||||||
|
info.DstPort = conn.LocalPort
|
||||||
|
} else {
|
||||||
|
// Outbound
|
||||||
|
info.SrcPort = conn.LocalPort
|
||||||
|
info.DstPort = conn.RemotePort
|
||||||
|
}
|
||||||
|
|
||||||
|
packets <- new
|
||||||
|
}
|
||||||
|
|
||||||
|
if packetInfo.LogLines != nil {
|
||||||
|
for _, line := range *packetInfo.LogLines {
|
||||||
|
switch line.Severity {
|
||||||
|
case int(log.DebugLevel):
|
||||||
|
log.Debugf("kext: %s", line.Line)
|
||||||
|
case int(log.InfoLevel):
|
||||||
|
log.Infof("kext: %s", line.Line)
|
||||||
|
case int(log.WarningLevel):
|
||||||
|
log.Warningf("kext: %s", line.Line)
|
||||||
|
case int(log.ErrorLevel):
|
||||||
|
log.Errorf("kext: %s", line.Line)
|
||||||
|
case int(log.CriticalLevel):
|
||||||
|
log.Criticalf("kext: %s", line.Line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertArrayToIP converts an array of uint32 values to a net.IP address.
|
||||||
|
func convertArrayToIP(input [4]uint32, ipv6 bool) net.IP {
|
||||||
|
if !ipv6 {
|
||||||
|
addressBuf := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(addressBuf, input[0])
|
||||||
|
return net.IP(addressBuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
addressBuf := make([]byte, 16)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
binary.BigEndian.PutUint32(addressBuf[i*4:i*4+4], input[i])
|
||||||
|
}
|
||||||
|
return net.IP(addressBuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipAddressToArray(ip net.IP, isIPv6 bool) [4]uint32 {
|
||||||
|
array := [4]uint32{0}
|
||||||
|
if isIPv6 {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
binary.BigEndian.PutUint32(asByteArrayWithLength(&array[i], 4), getUInt32Value(&ip[i]))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binary.BigEndian.PutUint32(asByteArrayWithLength(&array[0], 4), getUInt32Value(&ip[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
|
||||||
|
func asByteArray[T any](obj *T) []byte {
|
||||||
|
return unsafe.Slice((*byte)(unsafe.Pointer(obj)), unsafe.Sizeof(*obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
func asByteArrayWithLength[T any](obj *T, size uint32) []byte {
|
||||||
|
return unsafe.Slice((*byte)(unsafe.Pointer(obj)), size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUInt32Value[T any](obj *T) uint32 {
|
||||||
|
return *(*uint32)(unsafe.Pointer(obj))
|
||||||
|
}
|
156
firewall/interception/windowskext2/kext.go
Normal file
156
firewall/interception/windowskext2/kext.go
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windowskext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/safing/portbase/log"
|
||||||
|
"github.com/safing/portmaster/network"
|
||||||
|
"github.com/vlabo/portmaster_windows_rust_kext/kext_interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Package errors
|
||||||
|
var (
|
||||||
|
ErrKextNotReady = errors.New("the windows kernel extension (driver) is not ready to accept commands")
|
||||||
|
ErrNoPacketID = errors.New("the packet has no ID, possibly because it was fast-tracked by the kernel extension")
|
||||||
|
|
||||||
|
driverPath string
|
||||||
|
|
||||||
|
service *kext_interface.KextService
|
||||||
|
kextFile *kext_interface.KextFile
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
driverName = "PortmasterKext"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init initializes the DLL and the Kext (Kernel Driver).
|
||||||
|
func Init(path string) error {
|
||||||
|
driverPath = path
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start intercepting.
|
||||||
|
func Start() error {
|
||||||
|
|
||||||
|
// initialize and start driver service
|
||||||
|
var err error
|
||||||
|
service, err = kext_interface.CreateKextService(driverName, driverPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start service and open file
|
||||||
|
service.Start(true)
|
||||||
|
kextFile, err = service.OpenFile(1024)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open driver: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop intercepting.
|
||||||
|
func Stop() error {
|
||||||
|
// Prepare kernel for shutdown
|
||||||
|
err := shutdownRequest()
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("winkext: shutdown request failed: %s", err)
|
||||||
|
}
|
||||||
|
// Close the interface to the driver. Driver will continue to run.
|
||||||
|
kextFile.Close()
|
||||||
|
|
||||||
|
// Stop and delete the driver.
|
||||||
|
service.Stop(true)
|
||||||
|
service.Delete()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func shutdownRequest() error {
|
||||||
|
return kext_interface.WriteCommand(kextFile, kext_interface.BuildShutdown())
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendLogRequest() error {
|
||||||
|
return kext_interface.WriteCommand(kextFile, kext_interface.BuildGetLogs())
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecvVerdictRequest waits for the next verdict request from the kext. If a timeout is reached, both *VerdictRequest and error will be nil.
|
||||||
|
func RecvVerdictRequest() (*kext_interface.Info, error) {
|
||||||
|
return kext_interface.ReadInfo(kextFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetVerdict sets the verdict for a packet and/or connection.
|
||||||
|
func SetVerdict(pkt *Packet, verdict network.Verdict) error {
|
||||||
|
if verdict == network.VerdictRerouteToNameserver {
|
||||||
|
redirect := kext_interface.Redirect{Id: pkt.verdictRequest, RemoteAddress: []uint8{127, 0, 0, 1}, RemotePort: 53}
|
||||||
|
command := kext_interface.BuildRedirect(redirect)
|
||||||
|
kext_interface.WriteCommand(kextFile, command)
|
||||||
|
} else if verdict == network.VerdictRerouteToTunnel {
|
||||||
|
redirect := kext_interface.Redirect{Id: pkt.verdictRequest, RemoteAddress: []uint8{192, 168, 122, 196}, RemotePort: 717}
|
||||||
|
command := kext_interface.BuildRedirect(redirect)
|
||||||
|
kext_interface.WriteCommand(kextFile, command)
|
||||||
|
} else {
|
||||||
|
verdict := kext_interface.Verdict{Id: pkt.verdictRequest, Verdict: uint8(verdict)}
|
||||||
|
command := kext_interface.BuildVerdict(verdict)
|
||||||
|
kext_interface.WriteCommand(kextFile, command)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearCache() error {
|
||||||
|
return kext_interface.WriteCommand(kextFile, kext_interface.BuildClearCache())
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateVerdict(conn *network.Connection) error {
|
||||||
|
redirectAddress := []uint8{}
|
||||||
|
redirectPort := 0
|
||||||
|
if conn.Verdict.Active == network.VerdictRerouteToNameserver {
|
||||||
|
redirectAddress = []uint8{127, 0, 0, 1}
|
||||||
|
redirectPort = 53
|
||||||
|
}
|
||||||
|
if conn.Verdict.Active == network.VerdictRerouteToTunnel {
|
||||||
|
redirectAddress = []uint8{192, 168, 122, 196}
|
||||||
|
redirectPort = 717
|
||||||
|
}
|
||||||
|
|
||||||
|
update := kext_interface.Update{
|
||||||
|
Protocol: conn.Entity.Protocol,
|
||||||
|
LocalAddress: conn.LocalIP,
|
||||||
|
LocalPort: conn.LocalPort,
|
||||||
|
RemoteAddress: conn.Entity.IP,
|
||||||
|
RemotePort: conn.Entity.Port,
|
||||||
|
Verdict: uint8(conn.Verdict.Active),
|
||||||
|
RedirectAddress: redirectAddress,
|
||||||
|
RedirectPort: uint16(redirectPort),
|
||||||
|
}
|
||||||
|
|
||||||
|
command := kext_interface.BuildUpdate(update)
|
||||||
|
kext_interface.WriteCommand(kextFile, command)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVersion() (*VersionInfo, error) {
|
||||||
|
data, err := kext_interface.ReadVersion(kextFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
version := &VersionInfo{
|
||||||
|
major: data[0],
|
||||||
|
minor: data[1],
|
||||||
|
revision: data[2],
|
||||||
|
build: data[3],
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var sizeOfConnectionStat = uint32(unsafe.Sizeof(ConnectionStat{}))
|
||||||
|
|
||||||
|
func GetConnectionsStats() ([]ConnectionStat, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
110
firewall/interception/windowskext2/packet.go
Normal file
110
firewall/interception/windowskext2/packet.go
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windowskext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/tevino/abool"
|
||||||
|
|
||||||
|
"github.com/safing/portmaster/network"
|
||||||
|
"github.com/safing/portmaster/network/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Packet represents an IP packet.
|
||||||
|
type Packet struct {
|
||||||
|
packet.Base
|
||||||
|
|
||||||
|
verdictRequest uint64
|
||||||
|
verdictSet *abool.AtomicBool
|
||||||
|
|
||||||
|
payloadLoaded bool
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// FastTrackedByIntegration returns whether the packet has been fast-track
|
||||||
|
// accepted by the OS integration.
|
||||||
|
func (pkt *Packet) FastTrackedByIntegration() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoOnly returns whether the packet is informational only and does not
|
||||||
|
// represent an actual packet.
|
||||||
|
func (pkt *Packet) InfoOnly() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectInfo returns whether the next packet is expected to be informational only.
|
||||||
|
func (pkt *Packet) ExpectInfo() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPayload returns the full raw packet.
|
||||||
|
func (pkt *Packet) LoadPacketData() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept accepts the packet.
|
||||||
|
func (pkt *Packet) Accept() error {
|
||||||
|
if pkt.verdictSet.SetToIf(false, true) {
|
||||||
|
return SetVerdict(pkt, -network.VerdictAccept)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block blocks the packet.
|
||||||
|
func (pkt *Packet) Block() error {
|
||||||
|
if pkt.verdictSet.SetToIf(false, true) {
|
||||||
|
return SetVerdict(pkt, -network.VerdictBlock)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop drops the packet.
|
||||||
|
func (pkt *Packet) Drop() error {
|
||||||
|
if pkt.verdictSet.SetToIf(false, true) {
|
||||||
|
return SetVerdict(pkt, -network.VerdictDrop)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PermanentAccept permanently accepts connection (and the current packet).
|
||||||
|
func (pkt *Packet) PermanentAccept() error {
|
||||||
|
if pkt.verdictSet.SetToIf(false, true) {
|
||||||
|
return SetVerdict(pkt, network.VerdictAccept)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PermanentBlock permanently blocks connection (and the current packet).
|
||||||
|
func (pkt *Packet) PermanentBlock() error {
|
||||||
|
if pkt.verdictSet.SetToIf(false, true) {
|
||||||
|
return SetVerdict(pkt, network.VerdictBlock)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PermanentDrop permanently drops connection (and the current packet).
|
||||||
|
func (pkt *Packet) PermanentDrop() error {
|
||||||
|
if pkt.verdictSet.SetToIf(false, true) {
|
||||||
|
return SetVerdict(pkt, network.VerdictDrop)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RerouteToNameserver permanently reroutes the connection to the local nameserver (and the current packet).
|
||||||
|
func (pkt *Packet) RerouteToNameserver() error {
|
||||||
|
if pkt.verdictSet.SetToIf(false, true) {
|
||||||
|
return SetVerdict(pkt, network.VerdictRerouteToNameserver)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RerouteToTunnel permanently reroutes the connection to the local tunnel entrypoint (and the current packet).
|
||||||
|
func (pkt *Packet) RerouteToTunnel() error {
|
||||||
|
if pkt.verdictSet.SetToIf(false, true) {
|
||||||
|
return SetVerdict(pkt, network.VerdictRerouteToTunnel)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
154
firewall/interception/windowskext2/service.go
Normal file
154
firewall/interception/windowskext2/service.go
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windowskext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vlabo/portmaster_windows_rust_kext/kext_interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createKextService(driverName string, driverPath string) (*kext_interface.KextService, error) {
|
||||||
|
return kext_interface.CreateKextService(driverName, driverPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// func deleteService(manager windows.Handle, service *KextService, driverName []uint16) error {
|
||||||
|
// // Stop and wait before deleting
|
||||||
|
// _ = service.stop(true)
|
||||||
|
|
||||||
|
// // Try to delete even if stop failed
|
||||||
|
// err := service.delete()
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("failed to delete old service: %s", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Wait until we can no longer open the old service.
|
||||||
|
// // Not very efficient but NotifyServiceStatusChange cannot be used with driver service.
|
||||||
|
// start := time.Now()
|
||||||
|
// timeLimit := time.Duration(30 * time.Second)
|
||||||
|
// for {
|
||||||
|
// handle, err := windows.OpenService(manager, &driverName[0], windows.SERVICE_ALL_ACCESS)
|
||||||
|
// if err != nil {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// _ = windows.CloseServiceHandle(handle)
|
||||||
|
|
||||||
|
// if time.Since(start) > timeLimit {
|
||||||
|
// return fmt.Errorf("time limit reached")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// time.Sleep(100 * time.Millisecond)
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (s *KextService) isValid() bool {
|
||||||
|
// return s != nil && s.handle != winInvalidHandleValue && s.handle != 0
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (s *KextService) isRunning() (bool, error) {
|
||||||
|
// if !s.isValid() {
|
||||||
|
// return false, fmt.Errorf("kext service not initialized")
|
||||||
|
// }
|
||||||
|
// var status windows.SERVICE_STATUS
|
||||||
|
// err := windows.QueryServiceStatus(s.handle, &status)
|
||||||
|
// if err != nil {
|
||||||
|
// return false, err
|
||||||
|
// }
|
||||||
|
// return status.CurrentState == windows.SERVICE_RUNNING, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func waitForServiceStatus(handle windows.Handle, neededStatus uint32, timeLimit time.Duration) (bool, error) {
|
||||||
|
// var status windows.SERVICE_STATUS
|
||||||
|
// status.CurrentState = windows.SERVICE_NO_CHANGE
|
||||||
|
// start := time.Now()
|
||||||
|
// for status.CurrentState == neededStatus {
|
||||||
|
// err := windows.QueryServiceStatus(handle, &status)
|
||||||
|
// if err != nil {
|
||||||
|
// return false, fmt.Errorf("failed while waiting for service to start: %w", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if time.Since(start) > timeLimit {
|
||||||
|
// return false, fmt.Errorf("time limit reached")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Sleep for 1/10 of the wait hint, recommended time from microsoft
|
||||||
|
// time.Sleep(time.Duration((status.WaitHint / 10)) * time.Millisecond)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return true, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (s *KextService) start(wait bool) error {
|
||||||
|
// if !s.isValid() {
|
||||||
|
// return fmt.Errorf("kext service not initialized")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Start the service:
|
||||||
|
// err := windows.StartService(s.handle, 0, nil)
|
||||||
|
|
||||||
|
// if err != nil {
|
||||||
|
// err = windows.GetLastError()
|
||||||
|
// if err != windows.ERROR_SERVICE_ALREADY_RUNNING {
|
||||||
|
// // Failed to start service; clean-up:
|
||||||
|
// var status windows.SERVICE_STATUS
|
||||||
|
// _ = windows.ControlService(s.handle, windows.SERVICE_CONTROL_STOP, &status)
|
||||||
|
// _ = windows.DeleteService(s.handle)
|
||||||
|
// _ = windows.CloseServiceHandle(s.handle)
|
||||||
|
// s.handle = winInvalidHandleValue
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Wait for service to start
|
||||||
|
// if wait {
|
||||||
|
// success, err := waitForServiceStatus(s.handle, windows.SERVICE_RUNNING, time.Duration(10*time.Second))
|
||||||
|
// if err != nil || !success {
|
||||||
|
// return fmt.Errorf("service did not start: %w", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (s *KextService) stop(wait bool) error {
|
||||||
|
// if !s.isValid() {
|
||||||
|
// return fmt.Errorf("kext service not initialized")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Stop the service
|
||||||
|
// var status windows.SERVICE_STATUS
|
||||||
|
// err := windows.ControlService(s.handle, windows.SERVICE_CONTROL_STOP, &status)
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("service failed to stop: %w", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Wait for service to stop
|
||||||
|
// if wait {
|
||||||
|
// success, err := waitForServiceStatus(s.handle, windows.SERVICE_STOPPED, time.Duration(10*time.Second))
|
||||||
|
// if err != nil || !success {
|
||||||
|
// return fmt.Errorf("service did not stop: %w", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (s *KextService) delete() error {
|
||||||
|
// if !s.isValid() {
|
||||||
|
// return fmt.Errorf("kext service not initialized")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// err := windows.DeleteService(s.handle)
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("failed to delete service: %s", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Service wont be deleted until all handles are closed.
|
||||||
|
// err = windows.CloseServiceHandle(s.handle)
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("failed to close service handle: %s", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// s.handle = winInvalidHandleValue
|
||||||
|
// return nil
|
||||||
|
// }
|
1
go.mod
1
go.mod
|
@ -39,6 +39,7 @@ require (
|
||||||
github.com/tevino/abool v1.2.0
|
github.com/tevino/abool v1.2.0
|
||||||
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
|
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
|
||||||
github.com/vincent-petithory/dataurl v1.0.0
|
github.com/vincent-petithory/dataurl v1.0.0
|
||||||
|
github.com/vlabo/portmaster_windows_rust_kext/kext_interface v0.0.0-20240120091731-1a3450b13959
|
||||||
golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e
|
golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e
|
||||||
golang.org/x/net v0.20.0
|
golang.org/x/net v0.20.0
|
||||||
golang.org/x/sync v0.6.0
|
golang.org/x/sync v0.6.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -275,6 +275,8 @@ github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OL
|
||||||
github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY=
|
github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY=
|
||||||
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
|
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
|
||||||
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||||
|
github.com/vlabo/portmaster_windows_rust_kext/kext_interface v0.0.0-20240120091731-1a3450b13959 h1:5j8cHx9n4drternoY4HXomea+4aYJuKMgnA3VhlG5WM=
|
||||||
|
github.com/vlabo/portmaster_windows_rust_kext/kext_interface v0.0.0-20240120091731-1a3450b13959/go.mod h1:PCv02zl4R2SbmEUDetMKO+kTfvMvsVVZuOzOXRMcHwE=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
|
|
@ -424,6 +424,7 @@ func NewIncompleteConnection(pkt packet.Packet) *Connection {
|
||||||
IPProtocol: info.Protocol,
|
IPProtocol: info.Protocol,
|
||||||
Started: info.SeenAt.Unix(),
|
Started: info.SeenAt.Unix(),
|
||||||
PID: info.PID,
|
PID: info.PID,
|
||||||
|
Inbound: info.Inbound,
|
||||||
dataComplete: abool.NewBool(false),
|
dataComplete: abool.NewBool(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue