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