Bug Summary

File:common/net.cpp
Location:line 875, column 59
Description:Access to field 'ai_protocol' results in a dereference of a null pointer (loaded from variable 'addr')

Annotated Source Code

1/**
2 * @file
3 * @note This file should fully support ipv6 and any other protocol that is
4 * compatible with the getaddrinfo interface, with the exception of
5 * NET_DatagramBroadcast() which must be amended for each protocol (and
6 * currently supports only ipv4)
7 */
8
9/*
10Copyright (C) 2002-2011 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(*__error()).h>
31#include <string.h>
32#include <fcntl.h>
33#include <unistd.h>
34
35#ifdef _WIN32
36#include "../ports/system.h"
37#endif
38
39#define MAX_STREAMS56 56
40#define MAX_DATAGRAM_SOCKETS7 7
41
42#ifdef _WIN32
43# include <winsock2.h>
44# include <ws2tcpip.h>
45# if WINVER < 0x501
46# include <wspiapi.h>
47# else
48# include <ws2spi.h>
49# endif
50# define netError(*__error()) WSAGetLastError()
51# define netStringErrorstrerror netStringErrorWin
52# define netCloseSocketclose closesocket
53# define gai_strerrorA netStringErrorWin
54
55#else /* WIN32 */
56
57# include <sys/ioctl.h>
58# include <sys/select.h>
59# include <sys/types.h>
60# include <sys/socket.h>
61# include <sys/time.h>
62# include <netdb.h>
63# include <arpa/inet.h>
64# include <netinet/in.h>
65# include <signal.h>
66typedef int SOCKET;
67# define INVALID_SOCKET(-1) (-1)
68# define netError(*__error()) errno(*__error())
69# define netStringErrorstrerror strerror
70# define netCloseSocketclose close
71# define ioctlsocketioctl ioctl
72#endif /* WIN32 */
73
74/**
75 * @todo Move this into the configure script
76 * AI_ADDRCONFIG, AI_ALL, and AI_V4MAPPED are available since glibc 2.3.3.
77 * AI_NUMERICSERV is available since glibc 2.3.4.
78 */
79#ifndef AI_NUMERICSERV0x00001000
80#define AI_NUMERICSERV0x00001000 0
81#endif
82#ifndef AI_ADDRCONFIG0x00000400
83#define AI_ADDRCONFIG0x00000400 0
84#endif
85
86/**
87 * @brief use an admin local address per default so that
88 * network admins can decide on how to handle traffic.
89 */
90#define NET_MULTICAST_IP6"ff04::696f:7175:616b:6533" "ff04::696f:7175:616b:6533"
91
92static cvar_t* net_ipv4;
93
94struct net_stream {
95 void *data;
96
97 bool loopback;
98 bool ready;
99 bool closed;
100 bool finished; /**< finished but maybe not yet closed */
101 SOCKET socket;
102 int index;
103 int family;
104 int addrlen;
105
106 struct dbuffer *inbound;
107 struct dbuffer *outbound;
108
109 stream_onclose_func *onclose;
110 stream_callback_func *func;
111 struct net_stream *loopback_peer;
112};
113
114struct datagram {
115 int len;
116 char *msg;
117 char *addr;
118 struct datagram *next;
119};
120
121struct datagram_socket {
122 SOCKET socket;
123 int index;
124 int family;
125 int addrlen;
126 struct datagram *queue;
127 struct datagram **queue_tail;
128 datagram_callback_func *func;
129};
130
131static fd_set read_fds;
132static fd_set write_fds;
133static SOCKET maxfd;
134static struct net_stream *streams[MAX_STREAMS56];
135static struct datagram_socket *datagram_sockets[MAX_DATAGRAM_SOCKETS7];
136
137static bool loopback_ready = false;
138static bool server_running = false;
139static stream_callback_func *server_func = NULL__null;
140static SOCKET server_socket = INVALID_SOCKET(-1);
141static int server_family, server_addrlen;
142
143#ifdef _WIN32
144static const char *netStringErrorWin (int code)
145{
146 switch (code) {
147 case WSAEINTR: return "WSAEINTR";
148 case WSAEBADF: return "WSAEBADF";
149 case WSAEACCES: return "WSAEACCES";
150 case WSAEDISCON: return "WSAEDISCON";
151 case WSAEFAULT: return "WSAEFAULT";
152 case WSAEINVAL: return "WSAEINVAL";
153 case WSAEMFILE: return "WSAEMFILE";
154 case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK";
155 case WSAEINPROGRESS: return "WSAEINPROGRESS";
156 case WSAEALREADY: return "WSAEALREADY";
157 case WSAENOTSOCK: return "WSAENOTSOCK";
158 case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ";
159 case WSAEMSGSIZE: return "WSAEMSGSIZE";
160 case WSAEPROTOTYPE: return "WSAEPROTOTYPE";
161 case WSAENOPROTOOPT: return "WSAENOPROTOOPT";
162 case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT";
163 case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT";
164 case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP";
165 case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT";
166 case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT";
167 case WSAEADDRINUSE: return "WSAEADDRINUSE";
168 case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL";
169 case WSAENETDOWN: return "WSAENETDOWN";
170 case WSAENETUNREACH: return "WSAENETUNREACH";
171 case WSAENETRESET: return "WSAENETRESET";
172 case WSAEHOSTDOWN: return "WSAEHOSTDOWN";
173 case WSAEHOSTUNREACH: return "WSAEHOSTUNREACH";
174 case WSAECONNABORTED: return "WSWSAECONNABORTEDAEINTR";
175 case WSAECONNRESET: return "WSAECONNRESET";
176 case WSAENOBUFS: return "WSAENOBUFS";
177 case WSAEISCONN: return "WSAEISCONN";
178 case WSAENOTCONN: return "WSAENOTCONN";
179 case WSAESHUTDOWN: return "WSAESHUTDOWN";
180 case WSAETOOMANYREFS: return "WSAETOOMANYREFS";
181 case WSAETIMEDOUT: return "WSAETIMEDOUT";
182 case WSAECONNREFUSED: return "WSAECONNREFUSED";
183 case WSAELOOP: return "WSAELOOP";
184 case WSAENAMETOOLONG: return "WSAENAMETOOLONG";
185 case WSASYSNOTREADY: return "WSASYSNOTREADY";
186 case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED";
187 case WSANOTINITIALISED: return "WSANOTINITIALISED";
188 case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND";
189 case WSATRY_AGAIN: return "WSATRY_AGAIN";
190 case WSANO_RECOVERY: return "WSANO_RECOVERY";
191 case WSANO_DATA: return "WSANO_DATA";
192 default: return "NO ERROR";
193 }
194}
195#endif
196
197/**
198 * @brief
199 * @sa NET_StreamNew
200 * @sa NET_StreamClose
201 * @sa NET_DatagramFindFreeSocket
202 */
203static int NET_StreamGetFree (void)
204{
205 static int start = 0;
206 int i;
207
208 for (i = 0; i < MAX_STREAMS56; i++) {
209 const int pos = (i + start) % MAX_STREAMS56;
210 if (streams[pos] == NULL__null) {
211 start = (pos + 1) % MAX_STREAMS56;
212 Com_DPrintf(DEBUG_SERVER0x40, "New stream at index: %i\n", pos);
213 return pos;
214 }
215 }
216 return -1;
217}
218
219/**
220 * @sa NET_StreamNew
221 */
222static int NET_DatagramFindFreeSocket (void)
223{
224 static int start = 0;
225 int i;
226
227 for (i = 0; i < MAX_DATAGRAM_SOCKETS7; i++) {
228 const int pos = (i + start) % MAX_DATAGRAM_SOCKETS7;
229 if (datagram_sockets[pos] == NULL__null) {
230 start = (pos + 1) % MAX_DATAGRAM_SOCKETS7;
231 Com_DPrintf(DEBUG_SERVER0x40, "New datagram at index: %i\n", pos);
232 return pos;
233 }
234 }
235 return -1;
236}
237
238/**
239 * @sa NET_StreamGetFree
240 * @sa NET_StreamClose
241 */
242static struct net_stream *NET_StreamNew (int index)
243{
244 net_stream* const s = Mem_PoolAllocType(net_stream, com_networkPool)static_cast<net_stream*>(static_cast<net_stream*>
(_Mem_Alloc((sizeof(net_stream) * (1)),true,(((com_networkPool
))),(0),"src/common/net.cpp",244)))
;
245 s->data = NULL__null;
246 s->loopback_peer = NULL__null;
247 s->loopback = false;
248 s->closed = false;
249 s->finished = false;
250 s->ready = false;
251 s->socket = INVALID_SOCKET(-1);
252 s->inbound = NULL__null;
253 s->outbound = NULL__null;
254 s->index = index;
255 s->family = 0;
256 s->addrlen = 0;
257 s->func = NULL__null;
258 if (streams[index])
259 NET_StreamFree(streams[index]);
260 streams[index] = s;
261 return s;
262}
263
264static void NET_ShowStreams_f (void)
265{
266 int i;
267 char buf[256];
268 int cnt = 0;
269
270 for (i = 0; i < MAX_STREAMS56; i++) {
271 if (streams[i] != NULL__null) {
272 Com_Printf("Steam %i is opened: %s on socket %i (closed: %i, finished: %i, outbound: " UFO_SIZE_T"%zu" ", inbound: " UFO_SIZE_T"%zu" ")\n", i,
273 NET_StreamPeerToName(streams[i], buf, sizeof(buf), true),
274 streams[i]->socket, streams[i]->closed, streams[i]->finished,
275 dbuffer_len(streams[i]->outbound)(streams[i]->outbound ? (streams[i]->outbound)->len :
0)
, dbuffer_len(streams[i]->inbound)(streams[i]->inbound ? (streams[i]->inbound)->len : 0
)
);
276 cnt++;
277 }
278 }
279 Com_Printf("%i/%i streams opened\n", cnt, MAX_STREAMS56);
280}
281
282/**
283 * @sa NET_Shutdown
284 * @sa Qcommon_Init
285 */
286void NET_Init (void)
287{
288 int i;
289#ifdef _WIN32
290 WSADATA winsockdata;
291#endif
292
293 Com_Printf("\n----- network initialization -------\n");
294
295#ifdef _WIN32
296 if (WSAStartup(MAKEWORD(2, 0), &winsockdata) != 0)
297 Com_Error(ERR_FATAL0, "Winsock initialization failed.");
298#endif
299
300 maxfd = 0;
301 FD_ZERO(&read_fds)__builtin_bzero(&read_fds, sizeof(*(&read_fds)));
302 FD_ZERO(&write_fds)__builtin_bzero(&write_fds, sizeof(*(&write_fds)));
303
304 for (i = 0; i < MAX_STREAMS56; i++)
305 streams[i] = NULL__null;
306 for (i = 0; i < MAX_DATAGRAM_SOCKETS7; i++)
307 datagram_sockets[i] = NULL__null;
308
309#ifndef _WIN32
310 signal(SIGPIPE13, SIG_IGN(void (*)(int))1);
311#endif
312
313 dbuffer_init();
314
315 net_ipv4 = Cvar_Get("net_ipv4", "1", CVAR_ARCHIVE1, "Only use ipv4");
316 Cmd_AddCommand("net_showstreams", NET_ShowStreams_f, "Show opened streams");
317}
318
319/**
320 * @sa NET_Init
321 */
322void NET_Shutdown (void)
323{
324#ifdef _WIN32
325 WSACleanup();
326#endif
327 dbuffer_shutdown();
328}
329
330/**
331 * @sa NET_StreamFinished
332 * @sa NET_StreamNew
333 */
334static void NET_StreamClose (struct net_stream *s)
335{
336 if (!s || s->closed)
337 return;
338
339 if (s->socket != INVALID_SOCKET(-1)) {
340 if (dbuffer_len(s->outbound)(s->outbound ? (s->outbound)->len : 0))
341 Com_Printf("The outbound buffer for this socket (%d) is not empty\n", s->socket);
342 else if (dbuffer_len(s->inbound)(s->inbound ? (s->inbound)->len : 0))
343 Com_Printf("The inbound buffer for this socket (%d) is not empty\n", s->socket);
344
345 FD_CLR(s->socket, &read_fds)do { int __fd = (s->socket); ((&read_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] &= ~(1<<(__fd % (sizeof
(__int32_t) * 8)))); } while(0)
;
346 FD_CLR(s->socket, &write_fds)do { int __fd = (s->socket); ((&write_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] &= ~(1<<(__fd % (sizeof
(__int32_t) * 8)))); } while(0)
;
347 netCloseSocketclose(s->socket);
348 s->socket = INVALID_SOCKET(-1);
349 }
350 if (s->index >= 0)
351 streams[s->index] = NULL__null;
352
353 if (s->loopback_peer) {
354 /* Detach the peer, so that it won't send us anything more */
355 s->loopback_peer->outbound = NULL__null;
356 s->loopback_peer->loopback_peer = NULL__null;
357 }
358
359 s->closed = true;
360 Com_DPrintf(DEBUG_SERVER0x40, "Close stream at index: %i\n", s->index);
361
362 /* If we have a loopback peer, don't free the outbound buffer,
363 * because it's our peer's inbound buffer */
364 if (!s->loopback_peer)
365 free_dbuffer(s->outbound);
366
367 s->outbound = NULL__null;
368 s->socket = INVALID_SOCKET(-1);
369
370 /* Note that this is potentially invalid after the callback returns */
371 if (s->finished) {
372 free_dbuffer(s->inbound);
373 if (s->onclose != NULL__null)
374 s->onclose();
375 Mem_Free(s)_Mem_Free((s),"src/common/net.cpp",375);
376 s = NULL__null;
377 } else if (s->func) {
378 s->func(s);
379 }
380
381 if (s != NULL__null && s->onclose != NULL__null)
382 s->onclose();
383}
384
385static void do_accept (SOCKET sock)
386{
387 const int index = NET_StreamGetFree();
388 struct net_stream *s;
389 if (index == -1) {
390 Com_Printf("Too many streams open, rejecting inbound connection\n");
391 netCloseSocketclose(sock);
392 return;
393 }
394
395 s = NET_StreamNew(index);
396 s->socket = sock;
397 s->inbound = new_dbuffer();
398 s->outbound = new_dbuffer();
399 s->family = server_family;
400 s->addrlen = server_addrlen;
401 s->func = server_func;
402
403 maxfd = std::max(sock + 1, maxfd);
404 FD_SET(sock, &read_fds)do { int __fd = (sock); ((&read_fds)->fds_bits[__fd/(sizeof
(__int32_t) * 8)] |= (1<<(__fd % (sizeof(__int32_t) * 8
)))); } while(0)
;
405
406 server_func(s);
407 /** @todo close stream? */
408}
409
410/**
411 * @sa Qcommon_Frame
412 */
413void NET_Wait (int timeout)
414{
415 struct timeval tv;
416 int ready;
417 int i;
418
419 fd_set read_fds_out;
420 fd_set write_fds_out;
421
422 memcpy(&read_fds_out, &read_fds, sizeof(read_fds_out));
423 memcpy(&write_fds_out, &write_fds, sizeof(write_fds_out));
424
425 /* select() won't notice that loopback streams are ready, so we'll
426 * eliminate the delay directly */
427 if (loopback_ready)
428 timeout = 0;
429
430 tv.tv_sec = timeout / 1000;
431 tv.tv_usec = 1000 * (timeout % 1000);
432#ifdef _WIN32
433 if (read_fds_out.fd_count == 0) {
434 Sys_Sleep(timeout);
435 ready = 0;
436 } else
437#endif
438 ready = select(maxfd, &read_fds_out, &write_fds_out, NULL__null, &tv);
439
440 if (ready == -1) {
441 Com_Printf("select failed: %s\n", netStringErrorstrerror(netError(*__error())));
442 return;
443 }
444
445 if (ready == 0 && !loopback_ready)
446 return;
447
448 if (server_socket != INVALID_SOCKET(-1) && FD_ISSET(server_socket, &read_fds_out)__darwin_fd_isset((server_socket), (&read_fds_out))) {
449 const SOCKET client_socket = accept(server_socket, NULL__null, 0);
450 if (client_socket == INVALID_SOCKET(-1)) {
451 if (errno(*__error()) != EAGAIN35)
452 Com_Printf("accept on socket %d failed: %s\n", server_socket, netStringErrorstrerror(netError(*__error())));
453 } else
454 do_accept(client_socket);
455 }
456
457 for (i = 0; i < MAX_STREAMS56; i++) {
458 struct net_stream *s = streams[i];
459
460 if (!s)
461 continue;
462
463 if (s->loopback) {
464 /* If the peer is gone and the buffer is empty, close the stream */
465 if (!s->loopback_peer && NET_StreamGetLength(s) == 0) {
466 NET_StreamClose(s);
467 }
468 /* Note that s is potentially invalid after the callback returns - we'll close dead streams on the next pass */
469 else if (s->ready && s->func) {
470 s->func(s);
471 }
472
473 continue;
474 }
475
476 if (s->socket == INVALID_SOCKET(-1))
477 continue;
478
479 if (FD_ISSET(s->socket, &write_fds_out)__darwin_fd_isset((s->socket), (&write_fds_out))) {
480 char buf[4096];
481 int len;
482
483 if (dbuffer_len(s->outbound)(s->outbound ? (s->outbound)->len : 0) == 0) {
484 FD_CLR(s->socket, &write_fds)do { int __fd = (s->socket); ((&write_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] &= ~(1<<(__fd % (sizeof
(__int32_t) * 8)))); } while(0)
;
485
486 /* Finished streams are closed when their outbound queues empty */
487 if (s->finished)
488 NET_StreamClose(s);
489
490 continue;
491 }
492
493 len = dbuffer_get(s->outbound, buf, sizeof(buf));
494 len = send(s->socket, buf, len, 0);
495
496 if (len < 0) {
497 Com_Printf("write on socket %d failed: %s\n", s->socket, netStringErrorstrerror(netError(*__error())));
498 NET_StreamClose(s);
499 continue;
500 }
501
502 Com_DPrintf(DEBUG_SERVER0x40, "wrote %d bytes to stream %d (%s)\n", len, i, NET_StreamPeerToName(s, buf, sizeof(buf), true));
503
504 dbuffer_remove(s->outbound, len);
505 }
506
507 if (FD_ISSET(s->socket, &read_fds_out)__darwin_fd_isset((s->socket), (&read_fds_out))) {
508 char buf[4096];
509 const int len = recv(s->socket, buf, sizeof(buf), 0);
510 if (len <= 0) {
511 if (len == -1)
512 Com_Printf("read on socket %d failed: %s\n", s->socket, netStringErrorstrerror(netError(*__error())));
513 NET_StreamClose(s);
514 continue;
515 } else {
516 if (s->inbound) {
517 dbuffer_add(s->inbound, buf, len);
518
519 Com_DPrintf(DEBUG_SERVER0x40, "read %d bytes from stream %d (%s)\n", len, i, NET_StreamPeerToName(s, buf, sizeof(buf), true));
520
521 /* Note that s is potentially invalid after the callback returns */
522 if (s->func)
523 s->func(s);
524
525 continue;
526 }
527 }
528 }
529 }
530
531 for (i = 0; i < MAX_DATAGRAM_SOCKETS7; i++) {
532 struct datagram_socket *s = datagram_sockets[i];
533
534 if (!s)
535 continue;
536
537 if (FD_ISSET(s->socket, &write_fds_out)__darwin_fd_isset((s->socket), (&write_fds_out))) {
538 if (s->queue) {
539 struct datagram *dgram = s->queue;
540 const int len = sendto(s->socket, dgram->msg, dgram->len, 0, (struct sockaddr *)dgram->addr, s->addrlen);
541 if (len == -1)
542 Com_Printf("sendto on socket %d failed: %s\n", s->socket, netStringErrorstrerror(netError(*__error())));
543 /* Regardless of whether it worked, we don't retry datagrams */
544 s->queue = dgram->next;
545 Mem_Free(dgram->msg)_Mem_Free((dgram->msg),"src/common/net.cpp",545);
546 Mem_Free(dgram->addr)_Mem_Free((dgram->addr),"src/common/net.cpp",546);
547 Mem_Free(dgram)_Mem_Free((dgram),"src/common/net.cpp",547);
548 if (!s->queue)
549 s->queue_tail = &s->queue;
550 } else {
551 FD_CLR(s->socket, &write_fds)do { int __fd = (s->socket); ((&write_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] &= ~(1<<(__fd % (sizeof
(__int32_t) * 8)))); } while(0)
;
552 }
553 }
554
555 if (FD_ISSET(s->socket, &read_fds_out)__darwin_fd_isset((s->socket), (&read_fds_out))) {
556 char buf[256];
557 char addrbuf[256];
558 socklen_t addrlen = sizeof(addrbuf);
559 const int len = recvfrom(s->socket, buf, sizeof(buf), 0, (struct sockaddr *)addrbuf, &addrlen);
560 if (len == -1)
561 Com_Printf("recvfrom on socket %d failed: %s\n", s->socket, netStringErrorstrerror(netError(*__error())));
562 else
563 s->func(s, buf, len, (struct sockaddr *)addrbuf);
564 }
565 }
566
567 loopback_ready = false;
568}
569
570static bool NET_SocketSetNonBlocking (SOCKET socketNum)
571{
572 unsigned long t = 1;
573 if (ioctlsocketioctl(socketNum, FIONBIO((__uint32_t)0x80000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('f')) << 8) | ((126)))
, &t) == -1) {
574 Com_Printf("ioctl FIONBIO failed: %s\n", strerror(errno(*__error())));
575 return false;
576 }
577 return true;
578}
579
580static struct net_stream *NET_DoConnect (const char *node, const char *service, const struct addrinfo *addr, int i, stream_onclose_func *onclose)
581{
582 struct net_stream *s;
583 SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
584 if (sock == INVALID_SOCKET(-1)) {
585 Com_Printf("Failed to create socket: %s\n", netStringErrorstrerror(netError(*__error())));
586 return NULL__null;
587 }
588
589 if (!NET_SocketSetNonBlocking(sock)) {
590 netCloseSocketclose(sock);
591 return NULL__null;
592 }
593
594 if (connect(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
595 const int err = netError(*__error());
596#ifdef _WIN32
597 if (err != WSAEWOULDBLOCK) {
598#else
599 if (err != EINPROGRESS36) {
600#endif
601 Com_Printf("Failed to start connection to %s:%s: %s\n", node, service, netStringErrorstrerror(err));
602 netCloseSocketclose(sock);
603 return NULL__null;
604 }
605 }
606
607 s = NET_StreamNew(i);
608 s->socket = sock;
609 s->inbound = new_dbuffer();
610 s->outbound = new_dbuffer();
611 s->family = addr->ai_family;
612 s->addrlen = addr->ai_addrlen;
613 s->onclose = onclose;
614
615 maxfd = std::max(sock + 1, maxfd);
616 FD_SET(sock, &read_fds)do { int __fd = (sock); ((&read_fds)->fds_bits[__fd/(sizeof
(__int32_t) * 8)] |= (1<<(__fd % (sizeof(__int32_t) * 8
)))); } while(0)
;
617
618 return s;
619}
620
621/**
622 * @brief Try to connect to a given host on a given port
623 * @param[in] node The host to connect to
624 * @param[in] service The port to connect to
625 * @param[in] onclose The callback that is called on closing the returned stream. This is useful if
626 * you hold the pointer for the returned stream anywhere else and would like to get notified once
627 * this pointer is invalid.
628 * @sa NET_DoConnect
629 * @sa NET_ConnectToLoopBack
630 * @todo What about a timeout
631 */
632struct net_stream *NET_Connect (const char *node, const char *service, stream_onclose_func *onclose)
633{
634 struct addrinfo *res;
635 struct addrinfo hints;
636 int rc;
637 struct net_stream *s = NULL__null;
638 int index;
639
640 OBJZERO(hints)(memset(&((hints)), (0), sizeof((hints))));
641 hints.ai_flags = AI_ADDRCONFIG0x00000400 | AI_NUMERICSERV0x00001000;
642 hints.ai_socktype = SOCK_STREAM1;
643 /* force ipv4 */
644 if (net_ipv4->integer)
645 hints.ai_family = AF_INET2;
646
647 rc = getaddrinfo(node, service, &hints, &res);
648 if (rc != 0) {
649 Com_Printf("Failed to resolve host %s:%s: %s\n", node, service, gai_strerror(rc));
650 return NULL__null;
651 }
652
653 index = NET_StreamGetFree();
654 if (index == -1) {
655 Com_Printf("Failed to connect to host %s:%s, too many streams open\n", node, service);
656 freeaddrinfo(res);
657 return NULL__null;
658 }
659
660 s = NET_DoConnect(node, service, res, index, onclose);
661
662 freeaddrinfo(res);
663 return s;
664}
665
666/**
667 * @param[in] onclose The callback that is called on closing the returned stream. This is useful if
668 * you hold the pointer for the returned stream anywhere else and would like to get notified once
669 * this pointer is invalid.
670 * @sa NET_Connect
671 */
672struct net_stream *NET_ConnectToLoopBack (stream_onclose_func *onclose)
673{
674 struct net_stream *client, *server;
675 int server_index, client_index;
676
677 if (!server_running)
678 return NULL__null;
679
680 server_index = NET_StreamGetFree();
681 client_index = NET_StreamGetFree();
682
683 if (server_index == -1 || client_index == -1 || server_index == client_index) {
684 Com_Printf("Failed to connect to loopback server, too many streams open\n");
685 return NULL__null;
686 }
687
688 client = NET_StreamNew(client_index);
689 client->loopback = true;
690 client->inbound = new_dbuffer();
691 client->outbound = new_dbuffer();
692 client->onclose = onclose;
693
694 server = NET_StreamNew(server_index);
695 server->loopback = true;
696 server->inbound = client->outbound;
697 server->outbound = client->inbound;
698 server->func = server_func;
699 server->onclose = NULL__null;
700
701 client->loopback_peer = server;
702 server->loopback_peer = client;
703
704 server_func(server);
705
706 return client;
707}
708
709/**
710 * @brief Enqueue a network message into a stream
711 * @sa NET_StreamDequeue
712 * @sa dbuffer_add
713 */
714void NET_StreamEnqueue (struct net_stream *s, const char *data, int len)
715{
716 if (len <= 0 || !s || s->closed || s->finished)
717 return;
718
719 if (s->outbound)
720 dbuffer_add(s->outbound, data, len);
721
722 if (s->socket >= 0)
723 FD_SET(s->socket, &write_fds)do { int __fd = (s->socket); ((&write_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] |= (1<<(__fd % (sizeof(__int32_t
) * 8)))); } while(0)
;
724
725 if (s->loopback_peer) {
726 loopback_ready = true;
727 s->loopback_peer->ready = true;
728 }
729}
730
731bool NET_StreamIsClosed (struct net_stream *s)
732{
733 return s ? (s->closed || s->finished) : true;
734}
735
736int NET_StreamGetLength (struct net_stream *s)
737{
738 return s ? dbuffer_len(s->inbound)(s->inbound ? (s->inbound)->len : 0) : 0;
739}
740
741/**
742 * @brief Returns the length of the waiting inbound buffer
743 * @sa dbuffer_get
744 */
745int NET_StreamPeek (struct net_stream *s, char *data, int len)
746{
747 if (len <= 0 || !s)
748 return 0;
749
750 if ((s->closed || s->finished) && dbuffer_len(s->inbound)(s->inbound ? (s->inbound)->len : 0) == 0)
751 return 0;
752
753 return dbuffer_get(s->inbound, data, len);
754}
755
756/**
757 * @sa NET_StreamEnqueue
758 * @sa dbuffer_extract
759 */
760int NET_StreamDequeue (struct net_stream *s, char *data, int len)
761{
762 if (len <= 0 || !s || s->finished)
763 return 0;
764
765 return dbuffer_extract(s->inbound, data, len);
766}
767
768void *NET_StreamGetData (struct net_stream *s)
769{
770 return s ? s->data : NULL__null;
771}
772
773void NET_StreamSetData (struct net_stream *s, void *data)
774{
775 if (!s)
776 return;
777 s->data = data;
778}
779
780/**
781 * @brief Call NET_StreamFree to dump the whole thing right now
782 * @sa NET_StreamClose
783 * @sa NET_StreamFinished
784 */
785void NET_StreamFree (struct net_stream *s)
786{
787 if (!s)
788 return;
789 s->finished = true;
790 NET_StreamClose(s);
791}
792
793/**
794 * @brief Call NET_StreamFinished to mark the stream as uninteresting, but to
795 * finish sending any data in the buffer. The stream will appear
796 * closed after this call, and at some unspecified point in the future
797 * s will become an invalid pointer, so it should not be further
798 * referenced.
799 */
800void NET_StreamFinished (struct net_stream *s)
801{
802 if (!s)
803 return;
804
805 s->finished = true;
806
807 if (s->socket >= 0)
808 FD_CLR(s->socket, &read_fds)do { int __fd = (s->socket); ((&read_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] &= ~(1<<(__fd % (sizeof
(__int32_t) * 8)))); } while(0)
;
809
810 /* Stop the loopback peer from queueing stuff up in here */
811 if (s->loopback_peer)
812 s->loopback_peer->outbound = NULL__null;
813
814 free_dbuffer(s->inbound);
815 s->inbound = NULL__null;
816
817 /* If there's nothing in the outbound buffer, any finished stream is
818 * ready to be closed */
819 if (dbuffer_len(s->outbound)(s->outbound ? (s->outbound)->len : 0) == 0)
820 NET_StreamClose(s);
821}
822
823/**
824 * @param[in] s The network stream to get the name for
825 * @param[out] dst The target buffer to store the ip and port in
826 * @param[in] len The length of the target buffer
827 * @param[in] appendPort Also append the port number to the target buffer
828 */
829const char *NET_StreamPeerToName (struct net_stream *s, char *dst, int len, bool appendPort)
830{
831 if (!s)
832 return "(null)";
833 else if (NET_StreamIsLoopback(s))
834 return "loopback connection";
835 else {
836 char buf[128];
837 char node[64];
838 char service[64];
839 int rc;
840 socklen_t addrlen = s->addrlen;
841 if (getpeername(s->socket, (struct sockaddr *)buf, &addrlen) != 0)
842 return "(error)";
843
844 rc = getnameinfo((struct sockaddr *)buf, addrlen, node, sizeof(node), service, sizeof(service),
845 NI_NUMERICHOST0x00000002 | NI_NUMERICSERV0x00000008);
846 if (rc != 0) {
847 Com_Printf("Failed to convert sockaddr to string: %s\n", gai_strerror(rc));
848 return "(error)";
849 }
850 if (!appendPort)
851 Q_strncpyz(dst, node, len)Q_strncpyzDebug( dst, node, len, "src/common/net.cpp", 851 );
852 else {
853 node[sizeof(node) - 1] = '\0';
854 service[sizeof(service) - 1] = '\0';
855 Com_sprintf(dst, len, "%s %s", node, service);
856 }
857 return dst;
858 }
859}
860
861void NET_StreamSetCallback (struct net_stream *s, stream_callback_func *func)
862{
863 if (!s)
864 return;
865 s->func = func;
866}
867
868bool NET_StreamIsLoopback (struct net_stream *s)
869{
870 return s && s->loopback;
871}
872
873static int NET_DoStartServer (const struct addrinfo *addr)
874{
875 SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
5
Access to field 'ai_protocol' results in a dereference of a null pointer (loaded from variable 'addr')
876 int t = 1;
877
878 if (sock == INVALID_SOCKET(-1)) {
879 Com_Printf("Failed to create socket: %s\n", netStringErrorstrerror(netError(*__error())));
880 return INVALID_SOCKET(-1);
881 }
882
883 if (!NET_SocketSetNonBlocking(sock)) {
884 netCloseSocketclose(sock);
885 return INVALID_SOCKET(-1);
886 }
887
888#ifdef _WIN32
889 if (setsockopt(sock, SOL_SOCKET0xffff, SO_REUSEADDR0x0004, (char *) &t, sizeof(t)) != 0) {
890#else
891 if (setsockopt(sock, SOL_SOCKET0xffff, SO_REUSEADDR0x0004, &t, sizeof(t)) != 0) {
892#endif
893 Com_Printf("Failed to set SO_REUSEADDR on socket: %s\n", netStringErrorstrerror(netError(*__error())));
894 netCloseSocketclose(sock);
895 return INVALID_SOCKET(-1);
896 }
897
898 if (bind(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
899 Com_Printf("Failed to bind socket: %s\n", netStringErrorstrerror(netError(*__error())));
900 netCloseSocketclose(sock);
901 return INVALID_SOCKET(-1);
902 }
903
904 if (listen(sock, SOMAXCONN128) != 0) {
905 Com_Printf("Failed to listen on socket: %s\n", netStringErrorstrerror(netError(*__error())));
906 netCloseSocketclose(sock);
907 return INVALID_SOCKET(-1);
908 }
909
910 maxfd = std::max(sock + 1, maxfd);
911 FD_SET(sock, &read_fds)do { int __fd = (sock); ((&read_fds)->fds_bits[__fd/(sizeof
(__int32_t) * 8)] |= (1<<(__fd % (sizeof(__int32_t) * 8
)))); } while(0)
;
912 server_family = addr->ai_family;
913 server_addrlen = addr->ai_addrlen;
914
915 return sock;
916}
917
918static struct addrinfo* NET_GetAddrinfoForNode (const char *node, const char *service)
919{
920 struct addrinfo *res;
921 struct addrinfo hints;
922 int rc;
923
924 OBJZERO(hints)(memset(&((hints)), (0), sizeof((hints))));
925 hints.ai_flags = AI_ADDRCONFIG0x00000400 | AI_PASSIVE0x00000001;
926 hints.ai_socktype = SOCK_STREAM1;
927 /* force ipv4 */
928 if (net_ipv4->integer)
929 hints.ai_family = AF_INET2;
930
931 rc = getaddrinfo(node, service, &hints, &res);
932 if (rc != 0) {
933 Com_Printf("Failed to resolve host %s:%s: %s\n", node ? node : "*", service, gai_strerror(rc));
934 return NULL__null;
935 }
936
937 return res;
938}
939
940/**
941 * @sa NET_DoStartServer
942 * @param[in] node The node to start the server with
943 * @param[in] service If this is NULL we are in single player mode
944 * @param[in] func The server callback function to read the packets
945 * @sa SV_ReadPacket
946 * @sa server_func
947 * @sa SV_Stop
948 */
949bool SV_Start (const char *node, const char *service, stream_callback_func *func)
950{
951 if (!func)
1
Taking false branch
952 return false;
953
954 if (server_running) {
2
Taking false branch
955 Com_Printf("SV_Start: Server is still running - call SV_Stop before\n");
956 return false;
957 }
958
959 if (service) {
3
Taking true branch
960 struct addrinfo *res = NET_GetAddrinfoForNode(node, service);
961
962 server_socket = NET_DoStartServer(res);
4
Calling 'NET_DoStartServer'
963 if (server_socket == INVALID_SOCKET(-1)) {
964 Com_Printf("Failed to start server on %s:%s\n", node ? node : "*", service);
965 } else {
966 server_running = true;
967 server_func = func;
968 }
969 freeaddrinfo(res);
970 } else {
971 /* Loopback server only */
972 server_running = true;
973 server_func = func;
974 }
975
976 return server_running;
977}
978
979/**
980 * @sa SV_Start
981 */
982void SV_Stop (void)
983{
984 server_running = false;
985 server_func = NULL__null;
986 if (server_socket != INVALID_SOCKET(-1)) {
987 FD_CLR(server_socket, &read_fds)do { int __fd = (server_socket); ((&read_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] &= ~(1<<(__fd % (sizeof
(__int32_t) * 8)))); } while(0)
;
988 netCloseSocketclose(server_socket);
989 }
990 server_socket = INVALID_SOCKET(-1);
991}
992
993/**
994 * @sa NET_DatagramSocketNew
995 */
996static struct datagram_socket *NET_DatagramSocketDoNew (const struct addrinfo *addr)
997{
998 SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
999 int t = 1;
1000 const int index = NET_DatagramFindFreeSocket();
1001
1002 if (index == -1) {
1003 Com_Printf("Too many datagram sockets open\n");
1004 return NULL__null;
1005 }
1006
1007 if (sock == INVALID_SOCKET(-1)) {
1008 Com_Printf("Failed to create socket: %s\n", netStringErrorstrerror(netError(*__error())));
1009 return NULL__null;
1010 }
1011
1012 if (!NET_SocketSetNonBlocking(sock)) {
1013 netCloseSocketclose(sock);
1014 return NULL__null;
1015 }
1016
1017 if (setsockopt(sock, SOL_SOCKET0xffff, SO_REUSEADDR0x0004, (char *) &t, sizeof(t)) != 0) {
1018 Com_Printf("Failed to set SO_REUSEADDR on socket: %s\n", netStringErrorstrerror(netError(*__error())));
1019 netCloseSocketclose(sock);
1020 return NULL__null;
1021 }
1022
1023 if (setsockopt(sock, SOL_SOCKET0xffff, SO_BROADCAST0x0020, (char *) &t, sizeof(t)) != 0) {
1024 Com_Printf("Failed to set SO_BROADCAST on socket: %s\n", netStringErrorstrerror(netError(*__error())));
1025 netCloseSocketclose(sock);
1026 return NULL__null;
1027 }
1028
1029 if (bind(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
1030 Com_Printf("Failed to bind socket: %s\n", netStringErrorstrerror(netError(*__error())));
1031 netCloseSocketclose(sock);
1032 return NULL__null;
1033 }
1034
1035 maxfd = std::max(sock + 1, maxfd);
1036 FD_SET(sock, &read_fds)do { int __fd = (sock); ((&read_fds)->fds_bits[__fd/(sizeof
(__int32_t) * 8)] |= (1<<(__fd % (sizeof(__int32_t) * 8
)))); } while(0)
;
1037
1038 datagram_socket* const s = Mem_PoolAllocType(datagram_socket, com_networkPool)static_cast<datagram_socket*>(static_cast<datagram_socket
*>(_Mem_Alloc((sizeof(datagram_socket) * (1)),true,(((com_networkPool
))),(0),"src/common/net.cpp",1038)))
;
1039 s->family = addr->ai_family;
1040 s->addrlen = addr->ai_addrlen;
1041 s->socket = sock;
1042 s->index = index;
1043 s->queue = NULL__null;
1044 s->queue_tail = &s->queue;
1045 s->func = NULL__null;
1046 datagram_sockets[index] = s;
1047
1048 return s;
1049}
1050
1051/**
1052 * @brief Opens a datagram socket (UDP)
1053 * @sa NET_DatagramSocketDoNew
1054 * @param[in] node The numeric address to resolv (might be NULL)
1055 * @param[in] service The port number
1056 * @param[in] func Callback function for data handling
1057 */
1058struct datagram_socket *NET_DatagramSocketNew (const char *node, const char *service, datagram_callback_func *func)
1059{
1060 struct datagram_socket *s;
1061 struct addrinfo *res;
1062 struct addrinfo hints;
1063 int rc;
1064
1065 if (!service || !func)
1066 return NULL__null;
1067
1068 OBJZERO(hints)(memset(&((hints)), (0), sizeof((hints))));
1069 hints.ai_flags = AI_NUMERICHOST0x00000004 | AI_ADDRCONFIG0x00000400 | AI_NUMERICSERV0x00001000 | AI_PASSIVE0x00000001;
1070 hints.ai_socktype = SOCK_DGRAM2;
1071 /* force ipv4 */
1072 if (net_ipv4->integer)
1073 hints.ai_family = AF_INET2;
1074
1075 rc = getaddrinfo(node, service, &hints, &res);
1076
1077 if (rc != 0) {
1078 Com_Printf("Failed to resolve host %s:%s: %s\n", node ? node : "*", service, gai_strerror(rc));
1079 return false;
1080 }
1081
1082 s = NET_DatagramSocketDoNew(res);
1083 if (s)
1084 s->func = func;
1085
1086 freeaddrinfo(res);
1087 return s;
1088}
1089
1090/**
1091 * @sa NET_DatagramSocketNew
1092 */
1093void NET_DatagramSend (struct datagram_socket *s, const char *buf, int len, struct sockaddr *to)
1094{
1095 if (!s || len <= 0 || !buf || !to)
1096 return;
1097
1098 datagram* const dgram = Mem_PoolAllocType(datagram, com_networkPool)static_cast<datagram*>(static_cast<datagram*>(_Mem_Alloc
((sizeof(datagram) * (1)),true,(((com_networkPool))),(0),"src/common/net.cpp"
,1098)))
;
1099 dgram->msg = Mem_PoolAllocTypeN(char, len, com_networkPool)static_cast<char*>(_Mem_Alloc((sizeof(char) * (len)),true
,((com_networkPool)),(0),"src/common/net.cpp",1099))
;
1100 dgram->addr = Mem_PoolAllocTypeN(char, s->addrlen, com_networkPool)static_cast<char*>(_Mem_Alloc((sizeof(char) * (s->addrlen
)),true,((com_networkPool)),(0),"src/common/net.cpp",1100))
;
1101 memcpy(dgram->msg, buf, len);
1102 memcpy(dgram->addr, to, len);
1103 dgram->len = len;
1104 dgram->next = NULL__null;
1105
1106 *s->queue_tail = dgram;
1107 s->queue_tail = &dgram->next;
1108
1109 FD_SET(s->socket, &write_fds)do { int __fd = (s->socket); ((&write_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] |= (1<<(__fd % (sizeof(__int32_t
) * 8)))); } while(0)
;
1110}
1111
1112/**
1113 * @sa NET_DatagramSend
1114 * @sa NET_DatagramSocketNew
1115 * @todo This is only sending on the first available device, what if we have several devices?
1116 */
1117void NET_DatagramBroadcast (struct datagram_socket *s, const char *buf, int len, int port)
1118{
1119 if (s->family == AF_INET2) {
1120 struct sockaddr_in addr;
1121 addr.sin_family = AF_INET2;
1122 addr.sin_port = htons(port)((__uint16_t)(__builtin_constant_p(port) ? ((__uint16_t)((((__uint16_t
)(port) & 0xff00) >> 8) | (((__uint16_t)(port) &
0x00ff) << 8))) : _OSSwapInt16(port)))
;
1123 addr.sin_addr.s_addr = INADDR_BROADCAST(u_int32_t)0xffffffff;
1124 NET_DatagramSend(s, buf, len, (struct sockaddr *)&addr);
1125 } else if (s->family == AF_INET630) {
1126 struct sockaddr_in addr;
1127 addr.sin_family = AF_INET630;
1128 addr.sin_port = htons(port)((__uint16_t)(__builtin_constant_p(port) ? ((__uint16_t)((((__uint16_t
)(port) & 0xff00) >> 8) | (((__uint16_t)(port) &
0x00ff) << 8))) : _OSSwapInt16(port)))
;
1129 addr.sin_addr.s_addr = INADDR_BROADCAST(u_int32_t)0xffffffff;
1130 NET_DatagramSend(s, buf, len, (struct sockaddr *)&addr);
1131 } else {
1132 Com_Error(ERR_DROP1, "Broadcast unsupported on address family %d\n", s->family);
1133 }
1134}
1135
1136/**
1137 * @sa NET_DatagramSocketNew
1138 * @sa NET_DatagramSocketDoNew
1139 */
1140void NET_DatagramSocketClose (struct datagram_socket *s)
1141{
1142 if (!s)
1143 return;
1144
1145 FD_CLR(s->socket, &read_fds)do { int __fd = (s->socket); ((&read_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] &= ~(1<<(__fd % (sizeof
(__int32_t) * 8)))); } while(0)
;
1146 FD_CLR(s->socket, &write_fds)do { int __fd = (s->socket); ((&write_fds)->fds_bits
[__fd/(sizeof(__int32_t) * 8)] &= ~(1<<(__fd % (sizeof
(__int32_t) * 8)))); } while(0)
;
1147 netCloseSocketclose(s->socket);
1148
1149 while (s->queue) {
1150 struct datagram *dgram = s->queue;
1151 s->queue = dgram->next;
1152 Mem_Free(dgram->msg)_Mem_Free((dgram->msg),"src/common/net.cpp",1152);
1153 Mem_Free(dgram->addr)_Mem_Free((dgram->addr),"src/common/net.cpp",1153);
1154 Mem_Free(dgram)_Mem_Free((dgram),"src/common/net.cpp",1154);
1155 }
1156
1157 datagram_sockets[s->index] = NULL__null;
1158 Mem_Free(s)_Mem_Free((s),"src/common/net.cpp",1158);
1159}
1160
1161/**
1162 * @brief Convert sockaddr to string
1163 * @param[in] s The datagram socket type to get the addrlen from
1164 * @param[in] addr The socket address to convert into a string
1165 * @param[out] node The target node name buffer
1166 * @param[in] nodelen The length of the node name buffer
1167 * @param[out] service The target service name buffer
1168 * @param[in] servicelen The length of the service name buffer
1169 */
1170void NET_SockaddrToStrings (struct datagram_socket *s, struct sockaddr *addr, char *node, size_t nodelen, char *service, size_t servicelen)
1171{
1172 const int rc = getnameinfo(addr, s->addrlen, node, nodelen, service, servicelen,
1173 NI_NUMERICHOST0x00000002 | NI_NUMERICSERV0x00000008 | NI_DGRAM0x00000010);
1174 if (rc != 0) {
1175 Com_Printf("Failed to convert sockaddr to string: %s\n", gai_strerror(rc));
1176 Q_strncpyz(node, "(error)", nodelen)Q_strncpyzDebug( node, "(error)", nodelen, "src/common/net.cpp"
, 1176 )
;
1177 Q_strncpyz(service, "(error)", servicelen)Q_strncpyzDebug( service, "(error)", servicelen, "src/common/net.cpp"
, 1177 )
;
1178 }
1179}
1180
1181static void NET_AddrinfoToString (const struct addrinfo *addr, char *buf, size_t bufLength)
1182{
1183 char *service = inet_ntoa(((struct sockaddr_in *)addr->ai_addr)->sin_addr);
1184 Q_strncpyz(buf, service, bufLength)Q_strncpyzDebug( buf, service, bufLength, "src/common/net.cpp"
, 1184 )
;
1185}
1186
1187void NET_ResolvNode (const char *node, char *buf, size_t bufLength)
1188{
1189 struct addrinfo* addrinfo = NET_GetAddrinfoForNode(node, NULL__null);
1190 if (addrinfo == NULL__null) {
1191 buf[0] = '\0';
1192 return;
1193 }
1194 NET_AddrinfoToString(addrinfo, buf, bufLength);
1195 freeaddrinfo(addrinfo);
1196}