mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-27 21:36:21 +02:00
221 lines
5.1 KiB
Go
221 lines
5.1 KiB
Go
//go:build shield_native
|
|
|
|
package engines
|
|
|
|
/*
|
|
#cgo LDFLAGS: -L${SRCDIR}/../../../../shield/build -lshield -lstdc++ -lm -lpthread
|
|
#cgo CFLAGS: -I${SRCDIR}/../../../../shield/include
|
|
|
|
#include <stdlib.h>
|
|
|
|
// Shield C FFI exports
|
|
extern int shield_init(void);
|
|
extern char* shield_inspect(const char* payload, int payload_len);
|
|
extern char* shield_status(void);
|
|
extern int shield_shutdown(void);
|
|
extern void shield_free(char* ptr);
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// NativeShield wraps the C Shield engine via CGo FFI.
|
|
// Build tag: shield_native
|
|
type NativeShield struct {
|
|
mu sync.RWMutex
|
|
initialized bool
|
|
version string
|
|
lastCheck time.Time
|
|
blocked []BlockedIP // In-memory block list
|
|
}
|
|
|
|
// NewNativeShield creates the FFI bridge to the C Shield engine.
|
|
func NewNativeShield() (*NativeShield, error) {
|
|
result := C.shield_init()
|
|
if result != 0 {
|
|
return nil, fmt.Errorf("shield_init failed with code %d", int(result))
|
|
}
|
|
|
|
// Get version from status
|
|
version := "0.1.0"
|
|
cStatus := C.shield_status()
|
|
if cStatus != nil {
|
|
var statusObj struct {
|
|
Version string `json:"version"`
|
|
}
|
|
if err := json.Unmarshal([]byte(C.GoString(cStatus)), &statusObj); err == nil && statusObj.Version != "" {
|
|
version = statusObj.Version
|
|
}
|
|
C.shield_free(cStatus)
|
|
}
|
|
|
|
return &NativeShield{
|
|
initialized: true,
|
|
version: version,
|
|
lastCheck: time.Now(),
|
|
blocked: make([]BlockedIP, 0),
|
|
}, nil
|
|
}
|
|
|
|
// shieldInspectResult matches the JSON returned by shield_inspect().
|
|
type shieldInspectResult struct {
|
|
Blocked bool `json:"blocked"`
|
|
Reason string `json:"reason"`
|
|
Confidence float64 `json:"confidence"`
|
|
InspectCount string `json:"inspect_count,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// inspect sends payload through the C Shield inspection pipeline.
|
|
func (n *NativeShield) inspect(payload []byte) shieldInspectResult {
|
|
n.mu.RLock()
|
|
defer n.mu.RUnlock()
|
|
|
|
if !n.initialized {
|
|
return shieldInspectResult{Error: "engine not initialized"}
|
|
}
|
|
|
|
cPayload := C.CBytes(payload)
|
|
defer C.free(cPayload)
|
|
|
|
cResult := C.shield_inspect((*C.char)(cPayload), C.int(len(payload)))
|
|
if cResult == nil {
|
|
return shieldInspectResult{Error: "shield_inspect returned null"}
|
|
}
|
|
defer C.shield_free(cResult)
|
|
|
|
jsonStr := C.GoString(cResult)
|
|
var result shieldInspectResult
|
|
if err := json.Unmarshal([]byte(jsonStr), &result); err != nil {
|
|
return shieldInspectResult{Error: fmt.Sprintf("json parse error: %v", err)}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// InspectTraffic analyzes network traffic / payload for threats.
|
|
func (n *NativeShield) InspectTraffic(_ context.Context, payload []byte, metadata map[string]string) (*ScanResult, error) {
|
|
start := time.Now()
|
|
res := n.inspect(payload)
|
|
|
|
if res.Error != "" {
|
|
return nil, fmt.Errorf("shield: %s", res.Error)
|
|
}
|
|
|
|
severity := "NONE"
|
|
threatType := ""
|
|
if res.Blocked {
|
|
severity = "CRITICAL"
|
|
threatType = "network_threat"
|
|
}
|
|
|
|
details := res.Reason
|
|
if src, ok := metadata["source_ip"]; ok {
|
|
details += fmt.Sprintf(" (from %s)", src)
|
|
}
|
|
|
|
return &ScanResult{
|
|
Engine: "shield",
|
|
ThreatFound: res.Blocked,
|
|
ThreatType: threatType,
|
|
Severity: severity,
|
|
Confidence: res.Confidence,
|
|
Details: details,
|
|
Duration: time.Since(start),
|
|
Timestamp: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
// BlockIP adds an IP to the in-memory block list.
|
|
func (n *NativeShield) BlockIP(_ context.Context, ip string, reason string, duration time.Duration) error {
|
|
n.mu.Lock()
|
|
defer n.mu.Unlock()
|
|
|
|
n.blocked = append(n.blocked, BlockedIP{
|
|
IP: ip,
|
|
Reason: reason,
|
|
BlockedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(duration),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// ListBlocked returns currently blocked IPs (filters expired).
|
|
func (n *NativeShield) ListBlocked(_ context.Context) ([]BlockedIP, error) {
|
|
n.mu.RLock()
|
|
defer n.mu.RUnlock()
|
|
|
|
now := time.Now()
|
|
active := make([]BlockedIP, 0, len(n.blocked))
|
|
for _, b := range n.blocked {
|
|
if b.ExpiresAt.After(now) {
|
|
active = append(active, b)
|
|
}
|
|
}
|
|
return active, nil
|
|
}
|
|
|
|
// Status returns the engine health via FFI.
|
|
func (n *NativeShield) Status() EngineStatus {
|
|
n.mu.RLock()
|
|
defer n.mu.RUnlock()
|
|
|
|
if !n.initialized {
|
|
return EngineOffline
|
|
}
|
|
|
|
cStatus := C.shield_status()
|
|
if cStatus == nil {
|
|
return EngineDegraded
|
|
}
|
|
defer C.shield_free(cStatus)
|
|
|
|
var statusObj struct {
|
|
Status string `json:"status"`
|
|
}
|
|
if err := json.Unmarshal([]byte(C.GoString(cStatus)), &statusObj); err != nil {
|
|
return EngineDegraded
|
|
}
|
|
|
|
switch statusObj.Status {
|
|
case "HEALTHY":
|
|
return EngineHealthy
|
|
case "OFFLINE":
|
|
return EngineOffline
|
|
default:
|
|
return EngineDegraded
|
|
}
|
|
}
|
|
|
|
// Name returns the engine identifier.
|
|
func (n *NativeShield) Name() string {
|
|
return "shield"
|
|
}
|
|
|
|
// Version returns the native library version.
|
|
func (n *NativeShield) Version() string {
|
|
return n.version
|
|
}
|
|
|
|
// Shutdown gracefully closes the FFI bridge.
|
|
func (n *NativeShield) Shutdown() error {
|
|
n.mu.Lock()
|
|
defer n.mu.Unlock()
|
|
|
|
if !n.initialized {
|
|
return nil
|
|
}
|
|
|
|
result := C.shield_shutdown()
|
|
n.initialized = false
|
|
if result != 0 {
|
|
return fmt.Errorf("shield_shutdown failed with code %d", int(result))
|
|
}
|
|
return nil
|
|
}
|