1#include "register.h"
2
3#include <base/lock.h>
4#include <base/log.h>
5#include <base/time.h>
6
7#include <engine/console.h>
8#include <engine/engine.h>
9#include <engine/shared/config.h>
10#include <engine/shared/http.h>
11#include <engine/shared/json.h>
12#include <engine/shared/masterserver.h>
13#include <engine/shared/network.h>
14#include <engine/shared/packer.h>
15#include <engine/shared/uuid_manager.h>
16
17class CRegister : public IRegister
18{
19 enum
20 {
21 STATUS_NONE = 0,
22 STATUS_OK,
23 STATUS_NEEDCHALLENGE,
24 STATUS_NEEDINFO,
25 STATUS_ERROR,
26 };
27
28 enum
29 {
30 PROTOCOL_TW6_IPV6 = 0,
31 PROTOCOL_TW6_IPV4,
32 PROTOCOL_TW7_IPV6,
33 PROTOCOL_TW7_IPV4,
34 NUM_PROTOCOLS,
35 };
36
37 static bool StatusFromString(int *pResult, const char *pString);
38 static const char *ProtocolToScheme(int Protocol);
39 static const char *ProtocolToString(int Protocol);
40 static bool ProtocolFromString(int *pResult, const char *pString);
41 static const char *ProtocolToSystem(int Protocol);
42 static IPRESOLVE ProtocolToIpresolve(int Protocol);
43
44 static void ConchainOnConfigChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
45
46 class CGlobal
47 {
48 public:
49 CLock m_Lock;
50 int m_InfoSerial GUARDED_BY(m_Lock) = -1;
51 int m_LatestSuccessfulInfoSerial GUARDED_BY(m_Lock) = -1;
52 };
53
54 class CProtocol
55 {
56 class CShared
57 {
58 public:
59 CShared(std::shared_ptr<CGlobal> pGlobal) :
60 m_pGlobal(std::move(pGlobal))
61 {
62 }
63
64 std::shared_ptr<CGlobal> m_pGlobal;
65 CLock m_Lock;
66 int m_NumTotalRequests GUARDED_BY(m_Lock) = 0;
67 int m_LatestResponseStatus GUARDED_BY(m_Lock) = STATUS_NONE;
68 int m_LatestResponseIndex GUARDED_BY(m_Lock) = -1;
69 };
70
71 class CJob : public IJob
72 {
73 int m_Protocol;
74 int m_ServerPort;
75 int m_Index;
76 int m_InfoSerial;
77 std::shared_ptr<CShared> m_pShared;
78 std::shared_ptr<CHttpRequest> m_pRegister;
79 IHttp *m_pHttp;
80 void Run() override;
81
82 public:
83 CJob(int Protocol, int ServerPort, int Index, int InfoSerial, std::shared_ptr<CShared> pShared, std::shared_ptr<CHttpRequest> &&pRegister, IHttp *pHttp) :
84 m_Protocol(Protocol),
85 m_ServerPort(ServerPort),
86 m_Index(Index),
87 m_InfoSerial(InfoSerial),
88 m_pShared(std::move(pShared)),
89 m_pRegister(std::move(pRegister)),
90 m_pHttp(pHttp)
91 {
92 }
93 ~CJob() override = default;
94 };
95
96 CRegister *m_pParent;
97 int m_Protocol;
98
99 std::shared_ptr<CShared> m_pShared;
100 bool m_NewChallengeToken = false;
101 bool m_HaveChallengeToken = false;
102 char m_aChallengeToken[128] = {0};
103
104 void CheckChallengeStatus();
105
106 public:
107 int64_t m_PrevRegister = -1;
108 int64_t m_NextRegister = -1;
109
110 CProtocol(CRegister *pParent, int Protocol);
111 void OnToken(const char *pToken);
112 void SendRegister();
113 void SendDeleteIfRegistered(bool Shutdown);
114 void Update();
115 };
116
117 CConfig *m_pConfig;
118 IConsole *m_pConsole;
119 IEngine *m_pEngine;
120 IHttp *m_pHttp;
121
122 // Don't start sending registers before the server has initialized
123 // completely.
124 bool m_GotFirstUpdateCall = false;
125 int m_ServerPort;
126 char m_aConnlessTokenHex[16];
127
128 std::shared_ptr<CGlobal> m_pGlobal = std::make_shared<CGlobal>();
129 bool m_aProtocolEnabled[NUM_PROTOCOLS] = {true, true, true, true};
130 CProtocol m_aProtocols[NUM_PROTOCOLS];
131
132 bool m_GotCommunityToken = false;
133 char m_aCommunityToken[128];
134
135 int m_NumExtraHeaders = 0;
136 char m_aaExtraHeaders[8][128];
137
138 char m_aVerifyPacketPrefix[sizeof(SERVERBROWSE_CHALLENGE) + UUID_MAXSTRSIZE];
139 CUuid m_Secret = RandomUuid();
140 CUuid m_ChallengeSecret = RandomUuid();
141 bool m_GotServerInfo = false;
142 char m_aServerInfo[32768];
143
144public:
145 CRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, IHttp *pHttp, int ServerPort, unsigned SixupSecurityToken);
146 void Update() override;
147 void OnConfigChange() override;
148 bool OnPacket(const CNetChunk *pPacket) override;
149 void OnNewInfo(const char *pInfo) override;
150 void OnShutdown() override;
151};
152
153bool CRegister::StatusFromString(int *pResult, const char *pString)
154{
155 if(str_comp(a: pString, b: "success") == 0)
156 {
157 *pResult = STATUS_OK;
158 }
159 else if(str_comp(a: pString, b: "need_challenge") == 0)
160 {
161 *pResult = STATUS_NEEDCHALLENGE;
162 }
163 else if(str_comp(a: pString, b: "need_info") == 0)
164 {
165 *pResult = STATUS_NEEDINFO;
166 }
167 else if(str_comp(a: pString, b: "error") == 0)
168 {
169 *pResult = STATUS_ERROR;
170 }
171 else
172 {
173 *pResult = -1;
174 return true;
175 }
176 return false;
177}
178
179const char *CRegister::ProtocolToScheme(int Protocol)
180{
181 switch(Protocol)
182 {
183 case PROTOCOL_TW6_IPV6: return "tw-0.6+udp://";
184 case PROTOCOL_TW6_IPV4: return "tw-0.6+udp://";
185 case PROTOCOL_TW7_IPV6: return "tw-0.7+udp://";
186 case PROTOCOL_TW7_IPV4: return "tw-0.7+udp://";
187 }
188 dbg_assert_failed("invalid protocol");
189}
190
191const char *CRegister::ProtocolToString(int Protocol)
192{
193 switch(Protocol)
194 {
195 case PROTOCOL_TW6_IPV6: return "tw0.6/ipv6";
196 case PROTOCOL_TW6_IPV4: return "tw0.6/ipv4";
197 case PROTOCOL_TW7_IPV6: return "tw0.7/ipv6";
198 case PROTOCOL_TW7_IPV4: return "tw0.7/ipv4";
199 }
200 dbg_assert_failed("invalid protocol");
201}
202
203bool CRegister::ProtocolFromString(int *pResult, const char *pString)
204{
205 if(str_comp(a: pString, b: "tw0.6/ipv6") == 0)
206 {
207 *pResult = PROTOCOL_TW6_IPV6;
208 }
209 else if(str_comp(a: pString, b: "tw0.6/ipv4") == 0)
210 {
211 *pResult = PROTOCOL_TW6_IPV4;
212 }
213 else if(str_comp(a: pString, b: "tw0.7/ipv6") == 0)
214 {
215 *pResult = PROTOCOL_TW7_IPV6;
216 }
217 else if(str_comp(a: pString, b: "tw0.7/ipv4") == 0)
218 {
219 *pResult = PROTOCOL_TW7_IPV4;
220 }
221 else
222 {
223 *pResult = -1;
224 return true;
225 }
226 return false;
227}
228
229const char *CRegister::ProtocolToSystem(int Protocol)
230{
231 switch(Protocol)
232 {
233 case PROTOCOL_TW6_IPV6: return "register/6/ipv6";
234 case PROTOCOL_TW6_IPV4: return "register/6/ipv4";
235 case PROTOCOL_TW7_IPV6: return "register/7/ipv6";
236 case PROTOCOL_TW7_IPV4: return "register/7/ipv4";
237 }
238 dbg_assert_failed("invalid protocol");
239}
240
241IPRESOLVE CRegister::ProtocolToIpresolve(int Protocol)
242{
243 switch(Protocol)
244 {
245 case PROTOCOL_TW6_IPV6: return IPRESOLVE::V6;
246 case PROTOCOL_TW6_IPV4: return IPRESOLVE::V4;
247 case PROTOCOL_TW7_IPV6: return IPRESOLVE::V6;
248 case PROTOCOL_TW7_IPV4: return IPRESOLVE::V4;
249 }
250 dbg_assert_failed("invalid protocol");
251}
252
253void CRegister::ConchainOnConfigChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
254{
255 pfnCallback(pResult, pCallbackUserData);
256 if(pResult->NumArguments())
257 {
258 ((CRegister *)pUserData)->OnConfigChange();
259 }
260}
261
262void CRegister::CProtocol::SendRegister()
263{
264 int64_t Now = time_get();
265 int64_t Freq = time_freq();
266
267 char aAddress[64];
268 str_format(buffer: aAddress, buffer_size: sizeof(aAddress), format: "%sconnecting-address.invalid:%d", ProtocolToScheme(Protocol: m_Protocol), m_pParent->m_ServerPort);
269
270 char aSecret[UUID_MAXSTRSIZE];
271 FormatUuid(Uuid: m_pParent->m_Secret, pBuffer: aSecret, BufferLength: sizeof(aSecret));
272
273 char aChallengeUuid[UUID_MAXSTRSIZE];
274 FormatUuid(Uuid: m_pParent->m_ChallengeSecret, pBuffer: aChallengeUuid, BufferLength: sizeof(aChallengeUuid));
275 char aChallengeSecret[64];
276 str_format(buffer: aChallengeSecret, buffer_size: sizeof(aChallengeSecret), format: "%s:%s", aChallengeUuid, ProtocolToString(Protocol: m_Protocol));
277 int InfoSerial;
278 bool SendInfo;
279
280 {
281 const CLockScope LockScope(m_pShared->m_pGlobal->m_Lock);
282 InfoSerial = m_pShared->m_pGlobal->m_InfoSerial;
283 SendInfo = InfoSerial > m_pShared->m_pGlobal->m_LatestSuccessfulInfoSerial;
284 }
285
286 std::unique_ptr<CHttpRequest> pRegister;
287 if(SendInfo)
288 {
289 pRegister = HttpPostJson(pUrl: m_pParent->m_pConfig->m_SvRegisterUrl, pJson: m_pParent->m_aServerInfo);
290 }
291 else
292 {
293 pRegister = HttpPost(pUrl: m_pParent->m_pConfig->m_SvRegisterUrl, pData: (unsigned char *)"", DataLength: 0);
294 }
295 pRegister->HeaderString(pName: "Address", pValue: aAddress);
296 pRegister->HeaderString(pName: "Secret", pValue: aSecret);
297 if(m_Protocol == PROTOCOL_TW7_IPV6 || m_Protocol == PROTOCOL_TW7_IPV4)
298 {
299 pRegister->HeaderString(pName: "Connless-Token", pValue: m_pParent->m_aConnlessTokenHex);
300 }
301 pRegister->HeaderString(pName: "Challenge-Secret", pValue: aChallengeSecret);
302 if(m_HaveChallengeToken)
303 {
304 pRegister->HeaderString(pName: "Challenge-Token", pValue: m_aChallengeToken);
305 }
306 pRegister->HeaderInt(pName: "Info-Serial", Value: InfoSerial);
307 if(m_pParent->m_GotCommunityToken)
308 {
309 pRegister->HeaderString(pName: "Community-Token", pValue: m_pParent->m_aCommunityToken);
310 }
311 for(int i = 0; i < m_pParent->m_NumExtraHeaders; i++)
312 {
313 pRegister->Header(pNameColonValue: m_pParent->m_aaExtraHeaders[i]);
314 }
315 pRegister->LogProgress(LogProgress: HTTPLOG::FAILURE);
316 pRegister->IpResolve(IpResolve: ProtocolToIpresolve(Protocol: m_Protocol));
317 pRegister->FailOnErrorStatus(FailOnErrorStatus: false);
318
319 int RequestIndex;
320 {
321 const CLockScope LockScope(m_pShared->m_Lock);
322 if(m_pShared->m_LatestResponseStatus != STATUS_OK)
323 {
324 log_info(ProtocolToSystem(m_Protocol), "registering...");
325 }
326 RequestIndex = m_pShared->m_NumTotalRequests;
327 m_pShared->m_NumTotalRequests += 1;
328 }
329 m_pParent->m_pEngine->AddJob(pJob: std::make_shared<CJob>(args&: m_Protocol, args&: m_pParent->m_ServerPort, args&: RequestIndex, args&: InfoSerial, args&: m_pShared, args: std::move(pRegister), args&: m_pParent->m_pHttp));
330 m_NewChallengeToken = false;
331
332 m_PrevRegister = Now;
333 m_NextRegister = Now + 15 * Freq;
334}
335
336void CRegister::CProtocol::SendDeleteIfRegistered(bool Shutdown)
337{
338 {
339 const CLockScope LockScope(m_pShared->m_Lock);
340 const bool ShouldSendDelete = m_pShared->m_LatestResponseStatus == STATUS_OK;
341 m_pShared->m_LatestResponseStatus = STATUS_NONE;
342 if(!ShouldSendDelete)
343 return;
344 }
345
346 char aAddress[64];
347 str_format(buffer: aAddress, buffer_size: sizeof(aAddress), format: "%sconnecting-address.invalid:%d", ProtocolToScheme(Protocol: m_Protocol), m_pParent->m_ServerPort);
348
349 char aSecret[UUID_MAXSTRSIZE];
350 FormatUuid(Uuid: m_pParent->m_Secret, pBuffer: aSecret, BufferLength: sizeof(aSecret));
351
352 std::shared_ptr<CHttpRequest> pDelete = HttpPost(pUrl: m_pParent->m_pConfig->m_SvRegisterUrl, pData: (const unsigned char *)"", DataLength: 0);
353 pDelete->HeaderString(pName: "Action", pValue: "delete");
354 pDelete->HeaderString(pName: "Address", pValue: aAddress);
355 pDelete->HeaderString(pName: "Secret", pValue: aSecret);
356 for(int i = 0; i < m_pParent->m_NumExtraHeaders; i++)
357 {
358 pDelete->Header(pNameColonValue: m_pParent->m_aaExtraHeaders[i]);
359 }
360 pDelete->LogProgress(LogProgress: HTTPLOG::FAILURE);
361 pDelete->IpResolve(IpResolve: ProtocolToIpresolve(Protocol: m_Protocol));
362 if(Shutdown)
363 {
364 // On shutdown, wait at most 1 second for the delete requests.
365 pDelete->Timeout(Timeout: CTimeout{.m_ConnectTimeoutMs: 1000, .m_TimeoutMs: 1000, .m_LowSpeedLimit: 0, .m_LowSpeedTime: 0});
366 }
367 log_info(ProtocolToSystem(m_Protocol), "deleting...");
368 m_pParent->m_pHttp->Run(pRequest: pDelete);
369}
370
371CRegister::CProtocol::CProtocol(CRegister *pParent, int Protocol) :
372 m_pParent(pParent),
373 m_Protocol(Protocol),
374 m_pShared(std::make_shared<CShared>(args&: pParent->m_pGlobal))
375{
376}
377
378void CRegister::CProtocol::CheckChallengeStatus()
379{
380 const CLockScope LockScope(m_pShared->m_Lock);
381 // No requests in flight?
382 if(m_pShared->m_LatestResponseIndex == m_pShared->m_NumTotalRequests - 1)
383 {
384 switch(m_pShared->m_LatestResponseStatus)
385 {
386 case STATUS_NEEDCHALLENGE:
387 if(m_NewChallengeToken)
388 {
389 // Immediately resend if we got the token.
390 m_NextRegister = time_get();
391 }
392 break;
393 case STATUS_NEEDINFO:
394 // Act immediately if the master requests more info.
395 m_NextRegister = time_get();
396 break;
397 }
398 }
399}
400
401void CRegister::CProtocol::Update()
402{
403 CheckChallengeStatus();
404 if(time_get() >= m_NextRegister)
405 {
406 SendRegister();
407 }
408}
409
410void CRegister::CProtocol::OnToken(const char *pToken)
411{
412 m_NewChallengeToken = true;
413 m_HaveChallengeToken = true;
414 str_copy(dst&: m_aChallengeToken, src: pToken);
415
416 CheckChallengeStatus();
417 if(time_get() >= m_NextRegister)
418 {
419 SendRegister();
420 }
421}
422
423void CRegister::CProtocol::CJob::Run()
424{
425 m_pHttp->Run(pRequest: m_pRegister);
426 m_pRegister->Wait();
427 if(m_pRegister->State() != EHttpState::DONE)
428 {
429 // TODO: exponential backoff
430 log_error(ProtocolToSystem(m_Protocol), "error sending request to master");
431 return;
432 }
433 json_value *pJson = m_pRegister->ResultJson();
434 if(!pJson)
435 {
436 log_error(ProtocolToSystem(m_Protocol), "non-JSON response from master");
437 return;
438 }
439 const json_value &Json = *pJson;
440 const json_value &StatusString = Json["status"];
441 if(StatusString.type != json_string)
442 {
443 json_value_free(pJson);
444 log_error(ProtocolToSystem(m_Protocol), "invalid JSON response from master");
445 return;
446 }
447 int Status;
448 if(StatusFromString(pResult: &Status, pString: StatusString))
449 {
450 log_error(ProtocolToSystem(m_Protocol), "invalid status from master: %s", (const char *)StatusString);
451 json_value_free(pJson);
452 return;
453 }
454 if(Status == STATUS_ERROR)
455 {
456 const json_value &Message = Json["message"];
457 if(Message.type != json_string)
458 {
459 json_value_free(pJson);
460 log_error(ProtocolToSystem(m_Protocol), "invalid JSON error response from master");
461 return;
462 }
463 log_error(ProtocolToSystem(m_Protocol), "error response from master: %d: %s", m_pRegister->StatusCode(), (const char *)Message);
464 json_value_free(pJson);
465 return;
466 }
467 if(m_pRegister->StatusCode() >= 400)
468 {
469 log_error(ProtocolToSystem(m_Protocol), "non-success status code %d from master without error code", m_pRegister->StatusCode());
470 json_value_free(pJson);
471 return;
472 }
473 {
474 const CLockScope LockScope(m_pShared->m_Lock);
475 if(Status != m_pShared->m_LatestResponseStatus)
476 {
477 if(Status != STATUS_OK)
478 {
479 log_debug(ProtocolToSystem(m_Protocol), "status: %s", (const char *)StatusString);
480 }
481 else
482 {
483 log_info(ProtocolToSystem(m_Protocol), "successfully registered");
484 }
485 }
486 if(Status == m_pShared->m_LatestResponseStatus && Status == STATUS_NEEDCHALLENGE)
487 {
488 log_error(ProtocolToSystem(m_Protocol), "ERROR: the master server reports that clients can not connect to this server.");
489 log_error(ProtocolToSystem(m_Protocol), "ERROR: configure your firewall/nat to let through udp on port %d.", m_ServerPort);
490 }
491 json_value_free(pJson);
492 if(m_Index > m_pShared->m_LatestResponseIndex)
493 {
494 m_pShared->m_LatestResponseIndex = m_Index;
495 m_pShared->m_LatestResponseStatus = Status;
496 }
497 }
498 if(Status == STATUS_OK)
499 {
500 const CLockScope LockScope(m_pShared->m_pGlobal->m_Lock);
501 if(m_InfoSerial > m_pShared->m_pGlobal->m_LatestSuccessfulInfoSerial)
502 {
503 m_pShared->m_pGlobal->m_LatestSuccessfulInfoSerial = m_InfoSerial;
504 }
505 }
506 else if(Status == STATUS_NEEDINFO)
507 {
508 const CLockScope LockScope(m_pShared->m_pGlobal->m_Lock);
509 if(m_InfoSerial == m_pShared->m_pGlobal->m_LatestSuccessfulInfoSerial)
510 {
511 // Tell other requests that they need to send the info again.
512 m_pShared->m_pGlobal->m_LatestSuccessfulInfoSerial -= 1;
513 }
514 }
515}
516
517CRegister::CRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, IHttp *pHttp, int ServerPort, unsigned SixupSecurityToken) :
518 m_pConfig(pConfig),
519 m_pConsole(pConsole),
520 m_pEngine(pEngine),
521 m_pHttp(pHttp),
522 m_ServerPort(ServerPort),
523 m_aProtocols{
524 CProtocol(this, PROTOCOL_TW6_IPV6),
525 CProtocol(this, PROTOCOL_TW6_IPV4),
526 CProtocol(this, PROTOCOL_TW7_IPV6),
527 CProtocol(this, PROTOCOL_TW7_IPV4),
528 }
529{
530 static constexpr int HEADER_LEN = sizeof(SERVERBROWSE_CHALLENGE);
531 mem_copy(dest: m_aVerifyPacketPrefix, source: SERVERBROWSE_CHALLENGE, size: HEADER_LEN);
532 FormatUuid(Uuid: m_ChallengeSecret, pBuffer: m_aVerifyPacketPrefix + HEADER_LEN, BufferLength: sizeof(m_aVerifyPacketPrefix) - HEADER_LEN);
533 m_aVerifyPacketPrefix[HEADER_LEN + UUID_MAXSTRSIZE - 1] = ':';
534
535 // The DDNet code uses the `unsigned` security token in big-endian byte order.
536 str_format(buffer: m_aConnlessTokenHex, buffer_size: sizeof(m_aConnlessTokenHex), format: "%08x", SixupSecurityToken);
537
538 m_pConsole->Chain(pName: "sv_register", pfnChainFunc: ConchainOnConfigChange, pUser: this);
539 m_pConsole->Chain(pName: "sv_register_extra", pfnChainFunc: ConchainOnConfigChange, pUser: this);
540 m_pConsole->Chain(pName: "sv_register_url", pfnChainFunc: ConchainOnConfigChange, pUser: this);
541 m_pConsole->Chain(pName: "sv_register_community_token", pfnChainFunc: ConchainOnConfigChange, pUser: this);
542 m_pConsole->Chain(pName: "sv_sixup", pfnChainFunc: ConchainOnConfigChange, pUser: this);
543 m_pConsole->Chain(pName: "sv_ipv4only", pfnChainFunc: ConchainOnConfigChange, pUser: this);
544}
545
546void CRegister::Update()
547{
548 if(!m_GotFirstUpdateCall)
549 {
550 bool Ipv6 = m_aProtocolEnabled[PROTOCOL_TW6_IPV6] || m_aProtocolEnabled[PROTOCOL_TW7_IPV6];
551 bool Ipv4 = m_aProtocolEnabled[PROTOCOL_TW6_IPV4] || m_aProtocolEnabled[PROTOCOL_TW7_IPV4];
552 if(Ipv6 && Ipv4)
553 {
554 dbg_assert(!HttpHasIpresolveBug(), "curl version < 7.77.0 does not support registering via both IPv4 and IPv6, set `sv_register ipv6` or `sv_register ipv4`");
555 }
556 m_GotFirstUpdateCall = true;
557 }
558 if(!m_GotServerInfo)
559 {
560 return;
561 }
562 for(int i = 0; i < NUM_PROTOCOLS; i++)
563 {
564 if(!m_aProtocolEnabled[i])
565 {
566 continue;
567 }
568 m_aProtocols[i].Update();
569 }
570}
571
572void CRegister::OnConfigChange()
573{
574 bool aOldProtocolEnabled[NUM_PROTOCOLS];
575 for(int i = 0; i < NUM_PROTOCOLS; i++)
576 {
577 aOldProtocolEnabled[i] = m_aProtocolEnabled[i];
578 }
579 const char *pProtocols = m_pConfig->m_SvRegister;
580 if(str_comp(a: pProtocols, b: "1") == 0)
581 {
582 for(auto &Enabled : m_aProtocolEnabled)
583 {
584 Enabled = true;
585 }
586 }
587 else if(str_comp(a: pProtocols, b: "0") == 0)
588 {
589 for(auto &Enabled : m_aProtocolEnabled)
590 {
591 Enabled = false;
592 }
593 }
594 else
595 {
596 for(auto &Enabled : m_aProtocolEnabled)
597 {
598 Enabled = false;
599 }
600 char aBuf[16];
601 while((pProtocols = str_next_token(str: pProtocols, delim: ",", buffer: aBuf, buffer_size: sizeof(aBuf))))
602 {
603 int Protocol;
604 if(str_comp(a: aBuf, b: "ipv6") == 0)
605 {
606 m_aProtocolEnabled[PROTOCOL_TW6_IPV6] = true;
607 m_aProtocolEnabled[PROTOCOL_TW7_IPV6] = true;
608 }
609 else if(str_comp(a: aBuf, b: "ipv4") == 0)
610 {
611 m_aProtocolEnabled[PROTOCOL_TW6_IPV4] = true;
612 m_aProtocolEnabled[PROTOCOL_TW7_IPV4] = true;
613 }
614 else if(str_comp(a: aBuf, b: "tw0.6") == 0)
615 {
616 m_aProtocolEnabled[PROTOCOL_TW6_IPV6] = true;
617 m_aProtocolEnabled[PROTOCOL_TW6_IPV4] = true;
618 }
619 else if(str_comp(a: aBuf, b: "tw0.7") == 0)
620 {
621 m_aProtocolEnabled[PROTOCOL_TW7_IPV6] = true;
622 m_aProtocolEnabled[PROTOCOL_TW7_IPV4] = true;
623 }
624 else if(!ProtocolFromString(pResult: &Protocol, pString: aBuf))
625 {
626 m_aProtocolEnabled[Protocol] = true;
627 }
628 else
629 {
630 log_warn("register", "unknown protocol '%s'", aBuf);
631 continue;
632 }
633 }
634 }
635 if(!m_pConfig->m_SvSixup)
636 {
637 m_aProtocolEnabled[PROTOCOL_TW7_IPV6] = false;
638 m_aProtocolEnabled[PROTOCOL_TW7_IPV4] = false;
639 }
640 if(m_pConfig->m_SvIpv4Only)
641 {
642 m_aProtocolEnabled[PROTOCOL_TW6_IPV6] = false;
643 m_aProtocolEnabled[PROTOCOL_TW7_IPV6] = false;
644 }
645 m_GotCommunityToken = (bool)m_pConfig->m_SvRegisterCommunityToken[0];
646 if(m_GotCommunityToken)
647 {
648 str_copy(dst&: m_aCommunityToken, src: m_pConfig->m_SvRegisterCommunityToken);
649 }
650 m_NumExtraHeaders = 0;
651 const char *pRegisterExtra = m_pConfig->m_SvRegisterExtra;
652 char aHeader[128];
653 while((pRegisterExtra = str_next_token(str: pRegisterExtra, delim: ",", buffer: aHeader, buffer_size: sizeof(aHeader))))
654 {
655 if(m_NumExtraHeaders == (int)std::size(m_aaExtraHeaders))
656 {
657 log_warn("register", "reached maximum of %d extra headers, dropping '%s' and all further headers", m_NumExtraHeaders, aHeader);
658 break;
659 }
660 if(!str_find(haystack: aHeader, needle: ": "))
661 {
662 log_warn("register", "header '%s' doesn't contain mandatory ': ', ignoring", aHeader);
663 continue;
664 }
665 str_copy(dst&: m_aaExtraHeaders[m_NumExtraHeaders], src: aHeader);
666 m_NumExtraHeaders += 1;
667 }
668 // Don't start registering before the first `CRegister::Update` call.
669 if(!m_GotFirstUpdateCall)
670 {
671 return;
672 }
673 for(int i = 0; i < NUM_PROTOCOLS; i++)
674 {
675 if(aOldProtocolEnabled[i] == m_aProtocolEnabled[i])
676 {
677 continue;
678 }
679 if(m_aProtocolEnabled[i])
680 {
681 m_aProtocols[i].SendRegister();
682 }
683 else
684 {
685 m_aProtocols[i].SendDeleteIfRegistered(Shutdown: false);
686 }
687 }
688}
689
690bool CRegister::OnPacket(const CNetChunk *pPacket)
691{
692 if((pPacket->m_Flags & NETSENDFLAG_CONNLESS) == 0)
693 {
694 return false;
695 }
696 if(pPacket->m_DataSize >= (int)sizeof(m_aVerifyPacketPrefix) &&
697 mem_comp(a: pPacket->m_pData, b: m_aVerifyPacketPrefix, size: sizeof(m_aVerifyPacketPrefix)) == 0)
698 {
699 CUnpacker Unpacker;
700 Unpacker.Reset(pData: pPacket->m_pData, Size: pPacket->m_DataSize);
701 Unpacker.GetRaw(Size: sizeof(m_aVerifyPacketPrefix));
702 const char *pProtocol = Unpacker.GetString(SanitizeType: 0);
703 const char *pToken = Unpacker.GetString(SanitizeType: 0);
704 if(Unpacker.Error())
705 {
706 log_error("register", "got erroneous challenge packet from master");
707 return true;
708 }
709
710 log_debug("register", "got challenge token, protocol='%s' token='%s'", pProtocol, pToken);
711 int Protocol;
712 if(ProtocolFromString(pResult: &Protocol, pString: pProtocol))
713 {
714 log_error("register", "got challenge packet with unknown protocol");
715 return true;
716 }
717 m_aProtocols[Protocol].OnToken(pToken);
718 return true;
719 }
720 return false;
721}
722
723void CRegister::OnNewInfo(const char *pInfo)
724{
725 log_trace("register", "info: %s", pInfo);
726 if(m_GotServerInfo && str_comp(a: m_aServerInfo, b: pInfo) == 0)
727 {
728 return;
729 }
730
731 m_GotServerInfo = true;
732 str_copy(dst&: m_aServerInfo, src: pInfo);
733 {
734 const CLockScope LockScope(m_pGlobal->m_Lock);
735 m_pGlobal->m_InfoSerial += 1;
736 }
737
738 // Don't start registering before the first `CRegister::Update` call.
739 if(!m_GotFirstUpdateCall)
740 {
741 return;
742 }
743
744 // Immediately send new info if it changes, but at most once per second.
745 int64_t Now = time_get();
746 int64_t Freq = time_freq();
747 int64_t MaximumPrevRegister = -1;
748 int64_t MinimumNextRegister = -1;
749 int MinimumNextRegisterProtocol = -1;
750 for(int i = 0; i < NUM_PROTOCOLS; i++)
751 {
752 if(!m_aProtocolEnabled[i])
753 {
754 continue;
755 }
756 if(m_aProtocols[i].m_NextRegister == -1)
757 {
758 m_aProtocols[i].m_NextRegister = Now;
759 continue;
760 }
761 if(m_aProtocols[i].m_PrevRegister > MaximumPrevRegister)
762 {
763 MaximumPrevRegister = m_aProtocols[i].m_PrevRegister;
764 }
765 if(MinimumNextRegisterProtocol == -1 || m_aProtocols[i].m_NextRegister < MinimumNextRegister)
766 {
767 MinimumNextRegisterProtocol = i;
768 MinimumNextRegister = m_aProtocols[i].m_NextRegister;
769 }
770 }
771 for(int i = 0; i < NUM_PROTOCOLS; i++)
772 {
773 if(!m_aProtocolEnabled[i])
774 {
775 continue;
776 }
777 if(i == MinimumNextRegisterProtocol)
778 {
779 m_aProtocols[i].m_NextRegister = std::min(a: m_aProtocols[i].m_NextRegister, b: MaximumPrevRegister + Freq);
780 }
781 if(Now >= m_aProtocols[i].m_NextRegister)
782 {
783 m_aProtocols[i].SendRegister();
784 }
785 }
786}
787
788void CRegister::OnShutdown()
789{
790 for(int i = 0; i < NUM_PROTOCOLS; i++)
791 {
792 if(!m_aProtocolEnabled[i])
793 {
794 continue;
795 }
796 m_aProtocols[i].SendDeleteIfRegistered(Shutdown: true);
797 }
798}
799
800IRegister *CreateRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, IHttp *pHttp, int ServerPort, unsigned SixupSecurityToken)
801{
802 return new CRegister(pConfig, pConsole, pEngine, pHttp, ServerPort, SixupSecurityToken);
803}
804