safing-portmaster/windows_kext/kextinterface/info.go
2024-06-28 13:20:18 +03:00

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
}