{"record":{"id":"ed1baee2a32d13f0","repo":"OpenNHP/opennhp","slug":"s-relay-source-address","errorCode":null,"errorMessage":"%s relay source address","messagePattern":"(.+?) relay source address","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/server/msghandler.go","lineNumber":805,"sourceCode":"\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\",\n\t\trelayAddrStr, realAddr, len(innerBytes))\n\n\t// Allocate a pool packet for the inner bytes.\n\tinnerPkt := s.device.AllocatePoolPacket()\n\tif len(innerBytes) > len(innerPkt.Buf) {\n\t\ts.device.ReleasePoolPacket(innerPkt)\n\t\tlog.Warning(\"server-relay[HandleRelayForward] inner packet too large (%d bytes)\", len(innerBytes))\n\t\treturn fmt.Errorf(\"inner packet too large\")\n\t}\n\tcopy(innerPkt.Buf[:], innerBytes)\n\tinnerPkt.Content = innerPkt.Buf[:len(innerBytes)]\n\n\t// Run standard RecvPrecheck on the inner packet.","sourceCodeStart":787,"sourceCodeEnd":823,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/msghandler.go#L787-L823","documentation":"HandleRelayForward validates the claimed real client address carried inside a relayed packet. validateRelaySourceAddr returns a reason (e.g. nil IP, disallowed private/reserved source) and the handler wraps it into this error, rejecting the forward from the relay. It prevents relays from spoofing client source addresses.","triggerScenarios":"A relay forwards a packet whose rlyMsg.SourceAddr.Ip fails net.ParseIP, is a private/loopback/reserved address while s.allowPrivateRelaySource is false, or has an invalid port (0).","commonSituations":"Relay deployed behind NAT reporting a private client IP; misconfigured relay stamping empty or malformed source IPs; operator not setting allowPrivateRelaySource when relays run on a private network.","solutions":["Check the relay is stamping the real public client IP into SourceAddr.Ip before forwarding","If relays legitimately sit on private networks, enable the allowPrivateRelaySource option on the server","Verify the relay software version matches the server's expected relay message format","Inspect the warning log line 'rejecting %s from relay' for the exact reason string"],"exampleFix":"// before (relay on private net, rejected)\n// server: allowPrivateRelaySource = false\n// after\ncfg.AllowPrivateRelaySource.Store(true) // only if relays are trusted and on private links","handlingStrategy":"validation","validationCode":"ip := net.ParseIP(sourceAddrIp)\nif ip == nil || (ip.IsPrivate() && !allowPrivateRelaySource) {\n    return fmt.Errorf(\"relay source address %q will be rejected\", sourceAddrIp)\n}","typeGuard":"func isRelaySourceAllowed(ipStr string, allowPrivate bool) bool {\n    ip := net.ParseIP(ipStr)\n    if ip == nil { return false }\n    if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() { return allowPrivate }\n    return true\n}","tryCatchPattern":null,"preventionTips":["Run relays with correct public source-IP stamping","Enable allowPrivateRelaySource only for trusted private-network relays","Monitor 'rejecting %s from relay' warnings for misbehaving relays"],"tags":["relay","validation","udp","source-address"],"backgroundTag":"invalid-argument-value","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"}