UFO: Alien Invasion
Loading...
Searching...
No Matches
net.cpp
Go to the documentation of this file.
1
8
9/*
10Copyright (C) 2002-2025 UFO: Alien Invasion.
11
12This program is free software; you can redistribute it and/or
13modify it under the terms of the GNU General Public License
14as published by the Free Software Foundation; either version 2
15of the License, or (at your option) any later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
21See the GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License
24along with this program; if not, write to the Free Software
25Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27*/
28
29#include "common.h"
30#include <errno.h>
31#include <string.h>
32#include <fcntl.h>
33#ifndef _MSC_VER
34#include <unistd.h>
35#endif
36#include <SDL_thread.h>
37#ifdef _WIN32
38#include "../ports/system.h"
39#endif
41
42#define MAX_STREAMS 56
43#define MAX_DATAGRAM_SOCKETS 7
44
45#ifdef _WIN32
46# include <winsock2.h>
47# include <ws2tcpip.h>
48# if WINVER < 0x501
49# include <wspiapi.h>
50# else
51# include <ws2spi.h>
52# endif
53# define netError WSAGetLastError()
54# define netStringError netStringErrorWin
55# define netCloseSocket closesocket
56# define gai_strerrorA netStringErrorWin
57
58#else /* WIN32 */
59
60# include <sys/ioctl.h>
61# include <sys/select.h>
62# include <sys/types.h>
63# include <sys/socket.h>
64# include <sys/time.h>
65# include <netdb.h>
66# include <arpa/inet.h>
67# include <netinet/in.h>
68# include <signal.h>
69typedef int SOCKET;
70# define INVALID_SOCKET (-1)
71# define netError errno
72# define netStringError strerror
73# define netCloseSocket close
74# define ioctlsocket ioctl
75#endif /* WIN32 */
76
77#ifdef EMSCRIPTEN
78#define NI_NUMERICHOST 0
79#define NI_NUMERICSERV 0
80#define NI_DGRAM 0
81#define AI_PASSIVE 0
82#define AI_NUMERICHOST 0
83#define INADDR_BROADCAST 0
84#endif
85
91#ifndef AI_NUMERICSERV
92#define AI_NUMERICSERV 0
93#endif
94#ifndef AI_ADDRCONFIG
95#define AI_ADDRCONFIG 0
96#endif
97
102#define NET_MULTICAST_IP6 "ff04::696f:7175:616b:6533"
103
104#define dbuffer_len(dbuf) (dbuf ? (dbuf)->length() : 0)
105
107static SDL_mutex* netMutex;
108
110 void* data;
111
113 bool ready;
114 bool closed;
115 bool finished;
117 int index;
120
121 /* these buffers must be used in a thread safe manner
122 * (lock netMutex) because the game thread can also write to it */
125
129};
130
131struct datagram {
132 int len;
133 char* msg;
134 char* addr;
135 struct datagram* next;
136};
137
147
148static fd_set read_fds;
149static fd_set write_fds;
153
154static bool loopback_ready = false;
155static bool server_running = false;
159
160#ifdef _WIN32
161static const char* netStringErrorWin (int code)
162{
163 switch (code) {
164 case WSAEINTR: return "WSAEINTR";
165 case WSAEBADF: return "WSAEBADF";
166 case WSAEACCES: return "WSAEACCES";
167 case WSAEDISCON: return "WSAEDISCON";
168 case WSAEFAULT: return "WSAEFAULT";
169 case WSAEINVAL: return "WSAEINVAL";
170 case WSAEMFILE: return "WSAEMFILE";
171 case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK";
172 case WSAEINPROGRESS: return "WSAEINPROGRESS";
173 case WSAEALREADY: return "WSAEALREADY";
174 case WSAENOTSOCK: return "WSAENOTSOCK";
175 case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ";
176 case WSAEMSGSIZE: return "WSAEMSGSIZE";
177 case WSAEPROTOTYPE: return "WSAEPROTOTYPE";
178 case WSAENOPROTOOPT: return "WSAENOPROTOOPT";
179 case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT";
180 case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT";
181 case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP";
182 case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT";
183 case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT";
184 case WSAEADDRINUSE: return "WSAEADDRINUSE";
185 case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL";
186 case WSAENETDOWN: return "WSAENETDOWN";
187 case WSAENETUNREACH: return "WSAENETUNREACH";
188 case WSAENETRESET: return "WSAENETRESET";
189 case WSAEHOSTDOWN: return "WSAEHOSTDOWN";
190 case WSAEHOSTUNREACH: return "WSAEHOSTUNREACH";
191 case WSAECONNABORTED: return "WSWSAECONNABORTEDAEINTR";
192 case WSAECONNRESET: return "WSAECONNRESET";
193 case WSAENOBUFS: return "WSAENOBUFS";
194 case WSAEISCONN: return "WSAEISCONN";
195 case WSAENOTCONN: return "WSAENOTCONN";
196 case WSAESHUTDOWN: return "WSAESHUTDOWN";
197 case WSAETOOMANYREFS: return "WSAETOOMANYREFS";
198 case WSAETIMEDOUT: return "WSAETIMEDOUT";
199 case WSAECONNREFUSED: return "WSAECONNREFUSED";
200 case WSAELOOP: return "WSAELOOP";
201 case WSAENAMETOOLONG: return "WSAENAMETOOLONG";
202 case WSASYSNOTREADY: return "WSASYSNOTREADY";
203 case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED";
204 case WSANOTINITIALISED: return "WSANOTINITIALISED";
205 case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND";
206 case WSATRY_AGAIN: return "WSATRY_AGAIN";
207 case WSANO_RECOVERY: return "WSANO_RECOVERY";
208 case WSANO_DATA: return "WSANO_DATA";
209 default: return "NO ERROR";
210 }
211}
212#endif
213
214static inline int NET_StreamGetLength (struct net_stream* s)
215{
216 return s ? dbuffer_len(s->inbound) : 0;
217}
218
225static int NET_StreamGetFree (void)
226{
227 static int start = 0;
228
229 for (int i = 0; i < MAX_STREAMS; i++) {
230 const int pos = (i + start) % MAX_STREAMS;
231 if (streams[pos] == nullptr) {
232 start = (pos + 1) % MAX_STREAMS;
233 Com_DPrintf(DEBUG_SERVER, "New stream at index: %i\n", pos);
234 return pos;
235 }
236 }
237 return -1;
238}
239
244{
245 static int start = 0;
246
247 for (int i = 0; i < MAX_DATAGRAM_SOCKETS; i++) {
248 const int pos = (i + start) % MAX_DATAGRAM_SOCKETS;
249 if (datagram_sockets[pos] == nullptr) {
250 start = (pos + 1) % MAX_DATAGRAM_SOCKETS;
251 Com_DPrintf(DEBUG_SERVER, "New datagram at index: %i\n", pos);
252 return pos;
253 }
254 }
255 return -1;
256}
257
262static struct net_stream* NET_StreamNew (int index)
263{
265 s->data = nullptr;
266 s->loopback_peer = nullptr;
267 s->loopback = false;
268 s->closed = false;
269 s->finished = false;
270 s->ready = false;
272 s->inbound = dbufferptr();
273 s->outbound = dbufferptr();
274 s->index = index;
275 s->family = 0;
276 s->addrlen = 0;
277 s->func = nullptr;
278 if (streams[index])
280 streams[index] = s;
281 return s;
282}
283
284static void NET_ShowStreams_f (void)
285{
286 char buf[256];
287 int cnt = 0;
288
289 for (int i = 0; i < MAX_STREAMS; i++) {
290 if (streams[i] == nullptr)
291 continue;
292 Com_Printf("Steam %i is opened: %s on socket %i (closed: %i, finished: %i, outbound: " UFO_SIZE_T ", inbound: " UFO_SIZE_T ")\n", i,
293 NET_StreamPeerToName(streams[i], buf, sizeof(buf), true),
296 cnt++;
297 }
298 Com_Printf("%i/%i streams opened\n", cnt, MAX_STREAMS);
299}
300
305void NET_Init (void)
306{
307 Com_Printf("\n----- network initialization -------\n");
308
309#ifdef _WIN32
310 WSADATA winsockdata;
311 if (WSAStartup(MAKEWORD(2, 0), &winsockdata) != 0)
312 Com_Error(ERR_FATAL, "Winsock initialization failed.");
313#endif
314
315 maxfd = 0;
316 FD_ZERO(&read_fds);
317 FD_ZERO(&write_fds);
318
319 for (int i = 0; i < MAX_STREAMS; i++)
320 streams[i] = nullptr;
321 for (int i = 0; i < MAX_DATAGRAM_SOCKETS; i++)
322 datagram_sockets[i] = nullptr;
323
324#ifndef _WIN32
325 signal(SIGPIPE, SIG_IGN);
326#endif
327
328 net_ipv4 = Cvar_Get("net_ipv4", "1", CVAR_ARCHIVE, "Only use ipv4");
329 Cmd_AddCommand("net_showstreams", NET_ShowStreams_f, "Show opened streams");
330
331 netMutex = SDL_CreateMutex();
332}
333
337void NET_Shutdown (void)
338{
339#ifdef _WIN32
340 WSACleanup();
341#endif
342 SDL_DestroyMutex(netMutex);
343 netMutex = nullptr;
344}
345
350static void NET_StreamClose (struct net_stream* s)
351{
352 if (!s || s->closed)
353 return;
354
355 if (s->socket != INVALID_SOCKET) {
356 if (dbuffer_len(s->outbound))
357 Com_Printf("The outbound buffer for this socket (%d) is not empty\n", s->socket);
358 else if (dbuffer_len(s->inbound))
359 Com_Printf("The inbound buffer for this socket (%d) is not empty\n", s->socket);
360
361 FD_CLR(s->socket, &read_fds);
362 FD_CLR(s->socket, &write_fds);
365 }
366 if (s->index >= 0)
367 streams[s->index] = nullptr;
368
369 if (s->loopback_peer) {
370 /* Detach the peer, so that it won't send us anything more */
372 s->loopback_peer->loopback_peer = nullptr;
373 }
374
375 s->closed = true;
376 Com_DPrintf(DEBUG_SERVER, "Close stream at index: %i\n", s->index);
377
378 s->outbound = dbufferptr();
380
381 /* Note that this is potentially invalid after the callback returns */
382 if (s->finished) {
383 s->inbound = dbufferptr();
384 if (s->onclose != nullptr)
385 s->onclose();
386 Mem_Free(s);
387 s = nullptr;
388 } else if (s->func) {
389 s->func(s);
390 }
391
392 if (s != nullptr && s->onclose != nullptr)
393 s->onclose();
394}
395
396static void do_accept (SOCKET sock)
397{
398 const int index = NET_StreamGetFree();
399 if (index == -1) {
400 Com_Printf("Too many streams open, rejecting inbound connection\n");
401 netCloseSocket(sock);
402 return;
403 }
404
405 struct net_stream* s = NET_StreamNew(index);
406 s->socket = sock;
407 s->inbound = dbufferptr(new dbuffer(4096));
408 s->outbound = dbufferptr(new dbuffer(4096));
411 s->func = server_func;
412
413 maxfd = std::max(sock + 1, maxfd);
414 FD_SET(sock, &read_fds);
415
416 server_func(s);
418}
419
423void NET_Wait (int timeout)
424{
425 struct timeval tv;
426 int ready;
427
428 fd_set read_fds_out;
429 fd_set write_fds_out;
430
431 memcpy(&read_fds_out, &read_fds, sizeof(read_fds_out));
432 memcpy(&write_fds_out, &write_fds, sizeof(write_fds_out));
433
434 /* select() won't notice that loopback streams are ready, so we'll
435 * eliminate the delay directly */
436 if (loopback_ready)
437 timeout = 0;
438
439 tv.tv_sec = timeout / 1000;
440 tv.tv_usec = 1000 * (timeout % 1000);
441#ifdef _WIN32
442 if (read_fds_out.fd_count == 0) {
443 Sys_Sleep(timeout);
444 ready = 0;
445 } else
446#endif
447 ready = select(maxfd, &read_fds_out, &write_fds_out, nullptr, &tv);
448
449 if (ready == -1) {
450 Com_Printf("select failed: %s\n", netStringError(netError));
451 return;
452 }
453
454 if (ready == 0 && !loopback_ready)
455 return;
456
457 if (server_socket != INVALID_SOCKET && FD_ISSET(server_socket, &read_fds_out)) {
458 const SOCKET client_socket = accept(server_socket, nullptr, 0);
459 if (client_socket == INVALID_SOCKET) {
460 if (errno != EAGAIN)
461 Com_Printf("accept on socket %d failed: %s\n", server_socket, netStringError(netError));
462 } else
463 do_accept(client_socket);
464 }
465
466 for (int i = 0; i < MAX_STREAMS; i++) {
467 struct net_stream* s = streams[i];
468
469 if (!s)
470 continue;
471
472 if (s->loopback) {
473 /* If the peer is gone and the buffer is empty, close the stream */
474 if (!s->loopback_peer && NET_StreamGetLength(s) == 0) {
476 }
477 /* Note that s is potentially invalid after the callback returns - we'll close dead streams on the next pass */
478 else if (s->ready && s->func) {
479 s->func(s);
480 }
481
482 continue;
483 }
484
485 if (s->socket == INVALID_SOCKET)
486 continue;
487
488 if (FD_ISSET(s->socket, &write_fds_out)) {
489 if (dbuffer_len(s->outbound) == 0) {
490 FD_CLR(s->socket, &write_fds);
491
492 /* Finished streams are closed when their outbound queues empty */
493 if (s->finished)
495
496 continue;
497 }
498
499 char buf[4096];
500 int len;
501 {
502 const ScopedMutex scopedMutex(netMutex);
503 len = s->outbound->get(buf, sizeof(buf));
504 len = send(s->socket, buf, len, 0);
505
506 s->outbound->remove(len);
507 }
508
509 if (len < 0) {
510 Com_Printf("write on socket %d failed: %s\n", s->socket, netStringError(netError));
512 continue;
513 }
514
515 Com_DPrintf(DEBUG_SERVER, "wrote %d bytes to stream %d (%s)\n", len, i, NET_StreamPeerToName(s, buf, sizeof(buf), true));
516 }
517
518 if (FD_ISSET(s->socket, &read_fds_out)) {
519 char buf[4096];
520 const int len = recv(s->socket, buf, sizeof(buf), 0);
521 if (len <= 0) {
522 if (len == -1)
523 Com_Printf("read on socket %d failed: %s\n", s->socket, netStringError(netError));
525 continue;
526 } else {
527 if (s->inbound) {
528 SDL_LockMutex(netMutex);
529 s->inbound->add(buf, len);
530 SDL_UnlockMutex(netMutex);
531
532 Com_DPrintf(DEBUG_SERVER, "read %d bytes from stream %d (%s)\n", len, i, NET_StreamPeerToName(s, buf, sizeof(buf), true));
533
534 /* Note that s is potentially invalid after the callback returns */
535 if (s->func)
536 s->func(s);
537
538 continue;
539 }
540 }
541 }
542 }
543
544 for (int i = 0; i < MAX_DATAGRAM_SOCKETS; i++) {
546
547 if (!s)
548 continue;
549
550 if (FD_ISSET(s->socket, &write_fds_out)) {
551 if (s->queue) {
552 struct datagram* dgram = s->queue;
553 const int len = sendto(s->socket, dgram->msg, dgram->len, 0, (struct sockaddr* )dgram->addr, s->addrlen);
554 if (len == -1)
555 Com_Printf("sendto on socket %d failed: %s\n", s->socket, netStringError(netError));
556 /* Regardless of whether it worked, we don't retry datagrams */
557 s->queue = dgram->next;
558 Mem_Free(dgram->msg);
559 Mem_Free(dgram->addr);
560 Mem_Free(dgram);
561 if (!s->queue)
562 s->queue_tail = &s->queue;
563 } else {
564 FD_CLR(s->socket, &write_fds);
565 }
566 }
567
568 if (FD_ISSET(s->socket, &read_fds_out)) {
569 char buf[256];
570 char addrbuf[256];
571 socklen_t addrlen = sizeof(addrbuf);
572 const int len = recvfrom(s->socket, buf, sizeof(buf), 0, (struct sockaddr* )addrbuf, &addrlen);
573 if (len == -1)
574 Com_Printf("recvfrom on socket %d failed: %s\n", s->socket, netStringError(netError));
575 else
576 s->func(s, buf, len, (struct sockaddr* )addrbuf);
577 }
578 }
579
580 loopback_ready = false;
581}
582
583static bool NET_SocketSetNonBlocking (SOCKET socketNum)
584{
585 unsigned long t = 1;
586 if (ioctlsocket(socketNum, FIONBIO, &t) == -1) {
587 Com_Printf("ioctl FIONBIO failed: %s\n", strerror(errno));
588 return false;
589 }
590 return true;
591}
592
593static struct net_stream* NET_DoConnect (const char* node, const char* service, const struct addrinfo* addr, int i, stream_onclose_func* onclose)
594{
595 SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
596 if (sock == INVALID_SOCKET) {
597 Com_Printf("Failed to create socket: %s\n", netStringError(netError));
598 return nullptr;
599 }
600
601 if (!NET_SocketSetNonBlocking(sock)) {
602 netCloseSocket(sock);
603 return nullptr;
604 }
605
606 if (connect(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
607 const int err = netError;
608#ifdef _WIN32
609 if (err != WSAEWOULDBLOCK) {
610#else
611 if (err != EINPROGRESS) {
612#endif
613 Com_Printf("Failed to start connection to %s:%s: %s\n", node, service, netStringError(err));
614 netCloseSocket(sock);
615 return nullptr;
616 }
617 }
618
619 struct net_stream* s = NET_StreamNew(i);
620 s->socket = sock;
621 s->inbound = dbufferptr(new dbuffer(4096));
622 s->outbound = dbufferptr(new dbuffer(4096));
623 s->family = addr->ai_family;
624 s->addrlen = addr->ai_addrlen;
625 s->onclose = onclose;
626
627 maxfd = std::max(sock + 1, maxfd);
628 FD_SET(sock, &read_fds);
629
630 return s;
631}
632
644struct net_stream* NET_Connect (const char* node, const char* service, stream_onclose_func* onclose)
645{
646 struct addrinfo* res;
647 struct addrinfo hints;
648
649 OBJZERO(hints);
650 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
651 hints.ai_socktype = SOCK_STREAM;
652 /* force ipv4 */
653 if (net_ipv4->integer)
654 hints.ai_family = AF_INET;
655
656 const int rc = getaddrinfo(node, service, &hints, &res);
657 if (rc != 0) {
658 Com_Printf("Failed to resolve host %s:%s: %s\n", node, service, gai_strerror(rc));
659 return nullptr;
660 }
661
662 const int index = NET_StreamGetFree();
663 if (index == -1) {
664 Com_Printf("Failed to connect to host %s:%s, too many streams open\n", node, service);
665 freeaddrinfo(res);
666 return nullptr;
667 }
668
669 struct net_stream* s = NET_DoConnect(node, service, res, index, onclose);
670
671 freeaddrinfo(res);
672 return s;
673}
674
682{
683 if (!server_running)
684 return nullptr;
685
686 const int server_index = NET_StreamGetFree();
687 const int client_index = NET_StreamGetFree();
688
689 if (server_index == -1 || client_index == -1 || server_index == client_index) {
690 Com_Printf("Failed to connect to loopback server, too many streams open\n");
691 return nullptr;
692 }
693
694 struct net_stream* client = NET_StreamNew(client_index);
695 client->loopback = true;
696 client->inbound = dbufferptr(new dbuffer(4096));
697 client->outbound = dbufferptr(new dbuffer(4096));
698 client->onclose = onclose;
699
700 struct net_stream* server = NET_StreamNew(server_index);
701 server->loopback = true;
702 server->inbound = client->outbound;
703 server->outbound = client->inbound;
704 server->func = server_func;
705 server->onclose = nullptr;
706
707 client->loopback_peer = server;
708 server->loopback_peer = client;
709
710 server_func(server);
711
712 return client;
713}
714
719void NET_StreamEnqueue (struct net_stream* s, const char* data, int len)
720{
722 return;
723
724 if (s->outbound) {
725 const ScopedMutex scopedMutex(netMutex);
726 s->outbound->add(data, len);
727 }
728
729 /* on linux, socket is int, and INVALID_SOCKET -1
730 * on windows it is unsigned and INVALID_SOCKET (~0)
731 * Let's hope that checking for INVALID_SOCKET is good enough for linux. */
732 //if (s->socket >= 0)
733 if (s->socket != INVALID_SOCKET)
734 FD_SET(s->socket, &write_fds);
735
736 if (s->loopback_peer) {
737 loopback_ready = true;
738 s->loopback_peer->ready = true;
739 }
740}
741
745static int NET_StreamPeek (struct net_stream* s, char* data, int len)
746{
747 if (len <= 0 || !s)
748 return 0;
749
750 const dbufferptr& dbuf = s->inbound;
751 if ((s->closed || s->finished) && dbuffer_len(dbuf) == 0)
752 return 0;
753
754 return dbuf->get(data, len);
755}
756
760int NET_StreamDequeue (struct net_stream* s, char* data, int len)
761{
763 return 0;
764
765 return s->inbound->extract(data, len);
766}
767
775{
776 unsigned int v;
777 const ScopedMutex scopedMutex(netMutex);
778
779 if (NET_StreamPeek(s, (char*)&v, 4) < 4)
780 return nullptr;
781
782 int len = LittleLong(v);
783 if (NET_StreamGetLength(s) < (4 + len))
784 return nullptr;
785
786 char tmp[256];
787 const int size = sizeof(tmp);
788 NET_StreamDequeue(s, tmp, 4);
789
790 dbuffer* buf = new dbuffer();
791 while (len > 0) {
792 const int x = NET_StreamDequeue(s, tmp, std::min(len, size));
793 buf->add(tmp, x);
794 len -= x;
795 }
796
797 return buf;
798}
799
801{
802 return s ? s->data : nullptr;
803}
804
805void NET_StreamSetData (struct net_stream* s, void* data)
806{
807 if (!s)
808 return;
809 s->data = data;
810}
811
818{
819 if (!s)
820 return;
821 s->finished = true;
823}
824
833{
834 if (!s)
835 return;
836
837 s->finished = true;
838
839 if (s->socket != INVALID_SOCKET)
840 FD_CLR(s->socket, &read_fds);
841
842 /* Stop the loopback peer from queueing stuff up in here */
843 if (s->loopback_peer)
845
846 const ScopedMutex scopedMutex(netMutex);
847 s->inbound = dbufferptr();
848
849 /* If there's nothing in the outbound buffer, any finished stream is
850 * ready to be closed */
851 if (dbuffer_len(s->outbound) == 0)
853}
854
859const char* NET_StreamToString (struct net_stream* s)
860{
861 static char node[64];
862 NET_StreamPeerToName(s, node, sizeof(node), false);
863 return node;
864}
865
872const char* NET_StreamPeerToName (struct net_stream* s, char* dst, int len, bool appendPort)
873{
874 if (!s)
875 return "(null)";
876
878 return "loopback connection";
879
880 char buf[128];
881 socklen_t addrlen = s->addrlen;
882 if (getpeername(s->socket, (struct sockaddr* )buf, &addrlen) != 0)
883 return "(error)";
884
885 char node[64];
886 char service[64];
887 const int rc = getnameinfo((struct sockaddr* )buf, addrlen, node, sizeof(node), service, sizeof(service),
888 NI_NUMERICHOST | NI_NUMERICSERV);
889 if (rc != 0) {
890 Com_Printf("Failed to convert sockaddr to string: %s\n", gai_strerror(rc));
891 return "(error)";
892 }
893 if (!appendPort) {
894 Q_strncpyz(dst, node, len);
895 } else {
896 node[sizeof(node) - 1] = '\0';
897 service[sizeof(service) - 1] = '\0';
898 Com_sprintf(dst, len, "%s %s", node, service);
899 }
900 return dst;
901}
902
904{
905 if (!s)
906 return;
907 s->func = func;
908}
909
911{
912 return s && s->loopback;
913}
914
915static int NET_DoStartServer (const struct addrinfo* addr)
916{
917 SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
918 int t = 1;
919
920 if (sock == INVALID_SOCKET) {
921 Com_Printf("Failed to create socket: %s\n", netStringError(netError));
922 return INVALID_SOCKET;
923 }
924
925 if (!NET_SocketSetNonBlocking(sock)) {
926 netCloseSocket(sock);
927 return INVALID_SOCKET;
928 }
929
930#ifdef _WIN32
931 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &t, sizeof(t)) != 0) {
932#else
933 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(t)) != 0) {
934#endif
935 Com_Printf("Failed to set SO_REUSEADDR on socket: %s\n", netStringError(netError));
936 netCloseSocket(sock);
937 return INVALID_SOCKET;
938 }
939
940 if (bind(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
941 Com_Printf("Failed to bind socket: %s\n", netStringError(netError));
942 netCloseSocket(sock);
943 return INVALID_SOCKET;
944 }
945
946 if (listen(sock, SOMAXCONN) != 0) {
947 Com_Printf("Failed to listen on socket: %s\n", netStringError(netError));
948 netCloseSocket(sock);
949 return INVALID_SOCKET;
950 }
951
952 maxfd = std::max(sock + 1, maxfd);
953 FD_SET(sock, &read_fds);
954 server_family = addr->ai_family;
955 server_addrlen = addr->ai_addrlen;
956
957 return sock;
958}
959
960static struct addrinfo* NET_GetAddrinfoForNode (const char* node, const char* service)
961{
962 struct addrinfo* res;
963 struct addrinfo hints;
964
965 OBJZERO(hints);
966 hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
967 hints.ai_socktype = SOCK_STREAM;
968 /* force ipv4 */
969 if (net_ipv4->integer)
970 hints.ai_family = AF_INET;
971
972 const int rc = getaddrinfo(node, service, &hints, &res);
973 if (rc != 0) {
974 Com_Printf("Failed to resolve host %s:%s: %s\n", node ? node : "*", service, gai_strerror(rc));
975 return nullptr;
976 }
977
978 return res;
979}
980
990bool SV_Start (const char* node, const char* service, stream_callback_func* func)
991{
992 if (!func)
993 return false;
994
995 if (server_running) {
996 Com_Printf("SV_Start: Server is still running - call SV_Stop before\n");
997 return false;
998 }
999
1000 if (service) {
1001 struct addrinfo* res = NET_GetAddrinfoForNode(node, service);
1002
1003 if (!res)
1004 return false;
1005
1008 Com_Printf("Failed to start server on %s:%s\n", node ? node : "*", service);
1009 } else {
1010 server_running = true;
1011 server_func = func;
1012 }
1013 freeaddrinfo(res);
1014 } else {
1015 /* Loopback server only */
1016 server_running = true;
1017 server_func = func;
1018 }
1019
1020 return server_running;
1021}
1022
1026void SV_Stop (void)
1027{
1028 server_running = false;
1029 server_func = nullptr;
1031 FD_CLR(server_socket, &read_fds);
1033 }
1035}
1036
1040static struct datagram_socket* NET_DatagramSocketDoNew (const struct addrinfo* addr)
1041{
1042 const int index = NET_DatagramFindFreeSocket();
1043
1044 if (index == -1) {
1045 Com_Printf("Too many datagram sockets open\n");
1046 return nullptr;
1047 }
1048
1049 SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
1050 if (sock == INVALID_SOCKET) {
1051 Com_Printf("Failed to create socket: %s\n", netStringError(netError));
1052 return nullptr;
1053 }
1054
1055 if (!NET_SocketSetNonBlocking(sock)) {
1056 netCloseSocket(sock);
1057 return nullptr;
1058 }
1059
1060 int socketOptTrue = 1;
1061 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &socketOptTrue, sizeof(socketOptTrue)) != 0) {
1062 Com_Printf("Failed to set SO_REUSEADDR on socket: %s\n", netStringError(netError));
1063 netCloseSocket(sock);
1064 return nullptr;
1065 }
1066
1067 if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*) &socketOptTrue, sizeof(socketOptTrue)) != 0) {
1068 Com_Printf("Failed to set SO_BROADCAST on socket: %s\n", netStringError(netError));
1069 netCloseSocket(sock);
1070 return nullptr;
1071 }
1072
1073 if (bind(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
1074 Com_Printf("Failed to bind socket: %s\n", netStringError(netError));
1075 netCloseSocket(sock);
1076 return nullptr;
1077 }
1078
1079 maxfd = std::max(sock + 1, maxfd);
1080 FD_SET(sock, &read_fds);
1081
1083 s->family = addr->ai_family;
1084 s->addrlen = addr->ai_addrlen;
1085 s->socket = sock;
1086 s->index = index;
1087 s->queue = nullptr;
1088 s->queue_tail = &s->queue;
1089 s->func = nullptr;
1091
1092 return s;
1093}
1094
1102struct datagram_socket* NET_DatagramSocketNew (const char* node, const char* service, datagram_callback_func* func)
1103{
1104 struct addrinfo* res;
1105 struct addrinfo hints;
1106
1107 if (!service || !func)
1108 return nullptr;
1109
1110 OBJZERO(hints);
1111 hints.ai_flags = AI_NUMERICHOST | AI_ADDRCONFIG | AI_NUMERICSERV | AI_PASSIVE;
1112 hints.ai_socktype = SOCK_DGRAM;
1113 /* force ipv4 */
1114 if (net_ipv4->integer)
1115 hints.ai_family = AF_INET;
1116
1117 const int rc = getaddrinfo(node, service, &hints, &res);
1118
1119 if (rc != 0) {
1120 Com_Printf("Failed to resolve host %s:%s: %s\n", node ? node : "*", service, gai_strerror(rc));
1121 return nullptr;
1122 }
1123
1125 if (s)
1126 s->func = func;
1127
1128 freeaddrinfo(res);
1129 return s;
1130}
1131
1135void NET_DatagramSend (struct datagram_socket* s, const char* buf, int len, struct sockaddr* to)
1136{
1137 if (!s || len <= 0 || !buf || !to)
1138 return;
1139
1141 dgram->msg = Mem_PoolAllocTypeN(char, len, com_networkPool);
1142 dgram->addr = Mem_PoolAllocTypeN(char, s->addrlen, com_networkPool);
1143 memcpy(dgram->msg, buf, len);
1144 memcpy(dgram->addr, to, len);
1145 dgram->len = len;
1146 dgram->next = nullptr;
1147
1148 *s->queue_tail = dgram;
1149 s->queue_tail = &dgram->next;
1150
1151 FD_SET(s->socket, &write_fds);
1152}
1153
1159void NET_DatagramBroadcast (struct datagram_socket* s, const char* buf, int len, int port)
1160{
1161 if (s->family == AF_INET) {
1162 struct sockaddr_in addr;
1163 addr.sin_family = AF_INET;
1164 addr.sin_port = htons(port);
1165 addr.sin_addr.s_addr = INADDR_BROADCAST;
1166 NET_DatagramSend(s, buf, len, (struct sockaddr* )&addr);
1167 } else if (s->family == AF_INET6) {
1168 struct sockaddr_in addr;
1169 addr.sin_family = AF_INET6;
1170 addr.sin_port = htons(port);
1171 addr.sin_addr.s_addr = INADDR_BROADCAST;
1172 NET_DatagramSend(s, buf, len, (struct sockaddr* )&addr);
1173 } else {
1174 Com_Error(ERR_DROP, "Broadcast unsupported on address family %d\n", s->family);
1175 }
1176}
1177
1183{
1184 if (!s)
1185 return;
1186
1187 FD_CLR(s->socket, &read_fds);
1188 FD_CLR(s->socket, &write_fds);
1190
1191 while (s->queue) {
1192 struct datagram* dgram = s->queue;
1193 s->queue = dgram->next;
1194 Mem_Free(dgram->msg);
1195 Mem_Free(dgram->addr);
1196 Mem_Free(dgram);
1197 }
1198
1199 datagram_sockets[s->index] = nullptr;
1200 Mem_Free(s);
1201}
1202
1212void NET_SockaddrToStrings (struct datagram_socket* s, struct sockaddr* addr, char* node, size_t nodelen, char* service, size_t servicelen)
1213{
1214 const int rc = getnameinfo(addr, s->addrlen, node, nodelen, service, servicelen,
1215 NI_NUMERICHOST | NI_NUMERICSERV | NI_DGRAM);
1216 if (rc != 0) {
1217 Com_Printf("Failed to convert sockaddr to string: %s\n", gai_strerror(rc));
1218 Q_strncpyz(node, "(error)", nodelen);
1219 Q_strncpyz(service, "(error)", servicelen);
1220 }
1221}
1222
1223static void NET_AddrinfoToString (const struct addrinfo* addr, char* buf, size_t bufLength)
1224{
1225 char* service = inet_ntoa(((struct sockaddr_in*)addr->ai_addr)->sin_addr);
1226 Q_strncpyz(buf, service, bufLength);
1227}
1228
1229bool NET_ResolvNode (const char* node, char* buf, size_t bufLength)
1230{
1231 struct addrinfo* addrinfo = NET_GetAddrinfoForNode(node, nullptr);
1232 if (addrinfo == nullptr) {
1233 buf[0] = '\0';
1234 return false;
1235 }
1236 NET_AddrinfoToString(addrinfo, buf, bufLength);
1237 freeaddrinfo(addrinfo);
1238 return true;
1239}
#define LittleLong(X)
Definition byte.h:37
void add(const char *, size_t)
Definition dbuffer.cpp:42
size_t extract(char *, size_t)
Read and delete data from a dbuffer.
Definition dbuffer.cpp:136
size_t remove(size_t)
Deletes data from a dbuffer.
Definition dbuffer.cpp:104
size_t get(char *, size_t) const
Read data from a dbuffer.
Definition dbuffer.cpp:61
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition cmd.cpp:744
cvar_t * port
Definition common.cpp:58
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition common.cpp:440
void Com_Error(int code, const char *fmt,...)
Definition common.cpp:459
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
memPool_t * com_networkPool
Definition common.cpp:73
definitions common between client and server, but not game lib
#define ERR_DROP
Definition common.h:211
#define ERR_FATAL
Definition common.h:210
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition cvar.cpp:342
#define CVAR_ARCHIVE
Definition cvar.h:40
SharedPtr< dbuffer > dbufferptr
Definition dbuffer.h:46
#define DEBUG_SERVER
Definition defines.h:60
voidpf void uLong size
Definition ioapi.h:42
voidpf void * buf
Definition ioapi.h:42
#define Mem_PoolAllocTypeN(type, n, pool)
Definition mem.h:42
#define Mem_Free(ptr)
Definition mem.h:35
#define Mem_PoolAllocType(type, pool)
Definition mem.h:43
bool NET_StreamIsLoopback(struct net_stream *s)
Definition net.cpp:910
static bool loopback_ready
Definition net.cpp:154
static int NET_DoStartServer(const struct addrinfo *addr)
Definition net.cpp:915
struct datagram_socket * NET_DatagramSocketNew(const char *node, const char *service, datagram_callback_func *func)
Opens a datagram socket (UDP).
Definition net.cpp:1102
static stream_callback_func * server_func
Definition net.cpp:156
static fd_set read_fds
Definition net.cpp:148
void SV_Stop(void)
Definition net.cpp:1026
static struct datagram_socket * datagram_sockets[MAX_DATAGRAM_SOCKETS]
Definition net.cpp:152
void NET_StreamSetCallback(struct net_stream *s, stream_callback_func *func)
Definition net.cpp:903
static int NET_StreamGetLength(struct net_stream *s)
Definition net.cpp:214
#define INVALID_SOCKET
Definition net.cpp:70
dbuffer * NET_ReadMsg(struct net_stream *s)
Reads messages from the network channel and adds them to the dbuffer where you can use the NET_Read* ...
Definition net.cpp:774
void NET_Init(void)
Definition net.cpp:305
void NET_Shutdown(void)
Definition net.cpp:337
void NET_StreamEnqueue(struct net_stream *s, const char *data, int len)
Enqueue a network message into a stream.
Definition net.cpp:719
static void NET_AddrinfoToString(const struct addrinfo *addr, char *buf, size_t bufLength)
Definition net.cpp:1223
void NET_SockaddrToStrings(struct datagram_socket *s, struct sockaddr *addr, char *node, size_t nodelen, char *service, size_t servicelen)
Convert sockaddr to string.
Definition net.cpp:1212
#define netCloseSocket
Definition net.cpp:73
void NET_StreamSetData(struct net_stream *s, void *data)
Definition net.cpp:805
static cvar_t * net_ipv4
Definition net.cpp:106
static void NET_StreamClose(struct net_stream *s)
Definition net.cpp:350
static bool server_running
Definition net.cpp:155
#define MAX_STREAMS
Definition net.cpp:42
bool SV_Start(const char *node, const char *service, stream_callback_func *func)
Definition net.cpp:990
void * NET_StreamGetData(struct net_stream *s)
Definition net.cpp:800
static struct datagram_socket * NET_DatagramSocketDoNew(const struct addrinfo *addr)
Definition net.cpp:1040
#define MAX_DATAGRAM_SOCKETS
Definition net.cpp:43
struct net_stream * NET_ConnectToLoopBack(stream_onclose_func *onclose)
Definition net.cpp:681
static struct net_stream * streams[MAX_STREAMS]
Definition net.cpp:151
#define dbuffer_len(dbuf)
Definition net.cpp:104
#define netError
Definition net.cpp:71
#define AI_NUMERICSERV
Definition net.cpp:92
int SOCKET
Definition net.cpp:69
static struct net_stream * NET_DoConnect(const char *node, const char *service, const struct addrinfo *addr, int i, stream_onclose_func *onclose)
Definition net.cpp:593
struct net_stream * NET_Connect(const char *node, const char *service, stream_onclose_func *onclose)
Try to connect to a given host on a given port.
Definition net.cpp:644
int NET_StreamDequeue(struct net_stream *s, char *data, int len)
Definition net.cpp:760
void NET_DatagramSend(struct datagram_socket *s, const char *buf, int len, struct sockaddr *to)
Definition net.cpp:1135
static void do_accept(SOCKET sock)
Definition net.cpp:396
void NET_StreamFinished(struct net_stream *s)
Call NET_StreamFinished to mark the stream as uninteresting, but to finish sending any data in the bu...
Definition net.cpp:832
void NET_DatagramBroadcast(struct datagram_socket *s, const char *buf, int len, int port)
Definition net.cpp:1159
static bool NET_SocketSetNonBlocking(SOCKET socketNum)
Definition net.cpp:583
void NET_StreamFree(struct net_stream *s)
Call NET_StreamFree to dump the whole thing right now.
Definition net.cpp:817
static int server_addrlen
Definition net.cpp:158
static int server_family
Definition net.cpp:158
const char * NET_StreamToString(struct net_stream *s)
Returns the numerical representation of a net_stream.
Definition net.cpp:859
#define AI_ADDRCONFIG
Definition net.cpp:95
static void NET_ShowStreams_f(void)
Definition net.cpp:284
static SOCKET maxfd
Definition net.cpp:150
bool NET_ResolvNode(const char *node, char *buf, size_t bufLength)
Definition net.cpp:1229
void NET_Wait(int timeout)
Definition net.cpp:423
#define ioctlsocket
Definition net.cpp:74
static struct addrinfo * NET_GetAddrinfoForNode(const char *node, const char *service)
Definition net.cpp:960
void NET_DatagramSocketClose(struct datagram_socket *s)
Definition net.cpp:1182
#define netStringError
Definition net.cpp:72
static struct net_stream * NET_StreamNew(int index)
Definition net.cpp:262
static int NET_DatagramFindFreeSocket(void)
Definition net.cpp:243
const char * NET_StreamPeerToName(struct net_stream *s, char *dst, int len, bool appendPort)
Definition net.cpp:872
static int NET_StreamGetFree(void)
Definition net.cpp:225
static int NET_StreamPeek(struct net_stream *s, char *data, int len)
Returns the length of the waiting inbound buffer.
Definition net.cpp:745
static SDL_mutex * netMutex
Definition net.cpp:107
static fd_set write_fds
Definition net.cpp:149
static SOCKET server_socket
Definition net.cpp:157
void stream_onclose_func()
Definition net.h:31
void datagram_callback_func(struct datagram_socket *s, const char *buf, int len, struct sockaddr *from)
Definition net.h:33
void stream_callback_func(struct net_stream *s)
Definition net.h:32
QGL_EXTERN GLuint GLchar GLuint * len
Definition r_gl.h:99
QGL_EXTERN int GLboolean GLfloat * v
Definition r_gl.h:120
QGL_EXTERN GLsizei const GLvoid * data
Definition r_gl.h:89
QGL_EXTERN GLuint index
Definition r_gl.h:110
QGL_EXTERN GLint i
Definition r_gl.h:113
#define OBJZERO(obj)
Definition shared.h:178
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
SOCKET socket
Definition net.cpp:139
datagram_callback_func * func
Definition net.cpp:145
struct datagram * queue
Definition net.cpp:143
struct datagram ** queue_tail
Definition net.cpp:144
struct datagram * next
Definition net.cpp:135
char * addr
Definition net.cpp:134
char * msg
Definition net.cpp:133
int len
Definition net.cpp:132
int addrlen
Definition net.cpp:119
int family
Definition net.cpp:118
bool loopback
Definition net.cpp:112
dbufferptr inbound
Definition net.cpp:123
int index
Definition net.cpp:117
bool finished
Definition net.cpp:115
stream_onclose_func * onclose
Definition net.cpp:126
SOCKET socket
Definition net.cpp:116
dbufferptr outbound
Definition net.cpp:124
bool closed
Definition net.cpp:114
stream_callback_func * func
Definition net.cpp:127
struct net_stream * loopback_peer
Definition net.cpp:128
void * data
Definition net.cpp:110
bool ready
Definition net.cpp:113
System specific stuff.
void Sys_Sleep(int milliseconds)
Calls the win32 sleep function.
#define UFO_SIZE_T
Definition ufotypes.h:89