libvwad: updated -- vwadwrite: free file buffers on close (otherwise archive creation...
[k8vavoom.git] / source / net / net_datagram.cpp
blobb54ab57f21e0ef5293898e743e9b3e22477bddc6
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
16 //**
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
21 //**
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**
25 //**************************************************************************
26 //**
27 //** Datagram driver, handles all LAN drivers
28 //**
29 //**************************************************************************
31 // This is the network info/connection protocol. It is used to find k8vavoom
32 // servers, get info about them, and connect to them. Once connected, the
33 // k8vavoom game protocol (documented elsewhere) is used.
36 // CCREQ_CONNECT
37 // bytes[32] key
38 // vuint23 nonce
39 // other data is encrypted with ChaCha20
40 // bytes[4] crc32c
41 // bytes "K8VAVOOM"
42 // vuint8 CCREQ_CONNECT
43 // vuint8 net_protocol_version_hi NET_PROTOCOL_VERSION_HI
44 // vuint8 net_protocol_version_lo NET_PROTOCOL_VERSION_LO
45 // bytes[32] passwordSHA256
46 // vuint32 modlisthash
47 // vuint16 modlistcount
49 // CCREQ_SERVER_INFO
50 // bytes[32] key
51 // vuint23 nonce
52 // other data is encrypted with ChaCha20
53 // bytes[4] crc32c
54 // bytes "K8VAVOOM"
55 // vuint8 CCREQ_SERVER_INFO
56 // vuint8 net_protocol_version_hi NET_PROTOCOL_VERSION_HI
57 // vuint8 net_protocol_version_lo NET_PROTOCOL_VERSION_LO
60 // CCREP_ACCEPT
61 // bytes[32] key
62 // vuint23 nonce
63 // other data is encrypted with ChaCha20
64 // bytes[4] crc32c
65 // bytes "K8VAVOOM"
66 // vuint8 CCREP_ACCEPT
67 // vuint8 net_protocol_version_hi NET_PROTOCOL_VERSION_HI
68 // vuint8 net_protocol_version_lo NET_PROTOCOL_VERSION_LO
69 // vuint16 port
71 // CCREP_REJECT
72 // bytes[32] key
73 // vuint23 nonce
74 // other data is encrypted with ChaCha20
75 // bytes[4] crc32c
76 // bytes "K8VAVOOM"
77 // vuint8 CCREP_REJECT
78 // vuint8 extflags (bit 0: has wads?)
79 // string reason (127 bytes max, with terminating 0)
80 // if bit 0 of `extflags` is set, modlist follows
81 // modlist is asciz strings, terminated with empty string
83 // CCREP_SERVER_INFO
84 // bytes[32] key
85 // vuint23 nonce
86 // other data is encrypted with ChaCha20
87 // bytes[4] crc32c
88 // bytes "K8VAVOOM"
89 // vuint8 CCREP_SERVER_INFO
90 // vuint8 net_protocol_version_hi NET_PROTOCOL_VERSION_HI
91 // vuint8 net_protocol_version_lo NET_PROTOCOL_VERSION_LO
92 // vuint8 extflags (bit 0: has wads?; bit 1: server is password-protected)
93 // vuint8 current_players
94 // vuint8 max_players
95 // vuint8 deathmatchMode
96 // vuint32 modlisthash
97 // string host_name (max 127 bytes, with terminating 0)
98 // string level_name (max 63 bytes, with terminating 0)
99 // if bit 0 of `extflags` is set, modlist follows
100 // asciiz strings with loaded archive names, terminated with empty string
102 //**************************************************************************
103 #include "../gamedefs.h"
104 #include "../text.h"
105 #include "../server/server.h"
106 #include "../filesys/files.h"
107 #ifdef CLIENT
108 # include "../screen.h"
109 #endif
110 #include "net_local.h"
113 static int cli_NoLAN = 0;
115 /*static*/ bool cliRegister_datagram_args =
116 VParsedArgs::RegisterFlagSet("-nolan", "disable networking", &cli_NoLAN) &&
117 VParsedArgs::RegisterAlias("-no-lan", "-nolan");
120 static VCvarB net_dbg_dump_rejected_connections("net_dbg_dump_rejected_connections", true, "Dump rejected connections?", CVAR_NoShadow);
122 static VCvarS net_rcon_secret_key("net_rcon_secret_key", "", "Secret key for rcon commands", CVAR_NoShadow);
123 static VCvarS net_server_key("net_server_key", "", "Server key for password-protected servers", CVAR_NoShadow);
126 // ////////////////////////////////////////////////////////////////////////// //
127 class VDatagramSocket : public VSocket {
128 public:
129 VNetLanDriver *LanDriver;
130 int LanSocket;
131 sockaddr_t Addr;
132 bool Invalid;
134 public:
135 VDatagramSocket (VNetDriver *Drv) : VSocket(Drv), LanDriver(nullptr), LanSocket(-1), Invalid(false) {}
136 virtual ~VDatagramSocket() override;
138 virtual int GetMessage (void *dest, size_t destSize) override;
139 virtual int SendMessage (const vuint8 *Data, vuint32 Length) override;
140 virtual bool IsLocalConnection () const noexcept override;
144 // ////////////////////////////////////////////////////////////////////////// //
145 class VDatagramDriver : public VNetDriver {
146 public:
147 enum { MASTER_SERVER_PORT = 26002 };
148 enum { MASTER_PROTO_VERSION = 1 };
150 enum {
151 MAX_INFO_DGRAM_SIZE = MAX_DGRAM_SIZE-VNetUtils::ChaCha20HeaderSize,
154 // client request
155 enum {
156 CCREQ_CONNECT = 1,
157 CCREQ_SERVER_INFO = 2,
160 enum {
161 RCON_PROTO_VERSION = 1u, // 16-bit value
163 // client sends this to execute console command on the server
164 CCREQ_RCON_COMMAND = 25,
165 // server sends this to ack client command
166 CCREP_RCON_COMMAND = 26,
170 rcon protocol:
172 client connects, and sends CCREQ_RCON_COMMAND. key is used to reject duplicate commands.
173 then client must wait for CCREP_RCON_COMMAND. if it got no answer, it should send its
174 request again.
176 if the server accepts the command, it must remember its key, and don't repeat it, just
177 answer with the CCREP_RCON_COMMAND.
179 client sends CCREQ_RCON_COMMAND:
180 bytes[32] key
181 vuint23 nonce
182 other data is encrypted with ChaCha20
183 bytes[4] crc32c
184 // signed from here
185 bytes "K8VAVOOM"
186 vuint8 CCREQ_RCON_COMMAND
187 vuint16 RCON_PROTO_VERSION
188 bytes[32] secretSHA256
189 bytes command (trailing zero or packet end ends it)
191 server sends CCREP_RCON_COMMAND:
192 bytes[32] key
193 vuint23 nonce
194 other data is encrypted with ChaCha20
195 bytes[4] crc32c
196 // signed from here
197 bytes "K8VAVOOM"
198 vuint8 CCREP_RCON_COMMAND
199 vuint16 RCON_PROTO_VERSION
200 bytes errmsg ; message (zero-terminated, may be empty)
203 // server reply
204 enum {
205 CCREP_ACCEPT = 11,
206 CCREP_REJECT = 12,
207 CCREP_SERVER_INFO = 13,
210 // master server request
211 enum {
212 MCREQ_JOIN = 1,
213 MCREQ_QUIT = 2,
214 MCREQ_LIST = 3,
217 // master server reply
218 enum {
219 MCREP_LIST = 1,
222 struct {
223 vuint8 data[MAX_DGRAM_SIZE];
224 } packetBuffer;
226 private:
227 VStr LastMasterAddrStr;
228 sockaddr_t LastMasterAddr;
229 bool LastMasterIsBad;
231 private:
232 bool ResolveMasterAddr (VNetLanDriver *Drv);
234 public:
235 VDatagramDriver ();
237 virtual int Init () override;
238 virtual void Listen (bool) override;
239 virtual void SearchForHosts (bool, bool) override;
240 virtual VSocket *Connect (const char *) override;
241 virtual VSocket *CheckNewConnections (bool rconOnly) override;
242 virtual void UpdateMaster () override;
243 virtual void QuitMaster () override;
244 virtual bool QueryMaster (bool) override;
245 virtual void EndQueryMaster () override;
246 virtual void Shutdown () override;
248 void SearchForHosts (VNetLanDriver *, bool, bool);
249 VSocket *Connect (VNetLanDriver *, const char *);
250 VSocket *CheckNewConnections (VNetLanDriver *Drv, bool rconOnly);
251 void SendConnectionReject (VNetLanDriver *Drv, const vuint8 key[VNetUtils::ChaCha20KeySize], VStr reason, int acceptsock, sockaddr_t clientaddr, bool sendModList=false);
252 void UpdateMaster (VNetLanDriver *);
253 void QuitMaster (VNetLanDriver *);
254 bool QueryMaster (VNetLanDriver *, bool);
256 static void WriteGameSignature (VBitStreamWriter &strm);
257 static bool CheckGameSignature (VBitStreamReader &strm);
259 static void WritePakList (VBitStreamWriter &strm);
260 static bool ReadPakList (TArray<VStr> &list, VBitStreamReader &strm); // won't clear list
262 // returns size or -1
263 static int EncryptInfoBitStream (vuint8 *dest, VBitStreamWriter &strm, const vuint8 key[VNetUtils::ChaCha20KeySize]) noexcept;
264 static bool DecryptInfoBitStream (vuint8 key[VNetUtils::ChaCha20KeySize], VBitStreamReader &strm, void *srcbuf, int srclen);
266 public: // rcon
267 vuint8 rconLastKey[VNetUtils::ChaCha20KeySize];
271 // ////////////////////////////////////////////////////////////////////////// //
272 extern int num_connected;
273 extern TArray<VStr> fsysWadFileNames; // this is from corelib
275 static VCvarB UseMaster("master_allowed", false, "Is communication with master server allowed?", CVAR_Archive|CVAR_NoShadow);
276 static VCvarS MasterSrv("master_address", "ketmar.no-ip.org", "Master server domain name.", CVAR_Archive|CVAR_NoShadow);
278 static VDatagramDriver Impl;
281 //==========================================================================
283 // VDatagramDriver::VDatagramDriver
285 //==========================================================================
286 VDatagramDriver::VDatagramDriver () : VNetDriver(1, "Datagram") {
287 memset(&packetBuffer, 0, sizeof(packetBuffer));
288 memset((void *)&LastMasterAddr, 0, sizeof(LastMasterAddr));
289 LastMasterIsBad = true;
290 vassert(LastMasterAddrStr.isEmpty());
291 memset(rconLastKey, 0, VNetUtils::ChaCha20KeySize);
295 //==========================================================================
297 // VDatagramDriver::WriteGameSignature
299 //==========================================================================
300 void VDatagramDriver::WriteGameSignature (VBitStreamWriter &strm) {
301 const char *sign = "K8VAVOOM";
302 strm.Serialise((void *)sign, 8);
306 //==========================================================================
308 // VDatagramDriver::CheckGameSignature
310 //==========================================================================
311 bool VDatagramDriver::CheckGameSignature (VBitStreamReader &strm) {
312 char sign[8];
313 if (strm.IsError()) return false;
314 memset(sign, 0, sizeof(sign));
315 strm.Serialise(sign, 8);
316 if (strm.IsError()) return false;
317 return (memcmp(sign, "K8VAVOOM", 8) == 0);
321 //==========================================================================
323 // VDatagramDriver::WritePakList
325 //==========================================================================
326 void VDatagramDriver::WritePakList (VBitStreamWriter &strm) {
327 // write names
328 TArray<VStr> list;
329 FL_GetNetWads(list);
330 for (int f = 0; f < list.length(); ++f) {
331 VStr pak = list[f];
332 if (pak.isEmpty()) continue;
333 //GCon->Logf(NAME_Debug, "%d: <%s>", f, *pak);
334 //if (pak.length() > 255) pak = pak.right(255); // just in case
335 if (strm.GetNumBits()+pak.length()*8+8 > MAX_INFO_DGRAM_SIZE*8-8) break;
336 for (int sidx = 0; sidx < pak.length(); ++sidx) {
337 vuint8 ch = (vuint8)(pak[sidx]);
338 strm << ch;
340 vuint8 zero = 0;
341 strm << zero;
343 // write trailing zero
345 vuint8 zero = 0;
346 strm << zero;
351 //==========================================================================
353 // VDatagramDriver::ReadPakList
355 // won't clear list
357 //==========================================================================
358 bool VDatagramDriver::ReadPakList (TArray<VStr> &list, VBitStreamReader &strm) {
359 char buf[MAX_DGRAM_SIZE+2];
360 while (!strm.AtEnd()) {
361 int bufpos = 0;
362 for (;;) {
363 vuint8 ch = 0;
364 strm << ch;
365 if (strm.IsError()) return false;
366 if (bufpos >= (int)sizeof(buf)-2) return false;
367 buf[bufpos++] = ch;
368 if (!ch) break;
370 if (buf[0] == 0) return true; // done
371 list.append(VStr(buf));
373 return false;
377 //==========================================================================
379 // VDatagramDriver::Init
381 //==========================================================================
382 int VDatagramDriver::Init () {
383 if (cli_NoLAN > 0) return -1;
385 for (int i = 0; i < VNetworkLocal::NumLanDrivers; ++i) {
386 VNetworkLocal::LanDrivers[i]->Net = Net;
387 int csock = VNetworkLocal::LanDrivers[i]->Init();
388 if (csock == -1) continue;
389 VNetworkLocal::LanDrivers[i]->initialised = true;
390 VNetworkLocal::LanDrivers[i]->controlSock = csock;
393 return 0;
397 //==========================================================================
399 // VDatagramDriver::Listen
401 //==========================================================================
402 void VDatagramDriver::Listen (bool state) {
403 for (int i = 0; i < VNetworkLocal::NumLanDrivers; ++i) {
404 if (VNetworkLocal::LanDrivers[i]->initialised) VNetworkLocal::LanDrivers[i]->Listen(state);
409 //==========================================================================
411 // VDatagramDriver::EncryptInfoBitStream
413 //==========================================================================
414 int VDatagramDriver::EncryptInfoBitStream (vuint8 *dest, VBitStreamWriter &strm, const vuint8 key[VNetUtils::ChaCha20KeySize]) noexcept {
415 if (!dest) return -1;
416 if (strm.IsError() || strm.GetNumBytes() > MAX_INFO_DGRAM_SIZE) return -1;
418 // align (just in case)
419 while (strm.GetNumBits()&7) strm.WriteBit(false);
420 if (strm.IsError() || strm.GetNumBytes() > MAX_INFO_DGRAM_SIZE) return -1;
422 // encrypt
423 return VNetUtils::EncryptInfoPacket(dest, strm.GetData(), strm.GetNumBytes(), key);
427 //==========================================================================
429 // VDatagramDriver::DecryptInfoBitStream
431 //==========================================================================
432 bool VDatagramDriver::DecryptInfoBitStream (vuint8 key[VNetUtils::ChaCha20KeySize], VBitStreamReader &strm, void *srcbuf, int srclen) {
433 strm.Clear(true);
435 if (srclen < VNetUtils::ChaCha20KeySize+4) {
436 strm.SetError();
437 return false;
440 int dlen = VNetUtils::DecryptInfoPacket(key, srcbuf, srcbuf, srclen);
441 if (dlen <= 0) {
442 // it must have data
443 strm.SetError();
444 return false;
447 strm.SetupFrom((const vuint8 *)srcbuf, dlen<<3);
448 return true;
452 //==========================================================================
454 // VDatagramDriver::SearchForHosts
456 //==========================================================================
457 void VDatagramDriver::SearchForHosts (VNetLanDriver *Drv, bool xmit, bool ForMaster) {
458 sockaddr_t myaddr;
459 sockaddr_t readaddr;
460 //vuint8 control;
461 vuint8 msgtype;
462 int n;
463 vuint8 TmpByte;
465 Drv->GetSocketAddr(Drv->controlSock, &myaddr);
466 if (xmit && Drv->CanBroadcast()) {
467 GCon->Log(NAME_DevNet, "sending broadcast query");
468 vuint8 edata[MAX_DGRAM_SIZE];
470 VBitStreamWriter Reply(MAX_INFO_DGRAM_SIZE<<3);
471 WriteGameSignature(Reply);
472 TmpByte = CCREQ_SERVER_INFO;
473 Reply << TmpByte;
474 TmpByte = NET_PROTOCOL_VERSION_HI;
475 Reply << TmpByte;
476 TmpByte = NET_PROTOCOL_VERSION_LO;
477 Reply << TmpByte;
479 // encrypt
480 vuint8 key[VNetUtils::ChaCha20KeySize];
481 VNetUtils::GenerateKey(key);
482 int elen = EncryptInfoBitStream(edata, Reply, key);
483 if (elen <= 0) return; // just in case
484 if (elen > 0) Drv->Broadcast(Drv->controlSock, edata, elen);
487 //GCon->Logf(NAME_Debug, "SearchForHosts: trying to read a datagram (me:%s)", Drv->AddrToString(&myaddr));
488 int pktleft = 128;
489 while (pktleft-- > 0) {
490 int len = Drv->Read(Drv->controlSock, packetBuffer.data, MAX_DGRAM_SIZE, &readaddr);
491 if (len < 0) break; // no message or error
492 if (len < VNetUtils::ChaCha20KeySize+4) continue;
494 // don't answer our own query
495 if (!ForMaster && Drv->AddrCompare(&readaddr, &myaddr) == 0) continue;
497 // is the cache full?
498 //if (Net->HostCacheCount == HOSTCACHESIZE) continue;
500 // decrypt
501 vuint8 key[VNetUtils::ChaCha20KeySize];
502 VBitStreamReader msg;
504 if (!DecryptInfoBitStream(key, msg, packetBuffer.data, len)) continue;
506 GCon->Logf(NAME_DevNet, "SearchForHosts: got datagram from %s (len=%d; dlen=%d)", Drv->AddrToString(&readaddr), len, msg.GetNumBytes());
508 if (!CheckGameSignature(msg)) continue;
510 msg << msgtype;
511 if (msg.IsError() || msgtype != CCREP_SERVER_INFO) continue;
513 VStr str;
514 VStr addr = Drv->AddrToString(&readaddr);
516 GCon->Logf(NAME_DevNet, "SearchForHosts: got valid packet from %s", Drv->AddrToString(&readaddr));
518 // search the cache for this server
519 for (n = 0; n < Net->HostCacheCount; ++n) {
520 if (addr.strEqu(Net->HostCache[n].CName)) break;
523 // is it already there?
524 if (n < Net->HostCacheCount) continue;
526 if (Net->HostCacheCount == HOSTCACHESIZE) {
527 GCon->Logf(NAME_DevNet, "too many hosts, ignoring");
528 continue;
531 // add it
532 ++Net->HostCacheCount;
533 vassert(n >= 0 && n < Net->HostCacheCount);
534 hostcache_t *hinfo = &Net->HostCache[n];
535 hinfo->Flags = 0;
536 // protocol version
537 vuint8 protoHi = 0, protoLo = 0;
538 msg << protoHi << protoLo;
539 // flags
540 vuint8 extflags = 0;
541 msg << extflags;
542 // current players
543 msg << TmpByte;
544 hinfo->Users = TmpByte;
545 // max players
546 msg << TmpByte;
547 hinfo->MaxUsers = TmpByte;
548 // deathmatch mode
549 msg << TmpByte;
550 hinfo->DeathMatch = TmpByte;
551 // wadlist hash
552 vuint32 mhash = 0;
553 msg << mhash;
554 // server name
555 msg << str;
556 hinfo->Name = str;
557 // map name
558 msg << str;
559 hinfo->Map = str;
561 if (protoHi == NET_PROTOCOL_VERSION_HI && protoLo == NET_PROTOCOL_VERSION_LO) {
562 hinfo->Flags |= hostcache_t::Flag_GoodProtocol;
565 if (extflags&2) hinfo->Flags |= hostcache_t::Flag_PasswordProtected;
567 //GCon->Logf(NAME_DevNet, " WHASH: theirs=0x%08x mine=0x%08x", mhash, FL_GetNetWadsHash());
568 if (mhash == FL_GetNetWadsHash()) hinfo->Flags |= hostcache_t::Flag_GoodWadList;
569 hinfo->CName = addr;
570 hinfo->WadFiles.clear();
571 if (!msg.IsError() && (extflags&1)) ReadPakList(hinfo->WadFiles, msg);
573 if (msg.IsError()) {
574 // remove it
575 hinfo->WadFiles.clear();
576 --Net->HostCacheCount;
577 continue;
579 //GCon->Logf(NAME_DevNet, " wcount: %d %d", hinfo->WadFiles.length(), FL_GetNetWadsCount());
580 if ((hinfo->Flags&hostcache_t::Flag_GoodWadList) && hinfo->WadFiles.length() != FL_GetNetWadsCount()) {
581 hinfo->Flags &= ~hostcache_t::Flag_GoodWadList;
583 //GCon->Logf(NAME_DevNet, " flags: 0x%04x", hinfo->Flags);
585 GCon->Logf(NAME_DevNet, "SearchForHosts: got server info from %s: name=%s; map=%s; users=%d; maxusers=%d; flags=0x%02x", *hinfo->CName, *hinfo->Name, *hinfo->Map, hinfo->Users, hinfo->MaxUsers, hinfo->Flags);
586 //for (int f = 0; f < hinfo->WadFiles.length(); ++f) GCon->Logf(" %d: <%s>", f, *hinfo->WadFiles[f]);
588 // check for a name conflict
590 for (int i = 0; i < Net->HostCacheCount; ++i) {
591 if (i == n) continue;
592 if (Net->HostCache[n].Name.strEquCI(Net->HostCache[i].Name)) {
593 i = Net->HostCache[n].Name.Length();
594 if (i < 15 && Net->HostCache[n].Name[i-1] > '8') {
595 Net->HostCache[n].Name += '0';
596 } else {
597 ++(*Net->HostCache[n].Name.GetMutableCharPointer(i-1));
598 //Net->HostCache[n].Name[i - 1]++;
600 i = -1;
608 //==========================================================================
610 // VDatagramDriver::SearchForHosts
612 //==========================================================================
613 void VDatagramDriver::SearchForHosts (bool xmit, bool ForMaster) {
614 for (int i = 0; i < VNetworkLocal::NumLanDrivers; ++i) {
615 if (Net->HostCacheCount == HOSTCACHESIZE) break;
616 if (VNetworkLocal::LanDrivers[i]->initialised) SearchForHosts(VNetworkLocal::LanDrivers[i], xmit, ForMaster);
621 //==========================================================================
623 // VDatagramDriver::Connect
625 //==========================================================================
626 VSocket *VDatagramDriver::Connect (VNetLanDriver *Drv, const char *host) {
627 #ifdef CLIENT
628 sockaddr_t sendaddr;
629 sockaddr_t readaddr;
630 VDatagramSocket *sock;
631 int newsock;
632 double start_time;
633 int reps;
634 int ret;
635 //vuint8 control;
636 VStr reason;
637 vuint8 msgtype;
638 vuint16 newport;
639 VBitStreamReader *msg = nullptr;
640 vuint8 TmpByte;
641 vuint8 otherProtoHi, otherProtoLo;
643 if (!host || !host[0]) return nullptr;
645 SCR_Update();
646 R_OSDMsgReset(OSD_Network);
648 R_OSDMsgShow(va("getting address for [%s]", host));
650 // see if we can resolve the host name
651 if (Drv->GetAddrFromName(host, &sendaddr, Net->HostPort) == -1) return nullptr;
653 //R_OSDMsgShow("creating socket");
654 R_OSDMsgShow(va("connecting to [%s]", Drv->AddrToString(&sendaddr)));
656 newsock = Drv->ConnectSocketTo(&sendaddr);
657 if (newsock == -1) return nullptr;
659 vuint8 origkey[VNetUtils::ChaCha20KeySize];
660 vuint8 srvkey[VNetUtils::ChaCha20KeySize]; // this is what we should receive from the server
661 bool replyWithServerKey = false;
662 VNetUtils::GenerateKey(origkey);
663 VNetUtils::DerivePublicKey(srvkey, origkey);
665 sock = new VDatagramSocket(this);
666 memcpy(sock->AuthKey, srvkey, VNetUtils::ChaCha20KeySize);
667 memcpy(sock->ClientKey, origkey, VNetUtils::ChaCha20KeySize);
668 sock->LanSocket = newsock;
669 sock->LanDriver = Drv;
670 sock->Addr = sendaddr;
671 sock->Address = Drv->AddrToString(&sock->Addr);
673 // send the connection request
674 GCon->Logf(NAME_DevNet, "trying %s", Drv->AddrToString(&sendaddr));
675 //SCR_Update();
676 Net->UpdateNetTime();
677 start_time = Net->GetNetTime();
679 for (reps = 0; reps < 3; ++reps) {
680 if (Net->CheckForUserAbort()) { ret = 0; break; }
682 R_OSDMsgShow("sending handshake");
684 vuint8 edata[MAX_DGRAM_SIZE];
686 VBitStreamWriter MsgOut(MAX_INFO_DGRAM_SIZE<<3);
687 WriteGameSignature(MsgOut);
688 // command
689 TmpByte = CCREQ_CONNECT;
690 MsgOut << TmpByte;
691 // protocol version
692 TmpByte = NET_PROTOCOL_VERSION_HI;
693 MsgOut << TmpByte;
694 TmpByte = NET_PROTOCOL_VERSION_LO;
695 MsgOut << TmpByte;
696 // password hash
697 vuint8 cldig[K8VNET_DIGEST_SIZE];
698 // we'll fix it later
699 vassert((MsgOut.GetPos()&7) == 0);
700 int digpos = MsgOut.GetPos()>>3;
701 memset(cldig, 0, K8VNET_DIGEST_SIZE);
702 MsgOut.Serialise(cldig, K8VNET_DIGEST_SIZE);
703 // mod info
704 vuint32 modhash = FL_GetNetWadsHash();
705 MsgOut << modhash;
706 vuint16 modcount = (vuint16)FL_GetNetWadsCount();
707 MsgOut << modcount;
709 // fix hash
710 crypto_blake2b_ctx hashctx;
711 crypto_blake2b_init(&hashctx, K8VNET_DIGEST_SIZE);
712 // hash key
713 crypto_blake2b_update(&hashctx, origkey, VNetUtils::ChaCha20KeySize);
714 // hash whole packet
715 crypto_blake2b_update(&hashctx, MsgOut.GetData(), MsgOut.GetNumBytes());
716 // hash password
717 crypto_blake2b_update(&hashctx, (const uint8_t *)*net_server_key.asStr(), (unsigned)net_server_key.asStr().length());
718 crypto_blake2b_final(&hashctx, cldig);
719 // update hash
720 memcpy(MsgOut.GetData()+digpos, cldig, K8VNET_DIGEST_SIZE);
722 // encrypt
723 int elen = EncryptInfoBitStream(edata, MsgOut, origkey);
724 if (elen <= 0) {
725 ret = -1; // network error
726 break;
728 Drv->Write(newsock, edata, elen, &sendaddr);
730 bool aborted = false;
731 vuint8 key[VNetUtils::ChaCha20KeySize];
732 replyWithServerKey = false;
733 do {
734 ret = Drv->Read(newsock, packetBuffer.data, MAX_DGRAM_SIZE, &readaddr);
735 if (ret == -2) ret = 0; // "no message" means "message with zero size"
737 // if we got something, validate it
738 if (ret > VNetUtils::ChaCha20KeySize+4) {
739 // is it from the right place?
740 if (sock->LanDriver->AddrCompare(&readaddr, &sendaddr) != 0) {
741 ret = 0;
742 continue;
745 // decrypt
746 msg = new VBitStreamReader();
747 if (!DecryptInfoBitStream(key, *msg, packetBuffer.data, ret)) {
748 delete msg;
749 ret = 0;
750 continue;
753 // check if we got the right key
754 // origkey may be used for rejection
755 if (memcmp(key, srvkey, VNetUtils::ChaCha20KeySize) != 0 &&
756 memcmp(key, origkey, VNetUtils::ChaCha20KeySize) != 0)
758 delete msg;
759 ret = 0;
760 continue;
762 replyWithServerKey = (memcmp(key, srvkey, VNetUtils::ChaCha20KeySize) == 0);
764 if (!CheckGameSignature(*msg)) {
765 ret = 0;
766 delete msg;
767 msg = nullptr;
768 continue;
770 } else if (ret > 0) {
771 ret = 0;
774 if (ret == 0) { aborted = Net->CheckForUserAbort(); if (aborted) break; }
775 Net->UpdateNetTime();
776 } while (ret == 0 && (Net->GetNetTime()-start_time) < 2.5);
778 if (ret || aborted) break;
780 GCon->Logf(NAME_DevNet, "still trying %s", Drv->AddrToString(&sendaddr));
781 //SCR_Update();
782 Net->UpdateNetTime();
783 start_time = Net->GetNetTime();
786 //SCR_Update();
788 if (ret == 0) {
789 reason = "No Response";
790 GCon->Logf(NAME_Error, "Connection failure: %s", *reason);
791 VStr::Cpy(Net->ReturnReason, *reason);
792 goto ErrorReturn;
795 if (ret == -1) {
796 reason = "Network Error";
797 GCon->Logf(NAME_Error, "Connection failure: %s", *reason);
798 VStr::Cpy(Net->ReturnReason, *reason);
799 goto ErrorReturn;
802 *msg << msgtype;
803 if (msgtype == CCREP_REJECT) {
804 vuint8 extflags = 0;
805 *msg << extflags;
806 *msg << reason;
807 GCon->Logf(NAME_Error, "Connection rejected: %s", *reason);
808 VStr::NCpy(Net->ReturnReason, *reason, 31);
809 if (extflags&1) {
810 TArray<VStr> list;
811 if (ReadPakList(list, *msg)) {
812 if (list.length()) {
813 GCon->Log(NAME_Error, "=== SERVER REJECTED CONNETION; WAD LIST: ===");
814 for (auto &&pak : list) GCon->Logf(NAME_Error, " %s", *pak);
818 if (!reason.isEmpty()) flWarningMessage = VStr("CONNECTION FAILURE\n\n")+reason;
819 goto ErrorReturn;
822 if (msgtype != CCREP_ACCEPT) {
823 reason = "Bad response";
824 GCon->Logf(NAME_Error, "Connection failure: %s", *reason);
825 VStr::Cpy(Net->ReturnReason, *reason);
826 goto ErrorReturn;
829 // check for good key
830 if (!replyWithServerKey) {
831 reason = "Bad response";
832 GCon->Logf(NAME_Error, "Connection failure: %s", *reason);
833 VStr::Cpy(Net->ReturnReason, *reason);
834 goto ErrorReturn;
837 *msg << otherProtoHi;
838 *msg << otherProtoLo;
839 *msg << newport;
841 if (msg->IsError()) {
842 reason = "Bad response";
843 GCon->Logf(NAME_Error, "Connection failure: %s", *reason);
844 VStr::Cpy(Net->ReturnReason, *reason);
845 goto ErrorReturn;
848 if (otherProtoHi != NET_PROTOCOL_VERSION_HI || otherProtoLo != NET_PROTOCOL_VERSION_LO) {
849 reason = "Bad protocol version";
850 GCon->Logf(NAME_Error, "Connection failure: %s", *reason);
851 VStr::Cpy(Net->ReturnReason, *reason);
852 goto ErrorReturn;
855 if (newport == 0) {
856 reason = "Bad port";
857 GCon->Logf(NAME_Error, "Connection failure: %s", *reason);
858 VStr::Cpy(Net->ReturnReason, *reason);
859 goto ErrorReturn;
862 delete msg;
864 // switch the connection to the specified address
865 memcpy(&sock->Addr, &readaddr, sizeof(sockaddr_t));
866 Drv->SetSocketPort(&sock->Addr, newport);
867 sock->Address = Drv->AddrToString(&sock->Addr);
869 GCon->Logf(NAME_DevNet, "Connection accepted at %s (redirected to port %u)", *sock->Address, newport);
870 Net->UpdateNetTime();
871 sock->LastMessageTime = Net->GetNetTime();
873 R_OSDMsgShow("receiving initial data");
875 //m_return_onerror = false;
876 return sock;
878 ErrorReturn:
879 delete sock;
880 sock = nullptr;
881 Drv->CloseSocket(newsock);
882 if (msg) delete msg;
883 SCR_Update();
884 #endif
886 return nullptr;
890 //==========================================================================
892 // VDatagramDriver::Connect
894 //==========================================================================
895 VSocket *VDatagramDriver::Connect (const char *host) {
896 for (int i = 0; i < VNetworkLocal::NumLanDrivers; ++i) {
897 if (VNetworkLocal::LanDrivers[i]->initialised) {
898 VSocket *ret = Connect(VNetworkLocal::LanDrivers[i], host);
899 if (ret) return ret;
902 return nullptr;
906 //==========================================================================
908 // VDatagramDriver::SendConnectionReject
910 //==========================================================================
911 void VDatagramDriver::SendConnectionReject (VNetLanDriver *Drv, const vuint8 key[VNetUtils::ChaCha20KeySize], VStr reason, int acceptsock, sockaddr_t clientaddr, bool sendModList) {
912 vuint8 TmpByte;
913 VBitStreamWriter MsgOut(MAX_INFO_DGRAM_SIZE<<3);
914 reason = reason.left(127);
915 WriteGameSignature(MsgOut);
917 TmpByte = CCREP_REJECT;
918 MsgOut << TmpByte;
919 // flags
920 TmpByte = (sendModList ? 1u : 0u);
921 MsgOut << TmpByte;
922 // reason
923 MsgOut << reason;
924 // modlist
925 if (sendModList) WritePakList(MsgOut);
927 vuint8 edata[MAX_DGRAM_SIZE];
928 int elen = EncryptInfoBitStream(edata, MsgOut, key);
929 if (elen <= 0) return;
931 Drv->Write(acceptsock, edata, elen, &clientaddr);
935 //==========================================================================
937 // VDatagramDriver::CheckNewConnections
939 //==========================================================================
940 VSocket *VDatagramDriver::CheckNewConnections (VNetLanDriver *Drv, bool rconOnly) {
941 #ifdef SERVER
942 sockaddr_t clientaddr;
943 sockaddr_t newaddr;
944 int acceptsock;
945 int newsock;
946 int len;
947 vuint8 command;
948 VDatagramSocket *sock;
949 vuint8 TmpByte;
950 VStr TmpStr;
952 Net->UpdateNetTime();
953 acceptsock = Drv->CheckNewConnections(rconOnly);
954 if (acceptsock == -1) return nullptr;
956 vuint8 clientKey[VNetUtils::ChaCha20KeySize];
957 vuint8 edata[MAX_DGRAM_SIZE];
959 len = Drv->Read(acceptsock, packetBuffer.data, MAX_DGRAM_SIZE, &clientaddr);
960 if (len == -2) len = 0; // "no message" means "zero size"
961 if (len < VNetUtils::ChaCha20KeySize+4) {
962 if (len > 0 && net_dbg_dump_rejected_connections) GCon->Logf(NAME_DevNet, "CONN: too short packet (%d) from %s", len, Drv->AddrToString(&clientaddr));
963 if (len < 0) GCon->Logf(NAME_DevNet, "CONN: error reading incoming packet from %s", Drv->AddrToString(&clientaddr));
964 return nullptr;
966 GCon->Logf(NAME_DevNet, "CONN: ACCEPTING something from %s", Drv->AddrToString(&clientaddr));
968 VBitStreamReader msg;
969 if (!DecryptInfoBitStream(clientKey, msg, packetBuffer.data, len)) {
970 GCon->Logf(NAME_DevNet, "CONN: got something wrong from %s", Drv->AddrToString(&clientaddr));
971 return nullptr;
974 if (!CheckGameSignature(msg)) {
975 /*if (net_dbg_dump_rejected_connections)*/ GCon->Logf(NAME_DevNet, "CONN: invalid packet type from %s", Drv->AddrToString(&clientaddr));
976 return nullptr;
979 msg << command;
980 //GCon->Logf(NAME_DevNet, "CONN: command=%u", command);
981 if (!rconOnly && command == CCREQ_SERVER_INFO) {
982 if (msg.AtEnd()) return nullptr;
984 // check request version
985 vuint8 reqVerHi = 7, reqVerLo = 7;
986 msg << reqVerHi << reqVerLo;
987 if (msg.IsError()) return nullptr;
989 // version sanity check
990 if (reqVerHi < 7 || reqVerHi > 42) {
991 // arbtrary
992 GCon->Logf(NAME_DevNet, "CONN: rejected info request due to wrong request version (%u.%u)", reqVerHi, reqVerLo);
994 //if (reqVerHi == NET_PROTOCOL_VERSION_HI && reqVerLo > NET_PROTOCOL_VERSION_LO) return nullptr;
996 GCon->Logf(NAME_DevNet, "CONN: sending server info to %s (request version is %u.%u)", Drv->AddrToString(&clientaddr), reqVerHi, reqVerLo);
998 VBitStreamWriter MsgOut(MAX_INFO_DGRAM_SIZE<<3);
999 WriteGameSignature(MsgOut);
1000 // type
1001 TmpByte = CCREP_SERVER_INFO;
1002 MsgOut << TmpByte;
1003 // protocol version
1004 TmpByte = NET_PROTOCOL_VERSION_HI;
1005 MsgOut << TmpByte;
1006 TmpByte = NET_PROTOCOL_VERSION_LO;
1007 MsgOut << TmpByte;
1008 // extflags
1009 TmpByte = 1u|(net_server_key.asStr().isEmpty() ? 0u : 2u); // has modlist
1010 MsgOut << TmpByte;
1011 // current number of players
1012 TmpByte = svs.num_connected;
1013 MsgOut << TmpByte;
1014 // max number of players
1015 TmpByte = svs.max_clients;
1016 MsgOut << TmpByte;
1017 // deathmatch type
1018 TmpByte = svs.deathmatch;
1019 MsgOut << TmpByte;
1020 // modlist hash
1021 vuint32 mhash = FL_GetNetWadsHash();
1022 MsgOut << mhash;
1023 // host name
1024 TmpStr = VNetworkLocal::HostName.asStr().left(127);
1025 MsgOut << TmpStr;
1026 // map name
1027 TmpStr = VStr(GLevel ? *GLevel->MapName : "intermission").left(63);
1028 MsgOut << TmpStr;
1029 // write pak list
1030 WritePakList(MsgOut);
1032 int elen = EncryptInfoBitStream(edata, MsgOut, clientKey);
1033 if (elen <= 0) return nullptr; // just in case
1034 Drv->Write(acceptsock, edata, elen, &clientaddr);
1035 return nullptr;
1038 // rcon
1039 if (command == CCREQ_RCON_COMMAND) {
1040 VStr mysecret = net_rcon_secret_key.asStr();
1041 if (mysecret.isEmpty()) return nullptr; // do noting
1043 // protocol version
1044 vuint16 pver = 0;
1045 msg << pver;
1046 if (msg.IsError() || pver != RCON_PROTO_VERSION) return nullptr; // do noting
1048 // read secret
1049 vassert((msg.GetPos()&7) == 0);
1050 int digpos = msg.GetPos()>>3;
1051 vuint8 cldig[K8VNET_DIGEST_SIZE];
1052 msg.Serialise(cldig, K8VNET_DIGEST_SIZE);
1053 if (msg.IsError()) return nullptr; // do noting
1055 // read command
1056 bool badCommand = false;
1057 VStr cmdtext;
1058 while (!msg.AtEnd()) {
1059 vuint8 ch = 0;
1060 msg << ch;
1061 if (msg.IsError()) return nullptr; // do noting
1062 if (!ch) break;
1063 if (ch != 9 && (ch < 32 || ch > 127)) { cmdtext += "?"; badCommand = true; continue; } // invalid char
1064 cmdtext += (char)ch;
1067 // check secret
1068 vuint8 svdig[K8VNET_DIGEST_SIZE];
1069 // clear hash buffer
1070 memset(msg.GetData()+digpos, 0, K8VNET_DIGEST_SIZE);
1071 crypto_blake2b_ctx hashctx;
1072 crypto_blake2b_init(&hashctx, K8VNET_DIGEST_SIZE);
1073 // hash key
1074 crypto_blake2b_update(&hashctx, clientKey, VNetUtils::ChaCha20KeySize);
1075 // hash whole packet
1076 crypto_blake2b_update(&hashctx, msg.GetData(), msg.GetNumBytes());
1077 // hash password
1078 crypto_blake2b_update(&hashctx, (const uint8_t *)*mysecret, (unsigned)mysecret.length());
1079 crypto_blake2b_final(&hashctx, svdig);
1080 // compare
1081 if (memcmp(cldig, svdig, K8VNET_DIGEST_SIZE) != 0) return nullptr; // invalid secret
1083 // check for duplicate command
1084 if (memcmp(rconLastKey, clientKey, VNetUtils::ChaCha20KeySize) != 0) {
1085 // new command
1086 memcpy(rconLastKey, clientKey, VNetUtils::ChaCha20KeySize);
1087 GCon->Logf(NAME_DevNet, "CONN: got new rcon command from %s: %s", Drv->AddrToString(&clientaddr), *cmdtext.quote(true));
1088 if (!badCommand) {
1089 cmdtext += "\n";
1090 GCmdBuf << cmdtext;
1092 } else {
1093 GCon->Logf(NAME_DevNet, "CONN: got duplicate rcon command from %s: %s", Drv->AddrToString(&clientaddr), *cmdtext.quote(true));
1096 // send reply
1097 VBitStreamWriter MsgOut(MAX_INFO_DGRAM_SIZE<<3);
1098 WriteGameSignature(MsgOut);
1099 TmpByte = CCREP_RCON_COMMAND;
1100 MsgOut << TmpByte;
1101 // protocol version
1102 pver = RCON_PROTO_VERSION;
1103 MsgOut << pver;
1104 // reply text
1105 VStr reptext;
1106 if (badCommand) reptext = "bad command text"; else reptext = "OK"; // why not?
1107 for (int f = 0; f < reptext.length(); ++f) {
1108 TmpByte = (vuint8)(reptext[f]);
1109 MsgOut << TmpByte;
1111 TmpByte = 0;
1112 MsgOut << TmpByte;
1114 // encrypt
1115 int elen = EncryptInfoBitStream(edata, MsgOut, clientKey);
1116 if (elen > 0) Drv->Write(acceptsock, edata, elen, &clientaddr);
1117 // done
1118 return nullptr;
1121 if (rconOnly) {
1122 return nullptr;
1125 // it should be "connect"
1126 if (command != CCREQ_CONNECT) {
1127 if (net_dbg_dump_rejected_connections) GCon->Logf(NAME_DevNet, "CONN: unknown packet command (%u) from %s", command, Drv->AddrToString(&clientaddr));
1128 SendConnectionReject(Drv, clientKey, "unknown query", acceptsock, clientaddr);
1129 return nullptr;
1132 // read version
1133 vuint8 remVerHi = 0, remVerLo = 0;
1134 msg << remVerHi << remVerLo;
1135 if (msg.IsError()) {
1136 GCon->Logf(NAME_DevNet, "connection error: no version");
1137 // send reject packet, why not?
1138 SendConnectionReject(Drv, clientKey, "invalid handshake", acceptsock, clientaddr);
1139 return nullptr;
1142 // read auth hash
1143 vassert((msg.GetPos()&7) == 0);
1144 int digpos = msg.GetPos()>>3;
1145 vuint8 cldig[K8VNET_DIGEST_SIZE];
1146 msg.Serialise(cldig, K8VNET_DIGEST_SIZE);
1147 if (msg.IsError()) {
1148 GCon->Logf(NAME_DevNet, "connection error: no password hash");
1149 // send reject packet, why not?
1150 SendConnectionReject(Drv, clientKey, "invalid handshake", acceptsock, clientaddr);
1151 return nullptr;
1154 // read modlist
1155 vuint32 modhash = 0;
1156 vuint16 modcount = 0;
1157 msg << modhash;
1158 msg << modcount;
1159 if (msg.IsError()) {
1160 GCon->Log(NAME_DevNet, "connection error: cannot read modlist");
1161 // send reject packet
1162 SendConnectionReject(Drv, clientKey, "invalid handshake", acceptsock, clientaddr);
1163 return nullptr;
1166 // fix packet
1167 memset(msg.GetData()+digpos, 0, K8VNET_DIGEST_SIZE);
1168 // check password
1169 vuint8 svdig[K8VNET_DIGEST_SIZE];
1170 crypto_blake2b_ctx hashctx;
1171 crypto_blake2b_init(&hashctx, K8VNET_DIGEST_SIZE);
1172 // hash key
1173 crypto_blake2b_update(&hashctx, clientKey, VNetUtils::ChaCha20KeySize);
1174 // hash whole packet
1175 crypto_blake2b_update(&hashctx, msg.GetData(), msg.GetNumBytes());
1176 // hash password
1177 crypto_blake2b_update(&hashctx, (const uint8_t *)*net_server_key.asStr(), (unsigned)net_server_key.asStr().length());
1178 crypto_blake2b_final(&hashctx, svdig);
1179 // compare
1180 if (memcmp(cldig, svdig, K8VNET_DIGEST_SIZE) != 0) {
1181 GCon->Logf(NAME_DevNet, "connection error: invalid password");
1182 // send reject packet, why not?
1183 SendConnectionReject(Drv, clientKey, "invalid password", acceptsock, clientaddr);
1184 return nullptr;
1187 // check version
1188 if (remVerHi != NET_PROTOCOL_VERSION_HI || remVerLo != NET_PROTOCOL_VERSION_LO) {
1189 GCon->Logf(NAME_DevNet, "connection error: invalid protocol version, got %u:%u, but expected %u:%u", remVerHi, remVerLo, NET_PROTOCOL_VERSION_HI, NET_PROTOCOL_VERSION_LO);
1190 // send reject packet, why not?
1191 SendConnectionReject(Drv, clientKey, "invalid protocol version", acceptsock, clientaddr);
1192 return nullptr;
1195 // check modlist
1196 if (modhash != FL_GetNetWadsHash() || modcount != (vuint16)FL_GetNetWadsCount()) {
1197 GCon->Log(NAME_DevNet, "connection error: incompatible mod list");
1198 // send reject packet
1199 SendConnectionReject(Drv, clientKey, "incompatible loaded mods list", acceptsock, clientaddr, true); // send modlist
1200 return nullptr;
1203 // see if this guy is already connected
1204 for (VSocket *as = Net->ActiveSockets; as; as = as->Next) {
1205 if (as->Driver != this) continue;
1206 VDatagramSocket *s = (VDatagramSocket *)as;
1207 if (s->Invalid) continue;
1208 // if we already have this client, resend accept packet, and replace client address
1209 if (memcmp(clientKey, s->ClientKey, VNetUtils::ChaCha20KeySize) == 0) {
1210 if (Net->GetNetTime()-s->ConnectTime < 2.0) {
1211 GCon->Logf(NAME_DevNet, "CONN: duplicate connection request from %s (this is ok)", Drv->AddrToString(&clientaddr));
1212 s->Addr = clientaddr; // update address
1213 // yes, so send a duplicate reply
1214 VBitStreamWriter MsgOut(MAX_INFO_DGRAM_SIZE<<3);
1215 Drv->GetSocketAddr(s->LanSocket, &newaddr);
1216 WriteGameSignature(MsgOut);
1217 TmpByte = CCREP_ACCEPT;
1218 MsgOut << TmpByte;
1219 // protocol version
1220 TmpByte = NET_PROTOCOL_VERSION_HI;
1221 MsgOut << TmpByte;
1222 TmpByte = NET_PROTOCOL_VERSION_LO;
1223 MsgOut << TmpByte;
1224 // client port
1225 vint16 TmpPort = Drv->GetSocketPort(&newaddr);
1226 MsgOut << TmpPort;
1227 int elen = EncryptInfoBitStream(edata, MsgOut, clientKey);
1228 if (elen > 0) {
1229 Drv->Write(acceptsock, edata, elen, &clientaddr);
1230 return nullptr;
1233 // drop this
1234 GCon->Logf(NAME_DevNet, "CONN: DROPPING %s", Drv->AddrToString(&clientaddr));
1235 // marking existing socket as "invalid" will block all i/o on in (i/o attempt will return error)
1236 // the corresponding connection will close itself on i/o error
1237 s->Invalid = true;
1238 return nullptr;
1240 // it is prolly somebody coming back in from a crash/disconnect
1241 // so close the old socket and let their retry get them back in
1242 // but don't do that for localhost
1243 // meh, use strict comparison -- routers and such...
1244 if (ret != 0 && Drv->IsLocalAddress(&clientaddr) && Drv->IsLocalAddress(&s->Addr)) {
1245 GCon->Logf(NAME_DevNet, "CONN: LOCALHOST for %s", Drv->AddrToString(&clientaddr));
1246 } else if (ret == 0) {
1247 GCon->Logf(NAME_DevNet, "CONN: RETRYWAIT for %s", Drv->AddrToString(&clientaddr));
1248 s->Invalid = true;
1249 return nullptr;
1255 if (svs.num_connected >= svs.max_clients) {
1256 // no room; try to let him know
1257 SendConnectionReject(Drv, clientKey, "server is full", acceptsock, clientaddr);
1258 return nullptr;
1261 GCon->Logf(NAME_DevNet, "new client from %s (connecting back)", Drv->AddrToString(&clientaddr));
1263 // update client key
1264 vuint8 srvKey[VNetUtils::ChaCha20KeySize];
1265 VNetUtils::DerivePublicKey(srvKey, clientKey);
1266 memcpy(clientKey, srvKey, sizeof(clientKey));
1268 // allocate new "listening" network socket
1269 newsock = Drv->OpenListenSocket(0);
1270 if (newsock == -1) return nullptr;
1272 // allocate a VSocket
1273 // everything is allocated, just fill in the details
1274 sock = new VDatagramSocket(this);
1275 memcpy(sock->AuthKey, srvKey, VNetUtils::ChaCha20KeySize);
1276 memcpy(sock->ClientKey, clientKey, VNetUtils::ChaCha20KeySize);
1277 sock->LanSocket = newsock;
1278 sock->LanDriver = Drv;
1279 sock->Addr = clientaddr;
1280 sock->Address = Drv->AddrToString(&clientaddr);
1282 Drv->GetSocketAddr(newsock, &newaddr);
1284 GCon->Logf(NAME_DevNet, "allocated socket %s for client %s", Drv->AddrToString(&newaddr), *sock->Address);
1286 // send him back the info about the server connection he has been allocated
1287 VBitStreamWriter MsgOut(MAX_INFO_DGRAM_SIZE<<3);
1288 WriteGameSignature(MsgOut);
1289 TmpByte = CCREP_ACCEPT;
1290 MsgOut << TmpByte;
1291 // protocol version
1292 TmpByte = NET_PROTOCOL_VERSION_HI;
1293 MsgOut << TmpByte;
1294 TmpByte = NET_PROTOCOL_VERSION_LO;
1295 MsgOut << TmpByte;
1296 // client port
1297 vint16 TmpPort = Drv->GetSocketPort(&newaddr);
1298 MsgOut << TmpPort;
1299 int elen = EncryptInfoBitStream(edata, MsgOut, clientKey);
1300 if (elen <= 0) {
1301 delete sock;
1302 return nullptr;
1304 Drv->Write(acceptsock, edata, elen, &clientaddr);
1306 return sock;
1307 #else
1308 return nullptr;
1309 #endif
1313 //==========================================================================
1315 // VDatagramDriver::CheckNewConnections
1317 //==========================================================================
1318 VSocket *VDatagramDriver::CheckNewConnections (bool rconOnly) {
1319 for (int i = 0; i < Net->NumLanDrivers; ++i) {
1320 if (Net->LanDrivers[i]->initialised) {
1321 VSocket *ret = CheckNewConnections(Net->LanDrivers[i], rconOnly);
1322 if (ret != nullptr) return ret;
1325 return nullptr;
1329 //==========================================================================
1331 // VDatagramDriver::ResolveMasterAddr
1333 //==========================================================================
1334 bool VDatagramDriver::ResolveMasterAddr (VNetLanDriver *Drv) {
1335 if (LastMasterAddrStr == MasterSrv) return !LastMasterIsBad;
1336 LastMasterAddrStr = MasterSrv;
1337 if (Drv->GetAddrFromName(*LastMasterAddrStr, &LastMasterAddr, MASTER_SERVER_PORT) == -1) {
1338 GCon->Logf(NAME_DevNet, "Could not resolve server name (%s)", *LastMasterAddrStr);
1339 LastMasterIsBad = true;
1340 return false;
1342 GCon->Logf(NAME_DevNet, "resolved master address '%s' to '%s'", *LastMasterAddrStr, Drv->AddrToString(&LastMasterAddr));
1343 LastMasterIsBad = false;
1344 return true;
1348 //==========================================================================
1350 // VDatagramDriver::UpdateMaster
1352 //==========================================================================
1353 void VDatagramDriver::UpdateMaster (VNetLanDriver *Drv) {
1354 // see if we can resolve the host name
1355 if (!ResolveMasterAddr(Drv)) return;
1357 if (Drv->net_acceptsocket == -1) {
1358 GCon->Log(NAME_DevNet, "Listen socket not open");
1359 return;
1362 // send the connection request
1363 VBitStreamWriter MsgOut(MAX_DGRAM_SIZE<<3);
1364 WriteGameSignature(MsgOut);
1365 vuint8 TmpByte = MASTER_PROTO_VERSION;
1366 MsgOut << TmpByte;
1367 TmpByte = MCREQ_JOIN;
1368 MsgOut << TmpByte;
1369 TmpByte = NET_PROTOCOL_VERSION_HI;
1370 MsgOut << TmpByte;
1371 TmpByte = NET_PROTOCOL_VERSION_LO;
1372 MsgOut << TmpByte;
1373 Drv->Write(Drv->net_acceptsocket, MsgOut.GetData(), MsgOut.GetNumBytes(), &LastMasterAddr);
1377 //==========================================================================
1379 // VDatagramDriver::UpdateMaster
1381 //==========================================================================
1382 void VDatagramDriver::UpdateMaster () {
1383 if (!UseMaster) return;
1384 for (int i = 0; i < VNetworkLocal::NumLanDrivers; ++i) {
1385 if (VNetworkLocal::LanDrivers[i]->initialised) UpdateMaster(VNetworkLocal::LanDrivers[i]);
1390 //==========================================================================
1392 // VDatagramDriver::QuitMaster
1394 //==========================================================================
1395 void VDatagramDriver::QuitMaster (VNetLanDriver *Drv) {
1396 // see if we can resolve the host name
1397 if (!ResolveMasterAddr(Drv)) return;
1399 if (Drv->net_acceptsocket == -1) {
1400 GCon->Log(NAME_DevNet, "Listen socket not open");
1401 return;
1404 // send the quit request
1405 VBitStreamWriter MsgOut(MAX_DGRAM_SIZE<<3);
1406 WriteGameSignature(MsgOut);
1407 vuint8 TmpByte = MASTER_PROTO_VERSION;
1408 MsgOut << TmpByte;
1409 TmpByte = MCREQ_QUIT;
1410 MsgOut << TmpByte;
1411 Drv->Write(Drv->net_acceptsocket, MsgOut.GetData(), MsgOut.GetNumBytes(), &LastMasterAddr);
1415 //==========================================================================
1417 // VDatagramDriver::QuitMaster
1419 //==========================================================================
1420 void VDatagramDriver::QuitMaster () {
1421 if (!UseMaster) return;
1422 for (int i = 0; i < VNetworkLocal::NumLanDrivers; ++i) {
1423 if (VNetworkLocal::LanDrivers[i]->initialised) QuitMaster(VNetworkLocal::LanDrivers[i]);
1428 //==========================================================================
1430 // VDatagramDriver::QueryMaster
1432 //==========================================================================
1433 bool VDatagramDriver::QueryMaster (VNetLanDriver *Drv, bool xmit) {
1434 sockaddr_t myaddr;
1435 sockaddr_t readaddr;
1436 sockaddr_t tmpaddr;
1437 vuint8 control;
1438 vuint8 TmpByte;
1440 if (Drv->MasterQuerySocket < 0) Drv->MasterQuerySocket = Drv->OpenListenSocket(0);
1442 Drv->GetSocketAddr(Drv->MasterQuerySocket, &myaddr);
1443 if (xmit) {
1444 // see if we can resolve the host name
1445 if (!ResolveMasterAddr(Drv)) return false;
1446 // send the query request
1447 VBitStreamWriter MsgOut(MAX_DGRAM_SIZE<<3);
1448 WriteGameSignature(MsgOut);
1449 TmpByte = MASTER_PROTO_VERSION;
1450 MsgOut << TmpByte;
1451 TmpByte = MCREQ_LIST;
1452 MsgOut << TmpByte;
1453 Drv->Write(Drv->MasterQuerySocket, MsgOut.GetData(), MsgOut.GetNumBytes(), &LastMasterAddr);
1454 GCon->Logf(NAME_DevNet, "sent query to master at %s", Drv->AddrToString(&LastMasterAddr));
1455 return false;
1458 //GCon->Logf(NAME_DevNet, "waiting for master reply");
1460 bool res = false;
1461 int pktleft = 256;
1462 while (pktleft-- > 0) {
1463 int len = Drv->Read(Drv->MasterQuerySocket, packetBuffer.data, MAX_DGRAM_SIZE, &readaddr);
1464 if (len < 1) continue; // error, no message, zero message
1466 GCon->Logf(NAME_DevNet, "got master reply from %s (%d bytes)", Drv->AddrToString(&readaddr), len);
1467 // is the cache full?
1468 //if (Net->HostCacheCount == HOSTCACHESIZE) continue;
1470 //GCon->Logf("processing master reply");
1471 VBitStreamReader msg(packetBuffer.data, len<<3);
1472 if (!CheckGameSignature(msg)) continue;
1474 msg << TmpByte;
1475 if (msg.IsError() || TmpByte != MASTER_PROTO_VERSION) continue;
1477 msg << control;
1478 if (msg.IsError() || control != MCREP_LIST) continue;
1480 msg << control; // control byte: bit 0 means "first packet", bit 1 means "last packet"
1481 if (msg.IsError()) continue;
1482 //GCon->Logf(NAME_DevNet, " control byte: 0x%02x", (unsigned)control);
1484 //if ((control&0x01) == 0) continue; // first packed is missing, but nobody cares
1486 while (!msg.AtEnd()) {
1487 vuint8 pver0 = 0, pver1 = 0;
1488 tmpaddr = readaddr;
1489 msg.Serialise(&pver0, 1);
1490 msg.Serialise(&pver1, 1);
1491 msg.Serialise(tmpaddr.sa_data+2, 4);
1492 msg.Serialise(tmpaddr.sa_data, 2);
1493 if (!msg.IsError() && pver0 == NET_PROTOCOL_VERSION_HI && pver1 == NET_PROTOCOL_VERSION_LO) {
1494 GCon->Logf(NAME_DevNet, " sending server query to %s", Drv->AddrToString(&tmpaddr));
1495 VBitStreamWriter MsgOut(MAX_INFO_DGRAM_SIZE<<3);
1496 WriteGameSignature(MsgOut);
1497 TmpByte = CCREQ_SERVER_INFO;
1498 MsgOut << TmpByte;
1499 TmpByte = NET_PROTOCOL_VERSION_HI;
1500 MsgOut << TmpByte;
1501 TmpByte = NET_PROTOCOL_VERSION_LO;
1502 MsgOut << TmpByte;
1503 // encrypt and send
1504 vuint8 edata[MAX_DGRAM_SIZE];
1505 vuint8 key[VNetUtils::ChaCha20KeySize];
1506 VNetUtils::GenerateKey(key);
1507 int elen = EncryptInfoBitStream(edata, MsgOut, key);
1508 if (elen > 0) Drv->Write(Drv->controlSock, edata, elen, &tmpaddr);
1509 } else if (msg.IsError()) {
1510 GCon->Logf(NAME_DevNet, " server: %s, error reading reply, size=%d; pos=%d", Drv->AddrToString(&tmpaddr), msg.GetNumBits(), msg.GetPos());
1511 } else {
1512 GCon->Logf(NAME_DevNet, " server: %s, bad proto version %u:%u", Drv->AddrToString(&tmpaddr), pver0, pver1);
1514 int f = 0;
1515 while (f < len) {
1516 VStr s = " ";
1517 for (int c = 0; f+c < len && c < 16; ++c) {
1518 if (c == 8) s += " ";
1519 s += " ";
1520 const char *hexd = "0123456789abcdef";
1521 s += hexd[(packetBuffer.data[f+c]>>4)&0x0fu];
1522 s += hexd[packetBuffer.data[f+c]&0x0fu];
1524 GCon->Logf(NAME_DevNet, "%s", *s);
1525 f += 16;
1531 //if (control&0x02) return true; // nobody cares, again
1532 //return true;
1533 res = true;
1536 return res;
1540 //==========================================================================
1542 // VDatagramDriver::QueryMaster
1544 //==========================================================================
1545 bool VDatagramDriver::QueryMaster (bool xmit) {
1546 for (int i = 0; i < VNetworkLocal::NumLanDrivers; ++i) {
1547 if (Net->HostCacheCount == HOSTCACHESIZE) break;
1548 if (VNetworkLocal::LanDrivers[i]->initialised) {
1549 if (QueryMaster(VNetworkLocal::LanDrivers[i], xmit)) return true;
1552 return false;
1556 //==========================================================================
1558 // VDatagramDriver::EndQueryMaster
1560 //==========================================================================
1561 void VDatagramDriver::EndQueryMaster () {
1562 for (int i = 0; i < VNetworkLocal::NumLanDrivers; ++i) {
1563 if (VNetworkLocal::LanDrivers[i]->initialised && VNetworkLocal::LanDrivers[i]->MasterQuerySocket > 0) {
1564 VNetworkLocal::LanDrivers[i]->CloseSocket(VNetworkLocal::LanDrivers[i]->MasterQuerySocket);
1565 VNetworkLocal::LanDrivers[i]->MasterQuerySocket = -1;
1571 //==========================================================================
1573 // VDatagramDriver::Shutdown
1575 //==========================================================================
1576 void VDatagramDriver::Shutdown () {
1577 // shutdown the lan drivers
1578 for (int i = 0; i < Net->NumLanDrivers; ++i) {
1579 if (Net->LanDrivers[i]->initialised) {
1580 Net->LanDrivers[i]->Shutdown();
1581 Net->LanDrivers[i]->initialised = false;
1587 //==========================================================================
1589 // VDatagramSocket::~VDatagramSocket
1591 //==========================================================================
1592 VDatagramSocket::~VDatagramSocket () {
1593 LanDriver->CloseSocket(LanSocket);
1597 //==========================================================================
1599 // VDatagramSocket::GetMessage
1601 // dest should be at least `MAX_DGRAM_SIZE+4` (just in case)
1602 // returns number of bytes received, 0 for "no message", -1 for error
1604 //==========================================================================
1605 int VDatagramSocket::GetMessage (void *dest, size_t destSize) {
1606 if (Invalid) return -1;
1607 if (destSize == 0) return -1;
1608 if (!dest) return -1;
1610 sockaddr_t readaddr;
1611 vuint8 data[MAX_DGRAM_SIZE+4];
1613 for (;;) {
1614 // read message
1615 int length = LanDriver->Read(LanSocket, data, NET_DATAGRAMSIZE, &readaddr);
1616 if (length == 0) continue; // zero-sized message, oops
1618 if (length == -2) {
1619 // no more messages
1620 //if (net_dbg_dump_rejected_connections) GCon->Logf(NAME_DevNet, "CONN: no data from %s (expected address is %s)", LanDriver->AddrToString(&readaddr), *LanDriver->AddrToString(&Addr));
1621 return 0;
1624 if (length < 0) {
1625 GCon->Logf(NAME_DevNet, "%s: Read error", LanDriver->AddrToString(&Addr));
1626 return -1;
1629 if (LanDriver->AddrCompare(&readaddr, &Addr) != 0) {
1630 if (net_dbg_dump_rejected_connections) GCon->Logf(NAME_DevNet, "CONN: rejected packet from %s due to wrong address (%s expected)", LanDriver->AddrToString(&readaddr), LanDriver->AddrToString(&Addr));
1631 UpdateRejectedStats(length);
1632 continue;
1635 UpdateReceivedStats(length);
1637 if ((unsigned)length > destSize) {
1638 GCon->Logf(NAME_DevNet, "%s: Read error (message too big)", LanDriver->AddrToString(&Addr));
1639 return -1;
1642 if (length) memcpy(dest, data, length);
1643 return length;
1645 abort();
1649 //==========================================================================
1651 // VDatagramSocket::SendMessage
1653 // Send a packet over the net connection.
1654 // returns 1 if the packet was sent properly
1655 // returns -1 if the connection died
1656 // returns 0 if the connection is oversaturated (cannot send more data)
1658 //==========================================================================
1659 int VDatagramSocket::SendMessage (const vuint8 *Data, vuint32 Length) {
1660 vensure(Length > 0);
1661 vensure(Length <= MAX_DGRAM_SIZE);
1662 if (Invalid) return -1;
1663 const int res = LanDriver->Write(LanSocket, Data, Length, &Addr);
1664 if (res > 0) UpdateSentStats(Length);
1665 if (res == -2) return 0;
1666 //GCon->Logf(NAME_DevNet, "+++ SK:%d(%s): sent %u bytes of data (res=%d)", LanSocket, LanDriver->AddrToString(&Addr), Length, res);
1667 return res;
1671 //==========================================================================
1673 // VDatagramSocket::IsLocalConnection
1675 //==========================================================================
1676 bool VDatagramSocket::IsLocalConnection () const noexcept {
1677 return false;
1681 //==========================================================================
1683 // PrintStats
1685 //==========================================================================
1686 static void PrintStats (VSocket *sock) {
1687 GCon->Logf(NAME_DevNet, "=== SOCKET %s ===", *sock->Address);
1688 GCon->Logf(NAME_DevNet, " minimalSentPacket = %u", sock->minimalSentPacket);
1689 GCon->Logf(NAME_DevNet, " maximalSentPacket = %u", sock->maximalSentPacket);
1690 GCon->Logf(NAME_DevNet, " bytesSent = %s", *VSocketPublic::u64str(sock->bytesSent));
1691 GCon->Logf(NAME_DevNet, " bytesReceived = %s", *VSocketPublic::u64str(sock->bytesReceived));
1692 GCon->Logf(NAME_DevNet, " bytesRejected = %s", *VSocketPublic::u64str(sock->bytesRejected));
1696 //==========================================================================
1698 // COMMAND NetStats
1700 //==========================================================================
1701 COMMAND(NetStats) {
1702 VSocket *s;
1704 VNetworkLocal *Net = (VNetworkLocal *)GNet;
1705 if (Args.length() == 1) {
1706 GCon->Logf(NAME_DevNet, "unreliable messages sent = %d", Net->UnreliableMessagesSent);
1707 GCon->Logf(NAME_DevNet, "unreliable messages recv = %d", Net->UnreliableMessagesReceived);
1708 GCon->Logf(NAME_DevNet, "packetsSent = %d", Net->packetsSent);
1709 GCon->Logf(NAME_DevNet, "packetsReSent = %d", Net->packetsReSent);
1710 GCon->Logf(NAME_DevNet, "packetsReceived = %d", Net->packetsReceived);
1711 GCon->Logf(NAME_DevNet, "receivedDuplicateCount = %d", Net->receivedDuplicateCount);
1712 GCon->Logf(NAME_DevNet, "shortPacketCount = %d", Net->shortPacketCount);
1713 GCon->Logf(NAME_DevNet, "droppedDatagrams = %d", Net->droppedDatagrams);
1714 GCon->Logf(NAME_DevNet, "minimalSentPacket = %u", Net->minimalSentPacket);
1715 GCon->Logf(NAME_DevNet, "maximalSentPacket = %u", Net->maximalSentPacket);
1716 GCon->Logf(NAME_DevNet, "bytesSent = %s", *VSocketPublic::u64str(Net->bytesSent));
1717 GCon->Logf(NAME_DevNet, "bytesReceived = %s", *VSocketPublic::u64str(Net->bytesReceived));
1718 GCon->Logf(NAME_DevNet, "bytesRejected = %s", *VSocketPublic::u64str(Net->bytesRejected));
1719 } else {
1720 for (s = Net->ActiveSockets; s; s = s->Next) {
1721 bool hit = false;
1722 for (int f = 1; f < Args.length(); ++f) if (s->Address.globMatchCI(Args[f])) { hit = true; break; }
1723 if (hit) PrintStats(s);