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