88#include < unistd.h>
99
1010#include < algorithm>
11+ #include < cctype>
1112#include < chrono>
1213#include < cstring>
1314#include < fstream>
@@ -40,7 +41,9 @@ static uint64_t stableServerId() {
4041 std::getline (f, id);
4142
4243 // Remove whitespace/newlines just in case
43- id.erase (std::remove_if (id.begin (), id.end (), ::isspace), id.end ());
44+ id.erase (std::remove_if (id.begin (), id.end (),
45+ [](unsigned char c) { return std::isspace (c) != 0 ; }),
46+ id.end ());
4447
4548 if (!id.empty ()) {
4649 cached = fnv1a64 (reinterpret_cast <const uint8_t *>(id.data ()), id.size ());
@@ -54,6 +57,30 @@ static uint64_t stableServerId() {
5457 return cached;
5558}
5659
60+ static uint64_t bswap64_u (uint64_t v) {
61+ return ((v & 0x00000000000000FFull ) << 56 ) |
62+ ((v & 0x000000000000FF00ull ) << 40 ) |
63+ ((v & 0x0000000000FF0000ull ) << 24 ) |
64+ ((v & 0x00000000FF000000ull ) << 8 ) |
65+ ((v & 0x000000FF00000000ull ) >> 8 ) |
66+ ((v & 0x0000FF0000000000ull ) >> 24 ) |
67+ ((v & 0x00FF000000000000ull ) >> 40 ) |
68+ ((v & 0xFF00000000000000ull ) >> 56 );
69+ }
70+
71+ static uint64_t htonll_u (uint64_t v) {
72+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
73+ return bswap64_u (v);
74+ #else
75+ return v;
76+ #endif
77+ }
78+
79+ static std::string makeLegacyReply (const ServerStatus& st) {
80+ return " CONTROLLER_AVAILABLE;udp=" + std::to_string (st.control_port ) +
81+ " ;tcp=" + std::to_string (st.feedback_port );
82+ }
83+
5784// -------------------- DiscoveryService --------------------
5885
5986DiscoveryService::DiscoveryService (uint16_t port, StatusFn status_fn)
@@ -113,6 +140,8 @@ void DiscoveryService::stop() {
113140
114141void DiscoveryService::runLoop () {
115142 std::vector<uint8_t > buf (1024 );
143+ constexpr char kLegacyQuery [] = " WHO_IS_CONTROLLER" ;
144+ constexpr size_t kLegacyQueryLen = sizeof (kLegacyQuery ) - 1 ;
116145
117146 // Simple per-IP rate limit: respond at most once per 150ms per source IP
118147 using Clock = std::chrono::steady_clock;
@@ -131,28 +160,46 @@ void DiscoveryService::runLoop() {
131160 continue ;
132161 }
133162
134- // Strict minimum size check
135- if ((size_t )n < sizeof (DiscoverReqV1)) continue ;
136-
137- DiscoverReqV1 req{};
138- std::memcpy (&req, buf.data (), sizeof (req));
139-
140- // Validate request
141- if (req.magic != kDiscoveryMagic ) continue ;
142- if (req.version != kDiscoveryVersion ) continue ;
143- if (req.msg_type != (uint8_t )DiscMsgType::DiscoverReq) continue ;
144-
145163 // Rate limit by source IPv4
146164 uint32_t ipKey = src.sin_addr .s_addr ; // network order is fine as a key
147165 auto now = Clock::now ();
148166 auto it = lastReply.find (ipKey);
149167 if (it != lastReply.end () && (now - it->second ) < minInterval) {
150168 continue ;
151169 }
152- lastReply[ipKey] = now;
153170
154171 // Build response from status provider
155172 ServerStatus st = status_fn_ ? status_fn_ () : ServerStatus{};
173+ lastReply[ipKey] = now;
174+
175+ // Backward compatibility: old text-based discovery request.
176+ if ((size_t )n == kLegacyQueryLen &&
177+ std::memcmp (buf.data (), kLegacyQuery , kLegacyQueryLen ) == 0 ) {
178+ const std::string reply = makeLegacyReply (st);
179+ ::sendto (sock_, reply.data(), reply.size(), 0, (sockaddr*)&src, sizeof(src));
180+ continue ;
181+ }
182+
183+ // Strict minimum size check for binary discovery request.
184+ if ((size_t )n < sizeof (DiscoverReqV1)) continue ;
185+
186+ DiscoverReqV1 req{};
187+ std::memcpy (&req, buf.data (), sizeof (req));
188+
189+ // Accept both host-order (legacy implementation behavior) and network-order
190+ // client packets to avoid silent interoperability failures.
191+ bool request_network_order = false ;
192+ uint32_t req_nonce = req.nonce ;
193+ if (req.magic == kDiscoveryMagic ) {
194+ request_network_order = false ;
195+ } else if (ntohl (req.magic ) == kDiscoveryMagic ) {
196+ request_network_order = true ;
197+ req_nonce = ntohl (req.nonce );
198+ } else {
199+ continue ;
200+ }
201+ if (req.version != kDiscoveryVersion ) continue ;
202+ if (req.msg_type != (uint8_t )DiscMsgType::DiscoverReq) continue ;
156203
157204 // Stable ID unless caller explicitly overrides
158205 if (st.server_id == 0 ) st.server_id = stableServerId ();
@@ -170,14 +217,26 @@ void DiscoveryService::runLoop() {
170217 resp.version = kDiscoveryVersion ;
171218 resp.msg_type = (uint8_t )DiscMsgType::DiscoverResp;
172219 resp.reserved = 0 ;
173- resp.nonce = req. nonce ;
220+ resp.nonce = req_nonce ;
174221 resp.server_id = st.server_id ;
175222 resp.control_port = st.control_port ;
176223 resp.feedback_port = st.feedback_port ;
177224 resp.proto_ver = st.controller_proto_ver ;
178225 resp.name_len = (uint16_t )name.size ();
179226 resp.flags = flags;
180227
228+ if (request_network_order) {
229+ resp.magic = htonl (resp.magic );
230+ resp.reserved = htons (resp.reserved );
231+ resp.nonce = htonl (resp.nonce );
232+ resp.server_id = htonll_u (resp.server_id );
233+ resp.control_port = htons (resp.control_port );
234+ resp.feedback_port = htons (resp.feedback_port );
235+ resp.proto_ver = htons (resp.proto_ver );
236+ resp.name_len = htons (resp.name_len );
237+ resp.flags = htonl (resp.flags );
238+ }
239+
181240 std::vector<uint8_t > out (sizeof (DiscoverRespV1) + name.size ());
182241 std::memcpy (out.data (), &resp, sizeof (resp));
183242 if (!name.empty ()) {
@@ -189,4 +248,4 @@ void DiscoveryService::runLoop() {
189248 }
190249}
191250
192- } // namespace lj
251+ } // namespace lj
0 commit comments