Browse Source

engine: second iteration of IPv6 support

Made code smaller
Fixed problem where v6 and v4 socket can't use same port
Added support for v4-to-v6 mapped addresses, although it's kept unused
Probably final version
pull/2/head
Alibek Omarov 3 years ago
parent
commit
bd1bfea695
  1. 392
      engine/common/net_ws.c
  2. 87
      engine/platform/posix/net.h
  3. 5
      engine/platform/stub/net_stub.h
  4. 23
      engine/platform/win32/net.h

392
engine/common/net_ws.c

@ -19,90 +19,15 @@ GNU General Public License for more details.
#include "xash3d_mathlib.h" #include "xash3d_mathlib.h"
#include "ipv6text.h" #include "ipv6text.h"
#if XASH_WIN32 #if XASH_WIN32
// Winsock #include "platform/win32/net.h"
#include <WS2tcpip.h> #elif defined XASH_NO_NETWORK
typedef int WSAsize_t;
#define HAVE_GETADDRINFO
#elif !defined XASH_NO_NETWORK
// BSD sockets
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#define WSAGetLastError() errno
#define WSAEINTR EINTR
#define WSAEBADF EBADF
#define WSAEACCES EACCES
#define WSAEFAULT EFAULT
#define WSAEINVAL EINVAL
#define WSAEMFILE EMFILE
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEINPROGRESS EINPROGRESS
#define WSAEALREADY EALREADY
#define WSAENOTSOCK ENOTSOCK
#define WSAEDESTADDRREQ EDESTADDRREQ
#define WSAEMSGSIZE EMSGSIZE
#define WSAEPROTOTYPE EPROTOTYPE
#define WSAENOPROTOOPT ENOPROTOOPT
#define WSAEPROTONOSUPPORT EPROTONOSUPPORT
#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT
#define WSAEOPNOTSUPP EOPNOTSUPP
#define WSAEPFNOSUPPORT EPFNOSUPPORT
#define WSAEAFNOSUPPORT EAFNOSUPPORT
#define WSAEADDRINUSE EADDRINUSE
#define WSAEADDRNOTAVAIL EADDRNOTAVAIL
#define WSAENETDOWN ENETDOWN
#define WSAENETUNREACH ENETUNREACH
#define WSAENETRESET ENETRESET
#define WSAECONNABORTED ECONNABORTED
#define WSAECONNRESET ECONNRESET
#define WSAENOBUFS ENOBUFS
#define WSAEISCONN EISCONN
#define WSAENOTCONN ENOTCONN
#define WSAESHUTDOWN ESHUTDOWN
#define WSAETOOMANYREFS ETOOMANYREFS
#define WSAETIMEDOUT ETIMEDOUT
#define WSAECONNREFUSED ECONNREFUSED
#define WSAELOOP ELOOP
#define WSAENAMETOOLONG ENAMETOOLONG
#define WSAEHOSTDOWN EHOSTDOWN
#ifndef XASH_DOS4GW
#define HAVE_GETADDRINFO
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#if XASH_EMSCRIPTEN
/* All socket operations are non-blocking already */
static int ioctl_stub( int d, unsigned long r, ... )
{
return 0;
}
#define ioctlsocket ioctl_stub
#else // XASH_EMSCRIPTEN
#define ioctlsocket ioctl
#endif // XASH_EMSCRIPTEN
#define closesocket close
#endif
#define SOCKET int
typedef int WSAsize_t;
#else
#include "platform/stub/net_stub.h" #include "platform/stub/net_stub.h"
#else
#include "platform/posix/net.h"
#endif #endif
#define NET_USE_FRAGMENTS #define NET_USE_FRAGMENTS
static const uint8_t k_ipv6Bytes_LinkLocalAllNodes[16] = { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; // ff02:1
#define PORT_ANY -1 #define PORT_ANY -1
#define MAX_LOOPBACK 4 #define MAX_LOOPBACK 4
#define MASK_LOOPBACK (MAX_LOOPBACK - 1) #define MASK_LOOPBACK (MAX_LOOPBACK - 1)
@ -112,6 +37,10 @@ static const uint8_t k_ipv6Bytes_LinkLocalAllNodes[16] = { 0xff, 0x02, 0x00, 0x0
#define SPLITPACKET_MAX_SIZE 64000 #define SPLITPACKET_MAX_SIZE 64000
#define NET_MAX_FRAGMENTS ( NET_MAX_FRAGMENT / (SPLITPACKET_MIN_SIZE - sizeof( SPLITPACKET )) ) #define NET_MAX_FRAGMENTS ( NET_MAX_FRAGMENT / (SPLITPACKET_MIN_SIZE - sizeof( SPLITPACKET )) )
// ff02:1
static const uint8_t k_ipv6Bytes_LinkLocalAllNodes[16] =
{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
typedef struct typedef struct
{ {
byte data[NET_MAX_MESSAGE]; byte data[NET_MAX_MESSAGE];
@ -277,16 +206,18 @@ _inline qboolean NET_IsSocketValid( int socket )
#endif #endif
} }
_inline void NET_NetadrToIP6Bytes( uint8_t ip6[16], const netadr_t *adr ) _inline void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr )
{ {
memcpy( ip6, adr->ip6_0, 2 ); memcpy( ip6, adr->ipx, 10 );
memcpy( ip6 + 2, adr->ip6_1, 14 ); memcpy( ip6 + 10, adr->ip6_10, 2 );
memcpy( ip6 + 12, adr->ip, 4 );
} }
_inline void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t ip6[16] ) _inline void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t *ip6 )
{ {
memcpy( adr->ip6_0, ip6, 2 ); memcpy( adr->ipx, ip6, 10 );
memcpy( adr->ip6_1, ip6 + 2, 14 ); memcpy( adr->ip6_10, ip6 + 10, 2 );
memcpy( adr->ip, ip6 + 12, 4 );
} }
/* /*
@ -304,7 +235,7 @@ static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr_storage *s )
((struct sockaddr_in *)s)->sin_port = a->port; ((struct sockaddr_in *)s)->sin_port = a->port;
((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST; ((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST;
} }
else if( a->type == NA_IP ) else if( a->type == NA_IP || a->type == (0xffff0000 | NA_IP6) )
{ {
((struct sockaddr_in *)s)->sin_family = AF_INET; ((struct sockaddr_in *)s)->sin_family = AF_INET;
((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip; ((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip;
@ -339,8 +270,13 @@ static void NET_SockadrToNetadr( const struct sockaddr_storage *s, netadr_t *a )
} }
else if( s->ss_family == AF_INET6 ) else if( s->ss_family == AF_INET6 )
{ {
a->type6 = NA_IP6;
NET_IP6BytesToNetadr( a, ((struct sockaddr_in6 *)s)->sin6_addr.s6_addr ); NET_IP6BytesToNetadr( a, ((struct sockaddr_in6 *)s)->sin6_addr.s6_addr );
if( IN6_IS_ADDR_V4MAPPED( &((struct sockaddr_in6 *)s)->sin6_addr ))
a->type = NA_IP;
else
a->type6 = NA_IP6;
a->port = ((struct sockaddr_in6 *)s)->sin6_port; a->port = ((struct sockaddr_in6 *)s)->sin6_port;
} }
} }
@ -625,14 +561,13 @@ const char *NET_AdrToString( const netadr_t a )
return "loopback"; return "loopback";
if( a.type6 == NA_IP6 ) if( a.type6 == NA_IP6 )
{ {
// TODO: remove that!!! char s[64];
uint8_t ip6[16]; uint8_t ip6[16];
char *s = va( "" );
NET_NetadrToIP6Bytes( ip6, &a ); NET_NetadrToIP6Bytes( ip6, &a );
IPv6AddrToString( s, ip6, ntohs( a.port ), 0 ); IPv6AddrToString( s, ip6, ntohs( a.port ), 0 );
return s;
return va( "%s", s );
} }
return va( "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs( a.port )); return va( "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs( a.port ));
@ -649,14 +584,13 @@ const char *NET_BaseAdrToString( const netadr_t a )
return "loopback"; return "loopback";
if( a.type6 == NA_IP6 ) if( a.type6 == NA_IP6 )
{ {
// TODO: remove that!!! char s[64];
uint8_t ip6[16]; uint8_t ip6[16];
char *s = va( "" );
NET_NetadrToIP6Bytes( ip6, &a ); NET_NetadrToIP6Bytes( ip6, &a );
IPv6IPToString( s, ip6 ); IPv6IPToString( s, ip6 );
return s;
return va( "%s", s );
} }
return va( "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3] ); return va( "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3] );
} }
@ -684,7 +618,9 @@ qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b )
if( a.type6 == NA_IP6 ) if( a.type6 == NA_IP6 )
{ {
if( !memcmp( a.ip6_0, b.ip6_0, 2 ) && !memcmp( a.ip6_1, b.ip6_1, 14 )) if( !memcmp( a.ip, b.ip, 4 ) &&
!memcmp( a.ip6_10, b.ip6_10, 2 ) &&
!memcmp( a.ipx, b.ipx, 10 ))
return true; return true;
} }
@ -712,7 +648,9 @@ qboolean NET_CompareClassBAdr( netadr_t a, netadr_t b )
return true; return true;
} }
// TODO: ipv6 // NOTE: we don't check for IPv6 here
// this check is very dumb and only used for LAN restriction
// Actual check is in IsReservedAdr
return false; return false;
} }
@ -729,6 +667,7 @@ qboolean NET_IsReservedAdr( netadr_t a )
if( a.type == NA_LOOPBACK ) if( a.type == NA_LOOPBACK )
return true; return true;
// Following checks was imported from GameNetworkingSockets library
if( a.type == NA_IP ) if( a.type == NA_IP )
{ {
if(( a.ip[0] == 10 ) || // 10.x.x.x is reserved if(( a.ip[0] == 10 ) || // 10.x.x.x is reserved
@ -745,15 +684,15 @@ qboolean NET_IsReservedAdr( netadr_t a )
{ {
// Private addresses, fc00::/7 // Private addresses, fc00::/7
// Range is fc00:: to fdff:ffff:etc // Range is fc00:: to fdff:ffff:etc
if ( a.ip6_0[0] >= 0xFC && a.ip6_0[1] <= 0xFD ) if ( a.ipx[0] >= 0xFC && a.ipx[1] <= 0xFD )
{ {
return true; return true;
} }
// Link-local fe80::/10 // Link-local fe80::/10
// Range is fe80:: to febf:: // Range is fe80:: to febf::
if ( a.ip6_0[0] == 0xFE if ( a.ipx[0] == 0xFE
&& ( a.ip6_0[1] >= 0x80 && a.ip6_0[1] <= 0xBF ) ) && ( a.ipx[1] >= 0x80 && a.ipx[1] <= 0xBF ) )
{ {
return true; return true;
} }
@ -786,7 +725,10 @@ qboolean NET_CompareAdr( const netadr_t a, const netadr_t b )
if( a.type6 == NA_IP6 ) if( a.type6 == NA_IP6 )
{ {
if( !memcmp( a.ip6_0, b.ip6_0, 2 ) && !memcmp( a.ip6_1, b.ip6_1, 14 ) && a.port == b.port ) if( !memcmp( a.ip, b.ip, 4 ) &&
!memcmp( a.ip6_10, b.ip6_10, 2 ) &&
!memcmp( a.ipx, b.ipx, 10 ) &&
a.port == b.port )
return true; return true;
} }
@ -1231,11 +1173,10 @@ queue normal and lagged packets
static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length ) static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length )
{ {
byte buf[NET_MAX_FRAGMENT]; byte buf[NET_MAX_FRAGMENT];
int ret; int ret, protocol;
int net_socket; int net_socket;
WSAsize_t addr_len; WSAsize_t addr_len;
struct sockaddr_storage addr; struct sockaddr_storage addr;
int protocol;
*length = 0; *length = 0;
@ -1247,8 +1188,9 @@ static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size
case 1: net_socket = net.ip6_sockets[sock]; break; case 1: net_socket = net.ip6_sockets[sock]; break;
} }
if( NET_IsSocketValid( net_socket ) ) if( !NET_IsSocketValid( net_socket ))
{ continue;
addr_len = sizeof( addr ); addr_len = sizeof( addr );
ret = recvfrom( net_socket, buf, sizeof( buf ), 0, (struct sockaddr *)&addr, &addr_len ); ret = recvfrom( net_socket, buf, sizeof( buf ), 0, (struct sockaddr *)&addr, &addr_len );
@ -1297,7 +1239,6 @@ static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size
} }
} }
} }
}
return NET_LagPacket( false, sock, from, length, data ); return NET_LagPacket( false, sock, from, length, data );
} }
@ -1535,18 +1476,24 @@ qboolean NET_BufferToBufferDecompress( byte *dest, uint *destLen, byte *source,
NET_IPSocket NET_IPSocket
==================== ====================
*/ */
static int NET_IPSocket( const char *net_interface, int port, qboolean multicast, qboolean usev6 ) static int NET_IPSocket( const char *net_iface, int port, int family )
{ {
struct sockaddr_storage addr; struct sockaddr_storage addr;
int err, net_socket; int err, net_socket;
uint optval = 1; uint optval = 1;
dword _true = 1; dword _true = 1;
int pfamily;
if( family == AF_INET6 )
pfamily = PF_INET6;
else if( family == AF_INET )
pfamily == PF_INET;
if( NET_IsSocketError(( net_socket = socket( usev6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP )) ) ) if( NET_IsSocketError(( net_socket = socket( pfamily, SOCK_DGRAM, IPPROTO_UDP )) ) )
{ {
err = WSAGetLastError(); err = WSAGetLastError();
if( err != WSAEAFNOSUPPORT ) if( err != WSAEAFNOSUPPORT )
Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d socket: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); Con_DPrintf( S_WARN "NET_UDPSocket: port: %d socket: %s\n", port, NET_ErrorString( ));
return INVALID_SOCKET; return INVALID_SOCKET;
} }
@ -1554,7 +1501,7 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast
{ {
struct timeval timeout; struct timeval timeout;
Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d ioctl FIONBIO: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); Con_DPrintf( S_WARN "NET_UDPSocket: port: %d ioctl FIONBIO: %s\n", port, NET_ErrorString( ));
// try timeout instead of NBIO // try timeout instead of NBIO
timeout.tv_sec = timeout.tv_usec = 0; timeout.tv_sec = timeout.tv_usec = 0;
setsockopt( net_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); setsockopt( net_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
@ -1563,78 +1510,83 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast
// make it broadcast capable // make it broadcast capable
if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true ) ) ) ) if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true ) ) ) )
{ {
Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d setsockopt SO_BROADCAST: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_BROADCAST: %s\n", port, NET_ErrorString( ));
} }
if( Sys_CheckParm( "-reuse" ) || multicast )
{
if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( optval )) ) ) if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( optval )) ) )
{ {
Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d setsockopt SO_REUSEADDR: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_REUSEADDR: %s\n", port, NET_ErrorString( ));
closesocket( net_socket ); closesocket( net_socket );
return INVALID_SOCKET; return INVALID_SOCKET;
} }
addr.ss_family = family;
if( family == AF_INET6 )
{
if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_true, sizeof( _true ))))
{
Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt IPV6_V6ONLY: %s\n", port, NET_ErrorString( ));
closesocket( net_socket );
return INVALID_SOCKET;
} }
if( Sys_CheckParm( "-tos" ) && !usev6 ) if( Sys_CheckParm( "-loopback" ))
{ {
optval = 16; if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&_true, sizeof( _true ))))
Con_Printf( "Enabling LOWDELAY TOS option\n" ); Con_DPrintf( S_WARN "NET_UDPSocket: port %d setsockopt IPV6_MULTICAST_LOOP: %s\n", port, NET_ErrorString( ));
}
if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_TOS, (const char *)&optval, sizeof( optval )) ) ) if( COM_CheckStringEmpty( net_iface ) && Q_stricmp( net_iface, "localhost" ))
NET_StringToSockaddr( net_iface, &addr, false, AF_INET6 );
else memcpy(((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, &in6addr_any, sizeof( struct in6_addr ));
if( port == PORT_ANY ) ((struct sockaddr_in6 *)&addr)->sin6_port = 0;
else ((struct sockaddr_in6 *)&addr)->sin6_port = htons((short)port);
if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr ))))
{ {
err = WSAGetLastError(); Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind6: %s\n", port, NET_ErrorString( ));
if( err != WSAENOPROTOOPT )
Con_Printf( S_WARN "NET_UDPSocket%s: port: %d setsockopt IP_TOS: %s\n", usev6 ? "6" : "", port, NET_ErrorString( ));
closesocket( net_socket ); closesocket( net_socket );
return INVALID_SOCKET; return INVALID_SOCKET;
} }
} }
else if( family == AF_INET )
if( usev6 ) {
if( Sys_CheckParm( "-tos" ))
{ {
if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IPV6, IPV6_V6ONLY, &_true, sizeof( _true )))) optval = 0x10; // IPTOS_LOWDELAY
Con_Printf( "Enabling LOWDELAY TOS option\n" );
if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_TOS, (const char *)&optval, sizeof( optval ))))
{ {
err = WSAGetLastError(); err = WSAGetLastError();
if( err != WSAENOPROTOOPT ) if( err != WSAENOPROTOOPT )
Con_Printf( S_WARN "NET_UDPSocket%s: port: %d setsockopt IPV6_V6ONLY: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); Con_Printf( S_WARN "NET_UDPSocket: port: %d setsockopt IP_TOS: %s\n", port, NET_ErrorString( ));
closesocket( net_socket ); closesocket( net_socket );
return INVALID_SOCKET; return INVALID_SOCKET;
} }
if( !COM_CheckStringEmpty( net_interface ) || !Q_stricmp( net_interface, "localhost" ))
memcpy(((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, &in6addr_any, sizeof( struct in6_addr ));
else NET_StringToSockaddr( net_interface, &addr, false, AF_INET6 );
if( port == PORT_ANY ) ((struct sockaddr_in6 *)&addr)->sin6_port = 0;
else ((struct sockaddr_in6 *)&addr)->sin6_port = htons((short)port);
((struct sockaddr_in6 *)&addr)->sin6_family = AF_INET6;
} }
else
if( Sys_CheckParm( "-loopback" ))
{ {
if( !COM_CheckStringEmpty( net_interface ) || !Q_stricmp( net_interface, "localhost" )) if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&_true, sizeof( _true ))))
((struct sockaddr_in *)&addr)->sin_addr.s_addr = INADDR_ANY; Con_DPrintf( S_WARN "NET_UDPSocket: port %d setsockopt IP_MULTICAST_LOOP: %s\n", port, NET_ErrorString( ));
else NET_StringToSockaddr( net_interface, &addr, false, AF_INET ); }
if( COM_CheckStringEmpty( net_iface ) && Q_stricmp( net_iface, "localhost" ))
NET_StringToSockaddr( net_iface, &addr, false, AF_INET );
else ((struct sockaddr_in *)&addr)->sin_addr.s_addr = INADDR_ANY;
if( port == PORT_ANY ) ((struct sockaddr_in *)&addr)->sin_port = 0; if( port == PORT_ANY ) ((struct sockaddr_in *)&addr)->sin_port = 0;
else ((struct sockaddr_in *)&addr)->sin_port = htons((short)port); else ((struct sockaddr_in *)&addr)->sin_port = htons((short)port);
((struct sockaddr_in *)&addr)->sin_family = AF_INET;
}
if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr )))) if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr ))))
{ {
Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d bind: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind: %s\n", port, NET_ErrorString( ));
closesocket( net_socket ); closesocket( net_socket );
return INVALID_SOCKET; return INVALID_SOCKET;
} }
if( Sys_CheckParm( "-loopback" ))
{
optval = 1;
if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_MULTICAST_LOOP, (const char *)&optval, sizeof( optval )) ) )
Con_DPrintf( S_WARN "NET_UDPSocket%s: port %d setsockopt IP_MULTICAST_LOOP: %s\n", usev6 ? "6" : "", port, NET_ErrorString( ));
} }
return net_socket; return net_socket;
@ -1645,69 +1597,33 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast
NET_OpenIP NET_OpenIP
==================== ====================
*/ */
static void NET_OpenIP( void ) static void NET_OpenIP( int *sockets, const char *net_iface, int hostport, int clientport, int family )
{ {
int port; int port;
if( !NET_IsSocketValid( net.ip_sockets[NS_SERVER] ) ) if( !NET_IsSocketValid( sockets[NS_SERVER] ))
{ {
port = net_iphostport->value; port = hostport;
if( !port ) port = net_hostport->value; if( !port ) port = net_hostport->value;
if( !port ) port = PORT_SERVER; // forcing to default if( !port ) port = PORT_SERVER; // forcing to default
net.ip_sockets[NS_SERVER] = NET_IPSocket( net_ipname->string, port, false, false ); sockets[NS_SERVER] = NET_IPSocket( net_iface, port, family );
if( !NET_IsSocketValid( net.ip_sockets[NS_SERVER] ) && Host_IsDedicated() ) if( !NET_IsSocketValid( sockets[NS_SERVER] ) && Host_IsDedicated() )
Host_Error( "Couldn't allocate dedicated server IP port %d.\n", port ); Host_Error( "Couldn't allocate dedicated server IP port %d.\n", port );
} }
// dedicated servers don't need client ports // dedicated servers don't need client ports
if( Host_IsDedicated() ) return; if( Host_IsDedicated() ) return;
if( !NET_IsSocketValid( net.ip_sockets[NS_CLIENT] ) ) if( !NET_IsSocketValid( sockets[NS_CLIENT] ))
{ {
port = net_ipclientport->value; port = clientport;
if( !port ) port = net_clientport->value; if( !port ) port = net_clientport->value;
if( !port ) port = PORT_ANY; // forcing to default if( !port ) port = PORT_ANY; // forcing to default
net.ip_sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, port, false, false ); sockets[NS_CLIENT] = NET_IPSocket( net_iface, port, family );
if( !NET_IsSocketValid( net.ip_sockets[NS_CLIENT] ) ) if( !NET_IsSocketValid( sockets[NS_CLIENT] ))
net.ip_sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, PORT_ANY, false, false ); sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, PORT_ANY, family );
}
}
/*
====================
NET_OpenIP6
====================
*/
static void NET_OpenIP6( void )
{
int port;
if( !NET_IsSocketValid( net.ip6_sockets[NS_SERVER] ) )
{
port = net_ip6hostport->value;
if( !port ) port = net_hostport->value + 10000;
if( !port ) port = PORT_SERVER + 10000; // forcing to default
net.ip6_sockets[NS_SERVER] = NET_IPSocket( net_ip6name->string, port, false, true );
if( !NET_IsSocketValid( net.ip6_sockets[NS_SERVER] ) && Host_IsDedicated() )
Host_Error( "Couldn't allocate dedicated server IPv6 port %d.\n", port );
}
// dedicated servers don't need client ports
if( Host_IsDedicated() ) return;
if( !NET_IsSocketValid( net.ip6_sockets[NS_CLIENT] ) )
{
port = net_ip6clientport->value;
if( !port ) port = net_clientport->value;
if( !port ) port = PORT_ANY; // forcing to default
net.ip6_sockets[NS_CLIENT] = NET_IPSocket( net_ip6name->string, port, false, true );
if( !NET_IsSocketValid( net.ip6_sockets[NS_CLIENT] ) )
net.ip6_sockets[NS_CLIENT] = NET_IPSocket( net_ip6name->string, PORT_ANY, false, true );
} }
} }
@ -1720,96 +1636,65 @@ Returns the servers' ip address as a string.
*/ */
void NET_GetLocalAddress( void ) void NET_GetLocalAddress( void )
{ {
char hostname[512];
char buff[512]; char buff[512];
struct sockaddr_storage address; struct sockaddr_storage address;
WSAsize_t namelen; WSAsize_t namelen;
memset( &net_local, 0, sizeof( netadr_t )); memset( &net_local, 0, sizeof( netadr_t ));
memset( &net6_local, 0, sizeof( netadr_t )); memset( &net6_local, 0, sizeof( netadr_t ));
buff[0] = '\0';
if( !net.allow_ip && !net.allow_ip6 )
{
Con_Printf( "TCP/IP Disabled.\n" );
return;
}
gethostname( hostname, sizeof( hostname ));
hostname[sizeof(hostname) - 1] = 0;
if( net.allow_ip ) if( net.allow_ip )
{ {
// If we have changed the ip var from the command line, use that instead. // If we have changed the ip var from the command line, use that instead.
if( Q_strcmp( net_ipname->string, "localhost" )) if( Q_stricmp( net_ipname->string, "localhost" ))
{
Q_strncpy( buff, net_ipname->string, sizeof( buff )); Q_strncpy( buff, net_ipname->string, sizeof( buff ));
} else Q_strncpy( buff, hostname, sizeof( buff ));
else
{
gethostname( buff, 512 );
// ensure that it doesn't overrun the buffer
buff[511] = 0;
}
if( NET_StringToAdrEx( buff, &net_local, AF_INET )) if( NET_StringToAdrEx( buff, &net_local, AF_INET ))
{ {
namelen = sizeof( address ); namelen = sizeof( struct sockaddr_in );
if( NET_IsSocketError( getsockname( net.ip_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ) ) ) if( !NET_IsSocketError( getsockname( net.ip_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen )))
{
// this may happens if multiple clients running on single machine
Con_DPrintf( S_ERROR "Could not get TCP/IP address. Reason: %s\n", NET_ErrorString( ));
// net.allow_ip = false;
}
else
{ {
net_local.port = ((struct sockaddr_in *)&address)->sin_port; net_local.port = ((struct sockaddr_in *)&address)->sin_port;
Con_Printf( "Server IP address %s\n", NET_AdrToString( net_local )); Con_Printf( "Server IPv4 address %s\n", NET_AdrToString( net_local ));
Cvar_FullSet( "net_address", va( "%s", NET_AdrToString( net_local )), FCVAR_READ_ONLY ); Cvar_FullSet( "net_address", va( "%s", NET_AdrToString( net_local )), FCVAR_READ_ONLY );
} }
else Con_DPrintf( S_ERROR "Could not get TCP/IPv4 address. Reason: %s\n", NET_ErrorString( ));
} }
else else Con_DPrintf( S_ERROR "Could not get TCP/IPv4 address, Invalid hostname: '%s'\n", buff );
{
Con_DPrintf( S_ERROR "Could not get TCP/IP address, Invalid hostname: '%s'\n", buff );
}
} }
buff[0] = 0;
if( net.allow_ip6 ) if( net.allow_ip6 )
{ {
// If we have changed the ip var from the command line, use that instead. // If we have changed the ip var from the command line, use that instead.
if( Q_strcmp( net_ip6name->string, "localhost" )) if( Q_stricmp( net_ip6name->string, "localhost" ))
{
Q_strncpy( buff, net_ip6name->string, sizeof( buff )); Q_strncpy( buff, net_ip6name->string, sizeof( buff ));
} else Q_strncpy( buff, hostname, sizeof( buff ));
else
{
gethostname( buff, 512 );
// ensure that it doesn't overrun the buffer
buff[511] = 0;
}
if( NET_StringToAdrEx( buff, &net6_local, AF_INET6 )) if( NET_StringToAdrEx( buff, &net6_local, AF_INET6 ))
{ {
namelen = sizeof( address ); namelen = sizeof( struct sockaddr_in6 );
if( NET_IsSocketError( getsockname( net.ip6_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ) ) ) if( !NET_IsSocketError( getsockname( net.ip6_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen )))
{
// this may happens if multiple clients running on single machine
Con_DPrintf( S_ERROR "Could not get IPv6 address. Reason: %s\n", NET_ErrorString( ));
// net.allow_ip6 = false;
}
else
{ {
net6_local.port = ((struct sockaddr_in6 *)&address)->sin6_port; net6_local.port = ((struct sockaddr_in6 *)&address)->sin6_port;
Con_Printf( "Server IPv6 address %s\n", NET_AdrToString( net6_local )); Con_Printf( "Server IPv6 address %s\n", NET_AdrToString( net6_local ));
Cvar_FullSet( "net6_address", va( "%s", NET_AdrToString( net6_local )), FCVAR_READ_ONLY ); Cvar_FullSet( "net6_address", va( "%s", NET_AdrToString( net6_local )), FCVAR_READ_ONLY );
} }
else Con_DPrintf( S_ERROR "Could not get TCP/IPv6 address. Reason: %s\n", NET_ErrorString( ));
} }
else else Con_DPrintf( S_ERROR "Could not get TCP/IPv6 address, Invalid hostname: '%s'\n", buff );
{
Con_DPrintf( S_ERROR "Could not get TCP/IP address, Invalid hostname: '%s'\n", buff );
}
}
if( !net.allow_ip && !net.allow_ip6 )
{
Con_Printf( "TCP/IP Disabled.\n" );
} }
} }
@ -1836,8 +1721,11 @@ void NET_Config( qboolean multiplayer )
if( multiplayer ) if( multiplayer )
{ {
// open sockets // open sockets
if( net.allow_ip ) NET_OpenIP(); if( net.allow_ip )
if( net.allow_ip6 ) NET_OpenIP6(); NET_OpenIP( net.ip_sockets, net_ipname->string, net_iphostport->value, net_ipclientport->value, AF_INET );
if( net.allow_ip6 )
NET_OpenIP( net.ip6_sockets, net_ip6name->string, net_ip6hostport->value, net_ip6clientport->value, AF_INET6 );
// get our local address, if possible // get our local address, if possible
if( bFirst ) if( bFirst )
@ -1993,6 +1881,10 @@ void NET_Init( void )
if( Sys_GetParmFromCmdLine( "-port", cmd ) && Q_isdigit( cmd )) if( Sys_GetParmFromCmdLine( "-port", cmd ) && Q_isdigit( cmd ))
Cvar_FullSet( "hostport", cmd, FCVAR_READ_ONLY ); Cvar_FullSet( "hostport", cmd, FCVAR_READ_ONLY );
// specify custom IPv6 host port
if( Sys_GetParmFromCmdLine( "-port6", cmd ) && Q_isdigit( cmd ))
Cvar_FullSet( "ip6_hostport", cmd, FCVAR_READ_ONLY );
// specify custom ip // specify custom ip
if( Sys_GetParmFromCmdLine( "-ip", cmd )) if( Sys_GetParmFromCmdLine( "-ip", cmd ))
Cvar_FullSet( "ip", cmd, FCVAR_READ_ONLY ); Cvar_FullSet( "ip", cmd, FCVAR_READ_ONLY );

87
engine/platform/posix/net.h

@ -0,0 +1,87 @@
/*
net.h - WinSock to BSD sockets wrap
Copyright (C) 2022 a1batross
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.
*/
#ifndef NET_H
#define NET_H
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#define WSAGetLastError() errno
#define WSAEINTR EINTR
#define WSAEBADF EBADF
#define WSAEACCES EACCES
#define WSAEFAULT EFAULT
#define WSAEINVAL EINVAL
#define WSAEMFILE EMFILE
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEINPROGRESS EINPROGRESS
#define WSAEALREADY EALREADY
#define WSAENOTSOCK ENOTSOCK
#define WSAEDESTADDRREQ EDESTADDRREQ
#define WSAEMSGSIZE EMSGSIZE
#define WSAEPROTOTYPE EPROTOTYPE
#define WSAENOPROTOOPT ENOPROTOOPT
#define WSAEPROTONOSUPPORT EPROTONOSUPPORT
#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT
#define WSAEOPNOTSUPP EOPNOTSUPP
#define WSAEPFNOSUPPORT EPFNOSUPPORT
#define WSAEAFNOSUPPORT EAFNOSUPPORT
#define WSAEADDRINUSE EADDRINUSE
#define WSAEADDRNOTAVAIL EADDRNOTAVAIL
#define WSAENETDOWN ENETDOWN
#define WSAENETUNREACH ENETUNREACH
#define WSAENETRESET ENETRESET
#define WSAECONNABORTED ECONNABORTED
#define WSAECONNRESET ECONNRESET
#define WSAENOBUFS ENOBUFS
#define WSAEISCONN EISCONN
#define WSAENOTCONN ENOTCONN
#define WSAESHUTDOWN ESHUTDOWN
#define WSAETOOMANYREFS ETOOMANYREFS
#define WSAETIMEDOUT ETIMEDOUT
#define WSAECONNREFUSED ECONNREFUSED
#define WSAELOOP ELOOP
#define WSAENAMETOOLONG ENAMETOOLONG
#define WSAEHOSTDOWN EHOSTDOWN
#ifndef XASH_DOS4GW
#define HAVE_GETADDRINFO
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#if XASH_EMSCRIPTEN
/* All socket operations are non-blocking already */
static int ioctl_stub( int d, unsigned long r, ... )
{
return 0;
}
#define ioctlsocket ioctl_stub
#else // XASH_EMSCRIPTEN
#define ioctlsocket ioctl
#endif // XASH_EMSCRIPTEN
#define closesocket close
#endif
#define SOCKET int
typedef int WSAsize_t;
#endif // NET_H

5
engine/platform/stub/net_stub.h

@ -12,7 +12,8 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
*/ */
#ifndef NET_STUB_H
#define NET_STUB_H
#define INVALID_SOCKET (-1) #define INVALID_SOCKET (-1)
#define SOCKET_ERROR (-1) #define SOCKET_ERROR (-1)
@ -92,3 +93,5 @@ struct timeval {long tv_sec;long tv_usec;};
#define WSAELOOP 34 //ELOOP #define WSAELOOP 34 //ELOOP
#define WSAENAMETOOLONG 35 //ENAMETOOLONG #define WSAENAMETOOLONG 35 //ENAMETOOLONG
#define WSAEHOSTDOWN 36 //EHOSTDOWN #define WSAEHOSTDOWN 36 //EHOSTDOWN
#endif // NET_STUB_H

23
engine/platform/win32/net.h

@ -0,0 +1,23 @@
/*
net.h - WinSock to BSD sockets wrap
Copyright (C) 2022 a1batross
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.
*/
#ifndef NET_H
#define NET_H
#include <WS2tcpip.h>
typedef int WSAsize_t;
#define HAVE_GETADDRINFO
#endif // NET_H
Loading…
Cancel
Save