1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
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.
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.
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/>.
25 //**************************************************************************
27 //** Datagram driver, handles all LAN drivers
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.
39 // other data is encrypted with ChaCha20
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
52 // other data is encrypted with ChaCha20
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
63 // other data is encrypted with ChaCha20
66 // vuint8 CCREP_ACCEPT
67 // vuint8 net_protocol_version_hi NET_PROTOCOL_VERSION_HI
68 // vuint8 net_protocol_version_lo NET_PROTOCOL_VERSION_LO
74 // other data is encrypted with ChaCha20
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
86 // other data is encrypted with ChaCha20
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
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"
105 #include "../server/server.h"
106 #include "../filesys/files.h"
108 # include "../screen.h"
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
{
129 VNetLanDriver
*LanDriver
;
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
{
147 enum { MASTER_SERVER_PORT
= 26002 };
148 enum { MASTER_PROTO_VERSION
= 1 };
151 MAX_INFO_DGRAM_SIZE
= MAX_DGRAM_SIZE
-VNetUtils::ChaCha20HeaderSize
,
157 CCREQ_SERVER_INFO
= 2,
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,
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
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:
182 other data is encrypted with ChaCha20
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:
194 other data is encrypted with ChaCha20
198 vuint8 CCREP_RCON_COMMAND
199 vuint16 RCON_PROTO_VERSION
200 bytes errmsg ; message (zero-terminated, may be empty)
207 CCREP_SERVER_INFO
= 13,
210 // master server request
217 // master server reply
223 vuint8 data
[MAX_DGRAM_SIZE
];
227 VStr LastMasterAddrStr
;
228 sockaddr_t LastMasterAddr
;
229 bool LastMasterIsBad
;
232 bool ResolveMasterAddr (VNetLanDriver
*Drv
);
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
);
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
) {
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
) {
330 for (int f
= 0; f
< list
.length(); ++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
]);
343 // write trailing zero
351 //==========================================================================
353 // VDatagramDriver::ReadPakList
357 //==========================================================================
358 bool VDatagramDriver::ReadPakList (TArray
<VStr
> &list
, VBitStreamReader
&strm
) {
359 char buf
[MAX_DGRAM_SIZE
+2];
360 while (!strm
.AtEnd()) {
365 if (strm
.IsError()) return false;
366 if (bufpos
>= (int)sizeof(buf
)-2) return false;
370 if (buf
[0] == 0) return true; // done
371 list
.append(VStr(buf
));
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
;
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;
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
) {
435 if (srclen
< VNetUtils::ChaCha20KeySize
+4) {
440 int dlen
= VNetUtils::DecryptInfoPacket(key
, srcbuf
, srcbuf
, srclen
);
447 strm
.SetupFrom((const vuint8
*)srcbuf
, dlen
<<3);
452 //==========================================================================
454 // VDatagramDriver::SearchForHosts
456 //==========================================================================
457 void VDatagramDriver::SearchForHosts (VNetLanDriver
*Drv
, bool xmit
, bool ForMaster
) {
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
;
474 TmpByte
= NET_PROTOCOL_VERSION_HI
;
476 TmpByte
= NET_PROTOCOL_VERSION_LO
;
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));
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;
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;
511 if (msg
.IsError() || msgtype
!= CCREP_SERVER_INFO
) continue;
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");
532 ++Net
->HostCacheCount
;
533 vassert(n
>= 0 && n
< Net
->HostCacheCount
);
534 hostcache_t
*hinfo
= &Net
->HostCache
[n
];
537 vuint8 protoHi
= 0, protoLo
= 0;
538 msg
<< protoHi
<< protoLo
;
544 hinfo
->Users
= TmpByte
;
547 hinfo
->MaxUsers
= TmpByte
;
550 hinfo
->DeathMatch
= TmpByte
;
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
;
570 hinfo
->WadFiles
.clear();
571 if (!msg
.IsError() && (extflags
&1)) ReadPakList(hinfo
->WadFiles
, msg
);
575 hinfo
->WadFiles
.clear();
576 --Net
->HostCacheCount
;
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';
597 ++(*Net->HostCache[n].Name.GetMutableCharPointer(i-1));
598 //Net->HostCache[n].Name[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
) {
630 VDatagramSocket
*sock
;
639 VBitStreamReader
*msg
= nullptr;
641 vuint8 otherProtoHi
, otherProtoLo
;
643 if (!host
|| !host
[0]) return nullptr;
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
));
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
);
689 TmpByte
= CCREQ_CONNECT
;
692 TmpByte
= NET_PROTOCOL_VERSION_HI
;
694 TmpByte
= NET_PROTOCOL_VERSION_LO
;
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
);
704 vuint32 modhash
= FL_GetNetWadsHash();
706 vuint16 modcount
= (vuint16
)FL_GetNetWadsCount();
710 crypto_blake2b_ctx hashctx
;
711 crypto_blake2b_init(&hashctx
, K8VNET_DIGEST_SIZE
);
713 crypto_blake2b_update(&hashctx
, origkey
, VNetUtils::ChaCha20KeySize
);
715 crypto_blake2b_update(&hashctx
, MsgOut
.GetData(), MsgOut
.GetNumBytes());
717 crypto_blake2b_update(&hashctx
, (const uint8_t *)*net_server_key
.asStr(), (unsigned)net_server_key
.asStr().length());
718 crypto_blake2b_final(&hashctx
, cldig
);
720 memcpy(MsgOut
.GetData()+digpos
, cldig
, K8VNET_DIGEST_SIZE
);
723 int elen
= EncryptInfoBitStream(edata
, MsgOut
, origkey
);
725 ret
= -1; // network error
728 Drv
->Write(newsock
, edata
, elen
, &sendaddr
);
730 bool aborted
= false;
731 vuint8 key
[VNetUtils::ChaCha20KeySize
];
732 replyWithServerKey
= false;
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) {
746 msg
= new VBitStreamReader();
747 if (!DecryptInfoBitStream(key
, *msg
, packetBuffer
.data
, ret
)) {
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)
762 replyWithServerKey
= (memcmp(key
, srvkey
, VNetUtils::ChaCha20KeySize
) == 0);
764 if (!CheckGameSignature(*msg
)) {
770 } else if (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
));
782 Net
->UpdateNetTime();
783 start_time
= Net
->GetNetTime();
789 reason
= "No Response";
790 GCon
->Logf(NAME_Error
, "Connection failure: %s", *reason
);
791 VStr::Cpy(Net
->ReturnReason
, *reason
);
796 reason
= "Network Error";
797 GCon
->Logf(NAME_Error
, "Connection failure: %s", *reason
);
798 VStr::Cpy(Net
->ReturnReason
, *reason
);
803 if (msgtype
== CCREP_REJECT
) {
807 GCon
->Logf(NAME_Error
, "Connection rejected: %s", *reason
);
808 VStr::NCpy(Net
->ReturnReason
, *reason
, 31);
811 if (ReadPakList(list
, *msg
)) {
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
;
822 if (msgtype
!= CCREP_ACCEPT
) {
823 reason
= "Bad response";
824 GCon
->Logf(NAME_Error
, "Connection failure: %s", *reason
);
825 VStr::Cpy(Net
->ReturnReason
, *reason
);
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
);
837 *msg
<< otherProtoHi
;
838 *msg
<< otherProtoLo
;
841 if (msg
->IsError()) {
842 reason
= "Bad response";
843 GCon
->Logf(NAME_Error
, "Connection failure: %s", *reason
);
844 VStr::Cpy(Net
->ReturnReason
, *reason
);
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
);
857 GCon
->Logf(NAME_Error
, "Connection failure: %s", *reason
);
858 VStr::Cpy(Net
->ReturnReason
, *reason
);
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;
881 Drv
->CloseSocket(newsock
);
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
);
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
) {
913 VBitStreamWriter
MsgOut(MAX_INFO_DGRAM_SIZE
<<3);
914 reason
= reason
.left(127);
915 WriteGameSignature(MsgOut
);
917 TmpByte
= CCREP_REJECT
;
920 TmpByte
= (sendModList
? 1u : 0u);
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
) {
942 sockaddr_t clientaddr
;
948 VDatagramSocket
*sock
;
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
));
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
));
974 if (!CheckGameSignature(msg
)) {
975 /*if (net_dbg_dump_rejected_connections)*/ GCon
->Logf(NAME_DevNet
, "CONN: invalid packet type from %s", Drv
->AddrToString(&clientaddr
));
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) {
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
);
1001 TmpByte
= CCREP_SERVER_INFO
;
1004 TmpByte
= NET_PROTOCOL_VERSION_HI
;
1006 TmpByte
= NET_PROTOCOL_VERSION_LO
;
1009 TmpByte
= 1u|(net_server_key
.asStr().isEmpty() ? 0u : 2u); // has modlist
1011 // current number of players
1012 TmpByte
= svs
.num_connected
;
1014 // max number of players
1015 TmpByte
= svs
.max_clients
;
1018 TmpByte
= svs
.deathmatch
;
1021 vuint32 mhash
= FL_GetNetWadsHash();
1024 TmpStr
= VNetworkLocal::HostName
.asStr().left(127);
1027 TmpStr
= VStr(GLevel
? *GLevel
->MapName
: "intermission").left(63);
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
);
1039 if (command
== CCREQ_RCON_COMMAND
) {
1040 VStr mysecret
= net_rcon_secret_key
.asStr();
1041 if (mysecret
.isEmpty()) return nullptr; // do noting
1046 if (msg
.IsError() || pver
!= RCON_PROTO_VERSION
) return nullptr; // do noting
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
1056 bool badCommand
= false;
1058 while (!msg
.AtEnd()) {
1061 if (msg
.IsError()) return nullptr; // do noting
1063 if (ch
!= 9 && (ch
< 32 || ch
> 127)) { cmdtext
+= "?"; badCommand
= true; continue; } // invalid char
1064 cmdtext
+= (char)ch
;
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
);
1074 crypto_blake2b_update(&hashctx
, clientKey
, VNetUtils::ChaCha20KeySize
);
1075 // hash whole packet
1076 crypto_blake2b_update(&hashctx
, msg
.GetData(), msg
.GetNumBytes());
1078 crypto_blake2b_update(&hashctx
, (const uint8_t *)*mysecret
, (unsigned)mysecret
.length());
1079 crypto_blake2b_final(&hashctx
, svdig
);
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) {
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));
1093 GCon
->Logf(NAME_DevNet
, "CONN: got duplicate rcon command from %s: %s", Drv
->AddrToString(&clientaddr
), *cmdtext
.quote(true));
1097 VBitStreamWriter
MsgOut(MAX_INFO_DGRAM_SIZE
<<3);
1098 WriteGameSignature(MsgOut
);
1099 TmpByte
= CCREP_RCON_COMMAND
;
1102 pver
= RCON_PROTO_VERSION
;
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
]);
1115 int elen
= EncryptInfoBitStream(edata
, MsgOut
, clientKey
);
1116 if (elen
> 0) Drv
->Write(acceptsock
, edata
, elen
, &clientaddr
);
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
);
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
);
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
);
1155 vuint32 modhash
= 0;
1156 vuint16 modcount
= 0;
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
);
1167 memset(msg
.GetData()+digpos
, 0, K8VNET_DIGEST_SIZE
);
1169 vuint8 svdig
[K8VNET_DIGEST_SIZE
];
1170 crypto_blake2b_ctx hashctx
;
1171 crypto_blake2b_init(&hashctx
, K8VNET_DIGEST_SIZE
);
1173 crypto_blake2b_update(&hashctx
, clientKey
, VNetUtils::ChaCha20KeySize
);
1174 // hash whole packet
1175 crypto_blake2b_update(&hashctx
, msg
.GetData(), msg
.GetNumBytes());
1177 crypto_blake2b_update(&hashctx
, (const uint8_t *)*net_server_key
.asStr(), (unsigned)net_server_key
.asStr().length());
1178 crypto_blake2b_final(&hashctx
, svdig
);
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
);
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
);
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
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
;
1220 TmpByte
= NET_PROTOCOL_VERSION_HI
;
1222 TmpByte
= NET_PROTOCOL_VERSION_LO
;
1225 vint16 TmpPort
= Drv
->GetSocketPort(&newaddr
);
1227 int elen
= EncryptInfoBitStream(edata
, MsgOut
, clientKey
);
1229 Drv
->Write(acceptsock
, edata
, elen
, &clientaddr
);
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
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));
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
);
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
;
1292 TmpByte
= NET_PROTOCOL_VERSION_HI
;
1294 TmpByte
= NET_PROTOCOL_VERSION_LO
;
1297 vint16 TmpPort
= Drv
->GetSocketPort(&newaddr
);
1299 int elen
= EncryptInfoBitStream(edata
, MsgOut
, clientKey
);
1304 Drv
->Write(acceptsock
, edata
, elen
, &clientaddr
);
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
;
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;
1342 GCon
->Logf(NAME_DevNet
, "resolved master address '%s' to '%s'", *LastMasterAddrStr
, Drv
->AddrToString(&LastMasterAddr
));
1343 LastMasterIsBad
= false;
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");
1362 // send the connection request
1363 VBitStreamWriter
MsgOut(MAX_DGRAM_SIZE
<<3);
1364 WriteGameSignature(MsgOut
);
1365 vuint8 TmpByte
= MASTER_PROTO_VERSION
;
1367 TmpByte
= MCREQ_JOIN
;
1369 TmpByte
= NET_PROTOCOL_VERSION_HI
;
1371 TmpByte
= NET_PROTOCOL_VERSION_LO
;
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");
1404 // send the quit request
1405 VBitStreamWriter
MsgOut(MAX_DGRAM_SIZE
<<3);
1406 WriteGameSignature(MsgOut
);
1407 vuint8 TmpByte
= MASTER_PROTO_VERSION
;
1409 TmpByte
= MCREQ_QUIT
;
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
) {
1435 sockaddr_t readaddr
;
1440 if (Drv
->MasterQuerySocket
< 0) Drv
->MasterQuerySocket
= Drv
->OpenListenSocket(0);
1442 Drv
->GetSocketAddr(Drv
->MasterQuerySocket
, &myaddr
);
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
;
1451 TmpByte
= MCREQ_LIST
;
1453 Drv
->Write(Drv
->MasterQuerySocket
, MsgOut
.GetData(), MsgOut
.GetNumBytes(), &LastMasterAddr
);
1454 GCon
->Logf(NAME_DevNet
, "sent query to master at %s", Drv
->AddrToString(&LastMasterAddr
));
1458 //GCon->Logf(NAME_DevNet, "waiting for master reply");
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;
1475 if (msg
.IsError() || TmpByte
!= MASTER_PROTO_VERSION
) continue;
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;
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
;
1499 TmpByte
= NET_PROTOCOL_VERSION_HI
;
1501 TmpByte
= NET_PROTOCOL_VERSION_LO
;
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());
1512 GCon
->Logf(NAME_DevNet
, " server: %s, bad proto version %u:%u", Drv
->AddrToString(&tmpaddr
), pver0
, pver1
);
1517 for (int c = 0; f+c < len && c < 16; ++c) {
1518 if (c == 8) 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);
1531 //if (control&0x02) return true; // nobody cares, again
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;
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];
1615 int length
= LanDriver
->Read(LanSocket
, data
, NET_DATAGRAMSIZE
, &readaddr
);
1616 if (length
== 0) continue; // zero-sized message, oops
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));
1625 GCon
->Logf(NAME_DevNet
, "%s: Read error", LanDriver
->AddrToString(&Addr
));
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
);
1635 UpdateReceivedStats(length
);
1637 if ((unsigned)length
> destSize
) {
1638 GCon
->Logf(NAME_DevNet
, "%s: Read error (message too big)", LanDriver
->AddrToString(&Addr
));
1642 if (length
) memcpy(dest
, data
, length
);
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);
1671 //==========================================================================
1673 // VDatagramSocket::IsLocalConnection
1675 //==========================================================================
1676 bool VDatagramSocket::IsLocalConnection () const noexcept
{
1681 //==========================================================================
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 //==========================================================================
1700 //==========================================================================
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
));
1720 for (s
= Net
->ActiveSockets
; s
; s
= s
->Next
) {
1722 for (int f
= 1; f
< Args
.length(); ++f
) if (s
->Address
.globMatchCI(Args
[f
])) { hit
= true; break; }
1723 if (hit
) PrintStats(s
);