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 | |
16 | class 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 = 0; |
129 | char [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 | |
137 | public: |
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 | |
146 | bool 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 | |
172 | const 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 | |
185 | const 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 | |
198 | bool 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 | |
224 | const 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 | |
237 | IPRESOLVE 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 | |
250 | void 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 | |
259 | void 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 | |
329 | void 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 | |
364 | CRegister::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 | |
371 | void 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 | |
394 | void CRegister::CProtocol::Update() |
395 | { |
396 | CheckChallengeStatus(); |
397 | if(time_get() >= m_NextRegister) |
398 | { |
399 | SendRegister(); |
400 | } |
401 | } |
402 | |
403 | void 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 | |
416 | void 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 | |
503 | CRegister::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 = 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 | |
533 | void 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 | |
559 | void 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 * = m_pConfig->m_SvRegisterExtra; |
634 | char [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 | |
672 | bool 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 | |
705 | void 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 | |
770 | void 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 | |
782 | IRegister *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 | |