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') |
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 | /* | ||
10 | Copyright (C) 2002-2011 UFO: Alien Invasion. | ||
11 | |||
12 | This program is free software; you can redistribute it and/or | ||
13 | modify it under the terms of the GNU General Public License | ||
14 | as published by the Free Software Foundation; either version 2 | ||
15 | of the License, or (at your option) any later version. | ||
16 | |||
17 | This program is distributed in the hope that it will be useful, | ||
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
20 | |||
21 | See the GNU General Public License for more details. | ||
22 | |||
23 | You should have received a copy of the GNU General Public License | ||
24 | along with this program; if not, write to the Free Software | ||
25 | Foundation, 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> | ||
66 | typedef 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 | |||
92 | static cvar_t* net_ipv4; | ||
93 | |||
94 | struct 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 | |||
114 | struct datagram { | ||
115 | int len; | ||
116 | char *msg; | ||
117 | char *addr; | ||
118 | struct datagram *next; | ||
119 | }; | ||
120 | |||
121 | struct 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 | |||
131 | static fd_set read_fds; | ||
132 | static fd_set write_fds; | ||
133 | static SOCKET maxfd; | ||
134 | static struct net_stream *streams[MAX_STREAMS56]; | ||
135 | static struct datagram_socket *datagram_sockets[MAX_DATAGRAM_SOCKETS7]; | ||
136 | |||
137 | static bool loopback_ready = false; | ||
138 | static bool server_running = false; | ||
139 | static stream_callback_func *server_func = NULL__null; | ||
140 | static SOCKET server_socket = INVALID_SOCKET(-1); | ||
141 | static int server_family, server_addrlen; | ||
142 | |||
143 | #ifdef _WIN32 | ||
144 | static 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 | */ | ||
203 | static 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 | */ | ||
222 | static 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 | */ | ||
242 | static 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 | |||
264 | static 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 | */ | ||
286 | void 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 | */ | ||
322 | void 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 | */ | ||
334 | static 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 | |||
385 | static 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 | */ | ||
413 | void 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 | |||
570 | static 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 | |||
580 | static 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 | */ | ||
632 | struct 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 | */ | ||
672 | struct 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 | */ | ||
714 | void 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 | |||
731 | bool NET_StreamIsClosed (struct net_stream *s) | ||
732 | { | ||
733 | return s ? (s->closed || s->finished) : true; | ||
734 | } | ||
735 | |||
736 | int 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 | */ | ||
745 | int 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 | */ | ||
760 | int 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 | |||
768 | void *NET_StreamGetData (struct net_stream *s) | ||
769 | { | ||
770 | return s ? s->data : NULL__null; | ||
771 | } | ||
772 | |||
773 | void 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 | */ | ||
785 | void 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 | */ | ||
800 | void 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 | */ | ||
829 | const 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 | |||
861 | void NET_StreamSetCallback (struct net_stream *s, stream_callback_func *func) | ||
862 | { | ||
863 | if (!s) | ||
864 | return; | ||
865 | s->func = func; | ||
866 | } | ||
867 | |||
868 | bool NET_StreamIsLoopback (struct net_stream *s) | ||
869 | { | ||
870 | return s && s->loopback; | ||
871 | } | ||
872 | |||
873 | static int NET_DoStartServer (const struct addrinfo *addr) | ||
874 | { | ||
875 | SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); | ||
| |||
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 | |||
918 | static 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 | */ | ||
949 | bool SV_Start (const char *node, const char *service, stream_callback_func *func) | ||
950 | { | ||
951 | if (!func) | ||
| |||
952 | return false; | ||
953 | |||
954 | if (server_running) { | ||
| |||
955 | Com_Printf("SV_Start: Server is still running - call SV_Stop before\n"); | ||
956 | return false; | ||
957 | } | ||
958 | |||
959 | if (service) { | ||
| |||
960 | struct addrinfo *res = NET_GetAddrinfoForNode(node, service); | ||
961 | |||
962 | server_socket = NET_DoStartServer(res); | ||
| |||
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 | */ | ||
982 | void 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 | */ | ||
996 | static 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 | */ | ||
1058 | struct 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 | */ | ||
1093 | void 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 | */ | ||
1117 | void 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 | */ | ||
1140 | void 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 | */ | ||
1170 | void 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 | |||
1181 | static 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 | |||
1187 | void 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 | } |