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 ); } +