From e8c8a04c072ea4e4ad1fed4c07be138d8beaffb1 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 3 Mar 2013 21:39:27 +0100 Subject: [PATCH] rewrote posix_net.cpp based on win_net.cpp And suddenly hosting a server on linux works at least locally (with client and server on the same machine). Even though there are still strange bugs (massive lags in one direction, doesn't work in LAN), at least it works at all now. --- neo/sys/posix/posix_net.cpp | 1104 +++++++++++++++++-------- neo/sys/posix/posix_session_local.cpp | 3 +- neo/sys/sys_lobby.cpp | 3 +- neo/sys/sys_lobby_backend_direct.cpp | 4 +- neo/sys/sys_session_local.cpp | 3 +- neo/sys/win32/win_net.cpp | 11 +- 6 files changed, 768 insertions(+), 360 deletions(-) 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 ); } +