chore: release v0.26.0

This commit is contained in:
2026-08-20 17:53:55 +02:00
parent 47992b5f03
commit 1e507225dd
44 changed files with 3197 additions and 240 deletions
+92
View File
@@ -2,6 +2,7 @@ package udp
import (
"fmt"
"net"
"strings"
"hamlog/internal/applog"
@@ -185,3 +186,94 @@ func (m *Manager) sendTo(c Config, payload []byte) {
applog.Printf("udp: [%s] sent %d bytes to %s (%s)%s",
c.Name, len(payload), dst, c.ServiceType, sentPreview(c, payload))
}
// RelayInbound re-sends one received datagram, unchanged, to every configured
// relay destination.
//
// Called with the RAW bytes as they arrived — before parsing, and whatever the
// parse made of them. A packet this build cannot decode is still a packet the
// application downstream may understand perfectly well, and a relay that only
// forwards what it understood is a relay that silently drops the fields it has
// not learned about yet.
//
// Loop guard: a destination that is one of our OWN inbound ports on a local
// address would come straight back in and be relayed again, for ever, at line
// rate. Such a row is skipped and said so once — quietly dropping it would look
// like a relay that does not work.
func (m *Manager) RelayInbound(pkt []byte, fromPort int) {
if len(pkt) == 0 {
return
}
rows := m.Outbound(ServiceWSJTRelay)
if len(rows) == 0 {
return
}
m.mu.Lock()
ports := make(map[int]struct{}, len(m.inbound))
for _, s := range m.inbound {
ports[s.cfg.Port] = struct{}{}
}
m.mu.Unlock()
for _, c := range rows {
if isLoopback(c.DestinationIP) {
if _, mine := ports[c.Port]; mine {
m.relayLoopOnce(c)
continue
}
}
// Deliberately not sendTo: that logs a line per datagram, and this runs
// on every decode of every period — it would bury the log within minutes.
host := strings.TrimSpace(c.DestinationIP)
if host == "" {
host = "127.0.0.1"
}
dst := fmt.Sprintf("%s:%d", host, c.Port)
if err := SendUDP(dst, pkt); err != nil {
m.relayErrOnce(c, dst, err)
}
}
_ = fromPort
}
// isLoopback reports whether a destination names this machine. Empty counts:
// sendTo defaults it to 127.0.0.1.
func isLoopback(host string) bool {
h := strings.TrimSpace(host)
if h == "" || h == "localhost" {
return true
}
ip := net.ParseIP(h)
return ip != nil && ip.IsLoopback()
}
// relayLoopOnce and relayErrOnce keep a repeating relay complaint to one line a
// session. Both fire per datagram otherwise, which on a busy band is hundreds a
// minute and makes the log useless for anything else.
func (m *Manager) relayLoopOnce(c Config) {
m.relayWarnMu.Lock()
defer m.relayWarnMu.Unlock()
if m.relayWarned == nil {
m.relayWarned = map[int64]bool{}
}
if m.relayWarned[c.ID] {
return
}
m.relayWarned[c.ID] = true
applog.Printf("udp: [%s] relay target %s:%d is one of OpsLog's OWN listening ports — "+
"that would feed the stream back into itself for ever. Nothing is relayed on this row; "+
"point it at the other application's port.", c.Name, c.DestinationIP, c.Port)
}
func (m *Manager) relayErrOnce(c Config, dst string, err error) {
m.relayWarnMu.Lock()
defer m.relayWarnMu.Unlock()
if m.relayWarned == nil {
m.relayWarned = map[int64]bool{}
}
if m.relayWarned[-c.ID-1] {
return
}
m.relayWarned[-c.ID-1] = true
applog.Printf("udp: [%s] relay to %s failed: %v (said once)", c.Name, dst, err)
}