{"record":{"id":"649dfe1a4d43bcdd","repo":"OpenNHP/opennhp","slug":"missing-source-address","errorCode":null,"errorMessage":"missing source address","messagePattern":"missing source address","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/server/msghandler.go","lineNumber":792,"sourceCode":"// Noise pipeline.  The relay's identity has already been validated by\n// validatePeer as part of the standard decryption flow.\n//\n// The message body is a JSON-encoded RelayForwardMsg containing:\n//   - SourceAddr:  the real client's IP/port\n//   - InnerPacket: base64-encoded inner NHP packet (encrypted by agent)\n//\n// The inner packet is injected into the standard pipeline as if the agent\n// had connected directly.\nfunc (s *UdpServer) HandleRelayForward(ppd *core.PacketParserData) error {\n\tvar rlyMsg common.RelayForwardMsg\n\tif err := json.Unmarshal(ppd.BodyMessage, &rlyMsg); err != nil {\n\t\tlog.Error(\"server-relay[HandleRelayForward] failed to parse RelayForwardMsg: %v\", err)\n\t\treturn err\n\t}\n\n\tif rlyMsg.SourceAddr == nil {\n\t\tlog.Error(\"server-relay[HandleRelayForward] missing source address\")\n\t\treturn fmt.Errorf(\"missing source address\")\n\t}\n\n\tinnerBytes, err := base64.StdEncoding.DecodeString(rlyMsg.InnerPacket)\n\tif err != nil {\n\t\tlog.Error(\"server-relay[HandleRelayForward] failed to decode inner packet: %v\", err)\n\t\treturn err\n\t}\n\n\trealIP := net.ParseIP(rlyMsg.SourceAddr.Ip)\n\tif reason := validateRelaySourceAddr(realIP, rlyMsg.SourceAddr.Port, s.allowPrivateRelaySource.Load()); reason != \"\" {\n\t\tlog.Warning(\"server-relay[HandleRelayForward] rejecting %s from relay %s: %s:%d\",\n\t\t\treason, ppd.ConnData.RemoteAddr.String(), rlyMsg.SourceAddr.Ip, rlyMsg.SourceAddr.Port)\n\t\treturn fmt.Errorf(\"%s relay source address\", reason)\n\t}\n\trealAddr := &net.UDPAddr{IP: realIP, Port: rlyMsg.SourceAddr.Port}\n\n\trelayAddrStr := ppd.ConnData.RemoteAddr.String()\n\tlog.Info(\"server-relay[HandleRelayForward] from relay %s, real client %s, inner %d bytes\",","sourceCodeStart":774,"sourceCodeEnd":810,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/msghandler.go#L774-L810","documentation":"HandleRelayForward rejected a relayed NHP_RLY message because its JSON RelayForwardMsg carried no SourceAddr. Without the real client's IP/port the server cannot attribute or validate the forwarded inner packet, so it drops the message with \"missing source address\".","triggerScenarios":"A relay forwards a JSON body whose `sourceAddr` field is null/omitted — either the relay's forwarder code didn't populate it, the agent's original packet lacked it, or a field-name mismatch (wrong JSON tag) caused unmarshal to leave the pointer nil.","commonSituations":"Mismatched relay/server versions where the relay builds RelayForwardMsg with an older field name; a misbehaving or malicious relay sending empty forward frames; agents talking through a relay that lost the original connection metadata.","solutions":["Update the relay to a version that always populates RelayForwardMsg.SourceAddr with the real client's IP/port.","Confirm the JSON field name/tag matches between relay and server (go vet / compare common.RelayForwardMsg on both sides).","Check that the agent-to-relay path preserves the original source address metadata.","If a hostile relay is suspected, verify relay identity/allow-lists and drop traffic from it."],"exampleFix":"// before: relay omits source address\nfwd := common.RelayForwardMsg{InnerPacket: b64}\n// after: relay always sets the real client address\nfwd := common.RelayForwardMsg{SourceAddr: &common.SourceAddr{Ip: clientIP, Port: clientPort}, InnerPacket: b64}","handlingStrategy":"type-guard","validationCode":"var fwd common.RelayForwardMsg\nif err := json.Unmarshal(body, &fwd); err != nil { return err }\nif fwd.SourceAddr == nil || net.ParseIP(fwd.SourceAddr.Ip) == nil || fwd.SourceAddr.Port <= 0 {\n    return fmt.Errorf(\"relay forward missing/invalid source address\")\n}","typeGuard":"func hasValidSourceAddr(m *common.RelayForwardMsg) bool {\n    return m != nil && m.SourceAddr != nil &&\n        net.ParseIP(m.SourceAddr.Ip) != nil &&\n        m.SourceAddr.Port > 0 && m.SourceAddr.Port < 65536\n}","tryCatchPattern":"if err := server.HandleRelayForward(ppd); err != nil && err.Error() == \"missing source address\" {\n    log.Warn(\"relay %s sent forward without SourceAddr — check relay version\", ppd.ConnData.RemoteAddr)\n    // drop packet; optionally alert on the relay\n}","preventionTips":["Keep relay and server builds version-matched so RelayForwardMsg field tags agree.","Unit-test the relay forwarder with a fixture containing SourceAddr.","Alert/log when a relay repeatedly sends forwards lacking SourceAddr — indicates a broken or hostile relay."],"tags":["relay","network","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}