mirror of
https://github.com/safing/portmaster
synced 2025-04-07 20:49:10 +00:00
383 lines
8.7 KiB
Go
383 lines
8.7 KiB
Go
package kextinterface
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
InfoLogLine = 0
|
|
InfoConnectionIpv4 = 1
|
|
InfoConnectionIpv6 = 2
|
|
InfoConnectionEndEventV4 = 3
|
|
InfoConnectionEndEventV6 = 4
|
|
InfoBandwidthStatsV4 = 5
|
|
InfoBandwidthStatsV6 = 6
|
|
)
|
|
|
|
var (
|
|
ErrUnknownInfoType = errors.New("unknown info type")
|
|
ErrUnexpectedInfoSize = errors.New("unexpected info size")
|
|
ErrUnexpectedReadError = errors.New("unexpected read error")
|
|
)
|
|
|
|
type connectionV4Internal struct {
|
|
ID uint64
|
|
ProcessID uint64
|
|
Direction byte
|
|
Protocol byte
|
|
LocalIP [4]byte
|
|
RemoteIP [4]byte
|
|
LocalPort uint16
|
|
RemotePort uint16
|
|
PayloadLayer uint8
|
|
}
|
|
|
|
type ConnectionV4 struct {
|
|
connectionV4Internal
|
|
Payload []byte
|
|
}
|
|
|
|
func (c *ConnectionV4) Compare(other *ConnectionV4) bool {
|
|
return c.ID == other.ID &&
|
|
c.ProcessID == other.ProcessID &&
|
|
c.Direction == other.Direction &&
|
|
c.Protocol == other.Protocol &&
|
|
c.LocalIP == other.LocalIP &&
|
|
c.RemoteIP == other.RemoteIP &&
|
|
c.LocalPort == other.LocalPort &&
|
|
c.RemotePort == other.RemotePort
|
|
}
|
|
|
|
type connectionV6Internal struct {
|
|
ID uint64
|
|
ProcessID uint64
|
|
Direction byte
|
|
Protocol byte
|
|
LocalIP [16]byte
|
|
RemoteIP [16]byte
|
|
LocalPort uint16
|
|
RemotePort uint16
|
|
PayloadLayer uint8
|
|
}
|
|
|
|
type ConnectionV6 struct {
|
|
connectionV6Internal
|
|
Payload []byte
|
|
}
|
|
|
|
func (c ConnectionV6) Compare(other *ConnectionV6) bool {
|
|
return c.ID == other.ID &&
|
|
c.ProcessID == other.ProcessID &&
|
|
c.Direction == other.Direction &&
|
|
c.Protocol == other.Protocol &&
|
|
c.LocalIP == other.LocalIP &&
|
|
c.RemoteIP == other.RemoteIP &&
|
|
c.LocalPort == other.LocalPort &&
|
|
c.RemotePort == other.RemotePort
|
|
}
|
|
|
|
type ConnectionEndV4 struct {
|
|
ProcessID uint64
|
|
Direction byte
|
|
Protocol byte
|
|
LocalIP [4]byte
|
|
RemoteIP [4]byte
|
|
LocalPort uint16
|
|
RemotePort uint16
|
|
}
|
|
|
|
type ConnectionEndV6 struct {
|
|
ProcessID uint64
|
|
Direction byte
|
|
Protocol byte
|
|
LocalIP [16]byte
|
|
RemoteIP [16]byte
|
|
LocalPort uint16
|
|
RemotePort uint16
|
|
}
|
|
|
|
type LogLine struct {
|
|
Severity byte
|
|
Line string
|
|
}
|
|
|
|
type BandwidthValueV4 struct {
|
|
LocalIP [4]byte
|
|
LocalPort uint16
|
|
RemoteIP [4]byte
|
|
RemotePort uint16
|
|
TransmittedBytes uint64
|
|
ReceivedBytes uint64
|
|
}
|
|
|
|
type BandwidthValueV6 struct {
|
|
LocalIP [16]byte
|
|
LocalPort uint16
|
|
RemoteIP [16]byte
|
|
RemotePort uint16
|
|
TransmittedBytes uint64
|
|
ReceivedBytes uint64
|
|
}
|
|
|
|
type BandwidthStatsArray struct {
|
|
Protocol uint8
|
|
ValuesV4 []BandwidthValueV4
|
|
ValuesV6 []BandwidthValueV6
|
|
}
|
|
|
|
type Info struct {
|
|
ConnectionV4 *ConnectionV4
|
|
ConnectionV6 *ConnectionV6
|
|
ConnectionEndV4 *ConnectionEndV4
|
|
ConnectionEndV6 *ConnectionEndV6
|
|
LogLine *LogLine
|
|
BandwidthStats *BandwidthStatsArray
|
|
}
|
|
|
|
type readHelper struct {
|
|
infoType byte
|
|
commandSize uint32
|
|
|
|
readSize int
|
|
|
|
reader io.Reader
|
|
}
|
|
|
|
func newReadHelper(reader io.Reader) (*readHelper, error) {
|
|
helper := &readHelper{reader: reader}
|
|
|
|
err := binary.Read(reader, binary.LittleEndian, &helper.infoType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = binary.Read(reader, binary.LittleEndian, &helper.commandSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return helper, nil
|
|
}
|
|
|
|
func (r *readHelper) ReadData(data any) error {
|
|
err := binary.Read(r, binary.LittleEndian, data)
|
|
if err != nil {
|
|
return errors.Join(ErrUnexpectedReadError, err)
|
|
}
|
|
|
|
if err := r.checkOverRead(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Passing size = 0 will read the rest of the command.
|
|
func (r *readHelper) ReadBytes(size uint32) ([]byte, error) {
|
|
if uint32(r.readSize) >= r.commandSize {
|
|
return nil, errors.Join(fmt.Errorf("cannot read more bytes than the command size: %d >= %d", r.readSize, r.commandSize), ErrUnexpectedReadError)
|
|
}
|
|
|
|
if size == 0 {
|
|
size = r.commandSize - uint32(r.readSize)
|
|
}
|
|
|
|
if r.commandSize < uint32(r.readSize)+size {
|
|
return nil, ErrUnexpectedInfoSize
|
|
}
|
|
|
|
bytes := make([]byte, size)
|
|
err := binary.Read(r, binary.LittleEndian, bytes)
|
|
if err != nil {
|
|
return nil, errors.Join(ErrUnexpectedReadError, err)
|
|
}
|
|
|
|
return bytes, nil
|
|
}
|
|
|
|
func (r *readHelper) ReadUntilTheEnd() {
|
|
_, _ = r.ReadBytes(0)
|
|
}
|
|
|
|
func (r *readHelper) checkOverRead() error {
|
|
if uint32(r.readSize) > r.commandSize {
|
|
return ErrUnexpectedInfoSize
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *readHelper) Read(p []byte) (n int, err error) {
|
|
n, err = r.reader.Read(p)
|
|
r.readSize += n
|
|
return
|
|
}
|
|
|
|
func RecvInfo(reader io.Reader) (*Info, error) {
|
|
helper, err := newReadHelper(reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Make sure the whole command is read before return.
|
|
defer helper.ReadUntilTheEnd()
|
|
|
|
// Read data
|
|
switch helper.infoType {
|
|
case InfoConnectionIpv4:
|
|
{
|
|
parseError := fmt.Errorf("failed to parse InfoConnectionIpv4")
|
|
newInfo := ConnectionV4{}
|
|
var fixedSizeValues connectionV4Internal
|
|
// Read fixed size values.
|
|
err = helper.ReadData(&fixedSizeValues)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err, fmt.Errorf("fixed"))
|
|
}
|
|
newInfo.connectionV4Internal = fixedSizeValues
|
|
// Read size of payload.
|
|
var payloadSize uint32
|
|
err = helper.ReadData(&payloadSize)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err, fmt.Errorf("payloadsize"))
|
|
}
|
|
|
|
// Check if there is payload.
|
|
if payloadSize > 0 {
|
|
// Read payload.
|
|
newInfo.Payload, err = helper.ReadBytes(payloadSize)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err, fmt.Errorf("payload"))
|
|
}
|
|
}
|
|
return &Info{ConnectionV4: &newInfo}, nil
|
|
}
|
|
case InfoConnectionIpv6:
|
|
{
|
|
parseError := fmt.Errorf("failed to parse InfoConnectionIpv6")
|
|
newInfo := ConnectionV6{}
|
|
|
|
// Read fixed size values.
|
|
err = helper.ReadData(&newInfo.connectionV6Internal)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
|
|
// Read size of payload.
|
|
var payloadSize uint32
|
|
err = helper.ReadData(&payloadSize)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
|
|
// Check if there is payload.
|
|
if payloadSize > 0 {
|
|
// Read payload.
|
|
newInfo.Payload, err = helper.ReadBytes(payloadSize)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
}
|
|
|
|
return &Info{ConnectionV6: &newInfo}, nil
|
|
}
|
|
case InfoConnectionEndEventV4:
|
|
{
|
|
parseError := fmt.Errorf("failed to parse InfoConnectionEndEventV4")
|
|
var connectionEnd ConnectionEndV4
|
|
|
|
// Read fixed size values.
|
|
err = helper.ReadData(&connectionEnd)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
return &Info{ConnectionEndV4: &connectionEnd}, nil
|
|
}
|
|
case InfoConnectionEndEventV6:
|
|
{
|
|
parseError := fmt.Errorf("failed to parse InfoConnectionEndEventV6")
|
|
var connectionEnd ConnectionEndV6
|
|
|
|
// Read fixed size values.
|
|
err = helper.ReadData(&connectionEnd)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
return &Info{ConnectionEndV6: &connectionEnd}, nil
|
|
}
|
|
case InfoLogLine:
|
|
{
|
|
parseError := fmt.Errorf("failed to parse InfoLogLine")
|
|
logLine := LogLine{}
|
|
// Read severity
|
|
err = helper.ReadData(&logLine.Severity)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
// Read string
|
|
bytes, err := helper.ReadBytes(0)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
logLine.Line = string(bytes)
|
|
return &Info{LogLine: &logLine}, nil
|
|
}
|
|
case InfoBandwidthStatsV4:
|
|
{
|
|
parseError := fmt.Errorf("failed to parse InfoBandwidthStatsV4")
|
|
// Read Protocol
|
|
var protocol uint8
|
|
err = helper.ReadData(&protocol)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
// Read size of array
|
|
var size uint32
|
|
err = helper.ReadData(&size)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
// Read array
|
|
statsArray := make([]BandwidthValueV4, size)
|
|
for i := range int(size) {
|
|
err = helper.ReadData(&statsArray[i])
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
}
|
|
|
|
return &Info{BandwidthStats: &BandwidthStatsArray{Protocol: protocol, ValuesV4: statsArray}}, nil
|
|
}
|
|
case InfoBandwidthStatsV6:
|
|
{
|
|
parseError := fmt.Errorf("failed to parse InfoBandwidthStatsV6")
|
|
// Read Protocol
|
|
var protocol uint8
|
|
err = helper.ReadData(&protocol)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
// Read size of array
|
|
var size uint32
|
|
err = helper.ReadData(&size)
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
// Read array
|
|
statsArray := make([]BandwidthValueV6, size)
|
|
for i := range int(size) {
|
|
err = helper.ReadData(&statsArray[i])
|
|
if err != nil {
|
|
return nil, errors.Join(parseError, err)
|
|
}
|
|
}
|
|
|
|
return &Info{BandwidthStats: &BandwidthStatsArray{Protocol: protocol, ValuesV6: statsArray}}, nil
|
|
}
|
|
}
|
|
|
|
return nil, ErrUnknownInfoType
|
|
}
|