diff --git a/neo/sys/posix/posix_net.cpp b/neo/sys/posix/posix_net.cpp
index 02f4c9bb..34bd0710 100644
--- a/neo/sys/posix/posix_net.cpp
+++ b/neo/sys/posix/posix_net.cpp
@@ -1,30 +1,32 @@
/*
===========================================================================
-Doom 3 GPL Source Code
-Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
+Doom 3 BFG Edition GPL Source Code
+Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
+Copyright (C) 2013 Daniel Gibson (changes for POSIX)
-This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
+This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
-Doom 3 Source Code is free software: you can redistribute it and/or modify
+Doom 3 BFG Edition Source Code 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.
-Doom 3 Source Code is distributed in the hope that it will be useful,
+Doom 3 BFG Edition Source Code 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 Doom 3 Source Code. If not, see .
+along with Doom 3 BFG Edition Source Code. If not, see .
-In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
+In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
+
#include
#include
#include
@@ -40,22 +42,52 @@ If you have questions concerning this license or the applicable additional terms
#include
#include
#include
-#if MACOS_X
+#if defined(MACOS_X) || defined(__FreeBSD__)
#include
#endif
#include "../../idlib/precompiled.h"
-idUDP clientPort, serverPort;
+// DG: TODO: this could be unified with win_net.cpp quite easily, would just need some defines etc..
-idCVar net_ip( "net_ip", "localhost", CVAR_SYSTEM, "local IP address" );
+/*
+================================================================================================
+Contains the NetworkSystem implementation specific to POSIX Systems (Linux, *BSD, OSX, ...)
+================================================================================================
+*/
+
+static bool usingSocks = false;
+
+
+/*
+================================================================================================
+
+ Network CVars
+
+================================================================================================
+*/
+
+idCVar net_socksServer( "net_socksServer", "", CVAR_ARCHIVE, "" );
+idCVar net_socksPort( "net_socksPort", "1080", CVAR_ARCHIVE | CVAR_INTEGER, "" );
+idCVar net_socksUsername( "net_socksUsername", "", CVAR_ARCHIVE, "" );
+idCVar net_socksPassword( "net_socksPassword", "", CVAR_ARCHIVE, "" );
+
+idCVar net_ip( "net_ip", "localhost", CVAR_NOCHEAT, "local IP address" );
+
+static struct sockaddr_in socksRelayAddr;
+
+// static int ip_socket; FIXME: what was this about?
+static int socks_socket = 0;
+static char socksBuf[4096];
typedef struct
{
// RB: 64 bit fixes, changed long to int
+ // FIXME: IPv6?
unsigned int ip;
unsigned int mask;
// RB end
+ char addr[16];
} net_interface;
#define MAX_INTERFACES 32
@@ -63,63 +95,88 @@ int num_interfaces = 0;
net_interface netint[MAX_INTERFACES];
/*
-=============
-NetadrToSockadr
-=============
+================================================================================================
+
+ Free Functions
+
+================================================================================================
*/
-static void NetadrToSockadr( const netadr_t* a, struct sockaddr_in* s )
+
+/*
+========================
+NET_ErrorString
+========================
+*/
+const char* NET_ErrorString()
+{
+ return strerror( errno );
+}
+
+/*
+========================
+Net_NetadrToSockadr
+========================
+*/
+void Net_NetadrToSockadr( const netadr_t* a, sockaddr_in* s )
{
memset( s, 0, sizeof( *s ) );
if( a->type == NA_BROADCAST )
{
s->sin_family = AF_INET;
-
- s->sin_port = htons( ( short )a->port );
- *( int* ) &s->sin_addr = -1;
+ s->sin_addr.s_addr = INADDR_BROADCAST;
}
else if( a->type == NA_IP || a->type == NA_LOOPBACK )
{
s->sin_family = AF_INET;
-
- *( int* ) &s->sin_addr = *( int* ) &a->ip;
- s->sin_port = htons( ( short )a->port );
+ s->sin_addr.s_addr = *( ( in_addr_t* ) &a->ip );
}
+
+ s->sin_port = htons( ( short )a->port );
}
/*
-=============
-SockadrToNetadr
-=============
+========================
+Net_SockadrToNetadr
+========================
*/
-static void SockadrToNetadr( struct sockaddr_in* s, netadr_t* a )
+#define LOOPBACK_NET 0x7F000000
+#define LOOPBACK_PREFIX 0xFF000000 // /8 or 255.0.0.0
+void Net_SockadrToNetadr( sockaddr_in* s, netadr_t* a )
{
- unsigned int ip = *( int* )&s->sin_addr;
- *( int* )&a->ip = ip;
- a->port = ntohs( s->sin_port );
- // we store in network order, that loopback test is host order..
- ip = ntohl( ip );
- if( ip == INADDR_LOOPBACK )
+ in_addr_t ip;
+ if( s->sin_family == AF_INET )
{
- a->type = NA_LOOPBACK;
- }
- else
- {
- a->type = NA_IP;
+ ip = s->sin_addr.s_addr;
+ *( in_addr_t* )&a->ip = ip;
+ a->port = ntohs( s->sin_port );
+ // we store in network order, that loopback test is host order..
+ ip = ntohl( ip );
+ // DG: just comparing ip with INADDR_LOOPBACK is lame,
+ // because all of 127.0.0.0/8 is loopback.
+ // if( ( ip & LOOPBACK_PREFIX ) == LOOPBACK_NET )
+ if( ip == INADDR_LOOPBACK )
+ {
+ a->type = NA_LOOPBACK;
+ }
+ else
+ {
+ a->type = NA_IP;
+ }
}
}
/*
-=============
-ExtractPort
-=============
+========================
+Net_ExtractPort
+========================
*/
-static bool ExtractPort( const char* src, char* buf, int bufsize, int* port )
+static bool Net_ExtractPort( const char* src, char* buf, int bufsize, int* port )
{
char* p;
strncpy( buf, src, bufsize );
p = buf;
- p += Min( bufsize - 1, ( int )strlen( src ) );
+ p += Min( bufsize - 1, idStr::Length( src ) );
*p = '\0';
p = strchr( buf, ':' );
if( !p )
@@ -127,205 +184,527 @@ static bool ExtractPort( const char* src, char* buf, int bufsize, int* port )
return false;
}
*p = '\0';
- *port = strtol( p + 1, NULL, 10 );
- if( ( *port == 0 && errno == EINVAL ) ||
- // RB: 64 bit fixes, changed LONG_ to INT_
- ( ( *port == INT_MIN || *port == INT_MAX ) && errno == ERANGE ) )
+
+ long lport = strtol( p + 1, NULL, 10 );
+ if( lport == 0 || lport == LONG_MIN || lport == LONG_MAX )
{
- // RB end
+ *port = 0;
return false;
}
+ *port = lport;
return true;
}
/*
-=============
-StringToSockaddr
-=============
+========================
+Net_StringToSockaddr
+========================
*/
-static bool StringToSockaddr( const char* s, struct sockaddr_in* sadr, bool doDNSResolve )
+static bool Net_StringToSockaddr( const char* s, sockaddr_in* sadr, bool doDNSResolve )
{
- struct hostent* h;
+ /* NOTE: the doDNSResolve argument is ignored for two reasons:
+ * 1. domains can start with numbers nowadays so the old heuristic to find out if it's
+ * an IP (check if the first char is a digit) isn't reliable
+ * 2. gethostbyname() works fine for IPs and doesn't do a lookup if the passed string
+ * is an IP
+ */
+ struct hostent* h;
char buf[256];
int port;
memset( sadr, 0, sizeof( *sadr ) );
- sadr->sin_family = AF_INET;
+ sadr->sin_family = AF_INET;
sadr->sin_port = 0;
- if( s[0] >= '0' && s[0] <= '9' )
+ // try to remove the port first, otherwise the DNS gets confused into multiple timeouts
+ // failed or not failed, buf is expected to contain the appropriate host to resolve
+ if( Net_ExtractPort( s, buf, sizeof( buf ), &port ) )
{
- if( !inet_aton( s, &sadr->sin_addr ) )
- {
- // check for port
- if( !ExtractPort( s, buf, sizeof( buf ), &port ) )
- {
- return false;
- }
- if( !inet_aton( buf, &sadr->sin_addr ) )
- {
- return false;
- }
- sadr->sin_port = htons( port );
- }
+ sadr->sin_port = htons( port );
}
- else if( doDNSResolve )
- {
- // try to remove the port first, otherwise the DNS gets confused into multiple timeouts
- // failed or not failed, buf is expected to contain the appropriate host to resolve
- if( ExtractPort( s, buf, sizeof( buf ), &port ) )
- {
- sadr->sin_port = htons( port );
- }
- if( !( h = gethostbyname( buf ) ) )
- {
- return false;
- }
- *( int* ) &sadr->sin_addr =
- *( int* ) h->h_addr_list[0];
- }
-
- return true;
-}
-
-/*
-=============
-Sys_StringToAdr
-=============
-*/
-bool Sys_StringToNetAdr( const char* s, netadr_t* a, bool doDNSResolve )
-{
- struct sockaddr_in sadr;
-
- if( !StringToSockaddr( s, &sadr, doDNSResolve ) )
+ // buf contains the host, even if Net_ExtractPort returned false
+ h = gethostbyname( buf );
+ if( h == NULL )
{
return false;
}
+ sadr->sin_addr.s_addr = *( in_addr_t* ) h->h_addr_list[0];
- SockadrToNetadr( &sadr, a );
return true;
}
/*
-=============
-Sys_NetAdrToString
-=============
+========================
+NET_IPSocket
+========================
*/
-const char* Sys_NetAdrToString( const netadr_t a )
+int NET_IPSocket( const char* bind_ip, int port, netadr_t* bound_to )
{
- static char s[64];
+ int newsocket;
+ sockaddr_in address;
- if( a.type == NA_LOOPBACK )
+ if( port != PORT_ANY )
{
- if( a.port )
+ if( bind_ip )
{
- idStr::snPrintf( s, sizeof( s ), "localhost:%i", a.port );
+ idLib::Printf( "Opening IP socket: %s:%i\n", bind_ip, port );
}
else
{
- idStr::snPrintf( s, sizeof( s ), "localhost" );
+ idLib::Printf( "Opening IP socket: localhost:%i\n", port );
}
}
- else if( a.type == NA_IP )
+
+ if( ( newsocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) < 0 )
{
- idStr::snPrintf( s, sizeof( s ), "%i.%i.%i.%i:%i",
- a.ip[0], a.ip[1], a.ip[2], a.ip[3], a.port );
+ idLib::Printf( "WARNING: UDP_OpenSocket: socket: %s\n", NET_ErrorString() );
+ return 0;
}
- return s;
+
+ // make it non-blocking
+ int flags = fcntl( newsocket, F_GETFL, 0 );
+ if( flags < 0 )
+ {
+ idLib::Printf( "WARNING: UDP_OpenSocket: fcntl F_GETFL: %s\n", NET_ErrorString() );
+ close( newsocket );
+ return 0;
+ }
+ flags |= O_NONBLOCK;
+ if( fcntl( newsocket, F_SETFL, flags ) < 0 )
+ {
+ idLib::Printf( "WARNING: UDP_OpenSocket: fcntl F_SETFL with O_NONBLOCK: %s\n", NET_ErrorString() );
+ close( newsocket );
+ return 0;
+ }
+
+ // make it broadcast capable
+ int i = 1;
+ if( setsockopt( newsocket, SOL_SOCKET, SO_BROADCAST, ( void* )&i, sizeof( i ) ) < 0 )
+ {
+ idLib::Printf( "WARNING: UDP_OpenSocket: setsockopt SO_BROADCAST: %s\n", NET_ErrorString() );
+ close( newsocket );
+ return 0;
+ }
+
+ if( !bind_ip || !bind_ip[0] || !idStr::Icmp( bind_ip, "localhost" ) )
+ {
+ address.sin_addr.s_addr = INADDR_ANY;
+ }
+ else
+ {
+ Net_StringToSockaddr( bind_ip, &address, true );
+ }
+
+ if( port == PORT_ANY )
+ {
+ address.sin_port = 0;
+ }
+ else
+ {
+ address.sin_port = htons( ( short )port );
+ }
+
+ address.sin_family = AF_INET;
+
+ if( bind( newsocket, ( const sockaddr* )&address, sizeof( address ) ) < 0 )
+ {
+ idLib::Printf( "WARNING: UDP_OpenSocket: bind: %s\n", NET_ErrorString() );
+ close( newsocket );
+ return 0;
+ }
+
+ // if the port was PORT_ANY, we need to query again to know the real port we got bound to
+ // ( this used to be in idUDP::InitForPort )
+ if( bound_to )
+ {
+ socklen_t len = sizeof( address );
+ if( getsockname( newsocket, ( struct sockaddr* )&address, &len ) < 0 )
+ {
+ common->Printf( "ERROR: IPSocket: getsockname: %s\n", NET_ErrorString() );
+ close( newsocket );
+ return 0;
+ }
+ Net_SockadrToNetadr( &address, bound_to );
+ }
+
+ return newsocket;
}
/*
-==================
-Sys_IsLANAddress
-==================
+========================
+NET_OpenSocks
+========================
*/
-bool Sys_IsLANAddress( const netadr_t adr )
+void NET_OpenSocks( int port )
{
- int i;
- // RB: 64 bit fixes, changed long to int
- unsigned int* p_ip;
- unsigned int ip;
- // RB end
+ sockaddr_in address;
+ struct hostent* h;
+ int len;
+ bool rfc1929;
+ unsigned char buf[64];
-#if ID_NOLANADDRESS
- common->Printf( "Sys_IsLANAddress: ID_NOLANADDRESS\n" );
- return false;
+ usingSocks = false;
+
+ idLib::Printf( "Opening connection to SOCKS server.\n" );
+
+ if( ( socks_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) < 0 )
+ {
+ idLib::Printf( "WARNING: NET_OpenSocks: socket: %s\n", NET_ErrorString() );
+ return;
+ }
+
+ h = gethostbyname( net_socksServer.GetString() );
+ if( h == NULL )
+ {
+ idLib::Printf( "WARNING: NET_OpenSocks: gethostbyname: %s\n", NET_ErrorString() );
+ return;
+ }
+ if( h->h_addrtype != AF_INET )
+ {
+ idLib::Printf( "WARNING: NET_OpenSocks: gethostbyname: address type was not AF_INET\n" );
+ return;
+ }
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = *( in_addr_t* )h->h_addr_list[0];
+ address.sin_port = htons( ( short )net_socksPort.GetInteger() );
+
+ if( connect( socks_socket, ( sockaddr* )&address, sizeof( address ) ) < 0 )
+ {
+ idLib::Printf( "NET_OpenSocks: connect: %s\n", NET_ErrorString() );
+ return;
+ }
+
+ // send socks authentication handshake
+ if( *net_socksUsername.GetString() || *net_socksPassword.GetString() )
+ {
+ rfc1929 = true;
+ }
+ else
+ {
+ rfc1929 = false;
+ }
+
+ buf[0] = 5; // SOCKS version
+ // method count
+ if( rfc1929 )
+ {
+ buf[1] = 2;
+ len = 4;
+ }
+ else
+ {
+ buf[1] = 1;
+ len = 3;
+ }
+ buf[2] = 0; // method #1 - method id #00: no authentication
+ if( rfc1929 )
+ {
+ buf[2] = 2; // method #2 - method id #02: username/password
+ }
+ if( send( socks_socket, ( const char* )buf, len, 0 ) < 0 )
+ {
+ idLib::Printf( "NET_OpenSocks: send: %s\n", NET_ErrorString() );
+ return;
+ }
+
+ // get the response
+ len = recv( socks_socket, ( char* )buf, 64, 0 );
+ if( len < 0 )
+ {
+ idLib::Printf( "NET_OpenSocks: recv: %s\n", NET_ErrorString() );
+ return;
+ }
+ if( len != 2 || buf[0] != 5 )
+ {
+ idLib::Printf( "NET_OpenSocks: bad response\n" );
+ return;
+ }
+ switch( buf[1] )
+ {
+ case 0: // no authentication
+ break;
+ case 2: // username/password authentication
+ break;
+ default:
+ idLib::Printf( "NET_OpenSocks: request denied\n" );
+ return;
+ }
+
+ // do username/password authentication if needed
+ if( buf[1] == 2 )
+ {
+ int ulen;
+ int plen;
+
+ // build the request
+ ulen = idStr::Length( net_socksUsername.GetString() );
+ plen = idStr::Length( net_socksPassword.GetString() );
+
+ buf[0] = 1; // username/password authentication version
+ buf[1] = ulen;
+ if( ulen )
+ {
+ memcpy( &buf[2], net_socksUsername.GetString(), ulen );
+ }
+ buf[2 + ulen] = plen;
+ if( plen )
+ {
+ memcpy( &buf[3 + ulen], net_socksPassword.GetString(), plen );
+ }
+
+ // send it
+ if( send( socks_socket, ( const char* )buf, 3 + ulen + plen, 0 ) < 0 )
+ {
+ idLib::Printf( "NET_OpenSocks: send: %s\n", NET_ErrorString() );
+ return;
+ }
+
+ // get the response
+ len = recv( socks_socket, ( char* )buf, 64, 0 );
+ if( len < 0 )
+ {
+ idLib::Printf( "NET_OpenSocks: recv: %s\n", NET_ErrorString() );
+ return;
+ }
+ if( len != 2 || buf[0] != 1 )
+ {
+ idLib::Printf( "NET_OpenSocks: bad response\n" );
+ return;
+ }
+ if( buf[1] != 0 )
+ {
+ idLib::Printf( "NET_OpenSocks: authentication failed\n" );
+ return;
+ }
+ }
+
+ // send the UDP associate request
+ buf[0] = 5; // SOCKS version
+ buf[1] = 3; // command: UDP associate
+ buf[2] = 0; // reserved
+ buf[3] = 1; // address type: IPV4
+ *( int* )&buf[4] = INADDR_ANY;
+ *( short* )&buf[8] = htons( ( short )port ); // port
+ if( send( socks_socket, ( const char* )buf, 10, 0 ) < 0 )
+ {
+ idLib::Printf( "NET_OpenSocks: send: %s\n", NET_ErrorString() );
+ return;
+ }
+
+ // get the response
+ len = recv( socks_socket, ( char* )buf, 64, 0 );
+ if( len < 0 )
+ {
+ idLib::Printf( "NET_OpenSocks: recv: %s\n", NET_ErrorString() );
+ return;
+ }
+ if( len < 2 || buf[0] != 5 )
+ {
+ idLib::Printf( "NET_OpenSocks: bad response\n" );
+ return;
+ }
+ // check completion code
+ if( buf[1] != 0 )
+ {
+ idLib::Printf( "NET_OpenSocks: request denied: %i\n", buf[1] );
+ return;
+ }
+ if( buf[3] != 1 )
+ {
+ idLib::Printf( "NET_OpenSocks: relay address is not IPV4: %i\n", buf[3] );
+ return;
+ }
+ socksRelayAddr.sin_family = AF_INET;
+ socksRelayAddr.sin_addr.s_addr = *( int* )&buf[4];
+ socksRelayAddr.sin_port = *( short* )&buf[8];
+ memset( socksRelayAddr.sin_zero, 0, sizeof( socksRelayAddr.sin_zero ) );
+
+ usingSocks = true;
+}
+
+/*
+========================
+Net_WaitForData
+========================
+*/
+bool Net_WaitForData( int netSocket, int timeout )
+{
+ int ret;
+ fd_set set;
+ struct timeval tv;
+
+ if( !netSocket )
+ {
+ return false;
+ }
+
+ if( timeout < 0 )
+ {
+ return true;
+ }
+
+ FD_ZERO( &set );
+ FD_SET( netSocket, &set );
+
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = ( timeout % 1000 ) * 1000;
+
+ ret = select( netSocket + 1, &set, NULL, NULL, &tv );
+
+ if( ret == -1 )
+ {
+ idLib::Printf( "Net_WaitForData select(): %s\n", strerror( errno ) );
+ return false;
+ }
+
+ // timeout with no data
+ if( ret == 0 )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+========================
+Net_GetUDPPacket
+========================
+*/
+bool Net_GetUDPPacket( int netSocket, netadr_t& net_from, void* data, int& size, int maxSize )
+{
+ int ret;
+ sockaddr_in from;
+ socklen_t fromlen;
+ int err;
+
+ if( !netSocket )
+ {
+ return false;
+ }
+
+ fromlen = sizeof( from );
+ ret = recvfrom( netSocket, data, maxSize, 0, ( sockaddr* )&from, &fromlen );
+ if( ret < 0 )
+ {
+ err = errno;
+
+ if( err == EWOULDBLOCK || err == ECONNRESET )
+ {
+ return false;
+ }
+
+ idLib::Printf( "Net_GetUDPPacket: %s\n", NET_ErrorString() );
+ return false;
+ }
+#if 0
+ // TODO: WTF was this about?
+ // DG: ip_socket is never initialized, so this is dead code
+ // - and if netSocket is 0 (so this would be true) recvfrom above will already fail
+ if( static_cast( netSocket ) == ip_socket )
+ {
+ memset( from.sin_zero, 0, sizeof( from.sin_zero ) );
+ }
+
+ if( usingSocks && static_cast( netSocket ) == ip_socket && memcmp( &from, &socksRelayAddr, fromlen ) == 0 )
+ {
+ if( ret < 10 || data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 1 )
+ {
+ return false;
+ }
+ net_from.type = NA_IP;
+ net_from.ip[0] = data[4];
+ net_from.ip[1] = data[5];
+ net_from.ip[2] = data[6];
+ net_from.ip[3] = data[7];
+ net_from.port = *( short* )&data[8];
+ memmove( data, &data[10], ret - 10 );
+ }
+ else
+ {
+#endif // 0
+
+ Net_SockadrToNetadr( &from, &net_from );
+#if 0 // this is ugly, but else astyle is confused
+ }
#endif
- if( adr.type == NA_LOOPBACK )
- {
- return true;
- }
-
- if( adr.type != NA_IP )
+ if( ret > maxSize )
{
+ idLib::Printf( "Net_GetUDPPacket: oversize packet from %s\n", Sys_NetAdrToString( net_from ) );
return false;
}
- if( !num_interfaces )
- {
- return false; // well, if there's no networking, there are no LAN addresses, right
- }
+ size = ret;
- for( i = 0; i < num_interfaces; i++ )
- {
- // RB: 64 bit fixes, changed long to int
- p_ip = ( unsigned int* )&adr.ip[0];
- // RB end
- ip = ntohl( *p_ip );
- if( ( netint[i].ip & netint[i].mask ) == ( ip & netint[i].mask ) )
- {
- return true;
- }
- }
-
- return false;
+ return true;
}
/*
-===================
-Sys_CompareNetAdrBase
-
-Compares without the port
-===================
+========================
+Net_SendUDPPacket
+========================
*/
-bool Sys_CompareNetAdrBase( const netadr_t a, const netadr_t b )
+void Net_SendUDPPacket( int netSocket, int length, const void* data, const netadr_t to )
{
- if( a.type != b.type )
+ int ret;
+ sockaddr_in addr;
+
+ if( !netSocket )
{
- return false;
+ return;
}
- if( a.type == NA_LOOPBACK )
- {
- return true;
- }
+ Net_NetadrToSockadr( &to, &addr );
- if( a.type == NA_IP )
+ if( usingSocks && to.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] )
+ socksBuf[0] = 0; // reserved
+ socksBuf[1] = 0;
+ socksBuf[2] = 0; // fragment (not fragmented)
+ socksBuf[3] = 1; // address type: IPV4
+ *( int* )&socksBuf[4] = addr.sin_addr.s_addr;
+ *( short* )&socksBuf[8] = addr.sin_port;
+ memcpy( &socksBuf[10], data, length );
+ ret = sendto( netSocket, socksBuf, length + 10, 0, ( sockaddr* )&socksRelayAddr, sizeof( socksRelayAddr ) );
+ }
+ else
+ {
+ ret = sendto( netSocket, ( const char* )data, length, 0, ( sockaddr* )&addr, sizeof( addr ) );
+ }
+ if( ret < 0 )
+ {
+ // some PPP links do not allow broadcasts and return an error
+ if( ( errno == EADDRNOTAVAIL ) && ( to.type == NA_BROADCAST ) )
{
- return true;
+ return;
}
- return false;
+
+ // NOTE: EWOULDBLOCK used to be silently ignored,
+ // but that means the packet will be dropped so I don't feel it's a good thing to ignore
+ idLib::Printf( "UDP sendto error - packet dropped: %s\n", NET_ErrorString() );
}
-
- common->Printf( "Sys_CompareNetAdrBase: bad address type\n" );
- return false;
+}
+
+static void ip_to_addr( const char ip[4], char* addr )
+{
+ idStr::snPrintf( addr, 16, "%d.%d.%d.%d", ( unsigned char )ip[0], ( unsigned char )ip[1],
+ ( unsigned char )ip[2], ( unsigned char )ip[3] );
}
/*
-====================
-NET_InitNetworking
-====================
+========================
+Sys_InitNetworking
+========================
*/
void Sys_InitNetworking()
{
// haven't been able to clearly pinpoint which standards or RFCs define SIOCGIFCONF, SIOCGIFADDR, SIOCGIFNETMASK ioctls
// it seems fairly widespread, in Linux kernel ioctl, and in BSD .. so let's assume it's always available on our targets
+ bool foundloopback = false;
-#if MACOS_X
+#if defined(MACOS_X) || defined(__FreeBSD__)
unsigned int ip, mask;
struct ifaddrs* ifap, *ifp;
@@ -358,16 +737,17 @@ void Sys_InitNetworking()
if( ip == INADDR_LOOPBACK )
{
+ foundloopback = true;
common->Printf( "loopback\n" );
}
else
{
- common->Printf( "IP: %d.%d.%d.%d\n",
+ common->Printf( "%d.%d.%d.%d",
( unsigned char )ifp->ifa_addr->sa_data[2],
( unsigned char )ifp->ifa_addr->sa_data[3],
( unsigned char )ifp->ifa_addr->sa_data[4],
( unsigned char )ifp->ifa_addr->sa_data[5] );
- common->Printf( "NetMask: %d.%d.%d.%d\n",
+ common->Printf( "/%d.%d.%d.%d\n",
( unsigned char )ifp->ifa_netmask->sa_data[2],
( unsigned char )ifp->ifa_netmask->sa_data[3],
( unsigned char )ifp->ifa_netmask->sa_data[4],
@@ -375,6 +755,9 @@ void Sys_InitNetworking()
}
netint[ num_interfaces ].ip = ip;
netint[ num_interfaces ].mask = mask;
+ // DG: set netint addr
+ ip_to_addr( &ifp->ifa_addr->sa_data[2], netint[ num_interfaces ].addr );
+ // DG end
num_interfaces++;
}
#else
@@ -418,6 +801,7 @@ void Sys_InitNetworking()
// RB end
if( ip == INADDR_LOOPBACK )
{
+ foundloopback = true;
common->Printf( "loopback\n" );
}
else
@@ -428,6 +812,11 @@ void Sys_InitNetworking()
( unsigned char )ifr->ifr_addr.sa_data[4],
( unsigned char )ifr->ifr_addr.sa_data[5] );
}
+
+ // DG: set netint address before getting the mask
+ ip_to_addr( &ifr->ifr_addr.sa_data[2], netint[ num_interfaces ].addr );
+ // DG end
+
if( ioctl( s, SIOCGIFNETMASK, ifr ) < 0 )
{
common->Printf( " SIOCGIFNETMASK failed: %s\n", strerror( errno ) );
@@ -454,89 +843,154 @@ void Sys_InitNetworking()
ifindex += sizeof( ifreq );
}
#endif
+
+ // for some retarded reason, win32 doesn't count loopback as an adapter...
+ // and because I'm extra-cautious I add this check on real operating systems as well :)
+ if( !foundloopback && num_interfaces < MAX_INTERFACES )
+ {
+ idLib::Printf( "Sys_InitNetworking: adding loopback interface\n" );
+ netint[num_interfaces].ip = ntohl( inet_addr( "127.0.0.1" ) );
+ netint[num_interfaces].mask = ntohl( inet_addr( "255.0.0.0" ) );
+ num_interfaces++;
+ }
}
/*
-====================
-IPSocket
-====================
+========================
+Sys_ShutdownNetworking
+========================
*/
-static int IPSocket( const char* net_interface, int port, netadr_t* bound_to = NULL )
+void Sys_ShutdownNetworking()
{
- int newsocket;
- struct sockaddr_in address;
- int i = 1;
+ if( usingSocks )
+ close( socks_socket );
+}
+
+/*
+========================
+Sys_StringToNetAdr
+========================
+*/
+bool Sys_StringToNetAdr( const char* s, netadr_t* a, bool doDNSResolve )
+{
+ sockaddr_in sadr;
- if( net_interface )
+ if( !Net_StringToSockaddr( s, &sadr, doDNSResolve ) )
{
- common->Printf( "Opening IP socket: %s:%i\n", net_interface, port );
+ return false;
}
+
+ Net_SockadrToNetadr( &sadr, a );
+ return true;
+}
+
+/*
+========================
+Sys_NetAdrToString
+========================
+*/
+const char* Sys_NetAdrToString( const netadr_t a )
+{
+ // DG: FIXME: those static buffers look fishy - I would feel better if they were
+ // at least thread-local - so /maybe/ use ID_TLS here?
+ static int index = 0;
+ static char buf[ 4 ][ 64 ]; // flip/flop
+ char* s;
+
+ s = buf[index];
+ index = ( index + 1 ) & 3;
+ if( a.type == NA_IP || a.type == NA_LOOPBACK )
+ idStr::snPrintf( s, 64, "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], a.port );
+ else if( a.type == NA_BROADCAST )
+ idStr::snPrintf( s, 64, "BROADCAST" );
+ else if( a.type == NA_BAD )
+ idStr::snPrintf( s, 64, "BAD_IP" );
else
{
- common->Printf( "Opening IP socket: localhost:%i\n", port );
+ idStr::snPrintf( s, 64, "WTF_UNKNOWN_IP_TYPE_%i", a.type );
+ }
+ return s;
+}
+
+/*
+========================
+Sys_IsLANAddress
+========================
+*/
+bool Sys_IsLANAddress( const netadr_t adr )
+{
+ if( adr.type == NA_LOOPBACK )
+ {
+ return true;
}
- if( ( newsocket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == -1 )
+ if( adr.type != NA_IP )
{
- common->Printf( "ERROR: IPSocket: socket: %s", strerror( errno ) );
- return 0;
- }
- // make it non-blocking
- int on = 1;
- if( ioctl( newsocket, FIONBIO, &on ) == -1 )
- {
- common->Printf( "ERROR: IPSocket: ioctl FIONBIO:%s\n",
- strerror( errno ) );
- return 0;
- }
- // make it broadcast capable
- if( setsockopt( newsocket, SOL_SOCKET, SO_BROADCAST, ( char* ) &i, sizeof( i ) ) == -1 )
- {
- common->Printf( "ERROR: IPSocket: setsockopt SO_BROADCAST:%s\n", strerror( errno ) );
- return 0;
+ return false;
}
- if( !net_interface || !net_interface[ 0 ]
- || !idStr::Icmp( net_interface, "localhost" ) )
+ // NOTE: this function won't work reliably for addresses on the local net
+ // that are connected through a router (i.e. no IP from that net is on any interface)
+ // However, I don't expect most people to have such setups at home and the code
+ // would get a lot more complex and less portable.
+ // Furthermore, this function isn't even used currently
+ if( num_interfaces )
{
- address.sin_addr.s_addr = INADDR_ANY;
- }
- else
- {
- StringToSockaddr( net_interface, &address, true );
- }
-
- if( port == PORT_ANY )
- {
- address.sin_port = 0;
- }
- else
- {
- address.sin_port = htons( ( short ) port );
- }
-
- address.sin_family = AF_INET;
-
- if( bind( newsocket, ( const struct sockaddr* )&address, sizeof( address ) ) == -1 )
- {
- common->Printf( "ERROR: IPSocket: bind: %s\n", strerror( errno ) );
- close( newsocket );
- return 0;
- }
-
- if( bound_to )
- {
- unsigned int len = sizeof( address );
- if( ( unsigned int )( getsockname( newsocket, ( struct sockaddr* )&address, ( socklen_t* )&len ) ) == -1 )
+ int i;
+ // DG: for 64bit compatibility, make these longs ints.
+ unsigned int* p_ip;
+ unsigned int ip;
+ p_ip = ( unsigned int* )&adr.ip[0];
+ // DG end
+ ip = ntohl( *p_ip );
+
+ for( i = 0; i < num_interfaces; i++ )
{
- common->Printf( "ERROR: IPSocket: getsockname: %s\n", strerror( errno ) );
- close( newsocket );
- return 0;
+ if( ( netint[i].ip & netint[i].mask ) == ( ip & netint[i].mask ) )
+ {
+ return true;
+ }
}
- SockadrToNetadr( &address, bound_to );
+ }
+ return false;
+}
+
+/*
+========================
+Sys_CompareNetAdrBase
+
+Compares without the port.
+========================
+*/
+bool Sys_CompareNetAdrBase( const netadr_t a, const netadr_t b )
+{
+ if( a.type != b.type )
+ {
+ return false;
}
- return newsocket;
+ if( a.type == NA_LOOPBACK )
+ {
+ // DG: wtf is this comparison about, the comment above says "without the port"
+ if( a.port == b.port )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ 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;
+ }
+
+ idLib::Printf( "Sys_CompareNetAdrBase: bad address type\n" );
+ return false;
}
/*
@@ -560,35 +1014,37 @@ const char* Sys_GetLocalIP( int i )
{
return NULL;
}
-
- static char s[64];
-
- unsigned char bytes[4];
- bytes[0] = netint[i].ip & 0xFF;
- bytes[1] = ( netint[i].ip >> 8 ) & 0xFF;
- bytes[2] = ( netint[i].ip >> 16 ) & 0xFF;
- bytes[3] = ( netint[i].ip >> 24 ) & 0xFF;
-
- idStr::snPrintf( s, sizeof( s ), "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
-
- return s;
+ return netint[i].addr;
}
/*
-==================
+================================================================================================
+
+ idUDP
+
+================================================================================================
+*/
+
+/*
+========================
idUDP::idUDP
-==================
+========================
*/
idUDP::idUDP()
{
netSocket = 0;
memset( &bound_to, 0, sizeof( bound_to ) );
+ silent = false;
+ packetsRead = 0;
+ bytesRead = 0;
+ packetsWritten = 0;
+ bytesWritten = 0;
}
/*
-==================
+========================
idUDP::~idUDP
-==================
+========================
*/
idUDP::~idUDP()
{
@@ -596,9 +1052,30 @@ idUDP::~idUDP()
}
/*
-==================
+========================
+idUDP::InitForPort
+========================
+*/
+bool idUDP::InitForPort( int portNumber )
+{
+ // DG: don't specify an IP to bind for (and certainly not net_ip)
+ // => it'll listen on all addresses (0.0.0.0 / INADDR_ANY)
+ netSocket = NET_IPSocket( NULL, portNumber, &bound_to );
+ // DG end
+ if( netSocket <= 0 )
+ {
+ netSocket = 0;
+ memset( &bound_to, 0, sizeof( bound_to ) );
+ return false;
+ }
+
+ return true;
+}
+
+/*
+========================
idUDP::Close
-==================
+========================
*/
void idUDP::Close()
{
@@ -611,148 +1088,67 @@ void idUDP::Close()
}
/*
-==================
+========================
idUDP::GetPacket
-==================
+========================
*/
-bool idUDP::GetPacket( netadr_t& net_from, void* data, int& size, int maxSize )
+bool idUDP::GetPacket( netadr_t& from, void* data, int& size, int maxSize )
{
- int ret;
- struct sockaddr_in from;
- int fromlen;
-
- if( !netSocket )
+ // DG: this fake while(1) loop pissed me off so I replaced it.. no functional change.
+ if( ! Net_GetUDPPacket( netSocket, from, data, size, maxSize ) )
{
return false;
}
- fromlen = sizeof( from );
- ret = recvfrom( netSocket, data, maxSize, 0, ( struct sockaddr* ) &from, ( socklen_t* ) &fromlen );
+ packetsRead++;
+ bytesRead += size;
- if( ret == -1 )
- {
- if( errno == EWOULDBLOCK || errno == ECONNREFUSED )
- {
- // those commonly happen, don't verbose
- return false;
- }
- common->DPrintf( "idUDP::GetPacket recvfrom(): %s\n", strerror( errno ) );
- return false;
- }
-
- assert( ret < maxSize );
-
- SockadrToNetadr( &from, &net_from );
- size = ret;
return true;
+ // DG end
}
/*
-==================
+========================
idUDP::GetPacketBlocking
-==================
+========================
*/
-bool idUDP::GetPacketBlocking( netadr_t& net_from, void* data, int& size, int maxSize, int timeout )
+bool idUDP::GetPacketBlocking( netadr_t& from, void* data, int& size, int maxSize, int timeout )
{
- fd_set set;
- struct timeval tv;
- int ret;
-
- if( !netSocket )
+
+ if( !Net_WaitForData( netSocket, timeout ) )
{
return false;
}
- if( timeout < 0 )
+ if( GetPacket( from, data, size, maxSize ) )
{
- return GetPacket( net_from, data, size, maxSize );
+ return true;
}
- FD_ZERO( &set );
- FD_SET( netSocket, &set );
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = ( timeout % 1000 ) * 1000;
- ret = select( netSocket + 1, &set, NULL, NULL, &tv );
- if( ret == -1 )
- {
- if( errno == EINTR )
- {
- common->DPrintf( "idUDP::GetPacketBlocking: select EINTR\n" );
- return false;
- }
- else
- {
- common->Error( "idUDP::GetPacketBlocking: select failed: %s\n", strerror( errno ) );
- }
- }
-
- if( ret == 0 )
- {
- // timed out
- return false;
- }
- struct sockaddr_in from;
- int fromlen;
- fromlen = sizeof( from );
- ret = recvfrom( netSocket, data, maxSize, 0, ( struct sockaddr* )&from, ( socklen_t* )&fromlen );
- if( ret == -1 )
- {
- // there should be no blocking errors once select declares things are good
- common->DPrintf( "idUDP::GetPacketBlocking: %s\n", strerror( errno ) );
- return false;
- }
- assert( ret < maxSize );
- SockadrToNetadr( &from, &net_from );
- size = ret;
- return true;
+ return false;
}
/*
-==================
+========================
idUDP::SendPacket
-==================
+========================
*/
void idUDP::SendPacket( const netadr_t to, const void* data, int size )
{
- int ret;
- struct sockaddr_in addr;
-
if( to.type == NA_BAD )
{
- common->Warning( "idUDP::SendPacket: bad address type NA_BAD - ignored" );
+ idLib::Warning( "idUDP::SendPacket: bad address type NA_BAD - ignored" );
return;
}
- if( !netSocket )
+ packetsWritten++;
+ bytesWritten += size;
+
+ if( silent )
{
return;
}
- NetadrToSockadr( &to, &addr );
-
- ret = sendto( netSocket, data, size, 0, ( struct sockaddr* ) &addr, sizeof( addr ) );
- if( ret == -1 )
- {
- common->Printf( "idUDP::SendPacket ERROR: to %s: %s\n", Sys_NetAdrToString( to ), strerror( errno ) );
- }
+ Net_SendUDPPacket( netSocket, size, data, to );
}
-/*
-==================
-idUDP::InitForPort
-==================
-*/
-bool idUDP::InitForPort( int portNumber )
-{
- netSocket = IPSocket( net_ip.GetString(), portNumber, &bound_to );
- if( netSocket <= 0 )
- {
- netSocket = 0;
- memset( &bound_to, 0, sizeof( bound_to ) );
- return false;
- }
- return true;
-}
-
-
diff --git a/neo/sys/posix/posix_session_local.cpp b/neo/sys/posix/posix_session_local.cpp
index cca9802e..1b1462e3 100644
--- a/neo/sys/posix/posix_session_local.cpp
+++ b/neo/sys/posix/posix_session_local.cpp
@@ -402,7 +402,7 @@ void idSessionLocalWin::Connect_f( const idCmdArgs& args )
lobbyConnectInfo_t connectInfo;
Sys_StringToNetAdr( args.Argv( 1 ), &connectInfo.netAddr, true );
- connectInfo.netAddr.port = net_port.GetInteger();
+ connectInfo.netAddr.port = net_port.GetInteger(); // FIXME: really? what if it was specified in the connect cmd?
ConnectAndMoveToLobby( GetPartyLobby(), connectInfo, false );
}
@@ -600,6 +600,7 @@ idSessionLocalWin::EnsurePort
*/
void idSessionLocalWin::EnsurePort()
{
+ // XXX: fucked up?
// Init the port using reqular windows sockets
if( port.IsOpen() )
{
diff --git a/neo/sys/sys_lobby.cpp b/neo/sys/sys_lobby.cpp
index 710471f0..77676047 100644
--- a/neo/sys/sys_lobby.cpp
+++ b/neo/sys/sys_lobby.cpp
@@ -339,6 +339,7 @@ void idLobby::Shutdown( bool retainMigrationInfo, bool skipGoodbye )
idLobby::HandlePacket
========================
*/
+// FIXME: remoteAddress const?
void idLobby::HandlePacket( lobbyAddress_t& remoteAddress, idBitMsg fragMsg, idPacketProcessor::sessionId_t sessionID )
{
SCOPED_PROFILE_EVENT( "HandlePacket" );
@@ -1857,7 +1858,7 @@ int idLobby::HandleInitialPeerConnection( idBitMsg& msg, const lobbyAddress_t& p
if( !IsHost() )
{
- NET_VERBOSE_PRINT( "NET: Got connectionless hello from peer %s on session, and we are not a host\n", peerAddress.ToString() );
+ NET_VERBOSE_PRINT( "NET: Got connectionless hello from peer %s (num %i) on session, and we are not a host\n", peerAddress.ToString(), peerNum );
SendGoodbye( peerAddress );
return -1;
}
diff --git a/neo/sys/sys_lobby_backend_direct.cpp b/neo/sys/sys_lobby_backend_direct.cpp
index 2f2fab4b..e0f74555 100644
--- a/neo/sys/sys_lobby_backend_direct.cpp
+++ b/neo/sys/sys_lobby_backend_direct.cpp
@@ -103,10 +103,12 @@ void idLobbyBackendDirect::JoinFromConnectInfo( const lobbyConnectInfo_t& connec
{
Sys_StringToNetAdr( "localhost", &address, true );
address.port = net_port.GetInteger();
+ NET_VERBOSE_PRINT( "NET: idLobbyBackendDirect::JoinFromConnectInfo(): canJoinLocalHost\n" );
}
else
{
address = connectInfo.netAddr;
+ NET_VERBOSE_PRINT( "NET: idLobbyBackendDirect::JoinFromConnectInfo(): %s\n", Sys_NetAdrToString( address ) );
}
state = STATE_READY;
@@ -176,7 +178,7 @@ lobbyConnectInfo_t idLobbyBackendDirect::GetConnectInfo()
if( IsHost() )
{
// If we are the host, give them our ip address
- const char* ip = Sys_GetLocalIP( 0 );
+ const char* ip = Sys_GetLocalIP( 0 ); // XXX: ohweiha.
Sys_StringToNetAdr( ip, &address, false );
address.port = net_port.GetInteger();
}
diff --git a/neo/sys/sys_session_local.cpp b/neo/sys/sys_session_local.cpp
index c4d7d70f..3c2cde0e 100644
--- a/neo/sys/sys_session_local.cpp
+++ b/neo/sys/sys_session_local.cpp
@@ -1128,6 +1128,7 @@ bool idSessionLocal::State_Create_And_Move_To_Game_Lobby()
// Now that we've created our game lobby, send our own party users to it
// NOTE - We pass in false to wait on party members since we are the host, and we know they can connect to us
+ // TODO: special handling of ourselves here?
GetPartyLobby().SendMembersToLobby( GetGameLobby(), false );
return true;
}
@@ -1591,7 +1592,7 @@ idSession::~idSession()
dedicatedServerSearch = NULL;
}
-idCVar net_verbose( "net_verbose", "0", CVAR_BOOL, "Print a bunch of message about the network session" );
+idCVar net_verbose( "net_verbose", "0", CVAR_BOOL | CVAR_NOCHEAT, "Print a bunch of message about the network session" );
idCVar net_verboseResource( "net_verboseResource", "0", CVAR_BOOL, "Prints a bunch of message about network resources" );
idCVar net_verboseReliable( "net_verboseReliable", "0", CVAR_BOOL, "Prints the more spammy messages about reliable network msgs" );
idCVar si_splitscreen( "si_splitscreen", "0", CVAR_INTEGER, "force splitscreen" );
diff --git a/neo/sys/win32/win_net.cpp b/neo/sys/win32/win_net.cpp
index 7ab1fa34..43bab7af 100644
--- a/neo/sys/win32/win_net.cpp
+++ b/neo/sys/win32/win_net.cpp
@@ -61,7 +61,7 @@ idCVar net_socksPort( "net_socksPort", "1080", CVAR_ARCHIVE | CVAR_INTEGER, "" )
idCVar net_socksUsername( "net_socksUsername", "", CVAR_ARCHIVE, "" );
idCVar net_socksPassword( "net_socksPassword", "", CVAR_ARCHIVE, "" );
-idCVar net_ip( "net_ip", "localhost", 0, "local IP address" );
+idCVar net_ip( "net_ip", "localhost", CVAR_NOCHEAT, "local IP address" );
static struct sockaddr_in socksRelayAddr;
@@ -684,7 +684,9 @@ bool Net_GetUDPPacket( int netSocket, netadr_t& net_from, char* data, int& size,
idLib::Printf( buf );
return false;
}
-
+#if 0
+ // DG: ip_socket is never initialized, so this is dead code
+ // - and if netSocket is 0 (so this would be true) recvfrom above will already fail
if( static_cast( netSocket ) == ip_socket )
{
memset( from.sin_zero, 0, sizeof( from.sin_zero ) );
@@ -706,8 +708,12 @@ bool Net_GetUDPPacket( int netSocket, netadr_t& net_from, char* data, int& size,
}
else
{
+#endif // 0
+
Net_SockadrToNetadr( &from, &net_from );
+#if 0
}
+#endif
if( ret > maxSize )
{
@@ -1177,3 +1183,4 @@ void idUDP::SendPacket( const netadr_t to, const void* data, int size )
Net_SendUDPPacket( netSocket, size, data, to );
}
+