diff options
-rw-r--r-- | build/common.mk | 1 | ||||
-rw-r--r-- | src/cl_local.h | 1 | ||||
-rw-r--r-- | src/cl_main.c | 6 | ||||
-rw-r--r-- | src/cl_public.h | 3 | ||||
-rw-r--r-- | src/common.c | 5 | ||||
-rw-r--r-- | src/in_evdev.c | 12 | ||||
-rw-r--r-- | src/in_lirc.c | 13 | ||||
-rw-r--r-- | src/io_sleep.c | 334 | ||||
-rw-r--r-- | src/io_sleep.h | 45 | ||||
-rw-r--r-- | src/mvd_client.c | 4 | ||||
-rw-r--r-- | src/net_chan.c | 14 | ||||
-rw-r--r-- | src/net_chan.h | 4 | ||||
-rw-r--r-- | src/net_common.c | 1318 | ||||
-rw-r--r-- | src/net_sock.h | 62 | ||||
-rw-r--r-- | src/net_stream.h | 4 | ||||
-rw-r--r-- | src/net_unix.h | 409 | ||||
-rw-r--r-- | src/net_win.h | 401 | ||||
-rw-r--r-- | src/sv_ac.c | 10 | ||||
-rw-r--r-- | src/sv_local.h | 1 | ||||
-rw-r--r-- | src/sv_main.c | 85 | ||||
-rw-r--r-- | src/sv_mvd.c | 6 | ||||
-rw-r--r-- | src/sv_public.h | 2 | ||||
-rw-r--r-- | src/sv_send.c | 13 | ||||
-rw-r--r-- | src/sys_unix.c | 6 | ||||
-rw-r--r-- | src/wsaerr.h | 86 |
25 files changed, 1530 insertions, 1315 deletions
diff --git a/build/common.mk b/build/common.mk index e11417a..101ea13 100644 --- a/build/common.mk +++ b/build/common.mk @@ -29,7 +29,6 @@ SRCFILES+=cmd.c \ q_msg.c \ q_shared.c \ q_field.c \ - io_sleep.c \ error.c \ fpu.c diff --git a/src/cl_local.h b/src/cl_local.h index cbd5e97..f0fc535 100644 --- a/src/cl_local.h +++ b/src/cl_local.h @@ -37,7 +37,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "cl_public.h" #include "ui_public.h" #include "sv_public.h" -#include "io_sleep.h" #include "vid_public.h" #if USE_ZLIB #include <zlib.h> diff --git a/src/cl_main.c b/src/cl_main.c index ccc627e..b5f3b79 100644 --- a/src/cl_main.c +++ b/src/cl_main.c @@ -1583,7 +1583,7 @@ static void CL_PacketEvent(void) } #if USE_ICMP -void CL_ErrorEvent(void) +void CL_ErrorEvent(netadr_t *from) { // // error packet from server @@ -1594,10 +1594,10 @@ void CL_ErrorEvent(void) if (!cls.netchan) { return; // dump it if not connected } - if (!NET_IsEqualBaseAdr(&net_from, &cls.netchan->remote_address)) { + if (!NET_IsEqualBaseAdr(from, &cls.netchan->remote_address)) { return; } - if (net_from.port && net_from.port != cls.netchan->remote_address.port) { + if (from->port && from->port != cls.netchan->remote_address.port) { return; } diff --git a/src/cl_public.h b/src/cl_public.h index 77cee04..12c9628 100644 --- a/src/cl_public.h +++ b/src/cl_public.h @@ -49,7 +49,8 @@ typedef enum { qboolean CL_ProcessEvents(void); #if USE_ICMP -void CL_ErrorEvent(void); +struct netadr_s; +void CL_ErrorEvent(struct netadr_s *from); #endif void CL_Init(void); void CL_Disconnect(error_type_t type); diff --git a/src/common.c b/src/common.c index dd64000..a530fce 100644 --- a/src/common.c +++ b/src/common.c @@ -37,7 +37,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "cmodel.h" #include "q_field.h" #include "prompt.h" -#include "io_sleep.h" #include "fpu.h" #include <setjmp.h> @@ -2203,7 +2202,7 @@ void Qcommon_Frame(void) // sleep on network sockets when running a dedicated server // still do a select(), but don't sleep when running a client! - IO_Sleep(remaining); + NET_Sleep(remaining); // calculate time spent running last frame and sleeping oldtime = com_eventTime; @@ -2255,6 +2254,8 @@ void Qcommon_Frame(void) Sys_RunConsole(); #endif + NET_UpdateStats(); + remaining = SV_Frame(msec); #if USE_CLIENT diff --git a/src/in_evdev.c b/src/in_evdev.c index 8013f1b..cfdc936 100644 --- a/src/in_evdev.c +++ b/src/in_evdev.c @@ -22,7 +22,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "key_public.h" #include "in_public.h" #include "cl_public.h" -#include "io_sleep.h" #include "q_list.h" #include <sys/types.h> @@ -48,7 +47,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. typedef struct { list_t entry; int fd; - ioentry_t *io; } evdev_t; static struct { @@ -63,7 +61,6 @@ static struct { static void evdev_remove(evdev_t *dev) { - IO_Remove(dev->fd); close(dev->fd); List_Remove(&dev->entry); Z_Free(dev); @@ -76,12 +73,6 @@ static void evdev_read(evdev_t *dev) size_t i, count; unsigned button, time; -#if 0 - if (!dev->io->canread) { - return; - } -#endif - bytes = read(dev->fd, ev, EVENT_SIZE * MAX_EVENTS); if (bytes == -1) { if (errno == EAGAIN || errno == EINTR) { @@ -206,7 +197,6 @@ static evdev_t *evdev_add(const char *path) dev = Z_Malloc(sizeof(*dev)); dev->fd = fd; - dev->io = IO_Add(fd); List_Append(&evdev.devices, &dev->entry); return dev; @@ -328,12 +318,10 @@ static void GrabMouse(grab_t grab) FOR_EACH_EVDEV(dev) { if (!grab) { - dev->io->wantread = qfalse; continue; } // pump pending events - dev->io->wantread = qtrue; while (read(dev->fd, &ev, EVENT_SIZE) == EVENT_SIZE) ; } diff --git a/src/in_lirc.c b/src/in_lirc.c index 287d4d2..bc46c53 100644 --- a/src/in_lirc.c +++ b/src/in_lirc.c @@ -23,7 +23,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "in_public.h" #include "cl_public.h" #include "sys_public.h" -#include "io_sleep.h" #include "in_lirc.h" #include <sys/types.h> @@ -88,7 +87,7 @@ void Lirc_Shutdown(void) return; } lirc_freeconfig(lirc.config); - IO_Remove(lirc.fd); + NET_RemoveFd(lirc.fd); lirc_deinit(); memset(&lirc, 0, sizeof(lirc)); } @@ -101,6 +100,8 @@ static void lirc_param_changed(cvar_t *self) qboolean Lirc_Init(void) { + int ret; + lirc_enable = Cvar_Get("lirc_enable", "0", 0); lirc_enable->changed = lirc_param_changed; lirc_config = Cvar_Get("lirc_config", "", CVAR_NOSET); @@ -125,8 +126,12 @@ qboolean Lirc_Init(void) return qfalse; } - fcntl(lirc.fd, F_SETFL, fcntl(lirc.fd, F_GETFL, 0) | FNDELAY); - lirc.io = IO_Add(lirc.fd); + // change it to non-blocking + ret = fcntl(lirc.fd, F_GETFL, 0); + if (!(ret & O_NONBLOCK)) + fcntl(lirc.fd, F_SETFL, ret | O_NONBLOCK); + + lirc.io = NET_AddFd(lirc.fd); lirc.io->wantread = qtrue; Com_Printf("LIRC interface initialized.\n"); diff --git a/src/io_sleep.c b/src/io_sleep.c deleted file mode 100644 index 3116e44..0000000 --- a/src/io_sleep.c +++ /dev/null @@ -1,334 +0,0 @@ -/* -Copyright (C) 2009 Andrey Nazarov - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "common.h" -#include "sys_public.h" -#include "io_sleep.h" -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <winsock2.h> -#else -#include <sys/select.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> -#endif - -static ioentry_t entries[FD_SETSIZE]; -static int numfds; - -ioentry_t *IO_Add(int fd) -{ - ioentry_t *e; -#ifdef _WIN32 - int i; - - for (i = 0, e = entries; i < numfds; i++, e++) { - if (!e->inuse) { - break; - } - } - - if (i == numfds) { - if (++numfds > FD_SETSIZE) { - Com_Error(ERR_FATAL, "%s: no more space for fd: %d", __func__, fd); - } - } - - e->fd = fd; -#else - -#define CHECK_FD \ - if( fd < 0 || fd >= FD_SETSIZE ) { \ - Com_Error( ERR_FATAL, "%s: fd out of range: %d", __func__, fd ); \ - } - - CHECK_FD - - if (fd >= numfds) { - numfds = fd + 1; - } - - e = &entries[fd]; -#endif - e->inuse = qtrue; - return e; -} - -void IO_Remove(int fd) -{ - ioentry_t *e; - int i; - - e = IO_Get(fd); - memset(e, 0, sizeof(*e)); - - for (i = numfds - 1; i >= 0; i--) { - e = &entries[i]; - if (e->inuse) { - break; - } - } - - numfds = i + 1; -} - -ioentry_t *IO_Get(int fd) -{ - ioentry_t *e; -#ifdef _WIN32 - int i; - - for (i = 0, e = entries; i < numfds; i++, e++) { - if (!e->inuse) { - continue; - } - if (e->fd == fd) { - return e; - } - } - - Com_Error(ERR_FATAL, "%s: fd not found: %d", __func__, fd); -#else - CHECK_FD - - e = &entries[fd]; - return e; -#endif -} - -//void IO_Mask( int fd ) { -//} - -/* -==================== -IO_Sleep - -Sleeps msec or until some file descriptor is ready -==================== -*/ -int IO_Sleep(int msec) -{ - struct timeval timeout; - fd_set rfd, wfd; -#ifdef _WIN32 - fd_set efd; -#endif - ioentry_t *e; - int i, ret; - - if (!numfds) { - // don't bother with select() - Sys_Sleep(msec); - return 0; - } - - FD_ZERO(&rfd); - FD_ZERO(&wfd); -#ifdef _WIN32 - FD_ZERO(&efd); -#endif - -#ifdef _WIN32 -#define FD e->fd -#else -#define FD i -#endif - - for (i = 0, e = entries; i < numfds; i++, e++) { - if (!e->inuse) { - continue; - } - e->canread = qfalse; - if (e->wantread) { - FD_SET(FD, &rfd); - } - e->canwrite = qfalse; - if (e->wantwrite) { - FD_SET(FD, &wfd); - } -#ifdef _WIN32 - e->canexcept = qfalse; - if (e->wantexcept) { - FD_SET(FD, &efd); - } -#endif - } - - timeout.tv_sec = msec / 1000; - timeout.tv_usec = (msec % 1000) * 1000; - - ret = select(numfds, &rfd, &wfd, -#ifdef _WIN32 - & efd, -#else - NULL, -#endif - & timeout); - if (ret == -1) { -#ifdef _WIN32 - // TODO: report WSA error -#else - if (errno != EINTR) { - Com_EPrintf("%s: %s\n", __func__, strerror(errno)); - } -#endif - return ret; - } - - if (!ret) { - return ret; - } - - for (i = 0; i < numfds; i++) { - e = &entries[i]; - if (!e->inuse) { - continue; - } - if (e->wantread && FD_ISSET(FD, &rfd)) { - e->canread = qtrue; - } - if (e->wantwrite && FD_ISSET(FD, &wfd)) { - e->canwrite = qtrue; - } -#ifdef _WIN32 - if (e->wantexcept && FD_ISSET(FD, &efd)) { - e->canexcept = qtrue; - } -#endif - } - - return ret; -} - -#if USE_AC_SERVER - -/* -==================== -IO_Sleep - -Sleeps msec or until some file descriptor from a given subset is ready -==================== -*/ -int IO_Sleepv(int msec, ...) -{ - va_list argptr; - struct timeval timeout; - fd_set rfd, wfd; -#ifdef _WIN32 - fd_set efd; -#endif - ioentry_t *e; - int i, ret; - - if (!numfds) { - // don't bother with select() - Sys_Sleep(msec); - return 0; - } - - FD_ZERO(&rfd); - FD_ZERO(&wfd); -#ifdef _WIN32 - FD_ZERO(&efd); -#endif - - va_start(argptr, msec); - while (1) { - i = va_arg(argptr, int); - if (i == -1) { - break; - } - e = &entries[i]; - if (!e->inuse) { - continue; - } - e->canread = qfalse; - if (e->wantread) { - FD_SET(FD, &rfd); - } - e->canwrite = qfalse; - if (e->wantwrite) { - FD_SET(FD, &wfd); - } -#ifdef _WIN32 - e->canexcept = qfalse; - if (e->wantexcept) { - FD_SET(FD, &efd); - } -#endif - } - va_end(argptr); - - timeout.tv_sec = msec / 1000; - timeout.tv_usec = (msec % 1000) * 1000; - - ret = select(numfds, &rfd, &wfd, -#ifdef _WIN32 - & efd, -#else - NULL, -#endif - & timeout); - if (ret == -1) { -#ifdef _WIN32 - // TODO: report WSA error -#else - if (errno != EINTR) { - Com_EPrintf("%s: %s\n", __func__, strerror(errno)); - } -#endif - return ret; - } - - if (!ret) { - return ret; - } - - va_start(argptr, msec); - while (1) { - i = va_arg(argptr, int); - if (i == -1) { - break; - } - e = &entries[i]; - if (!e->inuse) { - continue; - } - if (e->wantread && FD_ISSET(FD, &rfd)) { - e->canread = qtrue; - } - if (e->wantwrite && FD_ISSET(FD, &wfd)) { - e->canwrite = qtrue; - } -#ifdef _WIN32 - if (e->wantexcept && FD_ISSET(FD, &efd)) { - e->canexcept = qtrue; - } -#endif - } - va_end(argptr); - - return ret; -} - -#endif // USE_AC_SERVER - diff --git a/src/io_sleep.h b/src/io_sleep.h deleted file mode 100644 index fa80418..0000000 --- a/src/io_sleep.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright (C) 2009 Andrey Nazarov - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -typedef struct { -#ifdef _WIN32 - int fd; -#endif - qboolean inuse: 1; - qboolean canread: 1; - qboolean canwrite: 1; -#ifdef _WIN32 - qboolean canexcept: 1; -#endif - qboolean wantread: 1; - qboolean wantwrite: 1; -#ifdef _WIN32 - qboolean wantexcept: 1; -#endif -} ioentry_t; - -ioentry_t *IO_Add(int fd); -void IO_Remove(int fd); -ioentry_t *IO_Get(int fd); -int IO_Sleep(int msec); -#if USE_AC_SERVER -int IO_Sleepv(int msec, ...); -#endif - diff --git a/src/mvd_client.c b/src/mvd_client.c index 22c9da7..3c098d9 100644 --- a/src/mvd_client.c +++ b/src/mvd_client.c @@ -1579,7 +1579,7 @@ static void gtv_destroy(gtv_t *gtv) } // make sure network connection is closed - NET_Close(>v->stream); + NET_CloseStream(>v->stream); // unlink from the list of connections List_Remove(>v->entry); @@ -1624,7 +1624,7 @@ static void gtv_drop(gtv_t *gtv) Com_FormatTimeLong(buffer, sizeof(buffer), sec); Com_Printf("[%s] -=- Reconnecting in %s.\n", gtv->name, buffer); - NET_Close(>v->stream); + NET_CloseStream(>v->stream); #if USE_ZLIB inflateReset(>v->z_str); FIFO_Clear(>v->z_buf); diff --git a/src/net_chan.c b/src/net_chan.c index f4acd89..88fa0a8 100644 --- a/src/net_chan.c +++ b/src/net_chan.c @@ -158,7 +158,7 @@ void Netchan_OutOfBand(netsrc_t sock, const netadr_t *address, } // send the datagram - NET_SendPacket(sock, address, len + 4, &packet); + NET_SendPacket(sock, &packet, len + 4, address); } // ============================================================================ @@ -261,8 +261,8 @@ static size_t NetchanOld_Transmit(netchan_t *netchan, size_t length, const void // send the datagram for (i = 0; i < numpackets; i++) { - NET_SendPacket(netchan->sock, &netchan->remote_address, - send.cursize, send.data); + NET_SendPacket(netchan->sock, send.data, send.cursize, + &netchan->remote_address); } netchan->outgoing_sequence++; @@ -506,8 +506,8 @@ static size_t NetchanNew_TransmitNextFragment(netchan_t *netchan) } // send the datagram - NET_SendPacket(netchan->sock, &netchan->remote_address, - send.cursize, send.data); + NET_SendPacket(netchan->sock, send.data, send.cursize, + &netchan->remote_address); return send.cursize; } @@ -610,8 +610,8 @@ static size_t NetchanNew_Transmit(netchan_t *netchan, size_t length, const void // send the datagram for (i = 0; i < numpackets; i++) { - NET_SendPacket(netchan->sock, &netchan->remote_address, - send.cursize, send.data); + NET_SendPacket(netchan->sock, send.data, send.cursize, + &netchan->remote_address); } netchan->outgoing_sequence++; diff --git a/src/net_chan.h b/src/net_chan.h index ec56838..e7cf612 100644 --- a/src/net_chan.h +++ b/src/net_chan.h @@ -72,8 +72,8 @@ netchan_t *Netchan_Setup(netsrc_t sock, netchan_type_t type, const netadr_t *adr, int qport, size_t maxpacketlen, int protocol); void Netchan_Close(netchan_t *netchan); -#define OOB_PRINT(sock, addr, string) \ - NET_SendPacket(sock, addr, sizeof("\xff\xff\xff\xff" string) - 1, "\xff\xff\xff\xff" string) +#define OOB_PRINT(sock, addr, data) \ + NET_SendPacket(sock, CONST_STR_LEN("\xff\xff\xff\xff" data), addr) //============================================================================ diff --git a/src/net_common.c b/src/net_common.c index 84bce5a..5b86120 100644 --- a/src/net_common.c +++ b/src/net_common.c @@ -34,30 +34,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sys_public.h" #include "sv_public.h" #include "cl_public.h" -#include "io_sleep.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> -#define socklen_t int -#define IOCTLSOCKET_PARAM u_long -#define SETSOCKOPT_PARAM BOOL -#ifdef _WIN32_WCE -#define NET_GET_ERROR() (net_error = GetLastError()) -#else -#define NET_GET_ERROR() (net_error = WSAGetLastError()) -#define NET_WOULD_BLOCK() (NET_GET_ERROR() == WSAEWOULDBLOCK) -#endif #else // _WIN32 #include <unistd.h> +#include <sys/types.h> #include <sys/socket.h> -#include <sys/time.h> #include <netinet/in.h> #include <netdb.h> #include <sys/param.h> #include <sys/ioctl.h> -#include <sys/uio.h> #include <arpa/inet.h> #ifdef __linux__ #include <linux/types.h> @@ -65,19 +54,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include <linux/errqueue.h> #endif #endif // __linux__ -#define SOCKET int -#define INVALID_SOCKET -1 -#define closesocket close -#define ioctlsocket ioctl -#define IOCTLSOCKET_PARAM int -#define SETSOCKOPT_PARAM int -#define NET_GET_ERROR() (net_error = errno) -#define NET_WOULD_BLOCK() (NET_GET_ERROR() == EWOULDBLOCK) #endif // !_WIN32 +// prevents infinite retry loops caused by broken TCP/IP stacks +#define MAX_ERROR_RETRIES 64 + #if USE_CLIENT -#define MAX_LOOPBACK 4 +#define MAX_LOOPBACK 4 typedef struct { byte data[MAX_PACKETLEN]; @@ -85,14 +69,14 @@ typedef struct { } loopmsg_t; typedef struct { - loopmsg_t msgs[MAX_LOOPBACK]; - unsigned get; - unsigned send; + loopmsg_t msgs[MAX_LOOPBACK]; + unsigned long get; + unsigned long send; } loopback_t; static loopback_t loopbacks[NS_COUNT]; -#endif +#endif // USE_CLIENT cvar_t *net_ip; cvar_t *net_port; @@ -121,13 +105,16 @@ static cvar_t *net_ignore_icmp; static netflag_t net_active; static int net_error; -static const char socketNames[NS_COUNT][8] = { "Client", "Server" }; -static SOCKET udp_sockets[NS_COUNT] = { INVALID_SOCKET, INVALID_SOCKET }; -static SOCKET tcp_socket = INVALID_SOCKET; +static qsocket_t udp_sockets[NS_COUNT] = { -1, -1 }; +static qsocket_t tcp_socket = -1; + #ifdef _DEBUG static qhandle_t net_logFile; #endif +static ioentry_t io_entries[FD_SETSIZE]; +static int io_numfds; + // current rate measurement static unsigned net_rate_time; static size_t net_rate_rcvd; @@ -148,11 +135,6 @@ static uint64_t net_packets_sent; //============================================================================= -/* -=================== -NET_NetadrToSockadr -=================== -*/ static void NET_NetadrToSockadr(const netadr_t *a, struct sockaddr_in *s) { memset(s, 0, sizeof(*s)); @@ -174,11 +156,6 @@ static void NET_NetadrToSockadr(const netadr_t *a, struct sockaddr_in *s) } } -/* -=================== -NET_SockadrToNetadr -=================== -*/ static void NET_SockadrToNetadr(const struct sockaddr_in *s, netadr_t *a) { memset(a, 0, sizeof(*a)); @@ -188,36 +165,17 @@ static void NET_SockadrToNetadr(const struct sockaddr_in *s, netadr_t *a) a->port = s->sin_port; } -/* -============= -NET_StringToSockaddr - -localhost -idnewt -idnewt:28000 -192.246.40.70 -192.246.40.70:28000 -============= -*/ static qboolean NET_StringToSockaddr(const char *s, struct sockaddr_in *sadr) { + uint32_t addr; struct hostent *h; - char copy[MAX_QPATH], *p; + const char *p; int dots; memset(sadr, 0, sizeof(*sadr)); - sadr->sin_family = AF_INET; - sadr->sin_port = 0; - Q_strlcpy(copy, s, sizeof(copy)); - // strip off a trailing :port if present - p = strchr(copy, ':'); - if (p) { - *p = 0; - sadr->sin_port = htons((u_short)atoi(p + 1)); - } - for (p = copy, dots = 0; *p; p++) { + for (p = s, dots = 0; *p; p++) { if (*p == '.') { dots++; } else if (!Q_isdigit(*p)) { @@ -225,14 +183,11 @@ static qboolean NET_StringToSockaddr(const char *s, struct sockaddr_in *sadr) } } if (*p == 0 && dots <= 3) { - uint32_t addr = inet_addr(copy); - - if (addr == INADDR_NONE) { + if ((addr = inet_addr(s)) == INADDR_NONE) return qfalse; - } sadr->sin_addr.s_addr = addr; } else { - if (!(h = gethostbyname(copy))) + if (!(h = gethostbyname(s))) return qfalse; sadr->sin_addr.s_addr = *(uint32_t *)h->h_addr_list[0]; } @@ -240,6 +195,23 @@ static qboolean NET_StringToSockaddr(const char *s, struct sockaddr_in *sadr) return qtrue; } +static qboolean NET_StringToSockaddr2(const char *s, int port, struct sockaddr_in *sadr) +{ + if (*s) { + if (!NET_StringToSockaddr(s, sadr)) + return qfalse; + } else { + // empty string binds to all interfaces + memset(sadr, 0, sizeof(*sadr)); + sadr->sin_family = AF_INET; + sadr->sin_addr.s_addr = INADDR_ANY; + } + + if (port != PORT_ANY) + sadr->sin_port = htons((u_short)port); + + return qtrue; +} /* =================== @@ -249,7 +221,6 @@ NET_AdrToString char *NET_AdrToString(const netadr_t *a) { static char s[MAX_QPATH]; - const uint8_t *ip; switch (a->type) { case NA_LOOPBACK: @@ -257,9 +228,10 @@ char *NET_AdrToString(const netadr_t *a) return s; case NA_IP: case NA_BROADCAST: - ip = a->ip.u8; Q_snprintf(s, sizeof(s), "%u.%u.%u.%u:%u", - ip[0], ip[1], ip[2], ip[3], ntohs(a->port)); + a->ip.u8[0], a->ip.u8[1], + a->ip.u8[2], a->ip.u8[3], + BigShort(a->port)); return s; default: Com_Error(ERR_FATAL, "%s: bad address type", __func__); @@ -283,16 +255,27 @@ idnewt:28000 qboolean NET_StringToAdr(const char *s, netadr_t *a, int port) { struct sockaddr_in sadr; + char copy[MAX_STRING_CHARS], *p; + size_t len; - if (!NET_StringToSockaddr(s, &sadr)) { + len = Q_strlcpy(copy, s, sizeof(copy)); + if (len >= sizeof(copy)) + return qfalse; + + // strip off a trailing :port if present + p = strchr(copy, ':'); + if (p) + *p = 0; + + if (!NET_StringToSockaddr(copy, &sadr)) return qfalse; - } NET_SockadrToNetadr(&sadr, a); - if (!a->port) { + if (p) + a->port = BigShort(atoi(p + 1)); + if (!a->port) a->port = BigShort(port); - } return qtrue; } @@ -370,7 +353,8 @@ static void NET_LogPacket(const netadr_t *address, const char *prefix, return; } - FS_FPrintf(net_logFile, "%s : %s\n", prefix, NET_AdrToString(address)); + FS_FPrintf(net_logFile, "%u : %s : %s : %"PRIz" bytes\n", + com_localTime, prefix, NET_AdrToString(address), length); numRows = (length + 15) / 16; for (i = 0; i < numRows; i++) { @@ -386,7 +370,7 @@ static void NET_LogPacket(const netadr_t *address, const char *prefix, for (j = 0; j < 16; j++) { if (i * 16 + j < length) { c = data[i * 16 + j]; - FS_FPrintf(net_logFile, "%c", (c < 32 || c > 127) ? '.' : c); + FS_FPrintf(net_logFile, "%c", Q_isprint(c) ? c : '.'); } else { FS_FPrintf(net_logFile, " "); } @@ -399,9 +383,11 @@ static void NET_LogPacket(const netadr_t *address, const char *prefix, #endif +//============================================================================= + #define RATE_SECS 3 -static void NET_UpdateStats(void) +void NET_UpdateStats(void) { unsigned diff; @@ -420,6 +406,56 @@ static void NET_UpdateStats(void) net_rate_rcvd = 0; } +/* +==================== +NET_Stats_f +==================== +*/ +static void NET_Stats_f(void) +{ + time_t diff, now = time(NULL); + char buffer[MAX_QPATH]; + + if (com_startTime > now) { + com_startTime = now; + } + diff = now - com_startTime; + if (diff < 1) { + diff = 1; + } + + Com_FormatTime(buffer, sizeof(buffer), diff); + Com_Printf("Network uptime: %s\n", buffer); + Com_Printf("Bytes sent: %"PRIu64" (%"PRIu64" bytes/sec)\n", + net_bytes_sent, net_bytes_sent / diff); + Com_Printf("Bytes rcvd: %"PRIu64" (%"PRIu64" bytes/sec)\n", + net_bytes_rcvd, net_bytes_rcvd / diff); + Com_Printf("Packets sent: %"PRIu64" (%"PRIu64" packets/sec)\n", + net_packets_sent, net_packets_sent / diff); + Com_Printf("Packets rcvd: %"PRIu64" (%"PRIu64" packets/sec)\n", + net_packets_rcvd, net_packets_rcvd / diff); +#if USE_ICMP + Com_Printf("Total errors: %"PRIu64"/%"PRIu64"/%"PRIu64" (send/recv/icmp)\n", + net_send_errors, net_recv_errors, net_icmp_errors); +#else + Com_Printf("Total errors: %"PRIu64"/%"PRIu64" (send/recv)\n", + net_send_errors, net_recv_errors); +#endif + Com_Printf("Current upload rate: %"PRIz" bytes/sec\n", net_rate_up); + Com_Printf("Current download rate: %"PRIz" bytes/sec\n", net_rate_dn); +} + +static size_t NET_UpRate_m(char *buffer, size_t size) +{ + return Q_scnprintf(buffer, size, "%"PRIz, net_rate_up); +} + +static size_t NET_DnRate_m(char *buffer, size_t size) +{ + return Q_scnprintf(buffer, size, "%"PRIz, net_rate_dn); +} + +//============================================================================= #if USE_CLIENT @@ -433,8 +469,6 @@ qboolean NET_GetLoopPacket(netsrc_t sock) loopback_t *loop; loopmsg_t *loopmsg; - NET_UpdateStats(); - loop = &loopbacks[sock]; if (loop->send - loop->get > MAX_LOOPBACK - 1) { @@ -451,7 +485,7 @@ qboolean NET_GetLoopPacket(netsrc_t sock) memcpy(msg_read_buffer, loopmsg->data, loopmsg->datalen); #ifdef _DEBUG - if (net_log_enable->integer) { + if (net_log_enable->integer > 1) { NET_LogPacket(&net_from, "LP recv", loopmsg->data, loopmsg->datalen); } #endif @@ -465,30 +499,62 @@ qboolean NET_GetLoopPacket(netsrc_t sock) return qtrue; } +static qboolean NET_SendLoopPacket(netsrc_t sock, const void *data, + size_t len, const netadr_t *to) +{ + loopback_t *loop; + loopmsg_t *msg; + + if (net_dropsim->integer > 0 && (rand() % 100) < net_dropsim->integer) { + return qfalse; + } + + loop = &loopbacks[sock ^ 1]; + + msg = &loop->msgs[loop->send & (MAX_LOOPBACK - 1)]; + loop->send++; + + memcpy(msg->data, data, len); + msg->datalen = len; + +#ifdef _DEBUG + if (net_log_enable->integer > 1) { + NET_LogPacket(to, "LP send", data, len); + } #endif + if (sock == NS_CLIENT) { + net_rate_sent += len; + } + + return qtrue; +} + +#endif // USE_CLIENT + +//============================================================================= #if USE_ICMP -// prevents infinite retry loops caused by broken TCP/IP stacks -#define MAX_ERROR_RETRIES 64 +static const char *os_error_string(int err); -static void icmp_error_event(netsrc_t sock, int info) +static void NET_ErrorEvent(netsrc_t sock, netadr_t *from, + int ee_errno, int ee_info) { if (net_ignore_icmp->integer > 0) { return; } Com_DPrintf("%s: %s from %s\n", __func__, - NET_ErrorString(), NET_AdrToString(&net_from)); + os_error_string(ee_errno), NET_AdrToString(from)); net_icmp_errors++; switch (sock) { case NS_SERVER: - SV_ErrorEvent(info); + SV_ErrorEvent(from, ee_errno, ee_info); break; #if USE_CLIENT case NS_CLIENT: - CL_ErrorEvent(); + CL_ErrorEvent(from); break; #endif default: @@ -496,103 +562,207 @@ static void icmp_error_event(netsrc_t sock, int info) } } -#ifdef __linux__ +#endif // USE_ICMP -// Linux at least supports receiving ICMP errors on unconnected UDP sockets -// via IP_RECVERR cruft below... What about BSD? -// -// Returns true if failed socket operation should be retried, extremely hacky :/ -static qboolean process_error_queue(netsrc_t sock, struct sockaddr_in *to) +//============================================================================= + +// include our wrappers to hide platfrom-specific details +#ifdef _WIN32 +#include "net_win.h" +#else +#include "net_unix.h" +#endif + +/* +============= +NET_ErrorString +============= +*/ +const char *NET_ErrorString(void) { - byte buffer[1024]; - struct sockaddr_in from; - struct msghdr msg; - struct cmsghdr *cmsg; - struct sock_extended_err *ee; - int info; - int tries; - qboolean found; - - tries = 0; - found = qfalse; - -retry: - memset(&from, 0, sizeof(from)); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &from; - msg.msg_namelen = sizeof(from); - msg.msg_control = buffer; - msg.msg_controllen = sizeof(buffer); - - if (recvmsg(udp_sockets[sock], &msg, MSG_ERRQUEUE) == -1) { - if (NET_WOULD_BLOCK()) { - // wouldblock is silent - goto finish; + return os_error_string(net_error); +} + +/* +============= +NET_AddFd + +Adds file descriptor to the list of monitored descriptors +============= +*/ +ioentry_t *NET_AddFd(qsocket_t fd) +{ + ioentry_t *e = os_add_io(fd); + + e->inuse = qtrue; + return e; +} + +/* +============= +NET_RemoveFd + +Removes file descriptor from the list of monitored descriptors +============= +*/ +void NET_RemoveFd(qsocket_t fd) +{ + ioentry_t *e = os_get_io(fd); + int i; + + memset(e, 0, sizeof(*e)); + + for (i = io_numfds - 1; i >= 0; i--) { + e = &io_entries[i]; + if (e->inuse) { + break; } - Com_EPrintf("%s: %s\n", __func__, NET_ErrorString()); - goto finish; } - if (!(msg.msg_flags & MSG_ERRQUEUE)) { - Com_DPrintf("%s: no extended error received\n", __func__); - goto finish; + io_numfds = i + 1; +} + +/* +============= +NET_Sleep + +Sleeps msec or until some file descriptor is ready. Implementation is not +terribly efficient, but that's fine for a small number of descriptors we +typically have. +============= +*/ +int NET_Sleep(int msec) +{ + struct timeval tv; + fd_set rfds, wfds, efds; + ioentry_t *e; + qsocket_t fd; + int i, ret; + + if (!io_numfds) { + // don't bother with select() + Sys_Sleep(msec); + return 0; } - // find an ICMP error message - for (cmsg = CMSG_FIRSTHDR(&msg); - cmsg != NULL; - cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level != IPPROTO_IP) { + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + + for (i = 0, e = io_entries; i < io_numfds; i++, e++) { + if (!e->inuse) { continue; } - if (cmsg->cmsg_type != IP_RECVERR) { + fd = os_get_fd(e); + e->canread = qfalse; + e->canwrite = qfalse; + e->canexcept = qfalse; + if (e->wantread) FD_SET(fd, &rfds); + if (e->wantwrite) FD_SET(fd, &wfds); + if (e->wantexcept) FD_SET(fd, &efds); + } + + tv.tv_sec = msec / 1000; + tv.tv_usec = (msec % 1000) * 1000; + + ret = os_select(io_numfds, &rfds, &wfds, &efds, &tv); + if (ret == -1) { + Com_EPrintf("%s: %s\n", __func__, NET_ErrorString()); + return ret; + } + + if (ret == 0) + return ret; + + for (i = 0; i < io_numfds; i++) { + e = &io_entries[i]; + if (!e->inuse) { continue; } - ee = (struct sock_extended_err *)CMSG_DATA(cmsg); - if (ee->ee_origin == SO_EE_ORIGIN_ICMP) { - break; - } + fd = os_get_fd(e); + if (FD_ISSET(fd, &rfds)) e->canread = qtrue; + if (FD_ISSET(fd, &wfds)) e->canwrite = qtrue; + if (FD_ISSET(fd, &efds)) e->canexcept = qtrue; } - if (!cmsg) { - Com_DPrintf("%s: no ICMP error found\n", __func__); - goto finish; - } + return ret; +} + +#if USE_AC_SERVER + +/* +============= +NET_Sleepv - NET_SockadrToNetadr(&from, &net_from); +Sleeps msec or until some file descriptor from a given subset is ready +============= +*/ +int NET_Sleepv(int msec, ...) +{ + va_list argptr; + struct timeval tv; + fd_set rfds, wfds, efds; + ioentry_t *e; + qsocket_t fd; + int ret; - // check if this error was caused by a packet sent to the given address - // if so, do not retry send operation to prevent infinite loop - if (to != NULL && from.sin_addr.s_addr == to->sin_addr.s_addr && - (!from.sin_port || from.sin_port == to->sin_port)) { - Com_DPrintf("%s: found offending address %s:%d\n", - __func__, inet_ntoa(from.sin_addr), BigShort(from.sin_port)); - found = qtrue; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + + va_start(argptr, msec); + while (1) { + fd = va_arg(argptr, qsocket_t); + if (fd == -1) { + break; + } + e = os_get_io(fd); + if (!e->inuse) { + continue; + } + e->canread = qfalse; + e->canwrite = qfalse; + e->canexcept = qfalse; + if (e->wantread) FD_SET(fd, &rfds); + if (e->wantwrite) FD_SET(fd, &wfds); + if (e->wantexcept) FD_SET(fd, &efds); } + va_end(argptr); - // handle ICMP errors - net_error = ee->ee_errno; - info = 0; -#if USE_PMTUDISC - // for EMSGSIZE ee_info should hold discovered MTU - if (net_error == EMSGSIZE && ee->ee_info >= 576 && ee->ee_info < 4096) { - info = ee->ee_info; + tv.tv_sec = msec / 1000; + tv.tv_usec = (msec % 1000) * 1000; + + ret = os_select(io_numfds, &rfds, &wfds, &efds, &tv); + if (ret == -1) { + Com_EPrintf("%s: %s\n", __func__, NET_ErrorString()); + return ret; } -#endif - icmp_error_event(sock, info); - if (++tries < MAX_ERROR_RETRIES) { - goto retry; + if (ret == 0) + return ret; + + va_start(argptr, msec); + while (1) { + fd = va_arg(argptr, qsocket_t); + if (fd == -1) { + break; + } + e = os_get_io(fd); + if (!e->inuse) { + continue; + } + if (FD_ISSET(fd, &rfds)) e->canread = qtrue; + if (FD_ISSET(fd, &wfds)) e->canwrite = qtrue; + if (FD_ISSET(fd, &efds)) e->canexcept = qtrue; } + va_end(argptr); -finish: - return !!tries && !found; + return ret; } -#endif // __linux__ +#endif // USE_AC_SERVER -#endif // USE_ICMP +//============================================================================= /* ============= @@ -604,112 +774,37 @@ net_from variable receives source address. */ qboolean NET_GetPacket(netsrc_t sock) { - struct sockaddr_in from; - socklen_t fromlen; - int ret; -#if USE_ICMP - int tries; -#ifndef _WIN32 - int saved_error; -#endif -#endif ioentry_t *e; + ssize_t ret; - if (udp_sockets[sock] == INVALID_SOCKET) { + if (udp_sockets[sock] == -1) return qfalse; - } - - NET_UpdateStats(); - - e = IO_Get(udp_sockets[sock]); - if (!e->canread) { - return qfalse; - } -#if USE_ICMP - tries = 0; - -retry: -#endif - memset(&from, 0, sizeof(from)); - - fromlen = sizeof(from); - ret = recvfrom(udp_sockets[sock], (void *)msg_read_buffer, - MAX_PACKETLEN, 0, (struct sockaddr *)&from, &fromlen); - - if (!ret) { + e = os_get_io(udp_sockets[sock]); + if (!e->canread) return qfalse; - } - - NET_SockadrToNetadr(&from, &net_from); - - if (ret == -1) { - NET_GET_ERROR(); -#ifdef _WIN32 - switch (net_error) { - case WSAEWOULDBLOCK: - // wouldblock is silent - e->canread = qfalse; - break; -#if USE_ICMP - case WSAECONNRESET: - case WSAENETRESET: - // winsock has already provided us with - // a valid address from ICMP error packet - icmp_error_event(sock, 0); - if (++tries < MAX_ERROR_RETRIES) { - goto retry; - } - // intentional fallthrough -#endif // USE_ICMP - default: - Com_DPrintf("%s: %s from %s\n", __func__, - NET_ErrorString(), NET_AdrToString(&net_from)); - net_recv_errors++; - break; - } -#else // _WIN32 - switch (net_error) { - case EWOULDBLOCK: - // wouldblock is silent - e->canread = qfalse; - break; - default: -#if USE_ICMP - saved_error = net_error; - // recvfrom() fails on Linux if there's an ICMP originated - // pending error on socket. suck up error queue and retry... - if (process_error_queue(sock, NULL)) { - if (++tries < MAX_ERROR_RETRIES) { - goto retry; - } - } - net_error = saved_error; -#endif - Com_DPrintf("%s: %s from %s\n", __func__, - NET_ErrorString(), NET_AdrToString(&net_from)); - net_recv_errors++; - break; - } -#endif // !_WIN32 + ret = os_udp_recv(sock, msg_read_buffer, MAX_PACKETLEN, &net_from); + if (ret == NET_AGAIN) { + e->canread = qfalse; return qfalse; } - if (ret > MAX_PACKETLEN) { - Com_EPrintf("%s: oversize packet from %s\n", __func__, - NET_AdrToString(&net_from)); + if (ret == NET_ERROR) { + Com_DPrintf("%s: %s from %s\n", __func__, + NET_ErrorString(), NET_AdrToString(&net_from)); + net_recv_errors++; return qfalse; } #ifdef _DEBUG - if (net_log_enable->integer) { + if (net_log_enable->integer) NET_LogPacket(&net_from, "UDP recv", msg_read_buffer, ret); - } #endif SZ_Init(&msg_read, msg_read_buffer, sizeof(msg_read_buffer)); msg_read.cursize = ret; + net_rate_rcvd += ret; net_bytes_rcvd += ret; net_packets_rcvd++; @@ -717,155 +812,54 @@ retry: return qtrue; } -//============================================================================= - /* ============= NET_SendPacket ============= */ -qboolean NET_SendPacket(netsrc_t sock, const netadr_t *to, size_t length, const void *data) +qboolean NET_SendPacket(netsrc_t sock, const void *data, + size_t len, const netadr_t *to) { - struct sockaddr_in addr; - int ret; -#if USE_ICMP && (defined __linux__) - int tries; - int saved_error; -#endif + ssize_t ret; - if (!length) { + if (len == 0) return qfalse; - } - if (length > MAX_PACKETLEN) { + if (len > MAX_PACKETLEN) { Com_EPrintf("%s: oversize packet to %s\n", __func__, NET_AdrToString(to)); return qfalse; } - switch (to->type) { #if USE_CLIENT - case NA_LOOPBACK: { - loopback_t *loop; - loopmsg_t *msg; - - if (net_dropsim->integer > 0 && - (rand() % 100) < net_dropsim->integer) { - return NET_AGAIN; - } - - loop = &loopbacks[sock ^ 1]; - - msg = &loop->msgs[loop->send & (MAX_LOOPBACK - 1)]; - loop->send++; - - memcpy(msg->data, data, length); - msg->datalen = length; - -#ifdef _DEBUG - if (net_log_enable->integer) { - NET_LogPacket(to, "LB send", data, length); - } -#endif - if (sock == NS_CLIENT) { - net_rate_sent += length; - } - } - return qtrue; + if (to->type == NA_LOOPBACK) + return NET_SendLoopPacket(sock, data, len, to); #endif - case NA_IP: - case NA_BROADCAST: - break; - default: - Com_Error(ERR_FATAL, "%s: bad address type", __func__); - break; - } - if (udp_sockets[sock] == INVALID_SOCKET) { + if (udp_sockets[sock] == -1) return qfalse; - } - - NET_NetadrToSockadr(to, &addr); -#if USE_ICMP && (defined __linux__) - tries = 0; - -retry: -#endif - ret = sendto(udp_sockets[sock], data, length, 0, - (struct sockaddr *)&addr, sizeof(addr)); - if (ret == -1) { - NET_GET_ERROR(); + ret = os_udp_send(sock, data, len, to); + if (ret == NET_AGAIN) + return qfalse; -#ifdef _WIN32 - switch (net_error) { - case WSAEWOULDBLOCK: - case WSAEINTR: - // wouldblock is silent - break; - case WSAEADDRNOTAVAIL: - // some PPP links do not allow broadcasts - if (to->type == NA_BROADCAST) { - break; - } - // intentional fallthrough - default: - Com_DPrintf("%s: %s to %s\n", __func__, - NET_ErrorString(), NET_AdrToString(to)); - net_send_errors++; - break; - } -#else // _WIN32 - switch (net_error) { - case EWOULDBLOCK: - // wouldblock is silent - break; - default: -#if USE_ICMP - saved_error = net_error; - // sendto() fails on Linux if there's an ICMP originated - // pending error on socket. suck up error queue and retry... - // - // this one is especially lame - how do I distingiush between - // a failure caused by completely unrelated ICMP error sitting - // in the queue and an error explicit to this sendto() call? - // - // on one hand, I don't want to drop packets to legitimate - // clients because of this, and have to retry sendto() after - // processing error queue, on another hand, infinite loop should be - // avoided if this sendto() regenerates a message in error queue - // - // this mess is worked around by passing destination address - // to process_error_queue() and checking if this address/port - // pair is found in the queue. if it is found, or the queue - // is empty, do not retry - if (process_error_queue(sock, &addr)) { - if (++tries < MAX_ERROR_RETRIES) { - goto retry; - } - } - net_error = saved_error; -#endif - Com_DPrintf("%s: %s to %s\n", __func__, - NET_ErrorString(), NET_AdrToString(to)); - net_send_errors++; - break; - } -#endif // !_WIN32 + if (ret == NET_ERROR) { + Com_DPrintf("%s: %s to %s\n", __func__, + NET_ErrorString(), NET_AdrToString(to)); + net_send_errors++; return qfalse; } - if (ret != length) { + if (ret < len) Com_WPrintf("%s: short send to %s\n", __func__, NET_AdrToString(to)); - } #ifdef _DEBUG - if (net_log_enable->integer) { + if (net_log_enable->integer) NET_LogPacket(to, "UDP send", data, ret); - } #endif + net_rate_sent += ret; net_bytes_sent += ret; net_packets_sent++; @@ -873,116 +867,55 @@ retry: return qtrue; } - //============================================================================= -const char *NET_ErrorString(void) +static qsocket_t UDP_OpenSocket(const char *iface, int port) { -#ifdef _WIN32 - switch (net_error) { - case S_OK: - return "NO ERROR"; - default: - return "UNKNOWN ERROR"; -#include "wsaerr.h" - } -#else - return strerror(net_error); -#endif -} - -static qboolean get_bind_addr(const char *iface, int port, struct sockaddr_in *sadr) -{ - if (*iface) { - if (!NET_StringToSockaddr(iface, sadr)) { - return qfalse; - } - } else { - // empty string binds to all interfaces - memset(sadr, 0, sizeof(*sadr)); - sadr->sin_family = AF_INET; - sadr->sin_addr.s_addr = INADDR_ANY; - } - if (port != PORT_ANY) { - sadr->sin_port = htons((u_short)port); - } - - return qtrue; -} - -static SOCKET create_socket(int type, int proto) -{ - SOCKET ret = socket(PF_INET, type, proto); - - NET_GET_ERROR(); - return ret; -} - -static int set_option(SOCKET s, int level, int optname, int value) -{ - SETSOCKOPT_PARAM _value = value; - int ret = setsockopt(s, level, optname, (char *)&_value, sizeof(_value)); - - NET_GET_ERROR(); - return ret; -} - -#define enable_option(s,level,optname) set_option(s,level,optname,1) - -static int make_nonblock(SOCKET s) -{ - IOCTLSOCKET_PARAM _true = 1; - int ret = ioctlsocket(s, FIONBIO, &_true); - - NET_GET_ERROR(); - return ret; -} - -static int bind_socket(SOCKET s, struct sockaddr_in *sadr) -{ - int ret = bind(s, (struct sockaddr *)sadr, sizeof(*sadr)); - - NET_GET_ERROR(); - return ret; -} - -static SOCKET UDP_OpenSocket(const char *iface, int port) -{ - SOCKET s; struct sockaddr_in sadr; -#ifdef __linux__ - int pmtudisc; -#endif + int s; + + Com_DPrintf("Opening UDP socket: %s:%d\n", iface, port); - Com_DPrintf("Opening UDP socket: %s:%i\n", iface, port); + // resolve iface addr + if (!NET_StringToSockaddr2(iface, port, &sadr)) { + Com_EPrintf("%s: %s:%d: bad interface address\n", + __func__, iface, port); + return -1; + } - s = create_socket(SOCK_DGRAM, IPPROTO_UDP); - if (s == INVALID_SOCKET) { + s = os_socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { Com_EPrintf("%s: %s:%d: can't create socket: %s\n", __func__, iface, port, NET_ErrorString()); - return INVALID_SOCKET; + return -1; } // make it non-blocking - if (make_nonblock(s) == -1) { + if (os_make_nonblock(s, 1)) { Com_EPrintf("%s: %s:%d: can't make socket non-blocking: %s\n", __func__, iface, port, NET_ErrorString()); goto fail; } // make it broadcast capable - if (enable_option(s, SOL_SOCKET, SO_BROADCAST) == -1) { + if (os_setsockopt(s, SOL_SOCKET, SO_BROADCAST, 1)) { Com_WPrintf("%s: %s:%d: can't make socket broadcast capable: %s\n", __func__, iface, port, NET_ErrorString()); } #ifdef __linux__ - pmtudisc = IP_PMTUDISC_DONT; + // udp(7) says: "By default, Linux UDP does path MTU discovery". This means + // kernel will set "don't fragment" bit on outgoing packets. This is not + // what most Quake 2 server operators expect. Firewalled ICMP traffic may + // result in clients getting stuck on connect. Thus we enable IP + // fragmentation by default. + + int disc = IP_PMTUDISC_DONT; #if USE_ICMP // enable ICMP error queue if (net_ignore_icmp->integer <= 0) { - if (enable_option(s, IPPROTO_IP, IP_RECVERR) == -1) { + if (os_setsockopt(s, IPPROTO_IP, IP_RECVERR, 1)) { Com_WPrintf("%s: %s:%d: can't enable ICMP error queue: %s\n", __func__, iface, port, NET_ErrorString()); Cvar_Set("net_ignore_icmp", "1"); @@ -991,33 +924,26 @@ static SOCKET UDP_OpenSocket(const char *iface, int port) #if USE_PMTUDISC // overload negative values to enable path MTU discovery switch (net_ignore_icmp->integer) { - case -1: pmtudisc = IP_PMTUDISC_WANT; break; - case -2: pmtudisc = IP_PMTUDISC_DO; break; + case -1: disc = IP_PMTUDISC_WANT; break; + case -2: disc = IP_PMTUDISC_DO; break; #ifdef IP_PMTUDISC_PROBE - case -3: pmtudisc = IP_PMTUDISC_PROBE; break; + case -3: disc = IP_PMTUDISC_PROBE; break; #endif } #endif // USE_PMTUDISC } #endif // USE_ICMP - // disable or enable path MTU discovery - if (set_option(s, IPPROTO_IP, IP_MTU_DISCOVER, pmtudisc) == -1) { + // set path MTU discovery option + if (os_setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, disc)) { Com_WPrintf("%s: %s:%d: can't %sable path MTU discovery: %s\n", - __func__, iface, port, pmtudisc == IP_PMTUDISC_DONT ? "dis" : "en", + __func__, iface, port, + disc == IP_PMTUDISC_DONT ? "dis" : "en", NET_ErrorString()); } - #endif // __linux__ - // resolve iface sadr - if (!get_bind_addr(iface, port, &sadr)) { - Com_EPrintf("%s: %s:%d: bad interface address\n", - __func__, iface, port); - goto fail; - } - - if (bind_socket(s, &sadr) == -1) { + if (os_bind(s, (struct sockaddr *)&sadr, sizeof(sadr))) { Com_EPrintf("%s: %s:%d: can't bind socket: %s\n", __func__, iface, port, NET_ErrorString()); goto fail; @@ -1026,46 +952,47 @@ static SOCKET UDP_OpenSocket(const char *iface, int port) return s; fail: - closesocket(s); - return INVALID_SOCKET; + os_closesocket(s); + return -1; } -static SOCKET TCP_OpenSocket(const char *iface, int port, netsrc_t who) +static qsocket_t TCP_OpenSocket(const char *iface, int port, netsrc_t who) { - SOCKET s; struct sockaddr_in sadr; + int s; - Com_DPrintf("Opening TCP socket: %s:%i\n", iface, port); + Com_DPrintf("Opening TCP socket: %s:%d\n", iface, port); - s = create_socket(SOCK_STREAM, IPPROTO_TCP); - if (s == INVALID_SOCKET) { + // resolve iface addr + if (!NET_StringToSockaddr2(iface, port, &sadr)) { + Com_EPrintf("%s: %s:%d: bad interface address\n", + __func__, iface, port); + return -1; + } + + s = os_socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) { Com_EPrintf("%s: %s:%d: can't create socket: %s\n", __func__, iface, port, NET_ErrorString()); - return INVALID_SOCKET; + return -1; } // make it non-blocking - if (make_nonblock(s) == -1) { + if (os_make_nonblock(s, 1)) { Com_EPrintf("%s: %s:%d: can't make socket non-blocking: %s\n", __func__, iface, port, NET_ErrorString()); goto fail; } - // give it a chance to reuse previous port if (who == NS_SERVER) { - if (enable_option(s, SOL_SOCKET, SO_REUSEADDR) == -1) { + // give it a chance to reuse previous port + if (os_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 1)) { Com_WPrintf("%s: %s:%d: can't force socket to reuse address: %s\n", __func__, iface, port, NET_ErrorString()); } } - if (!get_bind_addr(iface, port, &sadr)) { - Com_EPrintf("%s: %s:%d: bad interface address\n", - __func__, iface, port); - goto fail; - } - - if (bind_socket(s, &sadr) == -1) { + if (os_bind(s, (struct sockaddr *)&sadr, sizeof(sadr))) { Com_EPrintf("%s: %s:%d: can't bind socket: %s\n", __func__, iface, port, NET_ErrorString()); goto fail; @@ -1074,21 +1001,21 @@ static SOCKET TCP_OpenSocket(const char *iface, int port, netsrc_t who) return s; fail: - closesocket(s); - return INVALID_SOCKET; + os_closesocket(s); + return -1; } static void NET_OpenServer(void) { static int saved_port; ioentry_t *e; - SOCKET s; + qsocket_t s; s = UDP_OpenSocket(net_ip->string, net_port->integer); - if (s != INVALID_SOCKET) { + if (s != -1) { saved_port = net_port->integer; udp_sockets[NS_SERVER] = s; - e = IO_Add(s); + e = NET_AddFd(s); e->wantread = qtrue; return; } @@ -1114,77 +1041,140 @@ static void NET_OpenServer(void) static void NET_OpenClient(void) { ioentry_t *e; - SOCKET s; - struct sockaddr_in sadr; - socklen_t len; + qsocket_t s; + netadr_t adr; s = UDP_OpenSocket(net_ip->string, net_clientport->integer); - if (s == INVALID_SOCKET) { + if (s == -1) { // now try with random port - if (net_clientport->integer != PORT_ANY) { + if (net_clientport->integer != PORT_ANY) s = UDP_OpenSocket(net_ip->string, PORT_ANY); - } - if (s == INVALID_SOCKET) { + + if (s == -1) { Com_WPrintf("Couldn't open client UDP port.\n"); return; } - len = sizeof(sadr); - getsockname(s, (struct sockaddr *)&sadr, &len); - Com_WPrintf("Client bound to UDP port %d.\n", ntohs(sadr.sin_port)); + + if (os_getsockname(s, &adr)) { + Com_EPrintf("Couldn't get client UDP socket name: %s\n", NET_ErrorString()); + os_closesocket(s); + return; + } + + Com_WPrintf("Client UDP socket bound to %s.\n", NET_AdrToString(&adr)); Cvar_SetByVar(net_clientport, va("%d", PORT_ANY), FROM_CODE); } udp_sockets[NS_CLIENT] = s; - e = IO_Add(s); + e = NET_AddFd(s); e->wantread = qtrue; } #endif +/* +==================== +NET_Config +==================== +*/ +void NET_Config(netflag_t flag) +{ + netsrc_t sock; + + if (flag == net_active) { + return; + } + + if (flag == NET_NONE) { + // shut down any existing sockets + for (sock = 0; sock < NS_COUNT; sock++) { + if (udp_sockets[sock] != -1) { + NET_RemoveFd(udp_sockets[sock]); + os_closesocket(udp_sockets[sock]); + udp_sockets[sock] = -1; + } + } + net_active = NET_NONE; + return; + } + +#if USE_CLIENT + if ((flag & NET_CLIENT) && udp_sockets[NS_CLIENT] == -1) { + NET_OpenClient(); + } +#endif + + if ((flag & NET_SERVER) && udp_sockets[NS_SERVER] == -1) { + NET_OpenServer(); + } + + net_active |= flag; +} + +/* +==================== +NET_GetAddress +==================== +*/ +qboolean NET_GetAddress(netsrc_t sock, netadr_t *adr) +{ + if (udp_sockets[sock] == -1) + return qfalse; + + if (os_getsockname(udp_sockets[sock], adr)) + return qfalse; + + return qtrue; +} + //============================================================================= -void NET_Close(netstream_t *s) +void NET_CloseStream(netstream_t *s) { if (!s->state) { return; } - IO_Remove(s->socket); - closesocket(s->socket); - s->socket = INVALID_SOCKET; + NET_RemoveFd(s->socket); + os_closesocket(s->socket); + s->socket = -1; s->state = NS_DISCONNECTED; } neterr_t NET_Listen(qboolean arg) { - SOCKET s; + qsocket_t s; ioentry_t *e; + neterr_t ret; if (!arg) { - if (tcp_socket != INVALID_SOCKET) { - IO_Remove(tcp_socket); - closesocket(tcp_socket); - tcp_socket = INVALID_SOCKET; + if (tcp_socket != -1) { + NET_RemoveFd(tcp_socket); + os_closesocket(tcp_socket); + tcp_socket = -1; } return NET_OK; } - if (tcp_socket != INVALID_SOCKET) { + if (tcp_socket != -1) { return NET_OK; } s = TCP_OpenSocket(net_tcp_ip->string, net_tcp_port->integer, NS_SERVER); - if (s == INVALID_SOCKET) { + if (s == -1) { return NET_ERROR; } - if (listen(s, net_tcp_backlog->integer) == -1) { - NET_GET_ERROR(); - closesocket(s); - return NET_ERROR; + + ret = os_listen(s, net_tcp_backlog->integer); + if (ret) { + os_closesocket(s); + return ret; } tcp_socket = s; - e = IO_Add(s); + + // initialize io entry + e = NET_AddFd(s); e->wantread = qtrue; return NET_OK; @@ -1193,39 +1183,30 @@ neterr_t NET_Listen(qboolean arg) // net_from variable receives source address neterr_t NET_Accept(netstream_t *s) { - struct sockaddr_in from; - socklen_t fromlen; - SOCKET newsocket; ioentry_t *e; + qsocket_t newsocket; + neterr_t ret; - if (tcp_socket == INVALID_SOCKET) { + if (tcp_socket == -1) { return NET_AGAIN; } - e = IO_Get(tcp_socket); + e = os_get_io(tcp_socket); if (!e->canread) { return NET_AGAIN; } - memset(&from, 0, sizeof(from)); - fromlen = sizeof(from); - newsocket = accept(tcp_socket, (struct sockaddr *)&from, &fromlen); - - NET_SockadrToNetadr(&from, &net_from); - - if (newsocket == -1) { - if (NET_WOULD_BLOCK()) { - // wouldblock is silent - e->canread = qfalse; - return NET_AGAIN; - } - return NET_ERROR; + ret = os_accept(tcp_socket, &newsocket, &net_from); + if (ret) { + e->canread = qfalse; + return ret; } // make it non-blocking - if (make_nonblock(newsocket) == -1) { - closesocket(newsocket); - return NET_ERROR; + ret = os_make_nonblock(newsocket, 1); + if (ret) { + os_closesocket(newsocket); + return ret; } // initialize stream @@ -1235,7 +1216,7 @@ neterr_t NET_Accept(netstream_t *s) s->state = NS_CONNECTED; // initialize io entry - e = IO_Add(newsocket); + e = NET_AddFd(newsocket); //e->wantwrite = qtrue; e->wantread = qtrue; @@ -1244,31 +1225,21 @@ neterr_t NET_Accept(netstream_t *s) neterr_t NET_Connect(const netadr_t *peer, netstream_t *s) { - SOCKET socket; + qsocket_t socket; ioentry_t *e; - struct sockaddr_in sadr; - int ret; + neterr_t ret; // always bind to `net_ip' for outgoing TCP connections // to avoid problems with AC or MVD/GTV auth on a multi IP system socket = TCP_OpenSocket(net_ip->string, PORT_ANY, NS_CLIENT); - if (socket == INVALID_SOCKET) { + if (socket == -1) { return NET_ERROR; } - NET_NetadrToSockadr(peer, &sadr); - - ret = connect(socket, (struct sockaddr *)&sadr, sizeof(sadr)); - if (ret == -1) { -#ifdef _WIN32 - if (NET_GET_ERROR() != WSAEWOULDBLOCK) { -#else - if (NET_GET_ERROR() != EINPROGRESS) { -#endif - // wouldblock is silent - closesocket(socket); - return NET_ERROR; - } + ret = os_connect(socket, peer); + if (ret) { + os_closesocket(socket); + return NET_ERROR; } // initialize stream @@ -1278,7 +1249,7 @@ neterr_t NET_Connect(const netadr_t *peer, netstream_t *s) s->socket = socket; // initialize io entry - e = IO_Add(socket); + e = NET_AddFd(socket); e->wantwrite = qtrue; #ifdef _WIN32 e->wantexcept = qtrue; @@ -1289,15 +1260,15 @@ neterr_t NET_Connect(const netadr_t *peer, netstream_t *s) neterr_t NET_RunConnect(netstream_t *s) { - socklen_t len; - int ret, err; ioentry_t *e; + neterr_t ret; + int err; if (s->state != NS_CONNECTING) { return NET_AGAIN; } - e = IO_Get(s->socket); + e = os_get_io(s->socket); if (!e->canwrite #ifdef _WIN32 && !e->canexcept @@ -1306,27 +1277,24 @@ neterr_t NET_RunConnect(netstream_t *s) return NET_AGAIN; } - len = sizeof(err); - ret = getsockopt(s->socket, SOL_SOCKET, SO_ERROR, (char *)&err, &len); - if (ret == -1) { - goto error1; + ret = os_getsockopt(s->socket, SOL_SOCKET, SO_ERROR, &err); + if (ret) { + goto fail; } if (err) { net_error = err; - goto error2; + goto fail; } + s->state = NS_CONNECTED; e->wantwrite = qfalse; e->wantread = qtrue; #ifdef _WIN32 e->wantexcept = qfalse; #endif - s->state = NS_CONNECTED; return NET_OK; -error1: - NET_GET_ERROR(); -error2: +fail: s->state = NS_BROKEN; e->wantwrite = qfalse; e->wantread = qfalse; @@ -1346,7 +1314,7 @@ void NET_UpdateStream(netstream_t *s) return; } - e = IO_Get(s->socket); + e = os_get_io(s->socket); FIFO_Reserve(&s->recv, &len); e->wantread = len ? qtrue : qfalse; @@ -1358,7 +1326,7 @@ void NET_UpdateStream(netstream_t *s) // returns NET_OK only when there was some data read neterr_t NET_RunStream(netstream_t *s) { - int ret; + ssize_t ret; size_t len; void *data; neterr_t result = NET_AGAIN; @@ -1368,22 +1336,21 @@ neterr_t NET_RunStream(netstream_t *s) return result; } - e = IO_Get(s->socket); + e = os_get_io(s->socket); if (e->wantread && e->canread) { // read as much as we can data = FIFO_Reserve(&s->recv, &len); if (len) { - ret = recv(s->socket, data, len, 0); + ret = os_recv(s->socket, data, len, 0); if (!ret) { goto closed; } - if (ret == -1) { - if (NET_WOULD_BLOCK()) { - // wouldblock is silent - e->canread = qfalse; - } else { - goto error; - } + if (ret == NET_ERROR) { + goto error; + } + if (ret == NET_AGAIN) { + // wouldblock is silent + e->canread = qfalse; } else { FIFO_Commit(&s->recv, ret); #if _DEBUG @@ -1409,17 +1376,16 @@ neterr_t NET_RunStream(netstream_t *s) // write as much as we can data = FIFO_Peek(&s->send, &len); if (len) { - ret = send(s->socket, data, len, 0); + ret = os_send(s->socket, data, len, 0); if (!ret) { goto closed; } - if (ret == -1) { - if (NET_WOULD_BLOCK()) { - // wouldblock is silent - e->canwrite = qfalse; - } else { - goto error; - } + if (ret == NET_ERROR) { + goto error; + } + if (ret == NET_AGAIN) { + // wouldblock is silent + e->canwrite = qfalse; } else { FIFO_Decommit(&s->send, ret); #if _DEBUG @@ -1458,51 +1424,7 @@ error: //=================================================================== -/* -==================== -NET_Stats_f -==================== -*/ -static void NET_Stats_f(void) -{ - time_t diff, now = time(NULL); - char buffer[MAX_QPATH]; - - if (com_startTime > now) { - com_startTime = now; - } - diff = now - com_startTime; - if (diff < 1) { - diff = 1; - } - - Com_FormatTime(buffer, sizeof(buffer), diff); - Com_Printf("Network uptime: %s\n", buffer); - Com_Printf("Bytes sent: %"PRIu64" (%"PRIu64" bytes/sec)\n", - net_bytes_sent, net_bytes_sent / diff); - Com_Printf("Bytes rcvd: %"PRIu64" (%"PRIu64" bytes/sec)\n", - net_bytes_rcvd, net_bytes_rcvd / diff); - Com_Printf("Packets sent: %"PRIu64" (%"PRIu64" packets/sec)\n", - net_packets_sent, net_packets_sent / diff); - Com_Printf("Packets rcvd: %"PRIu64" (%"PRIu64" packets/sec)\n", - net_packets_rcvd, net_packets_rcvd / diff); -#if USE_ICMP - Com_Printf("Total errors: %"PRIu64"/%"PRIu64"/%"PRIu64" (send/recv/icmp)\n", - net_send_errors, net_recv_errors, net_icmp_errors); -#else - Com_Printf("Total errors: %"PRIu64"/%"PRIu64" (send/recv)\n", - net_send_errors, net_recv_errors); -#endif - Com_Printf("Current upload rate: %"PRIz" bytes/sec\n", net_rate_up); - Com_Printf("Current download rate: %"PRIz" bytes/sec\n", net_rate_dn); -} - -/* -==================== -NET_DumpHostInfo -==================== -*/ -static void NET_DumpHostInfo(struct hostent *h) +static void dump_hostent(struct hostent *h) { byte **list; int i; @@ -1521,20 +1443,21 @@ static void NET_DumpHostInfo(struct hostent *h) } } -static void dump_socket(SOCKET s, const char *s1, const char *s2) +static void dump_socket(qsocket_t s, const char *s1, const char *s2) { - struct sockaddr_in sadr; - socklen_t len; netadr_t adr; - len = sizeof(sadr); - if (getsockname(s, (struct sockaddr *)&sadr, &len) == -1) { - NET_GET_ERROR(); - Com_EPrintf("%s: getsockname: %s\n", __func__, NET_ErrorString()); + if (s == -1) + return; + + if (os_getsockname(s, &adr)) { + Com_EPrintf("Couldn't get %s %s socket name: %s\n", + s1, s2, NET_ErrorString()); return; } - NET_SockadrToNetadr(&sadr, &adr); - Com_Printf("%s %s socket bound to %s\n", s1, s2, NET_AdrToString(&adr)); + + Com_Printf("%s %s socket bound to %s\n", + s1, s2, NET_AdrToString(&adr)); } /* @@ -1546,30 +1469,23 @@ static void NET_ShowIP_f(void) { char buffer[256]; struct hostent *h; - netsrc_t sock; if (gethostname(buffer, sizeof(buffer)) == -1) { - NET_GET_ERROR(); - Com_EPrintf("%s: gethostname: %s\n", __func__, NET_ErrorString()); + Com_EPrintf("Couldn't get system host name\n"); return; } if (!(h = gethostbyname(buffer))) { - NET_GET_ERROR(); - Com_EPrintf("%s: gethostbyname: %s\n", __func__, NET_ErrorString()); + Com_EPrintf("Couldn't resolve %s\n", buffer); return; } - NET_DumpHostInfo(h); + dump_hostent(h); - for (sock = 0; sock < NS_COUNT; sock++) { - if (udp_sockets[sock] != INVALID_SOCKET) { - dump_socket(udp_sockets[sock], socketNames[sock], "UDP"); - } - } - if (tcp_socket != INVALID_SOCKET) { - dump_socket(tcp_socket, socketNames[NS_SERVER], "TCP"); - } + // dump listening sockets + dump_socket(udp_sockets[NS_CLIENT], "Client", "UDP"); + dump_socket(udp_sockets[NS_SERVER], "Server", "UDP"); + dump_socket(tcp_socket, "Server", "TCP"); } /* @@ -1606,8 +1522,7 @@ static void NET_Dns_f(void) return; } - NET_DumpHostInfo(h); - + dump_hostent(h); } /* @@ -1618,7 +1533,7 @@ NET_Restart_f static void NET_Restart_f(void) { netflag_t flag = net_active; - qboolean listen = tcp_socket != INVALID_SOCKET; + qboolean listen = tcp_socket != -1; Com_DPrintf("%s\n", __func__); @@ -1636,75 +1551,6 @@ static void NET_Restart_f(void) #endif } -/* -==================== -NET_Config -==================== -*/ -void NET_Config(netflag_t flag) -{ - netsrc_t sock; - - if (flag == net_active) { - return; - } - - if (flag == NET_NONE) { - // shut down any existing sockets - for (sock = 0; sock < NS_COUNT; sock++) { - if (udp_sockets[sock] != INVALID_SOCKET) { - IO_Remove(udp_sockets[sock]); - closesocket(udp_sockets[sock]); - udp_sockets[sock] = INVALID_SOCKET; - } - } - net_active = NET_NONE; - return; - } - -#if USE_CLIENT - if ((flag & NET_CLIENT) && udp_sockets[NS_CLIENT] == INVALID_SOCKET) { - NET_OpenClient(); - } -#endif - - if ((flag & NET_SERVER) && udp_sockets[NS_SERVER] == INVALID_SOCKET) { - NET_OpenServer(); - } - - net_active |= flag; -} - -qboolean NET_GetAddress(netsrc_t sock, netadr_t *adr) -{ - struct sockaddr_in sadr; - socklen_t len; - SOCKET s = udp_sockets[sock]; - - if (s == INVALID_SOCKET) { - return qfalse; - } - - len = sizeof(sadr); - if (getsockname(s, (struct sockaddr *)&sadr, &len) == -1) { - return qfalse; - } - - NET_SockadrToNetadr(&sadr, adr); - - return qtrue; -} - -static size_t NET_UpRate_m(char *buffer, size_t size) -{ - return Q_scnprintf(buffer, size, "%"PRIz, net_rate_up); -} - -static size_t NET_DnRate_m(char *buffer, size_t size) -{ - return Q_scnprintf(buffer, size, "%"PRIz, net_rate_dn); -} - static void net_udp_param_changed(cvar_t *self) { // keep TCP socket vars in sync unless modified by user @@ -1720,7 +1566,7 @@ static void net_udp_param_changed(cvar_t *self) static void net_tcp_param_changed(cvar_t *self) { - if (tcp_socket != INVALID_SOCKET) { + if (tcp_socket != -1) { NET_Listen(qfalse); NET_Listen(qtrue); } @@ -1733,17 +1579,7 @@ NET_Init */ void NET_Init(void) { -#ifdef _WIN32 - WSADATA ws; - int ret; - - ret = WSAStartup(MAKEWORD(1, 1), &ws); - if (ret) { - Com_Error(ERR_FATAL, "Winsock initialization failed, returned %d", ret); - } - - Com_DPrintf("Winsock Initialized\n"); -#endif + os_net_init(); net_ip = Cvar_Get("net_ip", "", 0); net_ip->changed = net_udp_param_changed; @@ -1794,18 +1630,12 @@ NET_Shutdown void NET_Shutdown(void) { #if _DEBUG - if (net_logFile) { - FS_FCloseFile(net_logFile); - net_logFile = 0; - } + logfile_close(); #endif NET_Listen(qfalse); NET_Config(NET_NONE); - -#ifdef _WIN32 - WSACleanup(); -#endif + os_net_shutdown(); Cmd_RemoveCommand("net_restart"); Cmd_RemoveCommand("net_stats"); diff --git a/src/net_sock.h b/src/net_sock.h index 8e5a340..07cc660 100644 --- a/src/net_sock.h +++ b/src/net_sock.h @@ -33,6 +33,33 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define MAX_PACKETLEN_WRITABLE (MAX_PACKETLEN - PACKET_HEADER) #define MAX_PACKETLEN_WRITABLE_DEFAULT (MAX_PACKETLEN_DEFAULT - PACKET_HEADER) +// portable network error codes +#define NET_OK 0 // success +#define NET_ERROR -1 // failure (NET_ErrorString returns error message) +#define NET_AGAIN -2 // operation would block, try again +#define NET_CLOSED -3 // peer has closed connection + +typedef int neterr_t; + +#ifdef _WIN32 +typedef intptr_t qsocket_t; +#else +typedef int qsocket_t; +#endif + +typedef struct { +#ifdef _WIN32 + qsocket_t fd; +#endif + qboolean inuse: 1; + qboolean canread: 1; + qboolean canwrite: 1; + qboolean canexcept: 1; + qboolean wantread: 1; + qboolean wantwrite: 1; + qboolean wantexcept: 1; +} ioentry_t; + typedef enum { NA_BAD, NA_LOOPBACK, @@ -52,20 +79,13 @@ typedef enum { NET_SERVER = (1 << 1) } netflag_t; -typedef enum { - NET_OK, - NET_AGAIN, - NET_CLOSED, - NET_ERROR, -} neterr_t; - typedef union { uint8_t u8[4]; uint16_t u16[2]; uint32_t u32; } netadrip_t; -typedef struct { +typedef struct netadr_s { netadrtype_t type; netadrip_t ip; uint16_t port; @@ -137,27 +157,39 @@ static inline qboolean NET_IsLanAddress(const netadr_t *adr) return qfalse; } +static inline qboolean NET_IsLocalAddress(const netadr_t *adr) +{ +#if USE_CLIENT && USE_SERVER + if (adr->type == NA_LOOPBACK) + return qtrue; +#endif + return qfalse; +} + void NET_Init(void); void NET_Shutdown(void); void NET_Config(netflag_t flag); qboolean NET_GetAddress(netsrc_t sock, netadr_t *adr); +void NET_UpdateStats(void); + qboolean NET_GetPacket(netsrc_t sock); -qboolean NET_SendPacket(netsrc_t sock, const netadr_t *to, size_t length, const void *data); +qboolean NET_SendPacket(netsrc_t sock, const void *data, size_t len, const netadr_t *to); qboolean NET_GetLoopPacket(netsrc_t sock); char *NET_AdrToString(const netadr_t *a); qboolean NET_StringToAdr(const char *s, netadr_t *a, int port); -#if USE_CLIENT && USE_SERVER -#define NET_IsLocalAddress(adr) ((adr)->type == NA_LOOPBACK) -#else -#define NET_IsLocalAddress(adr) 0 -#endif - const char *NET_ErrorString(void); +ioentry_t *NET_AddFd(qsocket_t fd); +void NET_RemoveFd(qsocket_t fd); +int NET_Sleep(int msec); +#if USE_AC_SERVER +int NET_Sleepv(int msec, ...); +#endif + extern cvar_t *net_ip; extern cvar_t *net_port; diff --git a/src/net_stream.h b/src/net_stream.h index 7ccfae2..379e293 100644 --- a/src/net_stream.h +++ b/src/net_stream.h @@ -27,14 +27,14 @@ typedef enum netstate_e { } netstate_t; typedef struct netstream_s { - int socket; + qsocket_t socket; netadr_t address; netstate_t state; fifo_t recv; fifo_t send; } netstream_t; -void NET_Close(netstream_t *s); +void NET_CloseStream(netstream_t *s); neterr_t NET_Listen(qboolean listen); neterr_t NET_Accept(netstream_t *s); neterr_t NET_Connect(const netadr_t *peer, netstream_t *s); diff --git a/src/net_unix.h b/src/net_unix.h new file mode 100644 index 0000000..9d36aa8 --- /dev/null +++ b/src/net_unix.h @@ -0,0 +1,409 @@ +/* +Copyright (C) 2012 skuller.net + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// +// net_unix.h -- BSD sockets wrapper +// + +static const char *os_error_string(int err) +{ + return strerror(err); +} + +// Receiving ICMP errors on unconnected UDP sockets is tricky... +// Linux 2.2 and higher supports this via IP_RECVERR option, see ip(7). +// What about BSD? + +#ifdef __linux__ + +static qboolean check_offender(const struct sockaddr_in *from, + const struct sockaddr_in *to) +{ + if (!to) + return qfalse; + + if (from->sin_addr.s_addr != to->sin_addr.s_addr) + return qfalse; + + if (from->sin_port && from->sin_port != to->sin_port) + return qfalse; + + Com_DPrintf("%s: found offending address %s:%d\n", "process_error_queue", + inet_ntoa(from->sin_addr), ntohs(from->sin_port)); + + return qtrue; +} + +// Returns true if failed socket operation should be retried. +// May get called from NET_SendPacket() path, avoid interfering with net_from. +static qboolean process_error_queue(netsrc_t sock, const struct sockaddr_in *to_addr) +{ + byte buffer[1024]; + struct sockaddr_in from_addr; + struct msghdr msg; + struct cmsghdr *cmsg; + struct sock_extended_err *ee; + netadr_t from; + int tries; + qboolean found = qfalse; + + for (tries = 0; tries < MAX_ERROR_RETRIES; tries++) { + memset(&from_addr, 0, sizeof(from_addr)); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &from_addr; + msg.msg_namelen = sizeof(from_addr); + msg.msg_control = buffer; + msg.msg_controllen = sizeof(buffer); + + if (recvmsg(udp_sockets[sock], &msg, MSG_ERRQUEUE) == -1) { + if (errno != EWOULDBLOCK) + Com_DPrintf("%s: %s\n", __func__, strerror(errno)); + break; + } + + if (!(msg.msg_flags & MSG_ERRQUEUE)) { + Com_DPrintf("%s: no extended error received\n", __func__); + break; + } + + // find an ICMP error message + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != IPPROTO_IP) { + continue; + } + if (cmsg->cmsg_type != IP_RECVERR) { + continue; + } + ee = (struct sock_extended_err *)CMSG_DATA(cmsg); + if (ee->ee_origin == SO_EE_ORIGIN_ICMP) { + break; + } + } + + if (!cmsg) { + Com_DPrintf("%s: no ICMP error found\n", __func__); + break; + } + + found |= check_offender(&from_addr, to_addr); + + NET_SockadrToNetadr(&from_addr, &from); + + // handle ICMP error + NET_ErrorEvent(sock, &from, ee->ee_errno, ee->ee_info); + } + + return !!tries && !found; +} + +#endif // !__linux__ + +static ssize_t os_udp_recv(netsrc_t sock, void *data, + size_t len, netadr_t *from) +{ + struct sockaddr_in addr; + socklen_t addrlen; + ssize_t ret; + +#ifdef __linux__ + int tries; + for (tries = 0; tries < MAX_ERROR_RETRIES; tries++) { +#endif + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + ret = recvfrom(udp_sockets[sock], data, len, 0, + (struct sockaddr *)&addr, &addrlen); + + NET_SockadrToNetadr(&addr, from); + + if (ret >= 0) + return ret; + + net_error = errno; + + // wouldblock is silent + if (net_error == EWOULDBLOCK) + return NET_AGAIN; + +#ifdef __linux__ + // recvfrom() fails on Linux if there is an ICMP originated pending + // error on socket. Suck up error queue and retry... + + if (!process_error_queue(sock, NULL)) + break; + } +#endif + + return NET_ERROR; +} + +static ssize_t os_udp_send(netsrc_t sock, const void *data, + size_t len, const netadr_t *to) +{ + struct sockaddr_in addr; + ssize_t ret; + + NET_NetadrToSockadr(to, &addr); + +#ifdef __linux__ + int tries; + for (tries = 0; tries < MAX_ERROR_RETRIES; tries++) { +#endif + ret = sendto(udp_sockets[sock], data, len, 0, + (struct sockaddr *)&addr, sizeof(addr)); + if (ret >= 0) + return ret; + + net_error = errno; + + // wouldblock is silent + if (net_error == EWOULDBLOCK) + return NET_AGAIN; + +#ifdef __linux__ + // sendto() fails on Linux if there is an ICMP originated pending error + // on socket. Suck up error queue and retry... + // + // But how do I distingiush between a failure caused by completely + // unrelated ICMP error sitting in the queue and an error directly + // related to this sendto() call? + // + // On one hand, I don't want to drop packets to legitimate clients, and + // have to retry sendto() after processing error queue. On other + // hand, infinite loop should be avoided if sendto() call regenerates + // the message in error queue. + // + // This mess is worked around by passing destination address to + // process_error_queue() and checking if this address/port pair is + // found in the queue. If it is found, or the queue is empty, do not + // retry. + + if (!process_error_queue(sock, &addr)) + break; + } +#endif + + return NET_ERROR; +} + +static neterr_t os_get_error(void) +{ + net_error = errno; + if (net_error == EWOULDBLOCK) + return NET_AGAIN; + + return NET_ERROR; +} + +static ssize_t os_recv(qsocket_t sock, void *data, size_t len, int flags) +{ + ssize_t ret = recv(sock, data, len, flags); + + if (ret == -1) + return os_get_error(); + + return ret; +} + +static ssize_t os_send(qsocket_t sock, const void *data, size_t len, int flags) +{ + ssize_t ret = send(sock, data, len, flags); + + if (ret == -1) + return os_get_error(); + + return ret; +} + +static neterr_t os_listen(qsocket_t sock, int backlog) +{ + if (listen(sock, backlog) == -1) { + net_error = errno; + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_accept(qsocket_t sock, qsocket_t *newsock, netadr_t *from) +{ + struct sockaddr_in addr; + socklen_t addrlen; + int s; + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + s = accept(sock, (struct sockaddr *)&addr, &addrlen); + + NET_SockadrToNetadr(&addr, from); + + if (s == -1) { + *newsock = -1; + return os_get_error(); + } + + *newsock = s; + return NET_OK; +} + +static neterr_t os_connect(qsocket_t sock, const netadr_t *to) +{ + struct sockaddr_in addr; + + NET_NetadrToSockadr(to, &addr); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + net_error = errno; + if (net_error == EINPROGRESS) + return NET_OK; + + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_make_nonblock(qsocket_t sock, int val) +{ + if (ioctl(sock, FIONBIO, &val) == -1) { + net_error = errno; + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_setsockopt(qsocket_t sock, int level, int name, int val) +{ + if (setsockopt(sock, level, name, &val, sizeof(val)) == -1) { + net_error = errno; + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_getsockopt(qsocket_t sock, int level, int name, int *val) +{ + socklen_t _optlen = sizeof(*val); + + if (getsockopt(sock, level, name, val, &_optlen) == -1) { + net_error = errno; + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_bind(qsocket_t sock, const struct sockaddr *addr, size_t addrlen) +{ + if (bind(sock, addr, addrlen) == -1) { + net_error = errno; + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_getsockname(qsocket_t sock, netadr_t *name) +{ + struct sockaddr_in addr; + socklen_t addrlen; + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) == -1) { + net_error = errno; + return NET_ERROR; + } + + NET_SockadrToNetadr(&addr, name); + return NET_OK; +} + +static void os_closesocket(qsocket_t sock) +{ + close(sock); +} + +static qsocket_t os_socket(int domain, int type, int protocol) +{ + int s = socket(domain, type, protocol); + + if (s == -1) { + net_error = errno; + return -1; + } + + return s; +} + +static ioentry_t *_os_get_io(qsocket_t fd, const char *func) +{ + if (fd < 0 || fd >= FD_SETSIZE) + Com_Error(ERR_FATAL, "%s: fd out of range: %d", func, fd); + + return &io_entries[fd]; +} + +static ioentry_t *os_add_io(qsocket_t fd) +{ + if (fd >= io_numfds) { + io_numfds = fd + 1; + } + + return _os_get_io(fd, __func__); +} + +static ioentry_t *os_get_io(qsocket_t fd) +{ + return _os_get_io(fd, __func__); +} + +static qsocket_t os_get_fd(ioentry_t *e) +{ + return e - io_entries; +} + +static int os_select(int nfds, fd_set *rfds, fd_set *wfds, + fd_set *efds, struct timeval *tv) +{ + int ret = select(nfds, rfds, wfds, efds, tv); + + if (ret == -1) { + net_error = errno; + if (net_error == EINTR) + return 0; + } + + return ret; +} + +static void os_net_init(void) +{ +} + +static void os_net_shutdown(void) +{ +} + diff --git a/src/net_win.h b/src/net_win.h new file mode 100644 index 0000000..5204780 --- /dev/null +++ b/src/net_win.h @@ -0,0 +1,401 @@ +/* +Copyright (C) 2012 skuller.net + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// +// net_win.h -- Windows sockets wrapper +// + +static const struct { + int err; + const char *msg; +} wsa_error_table[] = { + { S_OK, "Success" }, + { WSAEINTR, "Interrupted function call" }, + { WSAEBADF, "File handle is not valid" }, + { WSAEACCES, "Permission denied" }, + { WSAEFAULT, "Bad address" }, + { WSAEINVAL, "Invalid argument" }, + { WSAEMFILE, "Too many open files" }, + { WSAEWOULDBLOCK, "Resource temporarily unavailable" }, + { WSAEINPROGRESS, "Operation now in progress" }, + { WSAEALREADY, "Operation already in progress" }, + { WSAENOTSOCK, "Socket operation on nonsocket" }, + { WSAEDESTADDRREQ, "Destination address required" }, + { WSAEMSGSIZE, "Message too long" }, + { WSAEPROTOTYPE, "Protocol wrong type for socket" }, + { WSAENOPROTOOPT, "Bad protocol option" }, + { WSAEPROTONOSUPPORT, "Protocol not supported" }, + { WSAESOCKTNOSUPPORT, "Socket type not supported" }, + { WSAEOPNOTSUPP, "Operation not supported" }, + { WSAEPFNOSUPPORT, "Protocol family not supported" }, + { WSAEAFNOSUPPORT, "Address family not supported by protocol family" }, + { WSAEADDRINUSE, "Address already in use" }, + { WSAEADDRNOTAVAIL, "Cannot assign requested address" }, + { WSAENETDOWN, "Network is down" }, + { WSAENETUNREACH, "Network is unreachable" }, + { WSAENETRESET, "Network dropped connection on reset" }, + { WSAECONNABORTED, "Software caused connection abort" }, + { WSAECONNRESET, "Connection reset by peer" }, + { WSAENOBUFS, "No buffer space available" }, + { WSAEISCONN, "Socket is already connected" }, + { WSAENOTCONN, "Socket is not connected" }, + { WSAESHUTDOWN, "Cannot send after socket shutdown" }, + { WSAETOOMANYREFS, "Too many references" }, + { WSAETIMEDOUT, "Connection timed out" }, + { WSAECONNREFUSED, "Connection refused" }, + { WSAELOOP, "Cannot translate name" }, + { WSAENAMETOOLONG, "Name too long" }, + { WSAEHOSTDOWN, "Host is down" }, + { WSAEHOSTUNREACH, "No route to host" }, + { WSAENOTEMPTY, "Directory not empty" }, + { WSAEPROCLIM, "Too many processes" }, + { WSAEUSERS, "User quota exceeded" }, + { WSAEDQUOT, "Disk quota exceeded" }, + { WSAESTALE, "Stale file handle reference" }, + { WSAEREMOTE, "Item is remote" }, + { WSASYSNOTREADY, "Network subsystem is unavailable" }, + { WSAVERNOTSUPPORTED, "Winsock.dll version out of range" }, + { WSANOTINITIALISED, "Successful WSAStartup not yet performed" }, + { WSAEDISCON, "Graceful shutdown in progress" }, + { WSAENOMORE, "No more results" }, + { WSAECANCELLED, "Call has been canceled" }, + { WSAEINVALIDPROCTABLE, "Procedure call table is invalid" }, + { WSAEINVALIDPROVIDER, "Service provider is invalid" }, + { WSAEPROVIDERFAILEDINIT, "Service provider failed to initialize" }, + { WSASYSCALLFAILURE, "System call failure" }, + { WSASERVICE_NOT_FOUND, "Service not found" }, + { WSATYPE_NOT_FOUND, "Class type not found" }, + { WSA_E_NO_MORE, "No more results" }, + { WSA_E_CANCELLED, "Call was canceled" }, + { WSAEREFUSED, "Database query was refused" }, + { WSAHOST_NOT_FOUND, "Host not found" }, + { WSATRY_AGAIN, "Nonauthoritative host not found" }, + { WSANO_RECOVERY, "This is a nonrecoverable error" }, + { WSANO_DATA, "Valid name, no data record of requested type" }, + { -1, "Unknown error" } +}; + +static const char *os_error_string(int err) +{ + int i; + + for (i = 0; wsa_error_table[i].err != -1; i++) { + if (wsa_error_table[i].err == err) + break; + } + + return wsa_error_table[i].msg; +} + +static ssize_t os_udp_recv(netsrc_t sock, void *data, + size_t len, netadr_t *from) +{ + struct sockaddr_in addr; + int addrlen; + int ret; + int tries; + + for (tries = 0; tries < MAX_ERROR_RETRIES; tries++) { + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + ret = recvfrom(udp_sockets[sock], data, len, 0, + (struct sockaddr *)&addr, &addrlen); + + NET_SockadrToNetadr(&addr, from); + + if (ret != SOCKET_ERROR) + return ret; + + net_error = WSAGetLastError(); + + // wouldblock is silent + if (net_error == WSAEWOULDBLOCK) + return NET_AGAIN; + +#if USE_ICMP + if (net_error == WSAECONNRESET || net_error == WSAENETRESET) { + // winsock has already provided us with + // a valid address from ICMP error packet + NET_ErrorEvent(sock, from, net_error, 0); + continue; + } +#endif + + break; + } + + return NET_ERROR; +} + +static ssize_t os_udp_send(netsrc_t sock, const void *data, + size_t len, const netadr_t *to) +{ + struct sockaddr_in addr; + int ret; + + NET_NetadrToSockadr(to, &addr); + + ret = sendto(udp_sockets[sock], data, len, 0, + (struct sockaddr *)&addr, sizeof(addr)); + + if (ret != SOCKET_ERROR) + return ret; + + net_error = WSAGetLastError(); + + // wouldblock is silent + if (net_error == WSAEWOULDBLOCK || net_error == WSAEINTR) + return NET_AGAIN; + + // some PPP links do not allow broadcasts + if (net_error == WSAEADDRNOTAVAIL && to->type == NA_BROADCAST) + return NET_AGAIN; + + return NET_ERROR; +} + +static neterr_t os_get_error(void) +{ + net_error = WSAGetLastError(); + if (net_error == WSAEWOULDBLOCK) + return NET_AGAIN; + + return NET_ERROR; +} + +static ssize_t os_recv(qsocket_t sock, void *data, size_t len, int flags) +{ + int ret = recv(sock, data, len, flags); + + if (ret == SOCKET_ERROR) + return os_get_error(); + + return ret; +} + +static ssize_t os_send(qsocket_t sock, const void *data, size_t len, int flags) +{ + int ret = send(sock, data, len, flags); + + if (ret == SOCKET_ERROR) + return os_get_error(); + + return ret; +} + +static neterr_t os_listen(qsocket_t sock, int backlog) +{ + if (listen(sock, backlog) == SOCKET_ERROR) { + net_error = WSAGetLastError(); + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_accept(qsocket_t sock, qsocket_t *newsock, netadr_t *from) +{ + struct sockaddr_in addr; + int addrlen; + int s; + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + s = accept(sock, (struct sockaddr *)&addr, &addrlen); + + NET_SockadrToNetadr(&addr, from); + + if (s == INVALID_SOCKET) { + *newsock = -1; + return os_get_error(); + } + + *newsock = s; + return NET_OK; +} + +static neterr_t os_connect(qsocket_t sock, const netadr_t *to) +{ + struct sockaddr_in addr; + + NET_NetadrToSockadr(to, &addr); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) { + net_error = WSAGetLastError(); + if (net_error == WSAEWOULDBLOCK) + return NET_OK; + + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_make_nonblock(qsocket_t sock, int val) +{ + u_long _val = val; + + if (ioctlsocket(sock, FIONBIO, &_val) == SOCKET_ERROR) { + net_error = WSAGetLastError(); + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_setsockopt(qsocket_t sock, int level, int name, int val) +{ + u_long _val = val; + + if (setsockopt(sock, level, name, (char *)&_val, sizeof(_val)) == SOCKET_ERROR) { + net_error = WSAGetLastError(); + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_getsockopt(qsocket_t sock, int level, int name, int *val) +{ + u_long _val; + int optlen = sizeof(_val); + + if (getsockopt(sock, level, name, (char *)&_val, &optlen) == SOCKET_ERROR) { + net_error = WSAGetLastError(); + return NET_ERROR; + } + + *val = _val; + return NET_OK; +} + +static neterr_t os_bind(qsocket_t sock, const struct sockaddr *addr, size_t addrlen) +{ + if (bind(sock, addr, addrlen) == SOCKET_ERROR) { + net_error = WSAGetLastError(); + return NET_ERROR; + } + + return NET_OK; +} + +static neterr_t os_getsockname(qsocket_t sock, netadr_t *name) +{ + struct sockaddr_in addr; + int addrlen; + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) == SOCKET_ERROR) { + net_error = WSAGetLastError(); + return NET_ERROR; + } + + NET_SockadrToNetadr(&addr, name); + return NET_OK; +} + +static void os_closesocket(qsocket_t sock) +{ + closesocket(sock); +} + +static qsocket_t os_socket(int domain, int type, int protocol) +{ + SOCKET s = socket(domain, type, protocol); + + if (s == INVALID_SOCKET) { + net_error = WSAGetLastError(); + return -1; + } + + return s; +} + +static ioentry_t *os_add_io(qsocket_t fd) +{ + ioentry_t *e; + int i; + + for (i = 0, e = io_entries; i < io_numfds; i++, e++) { + if (!e->inuse) + break; + } + + if (i == io_numfds) { + if (++io_numfds > FD_SETSIZE) + Com_Error(ERR_FATAL, "%s: no more space for fd: %d", __func__, fd); + } + + e->fd = fd; + return e; +} + +static ioentry_t *os_get_io(qsocket_t fd) +{ + ioentry_t *e; + int i; + + for (i = 0, e = io_entries; i < io_numfds; i++, e++) { + if (!e->inuse) + continue; + if (e->fd == fd) + return e; + } + + Com_Error(ERR_FATAL, "%s: fd not found: %d", __func__, fd); + return NULL; +} + +static qsocket_t os_get_fd(ioentry_t *e) +{ + return e->fd; +} + +static int os_select(int nfds, fd_set *rfds, fd_set *wfds, + fd_set *efds, struct timeval *tv) +{ + int ret = select(nfds, rfds, wfds, efds, tv); + + if (ret == SOCKET_ERROR) + net_error = WSAGetLastError(); + + return ret; +} + +static void os_net_init(void) +{ + WSADATA ws; + int ret; + + ret = WSAStartup(MAKEWORD(1, 1), &ws); + if (ret) { + Com_Error(ERR_FATAL, "Winsock initialization failed: %s (%d)", + os_error_string(ret), ret); + } + + Com_DPrintf("Winsock initialized\n"); +} + +static void os_net_shutdown(void) +{ + WSACleanup(); +} + diff --git a/src/sv_ac.c b/src/sv_ac.c index 7caf93e..bcb795f 100644 --- a/src/sv_ac.c +++ b/src/sv_ac.c @@ -531,7 +531,7 @@ static void AC_Drop(void) { client_t *cl; - NET_Close(&ac.stream); + NET_CloseStream(&ac.stream); if (!ac.connected) { Com_Printf("ANTICHEAT: Server connection failed.\n"); @@ -1189,11 +1189,11 @@ STARTUP STUFF static void AC_Spin(void) { // sleep on stdin and AC server socket - IO_Sleepv(100, + NET_Sleepv(100, #ifndef _WIN32 - 0, + STDIN_FILENO, #endif - ac.stream.socket, -1); + ac.stream.socket, -1); #if USE_SYSCON Sys_RunConsole(); #endif @@ -1487,7 +1487,7 @@ void AC_Connect(unsigned mvd_spawn) void AC_Disconnect(void) { - NET_Close(&ac.stream); + NET_CloseStream(&ac.stream); AC_FreeChecks(); diff --git a/src/sv_local.h b/src/sv_local.h index f52dfee..c81e1e8 100644 --- a/src/sv_local.h +++ b/src/sv_local.h @@ -39,7 +39,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #if USE_MVD_CLIENT #include "mvd_public.h" #endif -#include "io_sleep.h" #include "fpu.h" #if USE_ZLIB #include <zlib.h> diff --git a/src/sv_main.c b/src/sv_main.c index 5d44710..7b617b2 100644 --- a/src/sv_main.c +++ b/src/sv_main.c @@ -425,7 +425,7 @@ static void SVC_Status(void) len += SV_StatusString(buffer + len); // send the datagram - NET_SendPacket(NS_SERVER, &net_from, len, buffer); + NET_SendPacket(NS_SERVER, buffer, len, &net_from); } /* @@ -461,31 +461,24 @@ The second parameter should be the current protocol version number. */ static void SVC_Info(void) { - char string[MAX_QPATH]; + char buffer[MAX_QPATH]; size_t len; - int count; - int version; if (sv_maxclients->integer == 1) return; // ignore in single player - version = atoi(Cmd_Argv(1)); - - if (version != PROTOCOL_VERSION_DEFAULT) { + if (atoi(Cmd_Argv(1)) != PROTOCOL_VERSION_DEFAULT) return; - } - - count = SV_CountClients(); - len = Q_snprintf(string, sizeof(string), + len = Q_snprintf(buffer, sizeof(buffer), "\xff\xff\xff\xffinfo\n%16s %8s %2i/%2i\n", - sv_hostname->string, sv.name, count, sv_maxclients->integer - + sv_hostname->string, sv.name, SV_CountClients(), + sv_maxclients->integer - sv_reserved_slots->integer); - if (len >= sizeof(string)) { + if (len >= sizeof(buffer)) return; - } - NET_SendPacket(NS_SERVER, &net_from, len, string); + NET_SendPacket(NS_SERVER, buffer, len, &net_from); } /* @@ -810,7 +803,7 @@ static void redirect(const char *addr) MSG_WriteByte(svc_stufftext); MSG_WriteString(va("connect %s\n", addr)); - NET_SendPacket(NS_SERVER, &net_from, msg_write.cursize, msg_write.data); + NET_SendPacket(NS_SERVER, msg_write.data, msg_write.cursize, &net_from); SZ_Clear(&msg_write); } @@ -1365,13 +1358,46 @@ static void SV_PacketEvent(void) } } +#if USE_PMTUDISC +// We are doing path MTU discovery and got ICMP fragmentation-needed. +// Update MTU for connecting clients only to minimize spoofed ICMP interference. +// Total 64 bytes of headers is assumed. +static void update_client_mtu(client_t *client, int ee_info) +{ + netchan_t *netchan = client->netchan; + size_t newpacketlen; + + // sanity check discovered MTU + if (ee_info < 576 || ee_info > 4096) + return; + + if (client->state != cs_primed) + return; + + // TODO: old clients require entire queue flush :( + if (netchan->type == NETCHAN_OLD) + return; + + if (!netchan->reliable_length) + return; + + newpacketlen = ee_info - 64; + if (newpacketlen >= netchan->maxpacketlen) + return; + + Com_Printf("Fixing up maxmsglen for %s: %"PRIz" --> %"PRIz"\n", + client->name, netchan->maxpacketlen, newpacketlen); + netchan->maxpacketlen = newpacketlen; +} +#endif + #if USE_ICMP /* ================= SV_ErrorEvent ================= */ -void SV_ErrorEvent(int info) +void SV_ErrorEvent(netadr_t *from, int ee_errno, int ee_info) { client_t *client; netchan_t *netchan; @@ -1386,29 +1412,16 @@ void SV_ErrorEvent(int info) continue; // already a zombie } netchan = client->netchan; - if (!NET_IsEqualBaseAdr(&net_from, &netchan->remote_address)) { + if (!NET_IsEqualBaseAdr(from, &netchan->remote_address)) { continue; } - if (net_from.port && netchan->remote_address.port != net_from.port) { + if (from->port && netchan->remote_address.port != from->port) { continue; } #if USE_PMTUDISC - if (info) { - // we are doing path MTU discovery and got ICMP fragmentation-needed - // update MTU only for connecting clients to minimize spoofed ICMP interference - // MTU info has already been sanity checked for us by network code - // assume total 64 bytes of headers - if (client->state == cs_primed && - netchan->reliable_length && - info < netchan->maxpacketlen + 64) { - if (netchan->type == NETCHAN_OLD) { - // TODO: old clients require entire queue flush :( - continue; - } - Com_Printf("Fixing up maxmsglen for %s: %d --> %d\n", - client->name, (int)netchan->maxpacketlen, info - 64); - netchan->maxpacketlen = info - 64; - } + // for EMSGSIZE ee_info should hold discovered MTU + if (ee_errno == EMSGSIZE) { + update_client_mtu(client, ee_info); continue; } #endif @@ -1646,7 +1659,7 @@ static void SV_MasterHeartbeat(void) if (m->adr.port) { Com_DPrintf("Sending heartbeat to %s\n", NET_AdrToString(&m->adr)); - NET_SendPacket(NS_SERVER, &m->adr, len, buffer); + NET_SendPacket(NS_SERVER, buffer, len, &m->adr); } } } diff --git a/src/sv_mvd.c b/src/sv_mvd.c index 3707c4c..9f425f7 100644 --- a/src/sv_mvd.c +++ b/src/sv_mvd.c @@ -1216,7 +1216,7 @@ TCP CLIENTS HANDLING static void remove_client(gtv_client_t *client) { - NET_Close(&client->stream); + NET_CloseStream(&client->stream); List_Remove(&client->entry); if (client->data) { Z_Free(client->data); @@ -1655,7 +1655,7 @@ static void accept_client(netstream_t *stream) if (count >= sv_iplimit->integer) { Com_Printf("TCP client [%s] rejected: too many connections\n", NET_AdrToString(&stream->address)); - NET_Close(stream); + NET_CloseStream(stream); return; } } @@ -1665,7 +1665,7 @@ static void accept_client(netstream_t *stream) if (!client) { Com_Printf("TCP client [%s] rejected: no free slots\n", NET_AdrToString(&stream->address)); - NET_Close(stream); + NET_CloseStream(stream); return; } diff --git a/src/sv_public.h b/src/sv_public.h index 9b54e0b..e97827a 100644 --- a/src/sv_public.h +++ b/src/sv_public.h @@ -28,7 +28,7 @@ typedef enum { } server_state_t; #if USE_ICMP -void SV_ErrorEvent(int info); +void SV_ErrorEvent(netadr_t *from, int ee_errno, int ee_info); #endif void SV_Init(void); void SV_Shutdown(const char *finalmsg, error_type_t type); diff --git a/src/sv_send.c b/src/sv_send.c index 193e399..722a443 100644 --- a/src/sv_send.c +++ b/src/sv_send.c @@ -38,7 +38,7 @@ void SV_FlushRedirect(int redirected, char *outputbuf, size_t len) if (redirected == RD_PACKET) { memcpy(buffer, "\xff\xff\xff\xffprint\n", 10); memcpy(buffer + 10, outputbuf, len); - NET_SendPacket(NS_SERVER, &net_from, len + 10, buffer); + NET_SendPacket(NS_SERVER, buffer, len + 10, &net_from); } else if (redirected == RD_CLIENT) { MSG_WriteByte(svc_print); MSG_WriteByte(PRINT_HIGH); @@ -336,8 +336,6 @@ void SV_Multicast(vec3_t origin, multicast_t to) SZ_Clear(&msg_write); } - - /* ======================= SV_ClientAddMessage @@ -700,11 +698,14 @@ static void write_datagram_old(client_t *client) // send the datagram cursize = client->netchan->Transmit(client->netchan, - msg_write.cursize, msg_write.data, client->numpackets); + msg_write.cursize, + msg_write.data, + client->numpackets); // record the size for rate estimation SV_CalcSendTime(client, cursize); + // clear the write buffer SZ_Clear(&msg_write); } @@ -767,7 +768,9 @@ static void write_datagram_new(client_t *client) // send the datagram cursize = client->netchan->Transmit(client->netchan, - msg_write.cursize, msg_write.data, client->numpackets); + msg_write.cursize, + msg_write.data, + client->numpackets); // record the size for rate estimation SV_CalcSendTime(client, cursize); diff --git a/src/sys_unix.c b/src/sys_unix.c index d1d7dee..c8e371c 100644 --- a/src/sys_unix.c +++ b/src/sys_unix.c @@ -52,7 +52,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "vid_public.h" #endif #include "sys_public.h" -#include "io_sleep.h" +#include "net_sock.h" cvar_t *sys_basedir; cvar_t *sys_libdir; @@ -205,7 +205,7 @@ static void tty_init_input(void) static void tty_shutdown_input(void) { if (tty_io) { - IO_Remove(STDIN_FILENO); + NET_RemoveFd(STDIN_FILENO); tty_io = NULL; } if (tty_enabled) { @@ -771,7 +771,7 @@ void Sys_Init(void) fcntl(STDOUT_FILENO, F_SETFL, ret | O_NONBLOCK); // add stdin to the list of descriptors to wait on - tty_io = IO_Add(STDIN_FILENO); + tty_io = NET_AddFd(STDIN_FILENO); tty_io->wantread = qtrue; // init optional TTY support diff --git a/src/wsaerr.h b/src/wsaerr.h deleted file mode 100644 index e335db3..0000000 --- a/src/wsaerr.h +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright (C) 1997-2001 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#define MAPERR(x) case x: return #x; - -MAPERR(WSAEINTR) -MAPERR(WSAEBADF) -MAPERR(WSAEACCES) -MAPERR(WSAEFAULT) -MAPERR(WSAEINVAL) -MAPERR(WSAEMFILE) -MAPERR(WSAEWOULDBLOCK) -MAPERR(WSAEINPROGRESS) -MAPERR(WSAEALREADY) -MAPERR(WSAENOTSOCK) -MAPERR(WSAEDESTADDRREQ) -MAPERR(WSAEMSGSIZE) -MAPERR(WSAEPROTOTYPE) -MAPERR(WSAENOPROTOOPT) -MAPERR(WSAEPROTONOSUPPORT) -MAPERR(WSAESOCKTNOSUPPORT) -MAPERR(WSAEOPNOTSUPP) -MAPERR(WSAEPFNOSUPPORT) -MAPERR(WSAEAFNOSUPPORT) -MAPERR(WSAEADDRINUSE) -MAPERR(WSAEADDRNOTAVAIL) -MAPERR(WSAENETDOWN) -MAPERR(WSAENETUNREACH) -MAPERR(WSAENETRESET) -MAPERR(WSAECONNABORTED) -MAPERR(WSAECONNRESET) -MAPERR(WSAENOBUFS) -MAPERR(WSAEISCONN) -MAPERR(WSAENOTCONN) -MAPERR(WSAESHUTDOWN) -MAPERR(WSAETOOMANYREFS) -MAPERR(WSAETIMEDOUT) -MAPERR(WSAECONNREFUSED) -MAPERR(WSAELOOP) -MAPERR(WSAENAMETOOLONG) -MAPERR(WSAEHOSTDOWN) -MAPERR(WSAEHOSTUNREACH) -MAPERR(WSAENOTEMPTY) -MAPERR(WSAEPROCLIM) -MAPERR(WSAEUSERS) -MAPERR(WSAEDQUOT) -MAPERR(WSAESTALE) -MAPERR(WSAEREMOTE) -MAPERR(WSASYSNOTREADY) -MAPERR(WSAVERNOTSUPPORTED) -MAPERR(WSANOTINITIALISED) -MAPERR(WSAEDISCON) -MAPERR(WSAENOMORE) -MAPERR(WSAECANCELLED) -MAPERR(WSAEINVALIDPROCTABLE) -MAPERR(WSAEINVALIDPROVIDER) -MAPERR(WSAEPROVIDERFAILEDINIT) -MAPERR(WSASYSCALLFAILURE) -MAPERR(WSASERVICE_NOT_FOUND) -MAPERR(WSATYPE_NOT_FOUND) -MAPERR(WSA_E_NO_MORE) -MAPERR(WSA_E_CANCELLED) -MAPERR(WSAEREFUSED) -MAPERR(WSAHOST_NOT_FOUND) -MAPERR(WSATRY_AGAIN) -MAPERR(WSANO_RECOVERY) -MAPERR(WSANO_DATA) - -#undef MAPERR |