fix sv_user.c so that macro expansion isn't used on the
  server side, otherwise clients can potentially read
  rcon_password among others
* Forgot to add net_udp6.c from the last relnev merge... oops.
This commit is contained in:
Jamie Wilkinson 2002-03-21 03:17:25 +00:00
parent 065639f494
commit 7457b38f1e
2 changed files with 921 additions and 1 deletions

View File

@ -478,7 +478,10 @@ void SV_ExecuteUserCommand (char *s)
{
ucmd_t *u;
Cmd_TokenizeString (s, true);
/* http://www.quakesrc.org/forum/topicDisplay.php?topicID=160
* the client can read the rcon_password variable, among others
* so don't do any macro expansion on the server side */
Cmd_TokenizeString(s, false);
sv_player = sv_client->edict;
// SV_BeginRedirect (RD_CLIENT);

917
src/net_udp6.c Normal file
View File

@ -0,0 +1,917 @@
/*
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.
*/
// net_wins.c
#include "../qcommon/qcommon.h"
#include <unistd.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 <errno.h>
#include <arpa/inet.h>
#ifdef NeXT
#include <libc.h>
#endif
netadr_t net_local_adr;
#define LOOPBACK 0x7f000001
#define MAX_LOOPBACK 4
#define QUAKE2MCAST "ff12::666"
#include <net/if.h>
typedef struct
{
byte data[MAX_MSGLEN];
int datalen;
} loopmsg_t;
typedef struct
{
loopmsg_t msgs[MAX_LOOPBACK];
int get, send;
} loopback_t;
loopback_t loopbacks[2];
int ip_sockets[2];
int ip6_sockets[2];
int ipx_sockets[2];
char *multicast_interface = NULL;
int NET_Socket (char *net_interface, int port, netsrc_t type, int family);
char *NET_ErrorString (void);
//=============================================================================
void NetadrToSockadr (netadr_t *a, struct sockaddr_storage *s)
{
struct sockaddr_in6 *s6;
memset (s, 0, sizeof(*s));
switch (a->type) {
case NA_BROADCAST:
((struct sockaddr_in*)s)->sin_family = AF_INET;
((struct sockaddr_in*)s)->sin_port = a->port;
((struct sockaddr_in*)s)->sin_addr.s_addr = (in_addr_t)INADDR_BROADCAST;
break;
case NA_IP:
((struct sockaddr_in*)s)->sin_family = AF_INET;
*(int *)&((struct sockaddr_in*)s)->sin_addr = *(int *)&a->ip;
((struct sockaddr_in*)s)->sin_port = a->port;
break;
case NA_MULTICAST6:
s6 = (struct sockaddr_in6 *)s;
if (inet_pton(AF_INET6, QUAKE2MCAST, &s6->sin6_addr.s6_addr) != 1) {
Com_Printf("NET_NetadrToSockadr: inet_pton: %s", strerror (errno));
return;
}
s6->sin6_family = AF_INET6;
s6->sin6_port = a->port;
#ifdef HAVE_SIN6_LEN
s6->sin6_len = sizeof (struct sockaddr_in6);
#endif
/* scope_id is important for link-local
* destination. TODO: check in code for possible
* uninitialized netadr_t.scope_id instances */
s6->sin6_scope_id = a->scope_id;
break;
case NA_IP6:
if (IN6_IS_ADDR_V4MAPPED ((struct in6_addr *) a->ip)) {
#ifdef HAVE_SIN6_LEN
s->ss_len = sizeof (struct sockaddr_in);
#endif
s->ss_family = AF_INET;
memcpy (&((struct sockaddr_in *)s)->sin_addr,
&((struct in6_addr *) a->ip)->s6_addr[12],
sizeof (struct in_addr));
((struct sockaddr_in*)s)->sin_port = a->port;
} else {
s6 = (struct sockaddr_in6 *)s;
s6->sin6_family = AF_INET6;
memcpy(&s6->sin6_addr, a->ip, sizeof(s6->sin6_addr));
s6->sin6_port = a->port;
#ifdef HAVE_SIN6_LEN
s6->sin6_len = sizeof (struct sockaddr_in6);
#endif
/* scope_id is important for link-local
* destination. TODO: check in code for possible
* uninitialized netadr_t.scope_id instances */
s6->sin6_scope_id = a->scope_id;
}
break;
case NA_LOOPBACK:
case NA_IPX:
case NA_BROADCAST_IPX:
/* no handling of NA_LOOPBACK, NA_IPX, NA_BROADCAST_IPX */
break;
}
}
void SockadrToNetadr (struct sockaddr_storage *s, netadr_t *a)
{
struct sockaddr_in6 *s6;
if (s->ss_family == AF_INET)
{
*(int *)&a->ip = *(int *)&((struct sockaddr_in*)s)->sin_addr;
a->port = ((struct sockaddr_in*)s)->sin_port;
a->type = NA_IP;
}
else if (s->ss_family == AF_INET6)
{
s6 = (struct sockaddr_in6 *)s;
if (IN6_IS_ADDR_V4MAPPED ((struct in6_addr *)&s6->sin6_addr)) {
memcpy (a->ip,
(struct in_addr *)&s6->sin6_addr.s6_addr[12],
sizeof (struct in_addr));
a->port = ((struct sockaddr_in*)s)->sin_port;
a->type = NA_IP;
} else {
memcpy(a->ip, &s6->sin6_addr, sizeof(a->ip));
a->port = s6->sin6_port;
a->type = NA_IP6;
a->scope_id = s6->sin6_scope_id;
}
}
else
s = NULL;
}
qboolean NET_CompareAdr (netadr_t a, netadr_t b)
{
if (a.type != b.type)
return false;
if (a.type == NA_LOOPBACK)
return true;
if (a.type == NA_IP)
{
if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port)
return true;
}
if (a.type == NA_IP6)
{
if ((memcmp(a.ip, b.ip, 16) == 0) && a.port == b.port)
return true;
}
return false;
}
/*
===================
NET_CompareBaseAdr
Compares without the port
===================
*/
qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b)
{
if (a.type != b.type)
return false;
if (a.type == NA_LOOPBACK)
return true;
if (a.type == NA_IP)
{
if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3])
return true;
return false;
}
if (a.type == NA_IP6)
{
if ((memcmp(a.ip, b.ip, 16) == 0))
return true;
return false;
}
if (a.type == NA_IPX)
{
if ((memcmp(a.ipx, b.ipx, 10) == 0))
return true;
return false;
}
return false;
}
char *NET_BaseAdrToString (netadr_t a)
{
static char s[64], tmp[64];
struct sockaddr_storage ss;
struct sockaddr_in6 *s6;
int flags;
switch (a.type) {
case NA_IP:
case NA_LOOPBACK:
Com_sprintf (s, sizeof(s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]);
break;
case NA_BROADCAST:
Com_sprintf (s, sizeof(s), "255.255.255.255");
break;
case NA_IP6:
case NA_MULTICAST6:
memset (&ss, 0, sizeof (ss));
s6 = (struct sockaddr_in6 *)&ss;
if (IN6_IS_ADDR_V4MAPPED ((struct in6_addr *) a.ip)) {
#ifdef HAVE_SIN6_LEN
ss.ss_len = sizeof (struct sockaddr_in);
#endif
ss.ss_family = AF_INET;
memcpy (&((struct sockaddr_in *) &ss)->sin_addr,
&((struct in6_addr *) a.ip)->s6_addr[12],
sizeof (struct in_addr));
} else {
#ifdef HAVE_SIN6_LEN
s6->sin6_len = sizeof (struct sockaddr_in6);
#endif
/* Possible bug: a.scope_id may not have been
* initialized. See comments below on better
* way to handle this */
s6->sin6_scope_id = a.scope_id;
s6->sin6_family = AF_INET6;
memcpy (&s6->sin6_addr, a.ip, sizeof (struct in6_addr));
}
flags = NI_NUMERICHOST;
#ifdef HAVE_SIN6_LEN
if (getnameinfo ((struct sockaddr *) &ss, ss.ss_len, s, sizeof (s),
NULL, 0, NI_NUMERICHOST))
#else
if (getnameinfo ((struct sockaddr *) &ss, sizeof(ss), s, sizeof (s),
NULL, 0, NI_NUMERICHOST))
#endif
Com_sprintf (s, sizeof(s), "<invalid>");
else {
if (a.type == NA_MULTICAST6 ||
IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&ss)->sin6_addr)) {
/* If the address is multicast (link) or a
* link-local, need to carry the scope. The string
* format of the IPv6 address is used by the
* client to extablish the connect to the
* server. */
/* A better way to handle this is to always use
* sockaddr_storage to represent an IP (v4, v6)
* address */
Com_sprintf (tmp, sizeof(tmp), "%s%%%d", s, s6->sin6_scope_id);
memcpy(s, tmp, sizeof(s));
}
}
break;
default:
Com_sprintf (s, sizeof(s), "invalid IP address family type");
break;
}
return s;
}
char *NET_AdrToString (netadr_t a)
{
static char s[64];
const char *base;
base = NET_BaseAdrToString (a);
Com_sprintf (s, sizeof(s), "[%s]:%d", base, ntohs(a.port));
return s;
}
/*
=============
NET_StringToAdr
localhost
idnewt
idnewt:28000
192.246.40.70
192.246.40.70:28000
=============
*/
qboolean NET_StringToSockaddr (char *s, struct sockaddr_storage *sadr)
{
char copy[128];
char *addrs, *space;
char *ports = NULL;
int err;
struct addrinfo hints;
struct addrinfo *resultp;
memset (&hints,0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = PF_UNSPEC;
strcpy (copy, s);
addrs = space = copy;
if (*addrs == '[') {
addrs++;
for (; *space && *space != ']'; space++);
if (!*space) {
Com_Printf ("NET_StringToSockaddr: invalid IPv6 address %s\n", s);
return 0;
}
*space++ = '\0';
}
for (; *space; space++) {
if (*space == ':') {
*space = '\0';
ports = space + 1;
}
}
if ((err = getaddrinfo (addrs, ports, &hints, &resultp))) {
// Error
Com_Printf ("NET_StringToSockaddr: string %s:\n%s\n", s, gai_strerror(err));
return 0;
}
switch (resultp->ai_family) {
case AF_INET:
// convert to ipv4 addr
memset (sadr, 0, sizeof (struct sockaddr_storage));
memcpy (sadr, resultp->ai_addr, resultp->ai_addrlen);
break;
case AF_INET6:
// convert to ipv6 addr
memset (sadr, 0, sizeof (struct sockaddr_storage));
memcpy (sadr, resultp->ai_addr, resultp->ai_addrlen);
break;
default:
Com_Printf
("NET_StringToSockaddr: string %s:\nprotocol family %d not supported\n",
s, resultp->ai_family);
return 0;
}
return true;
}
/*
=============
NET_StringToAdr
localhost
idnewt
idnewt:28000
192.246.40.70
192.246.40.70:28000
=============
*/
qboolean NET_StringToAdr (char *s, netadr_t *a)
{
struct sockaddr_storage sadr;
memset (a, 0, sizeof(*a));
if (!strcmp (s, "localhost"))
{
a->type = NA_LOOPBACK;
return true;
}
if (!NET_StringToSockaddr (s, &sadr))
return false;
SockadrToNetadr (&sadr, a);
return true;
}
qboolean NET_IsLocalAddress (netadr_t adr)
{
return NET_CompareAdr (adr, net_local_adr);
}
/*
=============================================================================
LOOPBACK BUFFERS FOR LOCAL PLAYER
=============================================================================
*/
qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message)
{
int i;
loopback_t *loop;
loop = &loopbacks[sock];
if (loop->send - loop->get > MAX_LOOPBACK)
loop->get = loop->send - MAX_LOOPBACK;
if (loop->get >= loop->send)
return false;
i = loop->get & (MAX_LOOPBACK-1);
loop->get++;
memcpy (net_message->data, loop->msgs[i].data, loop->msgs[i].datalen);
net_message->cursize = loop->msgs[i].datalen;
*net_from = net_local_adr;
return true;
}
void NET_SendLoopPacket (netsrc_t sock, int length, void *data, netadr_t to)
{
int i;
loopback_t *loop;
loop = &loopbacks[sock^1];
i = loop->send & (MAX_LOOPBACK-1);
loop->send++;
memcpy (loop->msgs[i].data, data, length);
loop->msgs[i].datalen = length;
}
//=============================================================================
qboolean NET_GetPacket (netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message)
{
int ret;
struct sockaddr_storage from;
socklen_t fromlen;
int net_socket;
int protocol;
int err;
if (NET_GetLoopPacket (sock, net_from, net_message))
return true;
for (protocol = 0 ; protocol < 3 ; protocol++)
{
if (protocol == 0)
net_socket = ip_sockets[sock];
else if (protocol == 1)
net_socket = ip6_sockets[sock];
else
net_socket = ipx_sockets[sock];
if (!net_socket)
continue;
fromlen = sizeof(from);
ret = recvfrom (net_socket, net_message->data, net_message->maxsize,
0, (struct sockaddr *)&from, &fromlen);
SockadrToNetadr (&from, net_from);
if (ret == -1)
{
err = errno;
if (err == EWOULDBLOCK || err == ECONNREFUSED)
continue;
Com_Printf ("NET_GetPacket: %s from %s\n", NET_ErrorString(),
NET_AdrToString(*net_from));
continue;
}
if (ret == net_message->maxsize)
{
Com_Printf ("Oversize packet from %s\n", NET_AdrToString (*net_from));
continue;
}
net_message->cursize = ret;
return true;
}
return false;
}
//=============================================================================
void NET_SendPacket (netsrc_t sock, int length, void *data, netadr_t to)
{
int ret;
struct sockaddr_storage addr;
int net_socket;
int addr_size = sizeof(struct sockaddr_in);
switch (to.type) {
case NA_LOOPBACK:
NET_SendLoopPacket (sock, length, data, to);
return;
break;
case NA_BROADCAST:
case NA_IP:
net_socket = ip_sockets[sock];
if (!net_socket)
return;
break;
case NA_IP6:
case NA_MULTICAST6:
net_socket = ip6_sockets[sock];
addr_size = sizeof(struct sockaddr_in6);
if (!net_socket)
return;
break;
case NA_IPX:
case NA_BROADCAST_IPX:
net_socket = ipx_sockets[sock];
if (!net_socket)
return;
break;
default:
Com_Error (ERR_FATAL, "NET_SendPacket: bad address type");
return;
break;
}
NetadrToSockadr (&to, &addr);
/* Re-check the address family. If to.type is NA_IP6 but
* contains an IPv4 mapped address, NetadrToSockadr will
* return an AF_INET struct. If so, switch back to AF_INET
* socket.*/
if (to.type == NA_IP6 && addr.ss_family == AF_INET) {
net_socket = ip_sockets[sock];
addr_size = sizeof(struct sockaddr_in);
if (!net_socket)
return;
}
if (addr.ss_family == AF_INET6) {
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&addr;
/* If multicast socket, must specify scope. So multicast_interface must be specified */
if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
struct addrinfo hints;
struct addrinfo *res;
char tmp[128], mcast_addr[128], mcast_port[10];
int error;
if (multicast_interface != NULL) {
/* Do a getnameinfo/getaddrinfo cycle
* to calculate the scope_id of the
* multicast address. getaddrinfo is
* passed a multicast address of the
* form
* ff0x::xxx%multicast_interface */
#ifdef HAVE_SIN6_LEN
error = getnameinfo ((struct sockaddr *) s6, s6->sin6_len, tmp, sizeof (tmp),
NULL, 0, NI_NUMERICHOST);
#else
error = getnameinfo ((struct sockaddr *) s6, sizeof(struct sockaddr_in6), tmp, sizeof (tmp),
NULL, 0, NI_NUMERICHOST);
#endif
if (error) {
Com_Printf("NET_SendPacket: getnameinfo: %s", gai_strerror(error));
return;
}
Com_sprintf (mcast_addr, sizeof(mcast_addr), "%s%%%s", tmp, multicast_interface);
Com_sprintf (mcast_port, sizeof(mcast_port), "%d", ntohs(s6->sin6_port));
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_NUMERICHOST;
error = getaddrinfo (mcast_addr, mcast_port, &hints, &res);
if (error) {
Com_Printf("NET_SendPacket: getaddrinfo: %s", gai_strerror(error));
return;
}
/* sockaddr_in6 should now have a valid scope_id. */
memcpy(s6, res->ai_addr, res->ai_addrlen);
} else {
Com_Printf("NET_SendPacket: IPv6 multicast destination but +set multicast not specified: %s\n",
inet_ntop(AF_INET6, &s6->sin6_addr, tmp, sizeof(tmp)));
return;
}
}
}
ret = sendto (net_socket, data, length, 0, (struct sockaddr *)&addr, addr_size );
if (ret == -1)
{
Com_Printf ("NET_SendPacket ERROR: %s to %s\n", NET_ErrorString(),
NET_AdrToString (to));
}
}
//=============================================================================
/*
====================
NET_OpenIP
====================
*/
void NET_OpenIP (void)
{
cvar_t *port, *ip;
port = Cvar_Get ("port", va("%i", PORT_SERVER), CVAR_NOSET);
ip = Cvar_Get ("ip", "localhost", CVAR_NOSET);
if (!ip6_sockets[NS_SERVER])
ip6_sockets[NS_SERVER] = NET_Socket (ip->string, port->value, NS_SERVER, AF_INET6);
if (!ip6_sockets[NS_CLIENT])
ip6_sockets[NS_CLIENT] = NET_Socket (ip->string, PORT_ANY, NS_CLIENT, AF_INET6);
if (!ip_sockets[NS_SERVER])
ip_sockets[NS_SERVER] = NET_Socket (ip->string, port->value, NS_SERVER, AF_INET);
if (!ip_sockets[NS_CLIENT])
ip_sockets[NS_CLIENT] = NET_Socket (ip->string, PORT_ANY, NS_CLIENT, AF_INET);
}
/*
====================
NET_OpenIPX
====================
*/
void NET_OpenIPX (void)
{
}
/*
====================
NET_Config
A single player game will only use the loopback code
====================
*/
void NET_Config (qboolean multiplayer)
{
int i;
if (!multiplayer)
{ // shut down any existing sockets
for (i=0 ; i<2 ; i++)
{
if (ip_sockets[i])
{
close (ip_sockets[i]);
ip_sockets[i] = 0;
}
if (ip6_sockets[i])
{
close (ip6_sockets[i]);
ip6_sockets[i] = 0;
}
if (ipx_sockets[i])
{
close (ipx_sockets[i]);
ipx_sockets[i] = 0;
}
}
}
else
{ // open sockets
NET_OpenIP ();
NET_OpenIPX ();
}
}
//===================================================================
/*
====================
NET_Init
====================
*/
void NET_Init (void)
{
}
/*
====================
NET_Socket
====================
*/
int NET_Socket (char *net_interface, int port, netsrc_t type, int family)
{
char Buf[BUFSIZ], *Host, *Service;
int newsocket, Error;
struct sockaddr_storage ss;
struct addrinfo hints, *res, *ai;
qboolean _true = true;
int i = 1;
#ifdef IPV6_BINDV6ONLY
int dummy;
#endif
struct ipv6_mreq mreq;
cvar_t *mcast;
memset (&hints, 0, sizeof (hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;
if (!net_interface || !net_interface[0] || !stricmp(net_interface, "localhost"))
Host = (family == AF_INET6) ? "::" : "0.0.0.0";
else
Host = net_interface;
if (port == PORT_ANY)
Service = NULL;
else {
sprintf (Buf, "%5d", port);
Service = Buf;
}
if ((Error = getaddrinfo (Host, Service, &hints, &res))) {
/* Com_Printf("NET_Socket: getaddrinfo: %s\n", gai_strerror (Error)); */
return 0;
}
for (ai = res; ai != NULL; ai = ai->ai_next) {
if ((newsocket = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
Com_Printf("NET_Socket: socket: %s\n", strerror (errno));
continue;
}
// make it non-blocking
if (ioctl (newsocket, FIONBIO, (char *) &_true) == -1) {
Com_Printf("NET_Socket: ioctl FIONBIO: %s\n", strerror (errno));
continue;
}
#ifdef IPV6_BINDV6ONLY
if (family == AF_INET6)
if (setsockopt (newsocket, IPPROTO_IPV6, IPV6_BINDV6ONLY, &dummy,
sizeof (dummy)) < 0) {
Com_Printf("NET_Socket: setsockopt IPV6_BINDV6ONLY: %s\n", strerror (errno));
}
#endif
if (family == AF_INET) {
// make it broadcast capable
if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == -1)
{
Com_Printf ("ERROR: NET_Socket: setsockopt SO_BROADCAST:%s\n", NET_ErrorString());
return 0;
}
}
if (bind (newsocket, ai->ai_addr, ai->ai_addrlen) < 0) {
Com_Printf("NET_Socket: bind: %s\n", strerror (errno));
} else {
memcpy(&ss, ai->ai_addr, ai->ai_addrlen);
break;
}
}
if (res != NULL)
freeaddrinfo(res);
if (ai == NULL)
return 0;
switch (ss.ss_family) {
case AF_INET:
break;
case AF_INET6:
/* Multicast outgoing interface is specified for
* client and server (+set multicast <ifname>) */
mcast = Cvar_Get ("multicast", "NULL", CVAR_NOSET);
multicast_interface = (strcmp(mcast->string, "NULL") ? mcast->string : NULL);
if (multicast_interface != NULL) {
/* multicast_interface is a global variable. Also used in NET_SendPacket() */
if ((mreq.ipv6mr_interface = if_nametoindex(multicast_interface)) == 0) {
Com_Printf("NET_Socket: invalid interface: %s", multicast_interface);
}
if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq.ipv6mr_interface,
sizeof(mreq.ipv6mr_interface)) < 0) {
Com_Printf("NET_Socket: IPV6_MULTICAST_IF: %s", strerror (errno));
}
/* Join multicast group ONLY if server */
if (type == NS_SERVER) {
if (inet_pton(AF_INET6, QUAKE2MCAST, &mreq.ipv6mr_multiaddr.s6_addr) != 1) {
Com_Printf("NET_Socket: inet_pton: %s", strerror (errno));
}
if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) {
Com_Printf("NET_Socket: IPV6_JOIN_GROUP: %s", strerror (errno));
}
}
}
break;
}
return newsocket;
}
/*
====================
NET_Shutdown
====================
*/
void NET_Shutdown (void)
{
NET_Config (false); // close sockets
}
/*
====================
NET_ErrorString
====================
*/
char *NET_ErrorString (void)
{
int code;
code = errno;
return strerror (code);
}
// sleeps msec or until net socket is ready
void NET_Sleep(int msec)
{
struct timeval timeout;
fd_set fdset;
extern cvar_t *dedicated;
extern qboolean stdin_active;
if ( (!ip_sockets[NS_SERVER] && !ip6_sockets[NS_SERVER]) || (dedicated && !dedicated->value))
return; // we're not a server, just run full speed
FD_ZERO(&fdset);
if (stdin_active)
FD_SET(0, &fdset); // stdin is processed too
FD_SET(ip_sockets[NS_SERVER], &fdset); // IPv4 network socket
FD_SET(ip6_sockets[NS_SERVER], &fdset); // IPv6 network socket
timeout.tv_sec = msec/1000;
timeout.tv_usec = (msec%1000)*1000;
select(MAX(ip_sockets[NS_SERVER],ip6_sockets[NS_SERVER])+1, &fdset, NULL, NULL, &timeout);
}