This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/common/network.c

922 lines
24 KiB
C

/*
network.c - network interface
Copyright (C) 2007 Uncle Mike
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 3 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.
*/
#include <winsock.h>
#include <wsipx.h>
#include "common.h"
#include "netchan.h"
#define PORT_ANY -1
#define MAX_LOOPBACK 4
#define MASK_LOOPBACK (MAX_LOOPBACK - 1)
// wsock32.dll exports
static int (_stdcall *pWSACleanup)( void );
static word (_stdcall *pNtohs)( word netshort );
static int (_stdcall *pWSAGetLastError)( void );
static int (_stdcall *pCloseSocket)( SOCKET s );
static word (_stdcall *pHtons)( word hostshort );
static dword (_stdcall *pInet_Addr)( const char* cp );
static char* (_stdcall *pInet_Ntoa)( struct in_addr in );
static SOCKET (_stdcall *pSocket)( int af, int type, int protocol );
static struct hostent *(_stdcall *pGetHostByName)( const char* name );
static int (_stdcall *pIoctlSocket)( SOCKET s, long cmd, dword* argp );
static int (_stdcall *pWSAStartup)( word wVersionRequired, LPWSADATA lpWSAData );
static int (_stdcall *pBind)( SOCKET s, const struct sockaddr* addr, int namelen );
static int (_stdcall *pSetSockopt)( SOCKET s, int level, int optname, const char* optval, int optlen );
static int (_stdcall *pRecvFrom)( SOCKET s, char* buf, int len, int flags, struct sockaddr* from, int* fromlen );
static int (_stdcall *pSendTo)( SOCKET s, const char* buf, int len, int flags, const struct sockaddr* to, int tolen );
static int (_stdcall *pSelect)( int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const struct timeval* timeout );
static int (_stdcall *pConnect)( SOCKET s, const struct sockaddr *name, int namelen );
static int (_stdcall *pSend)( SOCKET s, const char *buf, int len, int flags );
static int (_stdcall *pRecv)( SOCKET s, char *buf, int len, int flags );
static int (_stdcall *pGetHostName)( char *name, int namelen );
static dword (_stdcall *pNtohl)( dword netlong );
static dllfunc_t winsock_funcs[] =
{
{ "bind", (void **) &pBind },
{ "send", (void **) &pSend },
{ "recv", (void **) &pRecv },
{ "ntohs", (void **) &pNtohs },
{ "htons", (void **) &pHtons },
{ "ntohl", (void **) &pNtohl },
{ "socket", (void **) &pSocket },
{ "select", (void **) &pSelect },
{ "sendto", (void **) &pSendTo },
{ "connect", (void **) &pConnect },
{ "recvfrom", (void **) &pRecvFrom },
{ "inet_addr", (void **) &pInet_Addr },
{ "inet_ntoa", (void **) &pInet_Ntoa },
{ "WSAStartup", (void **) &pWSAStartup },
{ "WSACleanup", (void **) &pWSACleanup },
{ "setsockopt", (void **) &pSetSockopt },
{ "ioctlsocket", (void **) &pIoctlSocket },
{ "closesocket", (void **) &pCloseSocket },
{ "gethostname", (void **) &pGetHostName },
{ "gethostbyname", (void **) &pGetHostByName },
{ "WSAGetLastError", (void **) &pWSAGetLastError },
{ NULL, NULL }
};
dll_info_t winsock_dll = { "wsock32.dll", winsock_funcs, false };
typedef struct
{
byte data[NET_MAX_PAYLOAD];
int datalen;
} loopmsg_t;
typedef struct
{
loopmsg_t msgs[MAX_LOOPBACK];
int get, send;
} loopback_t;
static loopback_t loopbacks[2];
static int ip_sockets[2];
static int ipx_sockets[2];
static WSADATA winsockdata;
static qboolean winsockInitialized = false;
static const char *net_src[2] = { "client", "server" };
static convar_t *net_ip;
static convar_t *net_hostport;
static convar_t *net_clientport;
static convar_t *net_showpackets;
void NET_Restart_f( void );
qboolean NET_OpenWinSock( void )
{
// initialize the Winsock function vectors (we do this instead of statically linking
// so we can run on Win 3.1, where there isn't necessarily Winsock)
if( Sys_LoadLibrary( &winsock_dll ))
return true;
return false;
}
void NET_FreeWinSock( void )
{
Sys_FreeLibrary( &winsock_dll );
}
/*
====================
NET_ErrorString
====================
*/
char *NET_ErrorString( void )
{
switch( pWSAGetLastError( ))
{
case WSAEINTR: return "WSAEINTR";
case WSAEBADF: return "WSAEBADF";
case WSAEACCES: return "WSAEACCES";
case WSAEDISCON: return "WSAEDISCON";
case WSAEFAULT: return "WSAEFAULT";
case WSAEINVAL: return "WSAEINVAL";
case WSAEMFILE: return "WSAEMFILE";
case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK";
case WSAEINPROGRESS: return "WSAEINPROGRESS";
case WSAEALREADY: return "WSAEALREADY";
case WSAENOTSOCK: return "WSAENOTSOCK";
case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ";
case WSAEMSGSIZE: return "WSAEMSGSIZE";
case WSAEPROTOTYPE: return "WSAEPROTOTYPE";
case WSAENOPROTOOPT: return "WSAENOPROTOOPT";
case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT";
case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT";
case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP";
case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT";
case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT";
case WSAEADDRINUSE: return "WSAEADDRINUSE";
case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL";
case WSAENETDOWN: return "WSAENETDOWN";
case WSAENETUNREACH: return "WSAENETUNREACH";
case WSAENETRESET: return "WSAENETRESET";
case WSAECONNABORTED: return "WSWSAECONNABORTEDAEINTR";
case WSAECONNRESET: return "WSAECONNRESET";
case WSAENOBUFS: return "WSAENOBUFS";
case WSAEISCONN: return "WSAEISCONN";
case WSAENOTCONN: return "WSAENOTCONN";
case WSAESHUTDOWN: return "WSAESHUTDOWN";
case WSAETOOMANYREFS: return "WSAETOOMANYREFS";
case WSAETIMEDOUT: return "WSAETIMEDOUT";
case WSAECONNREFUSED: return "WSAECONNREFUSED";
case WSAELOOP: return "WSAELOOP";
case WSAENAMETOOLONG: return "WSAENAMETOOLONG";
case WSAEHOSTDOWN: return "WSAEHOSTDOWN";
case WSASYSNOTREADY: return "WSASYSNOTREADY";
case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED";
case WSANOTINITIALISED: return "WSANOTINITIALISED";
case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND";
case WSATRY_AGAIN: return "WSATRY_AGAIN";
case WSANO_RECOVERY: return "WSANO_RECOVERY";
case WSANO_DATA: return "WSANO_DATA";
default: return "NO ERROR";
}
}
static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr *s )
{
Q_memset( s, 0, sizeof( *s ));
if( a->type == 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 = INADDR_BROADCAST;
}
else if( a->type == NA_IP )
{
((struct sockaddr_in *)s)->sin_family = AF_INET;
((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip;
((struct sockaddr_in *)s)->sin_port = a->port;
}
else if( a->type == NA_IPX )
{
((struct sockaddr_ipx *)s)->sa_family = AF_IPX;
Q_memcpy(((struct sockaddr_ipx *)s)->sa_netnum, &a->ipx[0], 4 );
Q_memcpy(((struct sockaddr_ipx *)s)->sa_nodenum, &a->ipx[4], 6 );
((struct sockaddr_ipx *)s)->sa_socket = a->port;
}
else if( a->type == NA_BROADCAST_IPX )
{
((struct sockaddr_ipx *)s)->sa_family = AF_IPX;
Q_memset(((struct sockaddr_ipx *)s)->sa_netnum, 0, 4 );
Q_memset(((struct sockaddr_ipx *)s)->sa_nodenum, 0xff, 6 );
((struct sockaddr_ipx *)s)->sa_socket = a->port;
}
}
static void NET_SockadrToNetadr( struct sockaddr *s, netadr_t *a )
{
if( s->sa_family == AF_INET )
{
a->type = NA_IP;
*(int *)&a->ip = ((struct sockaddr_in *)s)->sin_addr.s_addr;
a->port = ((struct sockaddr_in *)s)->sin_port;
}
else if( s->sa_family == AF_IPX )
{
a->type = NA_IPX;
Q_memcpy( &a->ipx[0], ((struct sockaddr_ipx *)s)->sa_netnum, 4 );
Q_memcpy( &a->ipx[4], ((struct sockaddr_ipx *)s)->sa_nodenum, 6 );
a->port = ((struct sockaddr_ipx *)s)->sa_socket;
}
}
/*
=============
NET_StringToAdr
localhost
idnewt
idnewt:28000
192.246.40.70
192.246.40.70:28000
=============
*/
#define DO(src,dest) \
copy[0] = s[src]; \
copy[1] = s[src + 1]; \
sscanf( copy, "%x", &val ); \
((struct sockaddr_ipx *)sadr)->dest = val
static qboolean NET_StringToSockaddr( const char *s, struct sockaddr *sadr )
{
struct hostent *h;
char *colon;
char copy[MAX_SYSPATH];
int val;
Q_memset( sadr, 0, sizeof( *sadr ));
if((Q_strlen( s ) >= 23 ) && ( s[8] == ':' ) && ( s[21] == ':' )) // check for an IPX address
{
((struct sockaddr_ipx *)sadr)->sa_family = AF_IPX;
copy[2] = 0;
DO( 0, sa_netnum[0] );
DO( 2, sa_netnum[1] );
DO( 4, sa_netnum[2] );
DO( 6, sa_netnum[3] );
DO( 9, sa_nodenum[0] );
DO( 11, sa_nodenum[1] );
DO( 13, sa_nodenum[2] );
DO( 15, sa_nodenum[3] );
DO( 17, sa_nodenum[4] );
DO( 19, sa_nodenum[5] );
sscanf( &s[22], "%u", &val );
((struct sockaddr_ipx *)sadr)->sa_socket = pHtons((word)val);
}
else
{
((struct sockaddr_in *)sadr)->sin_family = AF_INET;
((struct sockaddr_in *)sadr)->sin_port = 0;
Q_strncpy( copy, s, sizeof( copy ));
// strip off a trailing :port if present
for( colon = copy; *colon; colon++ )
{
if( *colon == ':' )
{
*colon = 0;
((struct sockaddr_in *)sadr)->sin_port = pHtons((short)Q_atoi( colon + 1 ));
}
}
if( copy[0] >= '0' && copy[0] <= '9' )
{
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = pInet_Addr( copy );
}
else
{
if(!( h = pGetHostByName( copy )))
return false;
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0];
}
}
return true;
}
#undef DO
char *NET_AdrToString( const netadr_t a )
{
if( a.type == NA_LOOPBACK )
return "loopback";
else if( a.type == NA_IP )
return va( "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], pNtohs( a.port ));
return va( "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%i", a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9], pNtohs( a.port ));
}
char *NET_BaseAdrToString( const netadr_t a )
{
if( a.type == NA_LOOPBACK )
return "loopback";
else if( a.type == NA_IP )
return va( "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3] );
return va( "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x", a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9] );
}
/*
===================
NET_CompareBaseAdr
Compares without the port
===================
*/
qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b )
{
if( a.type != b.type )
return false;
if( a.type == NA_LOOPBACK )
return true;
if( a.type == NA_IP )
{
if(!memcmp( a.ip, b.ip, 4 ))
return true;
return false;
}
if( a.type == NA_IPX )
{
if(!memcmp( a.ipx, b.ipx, 10 ))
return true;
return false;
}
MsgDev( D_ERROR, "NET_CompareBaseAdr: bad address type\n" );
return false;
}
qboolean NET_CompareAdr( const netadr_t a, const netadr_t b )
{
if( a.type != b.type )
return false;
if( a.type == NA_LOOPBACK )
return true;
if( a.type == NA_IP )
{
if(!memcmp( a.ip, b.ip, 4 ) && a.port == b.port )
return true;
return false;
}
if( a.type == NA_IPX )
{
if(!memcmp( a.ipx, b.ipx, 10 ) && a.port == b.port )
return true;
return false;
}
MsgDev( D_ERROR, "NET_CompareAdr: bad address type\n" );
return false;
}
qboolean NET_IsLocalAddress( netadr_t adr )
{
return adr.type == NA_LOOPBACK;
}
/*
=============
NET_StringToAdr
idnewt
192.246.40.70
=============
*/
qboolean NET_StringToAdr( const char *string, netadr_t *adr )
{
struct sockaddr s;
if( !Q_stricmp( string, "localhost" ))
{
Q_memset( adr, 0, sizeof( netadr_t ));
adr->type = NA_LOOPBACK;
return true;
}
if( !NET_StringToSockaddr( string, &s ))
return false;
NET_SockadrToNetadr( &s, adr );
return true;
}
/*
=============================================================================
LOOPBACK BUFFERS FOR LOCAL PLAYER
=============================================================================
*/
static qboolean NET_GetLoopPacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length )
{
loopback_t *loop;
int i;
if( !data || !length )
return false;
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 & MASK_LOOPBACK;
loop->get++;
Q_memcpy( data, loop->msgs[i].data, loop->msgs[i].datalen );
*length = loop->msgs[i].datalen;
Q_memset( from, 0, sizeof( *from ));
from->type = NA_LOOPBACK;
return true;
}
static void NET_SendLoopPacket( netsrc_t sock, size_t length, const void *data, netadr_t to )
{
int i;
loopback_t *loop;
loop = &loopbacks[sock^1];
i = loop->send & MASK_LOOPBACK;
loop->send++;
Q_memcpy( loop->msgs[i].data, data, length );
loop->msgs[i].datalen = length;
}
static void NET_ClearLoopback( void )
{
loopbacks[0].send = loopbacks[0].get = 0;
loopbacks[1].send = loopbacks[1].get = 0;
}
/*
==================
NET_GetPacket
Never called by the game logic, just the system event queing
==================
*/
qboolean NET_GetPacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length )
{
uint ret;
struct sockaddr addr;
int err, addr_len;
int net_socket;
int protocol;
if( !data || !length )
return false;
if( NET_GetLoopPacket( sock, from, data, length ))
return true;
for( protocol = 0; protocol < 2; protocol++ )
{
if( !protocol) net_socket = ip_sockets[sock];
else net_socket = ipx_sockets[sock];
if( !net_socket ) continue;
addr_len = sizeof( addr );
ret = pRecvFrom( net_socket, data, NET_MAX_PAYLOAD, 0, (struct sockaddr *)&addr, &addr_len );
NET_SockadrToNetadr( &addr, from );
if( ret == SOCKET_ERROR )
{
err = pWSAGetLastError();
// WSAEWOULDBLOCK and WSAECONNRESET are silent
if( err == WSAEWOULDBLOCK || err == WSAECONNRESET )
return false;
MsgDev( D_ERROR, "NET_GetPacket: %s from %s\n", NET_ErrorString(), NET_AdrToString( *from ));
continue;
}
if( ret == NET_MAX_PAYLOAD )
{
MsgDev( D_ERROR, "NET_GetPacket: oversize packet from %s\n", NET_AdrToString( *from ));
continue;
}
*length = ret;
return true;
}
return false;
}
/*
==================
NET_SendPacket
==================
*/
void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to )
{
int ret, err;
struct sockaddr addr;
SOCKET net_socket;
// sequenced packets are shown in netchan, so just show oob
if( net_showpackets->integer && *(int *)data == -1 )
MsgDev( D_INFO, "send packet %4i\n", length );
if( to.type == NA_LOOPBACK )
{
NET_SendLoopPacket( sock, length, data, to );
return;
}
else if( to.type == NA_BROADCAST )
{
net_socket = ip_sockets[sock];
if( !net_socket ) return;
}
else if( to.type == NA_IP )
{
net_socket = ip_sockets[sock];
if( !net_socket ) return;
}
else if( to.type == NA_IPX )
{
net_socket = ipx_sockets[sock];
if( !net_socket ) return;
}
else if( to.type == NA_BROADCAST_IPX )
{
net_socket = ipx_sockets[sock];
if( !net_socket ) return;
}
else Host_Error( "NET_SendPacket: bad address type %i\n", to.type );
if( to.type != NA_BROADCAST && to.type != NA_IP )
Host_Error( "NET_SendPacket: bad address type %i\n", to.type );
NET_NetadrToSockadr( &to, &addr );
ret = pSendTo( net_socket, data, length, 0, &addr, sizeof( addr ));
if( ret == SOCKET_ERROR )
{
err = pWSAGetLastError();
// WSAEWOULDBLOCK is silent
if( err == WSAEWOULDBLOCK )
return;
// some PPP links don't allow broadcasts
if(( err == WSAEADDRNOTAVAIL ) && (( to.type == NA_BROADCAST ) || ( to.type == NA_BROADCAST_IPX )))
return;
MsgDev( D_ERROR, "NET_SendPacket: %s to %s\n", NET_ErrorString(), NET_AdrToString( to ));
}
}
/*
====================
NET_IPSocket
====================
*/
static int NET_IPSocket( const char *netInterface, int port )
{
int err, net_socket;
struct sockaddr_in addr;
dword _true = 1;
MsgDev( D_NOTE, "NET_UDPSocket( %s, %i )\n", netInterface, port );
if(( net_socket = pSocket( PF_INET, SOCK_DGRAM, IPPROTO_UDP )) == SOCKET_ERROR )
{
err = pWSAGetLastError();
if( err != WSAEAFNOSUPPORT )
MsgDev( D_WARN, "NET_UDPSocket: socket = %s\n", NET_ErrorString( ));
return 0;
}
if( pIoctlSocket( net_socket, FIONBIO, &_true ) == SOCKET_ERROR )
{
MsgDev( D_WARN, "NET_UDPSocket: ioctlsocket FIONBIO = %s\n", NET_ErrorString( ));
pCloseSocket( net_socket );
return 0;
}
// make it broadcast capable
if( pSetSockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true )) == SOCKET_ERROR )
{
MsgDev( D_WARN, "NET_UDPSocket: setsockopt SO_BROADCAST = %s\n", NET_ErrorString( ));
pCloseSocket( net_socket );
return 0;
}
if( !netInterface[0] || !Q_stricmp( netInterface, "localhost" ))
addr.sin_addr.s_addr = INADDR_ANY;
else NET_StringToSockaddr( netInterface, (struct sockaddr *)&addr );
if( port == PORT_ANY ) addr.sin_port = 0;
else addr.sin_port = pHtons((short)port);
addr.sin_family = AF_INET;
if( pBind( net_socket, (void *)&addr, sizeof( addr )) == SOCKET_ERROR )
{
MsgDev( D_WARN, "NET_UDPSocket: bind = %s\n", NET_ErrorString( ));
pCloseSocket( net_socket );
return 0;
}
return net_socket;
}
/*
====================
NET_OpenIP
====================
*/
static void NET_OpenIP( void )
{
int port;
net_ip = Cvar_Get( "ip", "localhost", CVAR_INIT, "network ip address" );
if( !ip_sockets[NS_SERVER] )
{
port = Cvar_Get( "ip_hostport", "0", CVAR_INIT, "network server port" )->integer;
if( !port ) port = Cvar_Get( "port", va( "%i", PORT_SERVER ), CVAR_INIT, "network default port" )->integer;
ip_sockets[NS_SERVER] = NET_IPSocket( net_ip->string, port );
if( !ip_sockets[NS_SERVER] && host.type == HOST_DEDICATED )
Host_Error( "couldn't allocate dedicated server IP port\n" );
}
// dedicated servers don't need client ports
if( host.type == HOST_DEDICATED ) return;
if( !ip_sockets[NS_CLIENT] )
{
port = Cvar_Get( "ip_clientport", "0", CVAR_INIT, "network client port" )->integer;
if( !port )
{
port = Cvar_Get( "clientport", va( "%i", PORT_CLIENT ), CVAR_INIT, "network client port" )->integer;
if( !port ) port = PORT_ANY;
}
ip_sockets[NS_CLIENT] = NET_IPSocket( net_ip->string, port );
if( !ip_sockets[NS_CLIENT] ) ip_sockets[NS_CLIENT] = NET_IPSocket( net_ip->string, PORT_ANY );
}
}
/*
====================
NET_IPXSocket
====================
*/
static int NET_IPXSocket( int port )
{
int net_socket;
struct sockaddr_ipx addr;
int _true = 1;
int err;
MsgDev( D_NOTE, "NET_IPXSocket( %i )\n", port );
if(( net_socket = pSocket( PF_IPX, SOCK_DGRAM, NSPROTO_IPX )) == SOCKET_ERROR )
{
err = pWSAGetLastError();
if( err != WSAEAFNOSUPPORT )
MsgDev( D_WARN, "NET_IPXSocket: socket = %s\n", NET_ErrorString( ));
return 0;
}
// make it non-blocking
if( pIoctlSocket( net_socket, FIONBIO, &_true ) == SOCKET_ERROR )
{
MsgDev( D_WARN, "NET_IPXSocket: ioctlsocket FIONBIO = %s\n", NET_ErrorString( ));
pCloseSocket( net_socket );
return 0;
}
// make it broadcast capable
if( pSetSockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true )) == SOCKET_ERROR )
{
MsgDev( D_WARN, "NET_IPXSocket: setsockopt SO_BROADCAST = %s\n", NET_ErrorString( ));
pCloseSocket( net_socket );
return 0;
}
addr.sa_family = AF_IPX;
Q_memset( addr.sa_netnum, 0, 4 );
Q_memset( addr.sa_nodenum, 0, 6 );
if( port == PORT_ANY ) addr.sa_socket = 0;
else addr.sa_socket = pHtons((short)port );
if( pBind( net_socket, (void *)&addr, sizeof( addr )) == SOCKET_ERROR )
{
MsgDev( D_WARN, "NET_IPXSocket: bind = %s\n", NET_ErrorString( ));
pCloseSocket( net_socket );
return 0;
}
return net_socket;
}
/*
====================
NET_OpenIPX
====================
*/
void NET_OpenIPX( void )
{
int port;
if( !ipx_sockets[NS_SERVER] )
{
port = Cvar_Get( "ipx_hostport", "0", CVAR_INIT, "network server port" )->integer;
if( !port ) port = Cvar_Get( "port", va( "%i", PORT_SERVER ), CVAR_INIT, "network default port" )->integer;
ipx_sockets[NS_SERVER] = NET_IPXSocket( port );
}
// dedicated servers don't need client ports
if( host.type == HOST_DEDICATED ) return;
if( !ipx_sockets[NS_CLIENT] )
{
port = Cvar_Get( "ipx_clientport", "0", CVAR_INIT, "network client port" )->integer;
if( !port )
{
port = Cvar_Get( "clientport", va( "%i", PORT_CLIENT ), CVAR_INIT, "network client port" )->integer;
if( !port ) port = PORT_ANY;
}
ipx_sockets[NS_CLIENT] = NET_IPXSocket( port );
if( !ipx_sockets[NS_CLIENT] ) ipx_sockets[NS_CLIENT] = NET_IPXSocket( PORT_ANY );
}
}
/*
====================
NET_Config
A single player game will only use the loopback code
====================
*/
void NET_Config( qboolean multiplayer )
{
int i;
static qboolean old_config;
if( old_config == multiplayer )
return;
old_config = multiplayer;
if( !multiplayer )
{
// shut down any existing sockets
for( i = 0; i < 2; i++ )
{
if( ip_sockets[i] )
{
pCloseSocket( ip_sockets[i] );
ip_sockets[i] = 0;
}
if( ipx_sockets[i] )
{
pCloseSocket( ipx_sockets[i] );
ipx_sockets[i] = 0;
}
}
}
else
{
// open sockets
if( !Sys_CheckParm( "-noip" )) NET_OpenIP();
if( !Sys_CheckParm( "-noipx" )) NET_OpenIPX();
}
NET_ClearLoopback ();
}
// sleeps msec or until net socket is ready
void NET_Sleep( int msec )
{
struct timeval timeout;
fd_set fdset;
int i = 0;
if( host.type == HOST_NORMAL )
return; // we're not a server, just run full speed
FD_ZERO( &fdset );
if( ip_sockets[NS_SERVER] )
{
FD_SET( ip_sockets[NS_SERVER], &fdset ); // network socket
i = ip_sockets[NS_SERVER];
}
if( ipx_sockets[NS_SERVER] )
{
FD_SET( ipx_sockets[NS_SERVER], &fdset ); // network socket
if( ipx_sockets[NS_SERVER] > i )
i = ipx_sockets[NS_SERVER];
}
timeout.tv_sec = msec / 1000;
timeout.tv_usec = (msec % 1000) * 1000;
pSelect( i+1, &fdset, NULL, NULL, &timeout );
}
/*
=================
NET_ShowIP_f
=================
*/
void NET_ShowIP_f( void )
{
string s;
int i;
struct hostent *h;
struct in_addr in;
pGetHostName( s, sizeof( s ));
if( !( h = pGetHostByName( s )))
{
Msg( "Can't get host\n" );
return;
}
Msg( "HostName: %s\n", h->h_name );
for( i = 0; h->h_addr_list[i]; i++ )
{
in.s_addr = *(int *)h->h_addr_list[i];
Msg( "IP: %s\n", pInet_Ntoa( in ));
}
}
/*
====================
NET_Init
====================
*/
void NET_Init( void )
{
int r;
if( !NET_OpenWinSock()) // loading wsock32.dll
{
MsgDev( D_WARN, "NET_Init: wsock32.dll can't loaded\n" );
return;
}
r = pWSAStartup( MAKEWORD( 1, 1 ), &winsockdata );
if( r )
{
MsgDev( D_WARN, "NET_Init: winsock initialization failed: %d\n", r );
return;
}
net_showpackets = Cvar_Get( "net_showpackets", "0", 0, "show network packets" );
Cmd_AddCommand( "net_showip", NET_ShowIP_f, "show hostname and ip's" );
Cmd_AddCommand( "net_restart", NET_Restart_f, "restart the network subsystem" );
winsockInitialized = true;
MsgDev( D_NOTE, "NET_Init()\n" );
}
/*
====================
NET_Shutdown
====================
*/
void NET_Shutdown( void )
{
if( !winsockInitialized )
return;
Cmd_RemoveCommand( "net_showip" );
Cmd_RemoveCommand( "net_restart" );
NET_Config( false );
pWSACleanup();
NET_FreeWinSock();
winsockInitialized = false;
}
/*
=================
NET_Restart_f
=================
*/
void NET_Restart_f( void )
{
NET_Shutdown();
NET_Init();
}