wgctl/daemon/wgctl-conntrack/resolver/services.go
Nuno Duque Nunes d314ba376e feat: wgctl-conntrack Go daemon
- conntrack/event.go: TrafficEvent type
- conntrack/filter.go: WG subnet filter, IsExternal, ProtoName
- conntrack/subscriber.go: netlink conntrack DESTROY subscriber
- writer/log.go: JSON line writer with mutex
- resolver/peers.go: WG IP → peer name from conf files + endpoint index
- resolver/services.go: IP:port → service name from services.json
- config/config.go: reads wgctl.json, sensible defaults
- cmd/root.go: CLI flags
- main.go: wires everything together
- DESTROY events only: full byte/packet counts per connection
- filters to WireGuard subnet, marks external traffic
2026-05-28 02:51:27 +00:00

93 lines
No EOL
1.9 KiB
Go

package resolver
import (
"encoding/json"
"fmt"
"net"
"os"
"sync"
"time"
)
// ServiceResolver maps IP:port:proto to service names
type ServiceResolver struct {
mu sync.RWMutex
portToSvc map[string]string
servicesFile string
}
func NewServiceResolver(servicesFile string) *ServiceResolver {
r := &ServiceResolver{servicesFile: servicesFile, portToSvc: make(map[string]string)}
r.reload()
go r.watchReload()
return r
}
func (r *ServiceResolver) ServiceForDst(ip net.IP, port uint16, proto string) string {
r.mu.RLock()
defer r.mu.RUnlock()
// Try IP:port:proto first
if svc, ok := r.portToSvc[fmt.Sprintf("%s:%d:%s", ip.String(), port, proto)]; ok {
return svc
}
// Fall back to IP only
if svc, ok := r.portToSvc[ip.String()]; ok {
return svc
}
return ""
}
func (r *ServiceResolver) reload() {
data, err := os.ReadFile(r.servicesFile)
if err != nil {
return
}
var services map[string]interface{}
if json.Unmarshal(data, &services) != nil {
return
}
newMap := make(map[string]string)
for name, svcRaw := range services {
svc, ok := svcRaw.(map[string]interface{})
if !ok {
continue
}
hosts := map[string]bool{}
if hostsRaw, ok := svc["hosts"].(map[string]interface{}); ok {
for ip := range hostsRaw {
hosts[ip] = true
newMap[ip] = name
}
}
if portsRaw, ok := svc["ports"].([]interface{}); ok {
for _, portRaw := range portsRaw {
port, ok := portRaw.(map[string]interface{})
if !ok {
continue
}
portNum := fmt.Sprintf("%.0f", port["port"])
proto, _ := port["proto"].(string)
for ip := range hosts {
newMap[fmt.Sprintf("%s:%s:%s", ip, portNum, proto)] = name
}
}
}
}
r.mu.Lock()
r.portToSvc = newMap
r.mu.Unlock()
}
func (r *ServiceResolver) watchReload() {
ticker := time.NewTicker(60 * time.Second)
defer ticker.Stop()
for range ticker.C {
r.reload()
}
}