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