diff --git a/Engine/source/app/game.cpp b/Engine/source/app/game.cpp index 2f0e30c77..5b2070887 100644 --- a/Engine/source/app/game.cpp +++ b/Engine/source/app/game.cpp @@ -162,6 +162,13 @@ DefineConsoleFunction( setNetPort, bool, (int port, bool bind), (true), "(int po return Net::openPort((S32)port, bind); } +DefineConsoleFunction(isAddressTypeAvailable, bool, (int addressType), , "(protocol id)" + "@brief Determines if a specified address type can be reached.\n\n" + "@ingroup Networking") +{ + return Net::isAddressTypeAvailable((NetAddress::Type)addressType); +} + DefineConsoleFunction( closeNetPort, void, (), , "()" "@brief Closes the current network port\n\n" "@ingroup Networking") diff --git a/Engine/source/app/net/serverQuery.cpp b/Engine/source/app/net/serverQuery.cpp index 072f26fde..dc5cf14eb 100644 --- a/Engine/source/app/net/serverQuery.cpp +++ b/Engine/source/app/net/serverQuery.cpp @@ -177,7 +177,7 @@ static Vector gQueryList(__FILE__, __LINE__); struct PacketStatus { - U8 index; + U16 index; S32 key; U32 time; U32 tryCount; @@ -191,6 +191,9 @@ struct PacketStatus time = _time; tryCount = gPacketRetryCount; } + + inline U8 getOldIndex() { return (U8)index; } + inline U16 getIndex() { return index; } }; static Vector gPacketStatusList(__FILE__, __LINE__); @@ -212,6 +215,7 @@ struct ServerFilter OnlineQuery = 0, // Authenticated with master OfflineQuery = BIT(0), // On our own NoStringCompress = BIT(1), + NewStyleResponse = BIT(2), // Include IPV6 servers }; enum // Filter flags: @@ -222,6 +226,14 @@ struct ServerFilter CurrentVersion = BIT(7), NotXenon = BIT(6) }; + + enum // Region mask flags + { + RegionIsIPV4Address = BIT(30), + RegionIsIPV6Address = BIT(31), + + RegionAddressMask = RegionIsIPV4Address | RegionIsIPV6Address + }; //Rearranging the fields according to their sizes char* gameType; @@ -241,7 +253,7 @@ struct ServerFilter ServerFilter() { type = Normal; - queryFlags = 0; + queryFlags = NewStyleResponse; gameType = NULL; missionType = NULL; minPlayers = 0; @@ -401,10 +413,17 @@ void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missi NetAddress addr; char addrText[256]; + + // IPV4 dSprintf( addrText, sizeof( addrText ), "IP:BROADCAST:%d", port ); Net::stringToAddress( addrText, &addr ); pushPingBroadcast( &addr ); + // IPV6 + dSprintf(addrText, sizeof(addrText), "IP6:MULTICAST:%d", port); + Net::stringToAddress(addrText, &addr); + pushPingBroadcast(&addr); + Con::executef("onServerQueryStatus", "start", "Querying LAN servers", "0"); processPingsAndQueries( gPingSession ); } @@ -502,7 +521,7 @@ void queryMasterServer(U8 flags, const char* gameType, const char* missionType, dStrcpy( sActiveFilter.missionType, missionType ); } - sActiveFilter.queryFlags = flags; + sActiveFilter.queryFlags = flags | ServerFilter::NewStyleResponse; sActiveFilter.minPlayers = minPlayers; sActiveFilter.maxPlayers = maxPlayers; sActiveFilter.maxBots = maxBots; @@ -519,6 +538,7 @@ void queryMasterServer(U8 flags, const char* gameType, const char* missionType, sActiveFilter.type = ServerFilter::Buddy; sActiveFilter.buddyCount = buddyCount; sActiveFilter.buddyList = (U32*) dRealloc( sActiveFilter.buddyList, buddyCount * 4 ); + sActiveFilter.queryFlags = ServerFilter::NewStyleResponse; dMemcpy( sActiveFilter.buddyList, buddyList, buddyCount * 4 ); clearServerList(); } @@ -775,7 +795,7 @@ Vector* getMasterServerList() U32 region = 1; // needs to default to something > 0 dSscanf(master,"%d:",®ion); const char* madd = dStrchr(master,':') + 1; - if (region && Net::stringToAddress(madd,&address)) { + if (region && Net::stringToAddress(madd,&address) == Net::NoError) { masterList.increment(); MasterInfo& info = masterList.last(); info.address = address; @@ -1171,10 +1191,13 @@ static void processMasterServerQuery( U32 session ) // Send a request to the master server for the server list: BitStream *out = BitStream::getPacketStream(); out->clearStringBuffer(); + out->write( U8( NetInterface::MasterServerListRequest ) ); + out->write( U8( sActiveFilter.queryFlags) ); out->write( ( gMasterServerPing.session << 16 ) | ( gMasterServerPing.key & 0xFFFF ) ); out->write( U8( 255 ) ); + writeCString( out, sActiveFilter.gameType ); writeCString( out, sActiveFilter.missionType ); out->write( sActiveFilter.minPlayers ); @@ -1359,23 +1382,35 @@ static void processServerListPackets( U32 session ) if ( !p.tryCount ) { // Packet timed out :( - Con::printf( "Server list packet #%d timed out.", p.index + 1 ); + Con::printf( "Server list packet #%d timed out.", p.getIndex() + 1 ); gPacketStatusList.erase( i ); } else { // Try again... - Con::printf( "Rerequesting server list packet #%d...", p.index + 1 ); + Con::printf( "Rerequesting server list packet #%d...", p.getIndex() + 1 ); p.tryCount--; p.time = currentTime; p.key = gKey++; BitStream *out = BitStream::getPacketStream(); + bool extendedPacket = (sActiveFilter.queryFlags & ServerFilter::NewStyleResponse) != 0; + out->clearStringBuffer(); - out->write( U8( NetInterface::MasterServerListRequest ) ); + + if ( extendedPacket ) + out->write( U8( NetInterface::MasterServerExtendedListRequest ) ); + else + out->write( U8( NetInterface::MasterServerListRequest ) ); + out->write( U8( sActiveFilter.queryFlags ) ); // flags out->write( ( session << 16) | ( p.key & 0xFFFF ) ); - out->write( p.index ); // packet index + + if ( extendedPacket ) + out->write( p.getOldIndex() ); // packet index + else + out->write( p.getIndex() ); // packet index + out->write( U8( 0 ) ); // game type out->write( U8( 0 ) ); // mission type out->write( U8( 0 ) ); // minPlayers @@ -1569,6 +1604,98 @@ static void handleMasterServerListResponse( BitStream* stream, U32 key, U8 /*fla //----------------------------------------------------------------------------- +static void handleExtendedMasterServerListResponse(BitStream* stream, U32 key, U8 /*flags*/) +{ + U16 packetIndex, packetTotal; + U32 i; + U16 serverCount, port; + U8 netNum[16]; + char addressBuffer[256]; + NetAddress addr; + + stream->read(&packetIndex); + // Validate the packet key: + U32 packetKey = gMasterServerPing.key; + if (gGotFirstListPacket) + { + for (i = 0; i < gPacketStatusList.size(); i++) + { + if (gPacketStatusList[i].index == packetIndex) + { + packetKey = gPacketStatusList[i].key; + break; + } + } + } + + U32 testKey = (gPingSession << 16) | (packetKey & 0xFFFF); + if (testKey != key) + return; + + stream->read(&packetTotal); + stream->read(&serverCount); + + Con::printf("Received server list packet %d of %d from the master server (%d servers).", (packetIndex + 1), packetTotal, serverCount); + + // Enter all of the servers in this packet into the ping list: + for (i = 0; i < serverCount; i++) + { + U8 type; + stream->read(&type); + dMemset(&addr, '\0', sizeof(NetAddress)); + + if (type == 0) + { + // IPV4 + addr.type = NetAddress::IPAddress; + stream->read(4, &addr.address.ipv4.netNum[0]); + stream->read(&addr.port); + } + else + { + // IPV6 + addr.type = NetAddress::IPV6Address; + stream->read(16, &addr.address.ipv6.netNum[0]); + stream->read(&addr.port); + } + + pushPingRequest(&addr); + } + + // If this is the first list packet we have received, fill the packet status list + // and start processing: + if (!gGotFirstListPacket) + { + gGotFirstListPacket = true; + gMasterServerQueryAddress = gMasterServerPing.address; + U32 currentTime = Platform::getVirtualMilliseconds(); + for (i = 0; i < packetTotal; i++) + { + if (i != packetIndex) + { + PacketStatus* p = new PacketStatus(i, gMasterServerPing.key, currentTime); + gPacketStatusList.push_back(*p); + } + } + + processServerListPackets(gPingSession); + } + else + { + // Remove the packet we just received from the status list: + for (i = 0; i < gPacketStatusList.size(); i++) + { + if (gPacketStatusList[i].index == packetIndex) + { + gPacketStatusList.erase(i); + break; + } + } + } +} + +//----------------------------------------------------------------------------- + static void handleGameMasterInfoRequest( const NetAddress* address, U32 key, U8 flags ) { if ( GNet->doesAllowConnections() ) @@ -1585,7 +1712,7 @@ static void handleGameMasterInfoRequest( const NetAddress* address, U32 key, U8 for(U32 i = 0; i < masterList->size(); i++) { masterAddr = &(*masterList)[i].address; - if (*(U32*)(masterAddr->netNum) == *(U32*)(address->netNum)) + if (masterAddr->isSameAddress(*address)) { fromMaster = true; break; @@ -2098,6 +2225,10 @@ void DemoNetInterface::handleInfoPacket( const NetAddress* address, U8 packetTyp case GameMasterInfoRequest: handleGameMasterInfoRequest( address, key, flags ); break; + + case MasterServerExtendedListResponse: + handleExtendedMasterServerListResponse(stream, key, flags); + break; } } diff --git a/Engine/source/app/net/tcpObject.cpp b/Engine/source/app/net/tcpObject.cpp index 06f320576..628cff5ed 100644 --- a/Engine/source/app/net/tcpObject.cpp +++ b/Engine/source/app/net/tcpObject.cpp @@ -170,8 +170,8 @@ IMPLEMENT_CALLBACK(TCPObject, onDisconnect, void, (),(), TCPObject *TCPObject::find(NetSocket tag) { - for(TCPObject *walk = table[U32(tag) & TableMask]; walk; walk = walk->mNext) - if(walk->mTag == tag) + for(TCPObject *walk = table[tag.getHash() & TableMask]; walk; walk = walk->mNext) + if(walk->mTag.getHash() == tag.getHash()) return walk; return NULL; } @@ -180,13 +180,13 @@ void TCPObject::addToTable(NetSocket newTag) { removeFromTable(); mTag = newTag; - mNext = table[U32(mTag) & TableMask]; - table[U32(mTag) & TableMask] = this; + mNext = table[mTag.getHash() & TableMask]; + table[mTag.getHash() & TableMask] = this; } void TCPObject::removeFromTable() { - for(TCPObject **walk = &table[U32(mTag) & TableMask]; *walk; walk = &((*walk)->mNext)) + for(TCPObject **walk = &table[mTag.getHash() & TableMask]; *walk; walk = &((*walk)->mNext)) { if(*walk == this) { @@ -207,7 +207,7 @@ TCPObject::TCPObject() mBuffer = NULL; mBufferSize = 0; mPort = 0; - mTag = InvalidSocket; + mTag = NetSocket::INVALID; mNext = NULL; mState = Disconnected; @@ -242,7 +242,7 @@ bool TCPObject::processArguments(S32 argc, ConsoleValueRef *argv) return true; else if(argc == 1) { - addToTable(U32(dAtoi(argv[0]))); + addToTable(NetSocket::fromHandle(dAtoi(argv[0]))); return true; } return false; @@ -406,7 +406,7 @@ void TCPObject::onDisconnect() void TCPObject::listen(U16 port) { mState = Listening; - U32 newTag = Net::openListenPort(port); + NetSocket newTag = Net::openListenPort(port); addToTable(newTag); } @@ -418,7 +418,7 @@ void TCPObject::connect(const char *address) void TCPObject::disconnect() { - if( mTag != InvalidSocket ) { + if( mTag != NetSocket::INVALID ) { Net::closeConnectTo(mTag); } removeFromTable(); @@ -592,7 +592,7 @@ void processConnectedAcceptEvent(NetSocket listeningPort, NetSocket newConnectio if(!tcpo) return; - tcpo->onConnectionRequest(&originatingAddress, newConnection); + tcpo->onConnectionRequest(&originatingAddress, (U32)newConnection.getHandle()); } void processConnectedNotifyEvent( NetSocket sock, U32 state ) diff --git a/Engine/source/console/telnetConsole.cpp b/Engine/source/console/telnetConsole.cpp index 46e2421f4..2886bebdf 100644 --- a/Engine/source/console/telnetConsole.cpp +++ b/Engine/source/console/telnetConsole.cpp @@ -84,7 +84,7 @@ TelnetConsole::TelnetConsole() { Con::addConsumer(telnetCallback); - mAcceptSocket = InvalidSocket; + mAcceptSocket = NetSocket::INVALID; mAcceptPort = -1; mClientList = NULL; mRemoteEchoEnabled = false; @@ -93,13 +93,13 @@ TelnetConsole::TelnetConsole() TelnetConsole::~TelnetConsole() { Con::removeConsumer(telnetCallback); - if(mAcceptSocket != InvalidSocket) + if(mAcceptSocket != NetSocket::INVALID) Net::closeSocket(mAcceptSocket); TelnetClient *walk = mClientList, *temp; while(walk) { temp = walk->nextClient; - if(walk->socket != InvalidSocket) + if(walk->socket != NetSocket::INVALID) Net::closeSocket(walk->socket); delete walk; walk = temp; @@ -113,16 +113,20 @@ void TelnetConsole::setTelnetParameters(S32 port, const char *telnetPassword, co mRemoteEchoEnabled = remoteEcho; - if(mAcceptSocket != InvalidSocket) + if(mAcceptSocket != NetSocket::INVALID) { Net::closeSocket(mAcceptSocket); - mAcceptSocket = InvalidSocket; + mAcceptSocket = NetSocket::INVALID; } mAcceptPort = port; if(mAcceptPort != -1 && mAcceptPort != 0) { + NetAddress address; + Net::getIdealListenAddress(&address); + address.port = mAcceptPort; + mAcceptSocket = Net::openSocket(); - Net::bind(mAcceptSocket, mAcceptPort); + Net::bindAddress(address, mAcceptSocket); Net::listen(mAcceptSocket, 4); Net::setBlocking(mAcceptSocket, false); @@ -151,16 +155,17 @@ void TelnetConsole::process() { NetAddress address; - if(mAcceptSocket != InvalidSocket) + if(mAcceptSocket != NetSocket::INVALID) { // ok, see if we have any new connections: NetSocket newConnection; newConnection = Net::accept(mAcceptSocket, &address); - if(newConnection != InvalidSocket) + if(newConnection != NetSocket::INVALID) { - Con::printf ("Telnet connection from %i.%i.%i.%i", - address.netNum[0], address.netNum[1], address.netNum[2], address.netNum[3]); + char buffer[256]; + Net::addressToString(&address, buffer); + Con::printf("Telnet connection from %s", buffer); TelnetClient *cl = new TelnetClient; cl->socket = newConnection; @@ -201,7 +206,7 @@ void TelnetConsole::process() if((err != Net::NoError && err != Net::WouldBlock) || numBytes == 0) { Net::closeSocket(client->socket); - client->socket = InvalidSocket; + client->socket = NetSocket::INVALID; continue; } @@ -274,7 +279,7 @@ void TelnetConsole::process() if(client->state == DisconnectThisDude) { Net::closeSocket(client->socket); - client->socket = InvalidSocket; + client->socket = NetSocket::INVALID; } } } @@ -312,7 +317,7 @@ void TelnetConsole::process() TelnetClient *cl; while((cl = *walk) != NULL) { - if(cl->socket == InvalidSocket) + if(cl->socket == NetSocket::INVALID) { *walk = cl->nextClient; delete cl; diff --git a/Engine/source/console/telnetDebugger.cpp b/Engine/source/console/telnetDebugger.cpp index 914dee776..8f95aaf9f 100644 --- a/Engine/source/console/telnetDebugger.cpp +++ b/Engine/source/console/telnetDebugger.cpp @@ -151,8 +151,8 @@ TelnetDebugger::TelnetDebugger() Con::addConsumer(debuggerConsumer); mAcceptPort = -1; - mAcceptSocket = InvalidSocket; - mDebugSocket = InvalidSocket; + mAcceptSocket = NetSocket::INVALID; + mDebugSocket = NetSocket::INVALID; mState = NotConnected; mCurPos = 0; @@ -189,9 +189,9 @@ TelnetDebugger::~TelnetDebugger() { Con::removeConsumer(debuggerConsumer); - if(mAcceptSocket != InvalidSocket) + if(mAcceptSocket != NetSocket::INVALID) Net::closeSocket(mAcceptSocket); - if(mDebugSocket != InvalidSocket) + if(mDebugSocket != NetSocket::INVALID) Net::closeSocket(mDebugSocket); } @@ -217,10 +217,10 @@ void TelnetDebugger::send(const char *str) void TelnetDebugger::disconnect() { - if ( mDebugSocket != InvalidSocket ) + if ( mDebugSocket != NetSocket::INVALID ) { Net::closeSocket(mDebugSocket); - mDebugSocket = InvalidSocket; + mDebugSocket = NetSocket::INVALID; } removeAllBreakpoints(); @@ -236,16 +236,20 @@ void TelnetDebugger::setDebugParameters(S32 port, const char *password, bool wai // if(port == mAcceptPort) // return; - if(mAcceptSocket != InvalidSocket) + if(mAcceptSocket != NetSocket::INVALID) { Net::closeSocket(mAcceptSocket); - mAcceptSocket = InvalidSocket; + mAcceptSocket = NetSocket::INVALID; } mAcceptPort = port; if(mAcceptPort != -1 && mAcceptPort != 0) { + NetAddress address; + Net::getIdealListenAddress(&address); + address.port = mAcceptPort; + mAcceptSocket = Net::openSocket(); - Net::bind(mAcceptSocket, mAcceptPort); + Net::bindAddress(address, mAcceptSocket); Net::listen(mAcceptSocket, 4); Net::setBlocking(mAcceptSocket, false); @@ -279,32 +283,33 @@ void TelnetDebugger::process() { NetAddress address; - if(mAcceptSocket != InvalidSocket) + if(mAcceptSocket != NetSocket::INVALID) { // ok, see if we have any new connections: NetSocket newConnection; newConnection = Net::accept(mAcceptSocket, &address); - if(newConnection != InvalidSocket && mDebugSocket == InvalidSocket) + if(newConnection != NetSocket::INVALID && mDebugSocket == NetSocket::INVALID) { - Con::printf ("Debugger connection from %i.%i.%i.%i", - address.netNum[0], address.netNum[1], address.netNum[2], address.netNum[3]); + char buffer[256]; + Net::addressToString(&address, buffer); + Con::printf("Debugger connection from %s", buffer); mState = PasswordTry; mDebugSocket = newConnection; Net::setBlocking(newConnection, false); } - else if(newConnection != InvalidSocket) + else if(newConnection != NetSocket::INVALID) Net::closeSocket(newConnection); } // see if we have any input to process... - if(mDebugSocket == InvalidSocket) + if(mDebugSocket == NetSocket::INVALID) return; checkDebugRecv(); - if(mDebugSocket == InvalidSocket) + if(mDebugSocket == NetSocket::INVALID) removeAllBreakpoints(); } @@ -434,7 +439,7 @@ void TelnetDebugger::breakProcess() { Platform::sleep(10); checkDebugRecv(); - if(mDebugSocket == InvalidSocket) + if(mDebugSocket == NetSocket::INVALID) { mProgramPaused = false; removeAllBreakpoints(); diff --git a/Engine/source/core/stream/stream.cpp b/Engine/source/core/stream/stream.cpp index 50f99ae63..247feef76 100644 --- a/Engine/source/core/stream/stream.cpp +++ b/Engine/source/core/stream/stream.cpp @@ -300,16 +300,7 @@ bool Stream::write(const NetAddress &na) { bool success = write(na.type); success &= write(na.port); - success &= write(na.netNum[0]); - success &= write(na.netNum[1]); - success &= write(na.netNum[2]); - success &= write(na.netNum[3]); - success &= write(na.nodeNum[0]); - success &= write(na.nodeNum[1]); - success &= write(na.nodeNum[2]); - success &= write(na.nodeNum[3]); - success &= write(na.nodeNum[4]); - success &= write(na.nodeNum[5]); + success &= write(sizeof(na.address), &na.address); return success; } @@ -317,16 +308,20 @@ bool Stream::read(NetAddress *na) { bool success = read(&na->type); success &= read(&na->port); - success &= read(&na->netNum[0]); - success &= read(&na->netNum[1]); - success &= read(&na->netNum[2]); - success &= read(&na->netNum[3]); - success &= read(&na->nodeNum[0]); - success &= read(&na->nodeNum[1]); - success &= read(&na->nodeNum[2]); - success &= read(&na->nodeNum[3]); - success &= read(&na->nodeNum[4]); - success &= read(&na->nodeNum[5]); + success &= read(sizeof(na->address), &na->address); + return success; +} + +bool Stream::write(const NetSocket &so) +{ + return write(so.getHandle()); +} + +bool Stream::read(NetSocket* so) +{ + S32 handle = -1; + bool success = read(&handle); + *so = NetSocket::fromHandle(handle); return success; } diff --git a/Engine/source/core/stream/stream.h b/Engine/source/core/stream/stream.h index bc2495d70..b52d8f987 100644 --- a/Engine/source/core/stream/stream.h +++ b/Engine/source/core/stream/stream.h @@ -45,6 +45,7 @@ class ColorF; struct NetAddress; class RawData; class String; +class NetSocket; namespace Torque { class ByteBuffer; @@ -165,6 +166,11 @@ public: /// Read a network address from the stream. bool read(NetAddress*); + /// Write a network socket to the stream. + bool write(const NetSocket &); + /// Read a network socket from the stream. + bool read(NetSocket*); + /// Write some raw data onto the stream. bool write(const RawData &); /// Read some raw data from the stream. diff --git a/Engine/source/platform/platformNet.cpp b/Engine/source/platform/platformNet.cpp index 6ccef9db1..2c79f58eb 100644 --- a/Engine/source/platform/platformNet.cpp +++ b/Engine/source/platform/platformNet.cpp @@ -21,12 +21,19 @@ //----------------------------------------------------------------------------- #include "platform/platformNet.h" +#include "platform/threads/mutex.h" #include "core/strings/stringFunctions.h" +#include "core/util/hashFunction.h" +#include "console/consoleTypes.h" + +// jamesu - debug DNS +//#define TORQUE_DEBUG_LOOKUPS + #if defined (TORQUE_OS_WIN) #define TORQUE_USE_WINSOCK #include -#include +#include #ifndef EINPROGRESS #define EINPROGRESS WSAEINPROGRESS @@ -52,13 +59,14 @@ typedef sockaddr_in SOCKADDR_IN; typedef sockaddr * PSOCKADDR; typedef sockaddr SOCKADDR; typedef in_addr IN_ADDR; +typedef int SOCKET; #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket close -#elif defined TORQUE_OS_LINUX +#elif defined( TORQUE_OS_LINUX ) #include #include @@ -69,11 +77,15 @@ typedef in_addr IN_ADDR; #include #include #include +#include typedef sockaddr_in SOCKADDR_IN; +typedef sockaddr_in6 SOCKADDR_IN6; typedef sockaddr * PSOCKADDR; typedef sockaddr SOCKADDR; typedef in_addr IN_ADDR; +typedef in6_addr IN6_ADDR; +typedef int SOCKET; #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 @@ -138,52 +150,329 @@ static const char* strerror_wsa( S32 code ) #include "core/util/journal/process.h" #include "core/util/journal/journal.h" -static Net::Error getLastError(); -static S32 defaultPort = 28000; -static S32 netPort = 0; -static NetSocket udpSocket = InvalidSocket; + +NetSocket NetSocket::INVALID = NetSocket::fromHandle(-1); + +template class ReservedSocketList +{ +public: + Vector mSocketList; + Mutex *mMutex; + + ReservedSocketList() + { + mMutex = new Mutex; + } + + ~ReservedSocketList() + { + delete mMutex; + } + + inline void modify() { Mutex::lockMutex(mMutex); } + inline void endModify() { Mutex::unlockMutex(mMutex); } + + NetSocket reserve(SOCKET reserveId = -1, bool doLock = true); + void remove(NetSocket socketToRemove, bool doLock = true); + + T activate(NetSocket socketToActivate, int family, bool useUDP, bool clearOnFail = false); + T resolve(NetSocket socketToResolve); +}; + +const SOCKET InvalidSocketHandle = -1; + +static void IPSocketToNetAddress(const struct sockaddr_in *sockAddr, NetAddress *address); + +namespace PlatformNetState +{ + static S32 initCount = 0; + + static const S32 defaultPort = 28000; + static S32 netPort = 0; + + static NetSocket udpSocket = NetSocket::INVALID; + static NetSocket udp6Socket = NetSocket::INVALID; + static NetSocket multicast6Socket = NetSocket::INVALID; + + static ipv6_mreq multicast6Group; + + static ReservedSocketList smReservedSocketList; + + static Net::Error getLastError() + { +#if defined(TORQUE_USE_WINSOCK) + S32 err = WSAGetLastError(); + switch (err) + { + case 0: + return Net::NoError; + case WSAEWOULDBLOCK: + return Net::WouldBlock; + default: + return Net::UnknownError; + } +#else + if (errno == EAGAIN) + return Net::WouldBlock; + if (errno == 0) + return Net::NoError; + return Net::UnknownError; +#endif + } + + static S32 getDefaultGameProtocol() + { + // we turn off VDP in non-release builds because VDP does not support broadcast packets + // which are required for LAN queries (PC->Xbox connectivity). The wire protocol still + // uses the VDP packet structure, though. + S32 protocol = IPPROTO_UDP; + bool useVDP = false; +#ifdef TORQUE_DISABLE_PC_CONNECTIVITY + // Xbox uses a VDP (voice/data protocol) socket for networking + protocol = IPPROTO_VDP; + useVDP = true; +#endif + + return protocol; + } + + static struct addrinfo* pickAddressByProtocol(struct addrinfo* addr, int protocol) + { + for (addr; addr != NULL; addr = addr->ai_next) + { + if (addr->ai_family == protocol) + return addr; + } + + return NULL; + } + + /// Extracts core address parts from an address string. Returns false if it's malformed. + static bool extractAddressParts(const char *addressString, char outAddress[256], int &outPort, int &outFamily) + { + outPort = 0; + outFamily = AF_UNSPEC; + + if (!dStrnicmp(addressString, "ipx:", 4)) + // ipx support deprecated + return false; + + if (!dStrnicmp(addressString, "ip:", 3)) + { + addressString += 3; // eat off the ip: + outFamily = AF_INET; + } + else if (!dStrnicmp(addressString, "ip6:", 4)) + { + addressString += 4; // eat off the ip6: + outFamily = AF_INET6; + } + + if (strlen(addressString) > 255) + return false; + + char *portString = NULL; + + if (addressString[0] == '[') + { + // Must be ipv6 notation + dStrcpy(outAddress, addressString+1); + addressString = outAddress; + + portString = dStrchr(outAddress, ']'); + if (portString) + { + // Sort out the :port after the ] + *portString++ = '\0'; + if (*portString != ':') + { + portString = NULL; + } + else + { + *portString++ = '\0'; + } + } + + if (outFamily == AF_UNSPEC) + { + outFamily = AF_INET6; + } + } + else + { + dStrcpy(outAddress, addressString); + addressString = outAddress; + + // Check to see if we have multiple ":" which would indicate this is an ipv6 address + char* scan = outAddress; + int colonCount = 0; + while (*scan != '\0' && colonCount < 2) + { + if (*scan++ == ':') + colonCount++; + } + if (colonCount <= 1) + { + // either ipv4 or host + portString = dStrchr(outAddress, ':'); + + if (portString) + { + *portString++ = '\0'; + } + } + else if (outFamily == AF_UNSPEC) + { + // Must be ipv6 + outFamily = AF_INET6; + } + } + + if (portString) + { + outPort = dAtoi(portString); + } + + return true; + } +}; + + + +template NetSocket ReservedSocketList::reserve(SOCKET reserveId, bool doLock) +{ + MutexHandle handle; + if (doLock) + { + handle.lock(mMutex, true); + } + + S32 idx = mSocketList.find_next(-1); + if (idx == -1) + { + mSocketList.push_back(reserveId); + return NetSocket::fromHandle(mSocketList.size() - 1); + } + else + { + mSocketList[idx] = reserveId; + } + + return NetSocket::fromHandle(idx); +} + +template void ReservedSocketList::remove(NetSocket socketToRemove, bool doLock) +{ + MutexHandle handle; + if (doLock) + { + handle.lock(mMutex, true); + } + + if ((U32)socketToRemove.getHandle() >= (U32)mSocketList.size()) + return; + + mSocketList[socketToRemove.getHandle()] = -1; +} + +template T ReservedSocketList::activate(NetSocket socketToActivate, int family, bool useUDP, bool clearOnFail) +{ + MutexHandle h; + h.lock(mMutex, true); + + int typeID = useUDP ? SOCK_DGRAM : SOCK_STREAM; + int protocol = useUDP ? PlatformNetState::getDefaultGameProtocol() : 0; + + if ((U32)socketToActivate.getHandle() >= (U32)mSocketList.size()) + return -1; + + T socketFd = mSocketList[socketToActivate.getHandle()]; + if (socketFd == -1) + { + socketFd = ::socket(family, typeID, protocol); + + if (socketFd == InvalidSocketHandle) + { + if (clearOnFail) + { + remove(socketToActivate, false); + } + return InvalidSocketHandle; + } + else + { + mSocketList[socketToActivate.getHandle()] = socketFd; + return socketFd; + } + } + + return socketFd; +} + +template T ReservedSocketList::resolve(NetSocket socketToResolve) +{ + MutexHandle h; + h.lock(mMutex, true); + + if ((U32)socketToResolve.getHandle() >= (U32)mSocketList.size()) + return -1; + + return mSocketList[socketToResolve.getHandle()]; +} ConnectionNotifyEvent Net::smConnectionNotify; ConnectionAcceptedEvent Net::smConnectionAccept; ConnectionReceiveEvent Net::smConnectionReceive; PacketReceiveEvent Net::smPacketReceive; -// local enum for socket states for polled sockets -enum SocketState -{ - InvalidState, - Connected, - ConnectionPending, - Listening, - NameLookupRequired -}; +// Multicast stuff +bool Net::smMulticastEnabled = true; +// +// Protocol Stuff +bool Net::smIpv4Enabled = true; +bool Net::smIpv6Enabled = false; +// // the Socket structure helps us keep track of the // above states -struct Socket +struct PolledSocket { - Socket() + // local enum for socket states for polled sockets + enum SocketState { - fd = InvalidSocket; + InvalidState, + Connected, + ConnectionPending, + Listening, + NameLookupRequired + }; + + PolledSocket() + { + fd = -1; + handleFd = NetSocket::INVALID; state = InvalidState; remoteAddr[0] = 0; remotePort = -1; } - NetSocket fd; + SOCKET fd; + NetSocket handleFd; S32 state; char remoteAddr[256]; S32 remotePort; }; // list of polled sockets -static Vector gPolledSockets( __FILE__, __LINE__ ); +static Vector gPolledSockets( __FILE__, __LINE__ ); -static Socket* addPolledSocket(NetSocket& fd, S32 state, +static PolledSocket* addPolledSocket(NetSocket handleFd, SOCKET fd, S32 state, char* remoteAddr = NULL, S32 port = -1) { - Socket* sock = new Socket(); + PolledSocket* sock = new PolledSocket(); sock->fd = fd; + sock->handleFd = handleFd; sock->state = state; if (remoteAddr) dStrcpy(sock->remoteAddr, remoteAddr); @@ -193,34 +482,28 @@ static Socket* addPolledSocket(NetSocket& fd, S32 state, return sock; } -enum { - MaxConnections = 1024, -}; - - -bool netSocketWaitForWritable(NetSocket fd, S32 timeoutMs) +bool netSocketWaitForWritable(NetSocket handleFd, S32 timeoutMs) { fd_set writefds; timeval timeout; + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); - FD_ZERO(&writefds); - FD_SET( fd, &writefds ); + FD_ZERO( &writefds ); + FD_SET( socketFd, &writefds ); timeout.tv_sec = timeoutMs / 1000; timeout.tv_usec = ( timeoutMs % 1000 ) * 1000; - if( select(fd + 1, NULL, &writefds, NULL, &timeout) > 0 ) + if( select(socketFd + 1, NULL, &writefds, NULL, &timeout) > 0 ) return true; return false; } -static S32 initCount = 0; - bool Net::init() { #if defined(TORQUE_USE_WINSOCK) - if(!initCount) + if(!PlatformNetState::initCount) { #ifdef TORQUE_OS_XENON // Configure startup parameters @@ -242,7 +525,7 @@ bool Net::init() //logprintf("Winsock initialization %s", success ? "succeeded." : "failed!"); } #endif - initCount++; + PlatformNetState::initCount++; Process::notify(&Net::process, PROCESS_NET_ORDER); @@ -254,13 +537,13 @@ void Net::shutdown() Process::remove(&Net::process); while (gPolledSockets.size() > 0) - closeConnectTo(gPolledSockets[0]->fd); + closeConnectTo(gPolledSockets[0]->handleFd); closePort(); - initCount--; + PlatformNetState::initCount--; #if defined(TORQUE_USE_WINSOCK) - if(!initCount) + if(!PlatformNetState::initCount) { WSACleanup(); @@ -271,175 +554,235 @@ void Net::shutdown() #endif } -Net::Error getLastError() -{ -#if defined(TORQUE_USE_WINSOCK) - S32 err = WSAGetLastError(); - switch(err) - { - case 0: - return Net::NoError; - case WSAEWOULDBLOCK: - return Net::WouldBlock; - default: - return Net::UnknownError; - } -#else - if (errno == EAGAIN) - return Net::WouldBlock; - if (errno == 0) - return Net::NoError; - return Net::UnknownError; -#endif -} +// ipv4 version of name routines -static void netToIPSocketAddress(const NetAddress *address, struct sockaddr_in *sockAddr) +static void NetAddressToIPSocket(const NetAddress *address, struct sockaddr_in *sockAddr) { dMemset(sockAddr, 0, sizeof(struct sockaddr_in)); sockAddr->sin_family = AF_INET; sockAddr->sin_port = htons(address->port); - char tAddr[20]; - dSprintf(tAddr, 20, "%d.%d.%d.%d", address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3]); - //fprintf(stdout,"netToIPSocketAddress(): %s\n",tAddr);fflush(NULL); - sockAddr->sin_addr.s_addr = inet_addr(tAddr); + #if defined(TORQUE_OS_BSD) + sockAddr->sin_len = sizeof(struct sockaddr_in); + #endif + if (address->type == NetAddress::IPBroadcastAddress) + { + sockAddr->sin_addr.s_addr = htonl(INADDR_BROADCAST); + } + else + { + dMemcpy(&sockAddr->sin_addr, &address->address.ipv4.netNum[0], 4); + } } -static void IPSocketToNetAddress(const struct sockaddr_in *sockAddr, NetAddress *address) +static void IPSocketToNetAddress(const struct sockaddr_in *sockAddr, NetAddress *address) { address->type = NetAddress::IPAddress; - address->port = htons(sockAddr->sin_port); -#ifndef TORQUE_OS_XENON - char *tAddr; - tAddr = inet_ntoa(sockAddr->sin_addr); - //fprintf(stdout,"IPSocketToNetAddress(): %s\n",tAddr);fflush(NULL); - U8 nets[4]; - nets[0] = atoi(strtok(tAddr, ".")); - nets[1] = atoi(strtok(NULL, ".")); - nets[2] = atoi(strtok(NULL, ".")); - nets[3] = atoi(strtok(NULL, ".")); - //fprintf(stdout,"0 = %d, 1 = %d, 2 = %d, 3 = %d\n", nets[0], nets[1], nets[2], nets[3]); - address->netNum[0] = nets[0]; - address->netNum[1] = nets[1]; - address->netNum[2] = nets[2]; - address->netNum[3] = nets[3]; -#else - address->netNum[0] = sockAddr->sin_addr.s_net; - address->netNum[1] = sockAddr->sin_addr.s_host; - address->netNum[2] = sockAddr->sin_addr.s_lh; - address->netNum[3] = sockAddr->sin_addr.s_impno; -#endif + address->port = ntohs(sockAddr->sin_port); + dMemcpy(&address->address.ipv4.netNum[0], &sockAddr->sin_addr, 4); } -NetSocket Net::openListenPort(U16 port) +// ipv6 version of name routines + +static void NetAddressToIPSocket6(const NetAddress *address, struct sockaddr_in6 *sockAddr) +{ + dMemset(sockAddr, 0, sizeof(struct sockaddr_in6)); +#ifdef SIN6_LEN + sockAddr->sin6_len = sizeof(struct sockaddr_in6); +#endif + sockAddr->sin6_family = AF_INET6; + sockAddr->sin6_port = ntohs(address->port); + + if (address->type == NetAddress::IPV6MulticastAddress) + { + sockAddr->sin6_addr = PlatformNetState::multicast6Group.ipv6mr_multiaddr; + sockAddr->sin6_scope_id = PlatformNetState::multicast6Group.ipv6mr_interface; + } + else + { + sockAddr->sin6_flowinfo = address->address.ipv6.netFlow; + sockAddr->sin6_scope_id = address->address.ipv6.netScope; + dMemcpy(&sockAddr->sin6_addr, address->address.ipv6.netNum, sizeof(address->address.ipv6.netNum)); + } +} + +static void IPSocket6ToNetAddress(const struct sockaddr_in6 *sockAddr, NetAddress *address) +{ + address->type = NetAddress::IPV6Address; + address->port = ntohs(sockAddr->sin6_port); + dMemcpy(address->address.ipv6.netNum, &sockAddr->sin6_addr, sizeof(address->address.ipv6.netNum)); + address->address.ipv6.netFlow = sockAddr->sin6_flowinfo; + address->address.ipv6.netScope = sockAddr->sin6_scope_id; +} + +// + +NetSocket Net::openListenPort(U16 port, NetAddress::Type addressType) { if(Journal::IsPlaying()) { U32 ret; Journal::Read(&ret); - return NetSocket(ret); + return NetSocket::fromHandle(ret); } - NetSocket sock = openSocket(); - if (sock == InvalidSocket) + Net::Error error = NoError; + NetAddress address; + if (!Net::getListenAddress(addressType, &address)) + error = Net::WrongProtocolType; + + NetSocket handleFd = NetSocket::INVALID; + SOCKET sockId = InvalidSocketHandle; + + if (error == NoError) + { + handleFd = openSocket(); + sockId = PlatformNetState::smReservedSocketList.activate(handleFd, address.type == NetAddress::IPAddress ? AF_INET : AF_INET6, false, true); + } + + if (error == NoError && (handleFd == NetSocket::INVALID || sockId == InvalidSocketHandle)) { Con::errorf("Unable to open listen socket: %s", strerror(errno)); - return InvalidSocket; + error = NotASocket; + handleFd = NetSocket::INVALID; } - if (bind(sock, port) != NoError) + if (error == NoError) { - Con::errorf("Unable to bind port %d: %s", port, strerror(errno)); - ::closesocket(sock); - return InvalidSocket; - } - if (listen(sock, 4) != NoError) - { - Con::errorf("Unable to listen on port %d: %s", port, strerror(errno)); - ::closesocket(sock); - return InvalidSocket; + address.port = port; + error = bindAddress(address, handleFd, false); + if (error != NoError) + { + Con::errorf("Unable to bind port %d: %s", port, strerror(errno)); + closeSocket(handleFd); + handleFd = NetSocket::INVALID; + } } - setBlocking(sock, false); - addPolledSocket(sock, Listening); + if (error == NoError) + { + error = listen(handleFd, 4); + if (error != NoError) + { + Con::errorf("Unable to listen on port %d: %s", port, strerror(errno)); + closeSocket(handleFd); + handleFd = NetSocket::INVALID; + } + } + + if (error == NoError) + { + setBlocking(handleFd, false); + addPolledSocket(handleFd, sockId, PolledSocket::Listening); + } if(Journal::IsRecording()) - Journal::Write(U32(sock)); + Journal::Write(U32(handleFd.getHandle())); - return sock; + return handleFd; } NetSocket Net::openConnectTo(const char *addressString) { - if(!dStrnicmp(addressString, "ipx:", 4)) - // ipx support deprecated - return InvalidSocket; - if(!dStrnicmp(addressString, "ip:", 3)) - addressString += 3; // eat off the ip: - char remoteAddr[256]; - dStrcpy(remoteAddr, addressString); - - char *portString = dStrchr(remoteAddr, ':'); - - U16 port; - if(portString) - { - *portString++ = 0; - port = htons(dAtoi(portString)); - } - else - port = htons(defaultPort); - - if(!dStricmp(remoteAddr, "broadcast")) - return InvalidSocket; - - if(Journal::IsPlaying()) + if (Journal::IsPlaying()) { U32 ret; Journal::Read(&ret); - return NetSocket(ret); + return NetSocket::fromHandle(ret); } - NetSocket sock = openSocket(); - setBlocking(sock, false); - sockaddr_in ipAddr; - dMemset(&ipAddr, 0, sizeof(ipAddr)); - ipAddr.sin_addr.s_addr = inet_addr(remoteAddr); + NetAddress address; + NetSocket handleFd = NetSocket::INVALID; + Net::Error error = NoError; - if(ipAddr.sin_addr.s_addr != INADDR_NONE) + error = Net::stringToAddress(addressString, &address, false); + + if (error == NoError && address.type != NetAddress::IPAddress && address.type != NetAddress::IPV6Address) { - ipAddr.sin_port = port; - ipAddr.sin_family = AF_INET; - if(::connect(sock, (struct sockaddr *)&ipAddr, sizeof(ipAddr)) == -1) + error = Net::WrongProtocolType; + } + + if (error != NoError || error == NeedHostLookup) + { + handleFd = openSocket(); + } + + // Attempt to connect or queue a lookup + if (error == NoError && address.type == NetAddress::IPAddress) + { + sockaddr_in ipAddr; + NetAddressToIPSocket(&address, &ipAddr); + SOCKET socketFd = PlatformNetState::smReservedSocketList.activate(handleFd, AF_INET, false, true); + if (socketFd != InvalidSocketHandle) { - S32 err = getLastError(); - if(err != Net::WouldBlock) + setBlocking(handleFd, false); + if (::connect(socketFd, (struct sockaddr *)&ipAddr, sizeof(ipAddr)) == -1 && + errno != EINPROGRESS) { Con::errorf("Error connecting %s: %s", - addressString, strerror(err)); - ::closesocket(sock); - sock = InvalidSocket; + addressString, strerror(errno)); + closeSocket(handleFd); + handleFd = NetSocket::INVALID; } } - if(sock != InvalidSocket) + else + { + PlatformNetState::smReservedSocketList.remove(handleFd); + handleFd = NetSocket::INVALID; + } + + if (handleFd != NetSocket::INVALID) { // add this socket to our list of polled sockets - addPolledSocket(sock, ConnectionPending); + addPolledSocket(handleFd, socketFd, PolledSocket::ConnectionPending); } } - else + else if (error == NoError && address.type == NetAddress::IPV6Address) + { + sockaddr_in6 ipAddr6; + NetAddressToIPSocket6(&address, &ipAddr6); + SOCKET socketFd = PlatformNetState::smReservedSocketList.activate(handleFd, AF_INET6, false, true); + if (::connect(socketFd, (struct sockaddr *)&ipAddr6, sizeof(ipAddr6)) == -1 && + errno != EINPROGRESS) + { + setBlocking(handleFd, false); + Con::errorf("Error connecting %s: %s", + addressString, strerror(errno)); + closeSocket(handleFd); + handleFd = NetSocket::INVALID; + } + else + { + PlatformNetState::smReservedSocketList.remove(handleFd); + handleFd = NetSocket::INVALID; + } + + if (handleFd != NetSocket::INVALID) + { + // add this socket to our list of polled sockets + addPolledSocket(handleFd, socketFd, PolledSocket::ConnectionPending); + } + } + else if (error == Net::NeedHostLookup) { // need to do an asynchronous name lookup. first, add the socket // to the polled list - addPolledSocket(sock, NameLookupRequired, remoteAddr, port); + char addressString[256]; + Net::addressToString(&address, addressString); + addPolledSocket(handleFd, InvalidSocketHandle, PolledSocket::NameLookupRequired, addressString, address.port); // queue the lookup - gNetAsync.queueLookup(remoteAddr, sock); + gNetAsync.queueLookup(addressString, handleFd); } - if(Journal::IsRecording()) - Journal::Write(U32(sock)); - return sock; + else + { + handleFd = NetSocket::INVALID; + } + + if (Journal::IsRecording()) + Journal::Write(U32(handleFd.getHandle())); + return handleFd; } -void Net::closeConnectTo(NetSocket sock) +void Net::closeConnectTo(NetSocket handleFd) { if(Journal::IsPlaying()) return; @@ -447,7 +790,7 @@ void Net::closeConnectTo(NetSocket sock) // if this socket is in the list of polled sockets, remove it for (S32 i = 0; i < gPolledSockets.size(); ++i) { - if (gPolledSockets[i]->fd == sock) + if (gPolledSockets[i]->handleFd == handleFd) { delete gPolledSockets[i]; gPolledSockets.erase(i); @@ -455,10 +798,10 @@ void Net::closeConnectTo(NetSocket sock) } } - closeSocket(sock); + closeSocket(handleFd); } -Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, S32 bufferSize) +Net::Error Net::sendtoSocket(NetSocket handleFd, const U8 *buffer, S32 bufferSize) { if(Journal::IsPlaying()) { @@ -468,7 +811,7 @@ Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, S32 bufferSize return (Net::Error) e; } - Net::Error e = send(socket, buffer, bufferSize); + Net::Error e = send(handleFd, buffer, bufferSize); if(Journal::IsRecording()) Journal::Write(U32(e)); @@ -478,65 +821,149 @@ Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, S32 bufferSize bool Net::openPort(S32 port, bool doBind) { - if(udpSocket != InvalidSocket) - ::closesocket(udpSocket); + if (PlatformNetState::udpSocket != NetSocket::INVALID) + { + closeSocket(PlatformNetState::udpSocket); + PlatformNetState::udpSocket = NetSocket::INVALID; + } + if (PlatformNetState::udp6Socket != NetSocket::INVALID) + { + closeSocket(PlatformNetState::udp6Socket); + PlatformNetState::udp6Socket = NetSocket::INVALID; + } + + // Frequently port "0" is used even though it makes no sense, so instead use the default port. + if (port == 0) + { + port = PlatformNetState::defaultPort; + } + + // Update prefs + Net::smMulticastEnabled = Con::getBoolVariable("pref::Net::Multicast6Enabled", true); + Net::smIpv4Enabled = Con::getBoolVariable("pref::Net::IPV4Enabled", true); + Net::smIpv6Enabled = Con::getBoolVariable("pref::Net::IPV6Enabled", false); // we turn off VDP in non-release builds because VDP does not support broadcast packets // which are required for LAN queries (PC->Xbox connectivity). The wire protocol still // uses the VDP packet structure, though. - S32 protocol = 0; - bool useVDP = false; -#ifdef TORQUE_DISABLE_PC_CONNECTIVITY - // Xbox uses a VDP (voice/data protocol) socket for networking - protocol = IPPROTO_VDP; - useVDP = true; -#endif + S32 protocol = PlatformNetState::getDefaultGameProtocol(); - udpSocket = socket(AF_INET, SOCK_DGRAM, protocol); + SOCKET socketFd = InvalidSocketHandle; + NetAddress address; - if(udpSocket != InvalidSocket) + if (Net::smIpv4Enabled) { - Net::Error error = NoError; - if (doBind) - { - error = bind(udpSocket, port); - } - - if(error == NoError) - error = setBufferSize(udpSocket, 32768*8); - - if(error == NoError && !useVDP) - error = setBroadcast(udpSocket, true); - - if(error == NoError) - error = setBlocking(udpSocket, false); - - if(error == NoError) - Con::printf("UDP initialized on port %d", port); + if (Net::getListenAddress(NetAddress::IPAddress, &address)) + { + address.port = port; + socketFd = ::socket(AF_INET, SOCK_DGRAM, protocol); + + if (socketFd != InvalidSocketHandle) + { + PlatformNetState::udpSocket = PlatformNetState::smReservedSocketList.reserve(socketFd); + Net::Error error = NoError; + if (doBind) + { + error = bindAddress(address, PlatformNetState::udpSocket, true); + } + + if (error == NoError) + error = setBufferSize(PlatformNetState::udpSocket, 32768 * 8); + +#ifndef TORQUE_DISABLE_PC_CONNECTIVITY + if (error == NoError) + error = setBroadcast(PlatformNetState::udpSocket, true); +#endif + + if (error == NoError) + error = setBlocking(PlatformNetState::udpSocket, false); + + if (error == NoError) + { + Con::printf("UDP initialized on ipv4 port %d", port); + } + else + { + closeSocket(PlatformNetState::udpSocket); + PlatformNetState::udpSocket = NetSocket::INVALID; + Con::printf("Unable to initialize UDP on ipv4 - error %d", error); + } + } + } else { - ::closesocket(udpSocket); - udpSocket = InvalidSocket; - Con::printf("Unable to initialize UDP - error %d", error); + Con::errorf("Unable to initialize UDP on ipv4 - invalid address."); + PlatformNetState::udpSocket = NetSocket::INVALID; } } - netPort = port; - return udpSocket != InvalidSocket; + + if (Net::smIpv6Enabled) + { + if (Net::getListenAddress(NetAddress::IPV6Address, &address)) + { + address.port = port; + socketFd = ::socket(AF_INET6, SOCK_DGRAM, protocol); + + if (socketFd != InvalidSocketHandle) + { + PlatformNetState::udp6Socket = PlatformNetState::smReservedSocketList.reserve(socketFd); + + Net::Error error = NoError; + + int v = 1; + setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&v, sizeof(v)); + PlatformNetState::getLastError(); + + if (doBind) + { + error = bindAddress(address, PlatformNetState::udp6Socket, true); + } + + if (error == NoError) + error = setBufferSize(PlatformNetState::udp6Socket, 32768 * 8); + + if (error == NoError) + error = setBlocking(PlatformNetState::udp6Socket, false); + + if (error == NoError) + { + Con::printf("UDP initialized on ipv6 port %d", port); + } + else + { + closeSocket(PlatformNetState::udp6Socket); + PlatformNetState::udp6Socket = NetSocket::INVALID; + Con::printf("Unable to initialize UDP on ipv6 - error %d", error); + } + + if (Net::smMulticastEnabled && doBind) + { + Net::enableMulticast(); + } + else + { + Net::disableMulticast(); + } + } + } + } + + PlatformNetState::netPort = port; + + return PlatformNetState::udpSocket != NetSocket::INVALID || PlatformNetState::udp6Socket != NetSocket::INVALID; } NetSocket Net::getPort() - { - - return udpSocket; - + return PlatformNetState::udpSocket; } - void Net::closePort() { - if(udpSocket != InvalidSocket) - ::closesocket(udpSocket); + if (PlatformNetState::udpSocket != NetSocket::INVALID) + closeSocket(PlatformNetState::udpSocket); + if (PlatformNetState::udp6Socket != NetSocket::INVALID) + closeSocket(PlatformNetState::udp6Socket); } Net::Error Net::sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize) @@ -544,67 +971,55 @@ Net::Error Net::sendto(const NetAddress *address, const U8 *buffer, S32 bufferS if(Journal::IsPlaying()) return NoError; - if(address->type == NetAddress::IPAddress) + SOCKET socketFd; + + if(address->type == NetAddress::IPAddress || address->type == NetAddress::IPBroadcastAddress) { - sockaddr_in ipAddr; - netToIPSocketAddress(address, &ipAddr); - if(::sendto(udpSocket, (const char*)buffer, bufferSize, 0, - (sockaddr *) &ipAddr, sizeof(sockaddr_in)) == SOCKET_ERROR) - return getLastError(); + socketFd = PlatformNetState::smReservedSocketList.resolve(PlatformNetState::udpSocket); + if (socketFd != InvalidSocketHandle) + { + sockaddr_in ipAddr; + NetAddressToIPSocket(address, &ipAddr); + + if (::sendto(socketFd, (const char*)buffer, bufferSize, 0, + (sockaddr *)&ipAddr, sizeof(sockaddr_in)) == SOCKET_ERROR) + return PlatformNetState::getLastError(); + else + return NoError; + } else - return NoError; + { + return NotASocket; + } } - else + else if (address->type == NetAddress::IPV6Address || address->type == NetAddress::IPV6MulticastAddress) { - SOCKADDR_IN ipAddr; - netToIPSocketAddress(address, &ipAddr); - if(::sendto(udpSocket, (const char*)buffer, bufferSize, 0, - (PSOCKADDR) &ipAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) - return getLastError(); + socketFd = PlatformNetState::smReservedSocketList.resolve(address->type == NetAddress::IPV6MulticastAddress ? PlatformNetState::multicast6Socket : PlatformNetState::udp6Socket); + + if (socketFd != InvalidSocketHandle) + { + sockaddr_in6 ipAddr; + NetAddressToIPSocket6(address, &ipAddr); + if (::sendto(socketFd, (const char*)buffer, bufferSize, 0, + (struct sockaddr *) &ipAddr, sizeof(sockaddr_in6)) == SOCKET_ERROR) + return PlatformNetState::getLastError(); + else + return NoError; + } else - return NoError; + { + return NotASocket; + } } + + return WrongProtocolType; } void Net::process() { - sockaddr sa; - sa.sa_family = AF_UNSPEC; - NetAddress srcAddress; - RawData tmpBuffer; - tmpBuffer.alloc(MaxPacketDataSize); - - for(;;) - { - socklen_t addrLen = sizeof(sa); - S32 bytesRead = -1; - - if(udpSocket != InvalidSocket) - bytesRead = recvfrom(udpSocket, (char *) tmpBuffer.data, MaxPacketDataSize, 0, &sa, &addrLen); - - if(bytesRead == -1) - break; - - if(sa.sa_family == AF_INET) - IPSocketToNetAddress((sockaddr_in *) &sa, &srcAddress); - else - continue; - - if(bytesRead <= 0) - continue; - - if(srcAddress.type == NetAddress::IPAddress && - srcAddress.netNum[0] == 127 && - srcAddress.netNum[1] == 0 && - srcAddress.netNum[2] == 0 && - srcAddress.netNum[3] == 1 && - srcAddress.port == netPort) - continue; - - tmpBuffer.size = bytesRead; - - Net::smPacketReceive.trigger(srcAddress, tmpBuffer); - } + // Process listening sockets + processListenSocket(PlatformNetState::udpSocket); + processListenSocket(PlatformNetState::udp6Socket); // process the polled sockets. This blob of code performs functions // similar to WinsockProc in winNet.cc @@ -617,10 +1032,9 @@ void Net::process() S32 bytesRead; Net::Error err; bool removeSock = false; - Socket *currentSock = NULL; - sockaddr_in ipAddr; - NetSocket incoming = InvalidSocket; - char out_h_addr[1024]; + PolledSocket *currentSock = NULL; + NetSocket incomingHandleFd = NetSocket::INVALID; + NetAddress out_h_addr; S32 out_h_length = 0; RawData readBuff; @@ -631,10 +1045,10 @@ void Net::process() currentSock = gPolledSockets[i]; switch (currentSock->state) { - case ::InvalidState: + case PolledSocket::InvalidState: Con::errorf("Error, InvalidState socket in polled sockets list"); break; - case ::ConnectionPending: + case PolledSocket::ConnectionPending: // see if it is now connected #ifdef TORQUE_OS_XENON // WSASetLastError has no return value, however part of the SO_ERROR behavior @@ -647,7 +1061,7 @@ void Net::process() { Con::errorf("Error getting socket options: %s", strerror(errno)); - Net::smConnectionNotify.trigger(currentSock->fd, Net::ConnectFailed); + Net::smConnectionNotify.trigger(currentSock->handleFd, Net::ConnectFailed); removeSock = true; } else @@ -659,35 +1073,35 @@ void Net::process() if (optval == 0) { // poll for writable status to be sure we're connected. - bool ready = netSocketWaitForWritable(currentSock->fd,0); + bool ready = netSocketWaitForWritable(currentSock->handleFd,0); if(!ready) break; - currentSock->state = ::Connected; - Net::smConnectionNotify.trigger(currentSock->fd, Net::Connected); + currentSock->state = PolledSocket::Connected; + Net::smConnectionNotify.trigger(currentSock->handleFd, Net::Connected); } else { // some kind of error Con::errorf("Error connecting: %s", strerror(errno)); - Net::smConnectionNotify.trigger(currentSock->fd, Net::ConnectFailed); + Net::smConnectionNotify.trigger(currentSock->handleFd, Net::ConnectFailed); removeSock = true; } } break; - case ::Connected: + case PolledSocket::Connected: // try to get some data bytesRead = 0; readBuff.alloc(MaxPacketDataSize); - err = Net::recv(currentSock->fd, (U8*)readBuff.data, MaxPacketDataSize, &bytesRead); + err = Net::recv(currentSock->handleFd, (U8*)readBuff.data, MaxPacketDataSize, &bytesRead); if(err == Net::NoError) { if (bytesRead > 0) { // got some data, post it readBuff.size = bytesRead; - Net::smConnectionReceive.trigger(currentSock->fd, readBuff); + Net::smConnectionReceive.trigger(currentSock->handleFd, readBuff); } else { @@ -696,7 +1110,7 @@ void Net::process() Con::errorf("Unexpected error on socket: %s", strerror(errno)); // zero bytes read means EOF - Net::smConnectionNotify.trigger(currentSock->fd, Net::Disconnected); + Net::smConnectionNotify.trigger(currentSock->handleFd, Net::Disconnected); removeSock = true; } @@ -704,77 +1118,113 @@ void Net::process() else if (err != Net::NoError && err != Net::WouldBlock) { Con::errorf("Error reading from socket: %s", strerror(errno)); - Net::smConnectionNotify.trigger(currentSock->fd, Net::Disconnected); + Net::smConnectionNotify.trigger(currentSock->handleFd, Net::Disconnected); removeSock = true; } break; - case ::NameLookupRequired: + case PolledSocket::NameLookupRequired: + U32 newState; + // is the lookup complete? if (!gNetAsync.checkLookup( - currentSock->fd, out_h_addr, &out_h_length, + currentSock->handleFd, &out_h_addr, &out_h_length, sizeof(out_h_addr))) break; - U32 newState; if (out_h_length == -1) { - Con::errorf("DNS lookup failed: %s", currentSock->remoteAddr); + Con::errorf("DNS lookup failed: %s", currentSock->remoteAddr); newState = Net::DNSFailed; removeSock = true; } else { // try to connect - dMemcpy(&(ipAddr.sin_addr.s_addr), out_h_addr, out_h_length); - ipAddr.sin_port = currentSock->remotePort; - ipAddr.sin_family = AF_INET; - if(::connect(currentSock->fd, (struct sockaddr *)&ipAddr, - sizeof(ipAddr)) == -1) + out_h_addr.port = currentSock->remotePort; + const sockaddr *ai_addr = NULL; + int ai_addrlen = 0; + sockaddr_in socketAddress; + sockaddr_in6 socketAddress6; + + if (out_h_addr.type == NetAddress::IPAddress) { - S32 errorCode; -#if defined(TORQUE_USE_WINSOCK) - errorCode = WSAGetLastError(); - if( errorCode == WSAEINPROGRESS || errorCode == WSAEWOULDBLOCK ) -#else - errorCode = errno; - if (errno == EINPROGRESS) + ai_addr = (const sockaddr*)&socketAddress; + ai_addrlen = sizeof(socketAddress); + NetAddressToIPSocket(&out_h_addr, &socketAddress); + + currentSock->fd = PlatformNetState::smReservedSocketList.activate(currentSock->handleFd, AF_INET, false); + setBlocking(currentSock->handleFd, false); + +#ifdef TORQUE_DEBUG_LOOKUPS + char addrString[256]; + NetAddress addr; + IPSocketToNetAddress(&socketAddress, &addr); + Net::addressToString(&addr, addrString); + Con::printf("DNS: lookup resolved to %s", addrString); #endif - { - newState = Net::DNSResolved; - currentSock->state = ::ConnectionPending; - } - else - { - const char* errorString; -#if defined(TORQUE_USE_WINSOCK) - errorString = strerror_wsa( errorCode ); -#else - errorString = strerror( errorCode ); + } + else if (out_h_addr.type == NetAddress::IPV6Address) + { + ai_addr = (const sockaddr*)&socketAddress6; + ai_addrlen = sizeof(socketAddress6); + NetAddressToIPSocket6(&out_h_addr, &socketAddress6); + + currentSock->fd = PlatformNetState::smReservedSocketList.activate(currentSock->handleFd, AF_INET6, false); + setBlocking(currentSock->handleFd, false); + +#ifdef TORQUE_DEBUG_LOOKUPS + char addrString[256]; + NetAddress addr; + IPSocket6ToNetAddress(&socketAddress6, &addr); + Net::addressToString(&addr, addrString); + Con::printf("DNS: lookup resolved to %s", addrString); #endif - Con::errorf("Error connecting to %s: %s (%i)", - currentSock->remoteAddr, errorString, errorCode); - newState = Net::ConnectFailed; - removeSock = true; - } } else { - newState = Net::Connected; - currentSock->state = Net::Connected; + Con::errorf("Error connecting to %s: Invalid Protocol", + currentSock->remoteAddr); + newState = Net::ConnectFailed; + removeSock = true; + } + + if (ai_addr) + { + if (::connect(currentSock->fd, ai_addr, + ai_addrlen) == -1) + { + if (errno == EINPROGRESS) + { + newState = Net::DNSResolved; + currentSock->state = PolledSocket::ConnectionPending; + } + else + { + Con::errorf("Error connecting to %s: %s", + currentSock->remoteAddr, strerror(errno)); + newState = Net::ConnectFailed; + removeSock = true; + } + } + else + { + newState = Net::Connected; + currentSock->state = Connected; + } } } - Net::smConnectionNotify.trigger(currentSock->fd, newState); + Net::smConnectionNotify.trigger(currentSock->handleFd, newState); break; - case ::Listening: + case PolledSocket::Listening: NetAddress incomingAddy; - incoming = Net::accept(currentSock->fd, &incomingAddy); - if(incoming != InvalidSocket) + incomingHandleFd = Net::accept(currentSock->handleFd, &incomingAddy); + if(incomingHandleFd != NetSocket::INVALID) { - setBlocking(incoming, false); - addPolledSocket(incoming, Connected); - Net::smConnectionAccept.trigger(currentSock->fd, incoming, incomingAddy); + setBlocking(incomingHandleFd, false); + addPolledSocket(incomingHandleFd, PlatformNetState::smReservedSocketList.resolve(incomingHandleFd), Connected); + Net::smConnectionAccept.trigger(currentSock->handleFd, incomingHandleFd, incomingAddy); } break; } @@ -782,142 +1232,318 @@ void Net::process() // only increment index if we're not removing the connection, since // the removal will shift the indices down by one if (removeSock) - closeConnectTo(currentSock->fd); + closeConnectTo(currentSock->handleFd); else i++; } } -NetSocket Net::openSocket() +void Net::processListenSocket(NetSocket socketHandle) { - NetSocket retSocket; - retSocket = socket(AF_INET, SOCK_STREAM, 0); + if (socketHandle == NetSocket::INVALID) + return; - if(retSocket == InvalidSocket) - return InvalidSocket; - else - return retSocket; + sockaddr_storage sa; + sa.ss_family = AF_UNSPEC; + NetAddress srcAddress; + RawData tmpBuffer; + tmpBuffer.alloc(Net::MaxPacketDataSize); + + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(socketHandle); + + for (;;) + { + socklen_t addrLen = sizeof(sa); + S32 bytesRead = -1; + + if (socketHandle != NetSocket::INVALID) + bytesRead = ::recvfrom(socketFd, (char *)tmpBuffer.data, Net::MaxPacketDataSize, 0, (struct sockaddr*)&sa, &addrLen); + + if (bytesRead == -1) + break; + + if (sa.ss_family == AF_INET) + IPSocketToNetAddress((sockaddr_in *)&sa, &srcAddress); + else if (sa.ss_family == AF_INET6) + IPSocket6ToNetAddress((sockaddr_in6 *)&sa, &srcAddress); + else + continue; + + if (bytesRead <= 0) + continue; + + if (srcAddress.type == NetAddress::IPAddress && + srcAddress.address.ipv4.netNum[0] == 127 && + srcAddress.address.ipv4.netNum[1] == 0 && + srcAddress.address.ipv4.netNum[2] == 0 && + srcAddress.address.ipv4.netNum[3] == 1 && + srcAddress.port == PlatformNetState::netPort) + continue; + + tmpBuffer.size = bytesRead; + + Net::smPacketReceive.trigger(srcAddress, tmpBuffer); + } } -Net::Error Net::closeSocket(NetSocket socket) +NetSocket Net::openSocket() { - if(socket != InvalidSocket) + return PlatformNetState::smReservedSocketList.reserve(); +} + +Net::Error Net::closeSocket(NetSocket handleFd) +{ + if(handleFd != NetSocket::INVALID) { - if(!closesocket(socket)) + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); + PlatformNetState::smReservedSocketList.remove(handleFd); + + if(!::closesocket(socketFd)) return NoError; else - return getLastError(); + return PlatformNetState::getLastError(); } else return NotASocket; } -Net::Error Net::connect(NetSocket socket, const NetAddress *address) +Net::Error Net::connect(NetSocket handleFd, const NetAddress *address) { - if(address->type != NetAddress::IPAddress) + if(!(address->type == NetAddress::IPAddress || address->type == NetAddress::IPV6Address)) return WrongProtocolType; - sockaddr_in socketAddress; - netToIPSocketAddress(address, &socketAddress); - if(!::connect(socket, (sockaddr *) &socketAddress, sizeof(socketAddress))) - return NoError; - return getLastError(); -} -Net::Error Net::listen(NetSocket socket, S32 backlog) -{ - if(!::listen(socket, backlog)) - return NoError; - return getLastError(); -} + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); -NetSocket Net::accept(NetSocket acceptSocket, NetAddress *remoteAddress) -{ - sockaddr_in socketAddress; - socklen_t addrLen = sizeof(socketAddress); - - NetSocket retVal = ::accept(acceptSocket, (sockaddr *) &socketAddress, &addrLen); - if(retVal != InvalidSocket) + if (address->type == NetAddress::IPAddress) { - IPSocketToNetAddress(&socketAddress, remoteAddress); - return retVal; - } - return InvalidSocket; -} + sockaddr_in socketAddress; + NetAddressToIPSocket(address, &socketAddress); -Net::Error Net::bind(NetSocket socket, U16 port) -{ - S32 error; - - sockaddr_in socketAddress; - dMemset((char *)&socketAddress, 0, sizeof(socketAddress)); - socketAddress.sin_family = AF_INET; - // It's entirely possible that there are two NIC cards. - // We let the user specify which one the server runs on. - - // thanks to [TPG]P1aGu3 for the name - const char* serverIP = Con::getVariable( "pref::Net::BindAddress" ); - // serverIP is guaranteed to be non-0. - AssertFatal( serverIP, "serverIP is NULL!" ); - - if( serverIP[0] != '\0' ) { - // we're not empty - socketAddress.sin_addr.s_addr = inet_addr( serverIP ); - - if( socketAddress.sin_addr.s_addr != INADDR_NONE ) { - Con::printf( "Binding server port to %s", serverIP ); - } else { - Con::warnf( ConsoleLogEntry::General, - "inet_addr() failed for %s while binding!", - serverIP ); - socketAddress.sin_addr.s_addr = INADDR_ANY; + if (socketFd == InvalidSocketHandle) + { + socketFd = PlatformNetState::smReservedSocketList.activate(handleFd, AF_INET, false); } - } else { - Con::printf( "Binding server port to default IP" ); - socketAddress.sin_addr.s_addr = INADDR_ANY; + if (!::connect(socketFd, (struct sockaddr *) &socketAddress, sizeof(socketAddress))) + return NoError; + } + else if (address->type == NetAddress::IPV6Address) + { + sockaddr_in6 socketAddress; + NetAddressToIPSocket6(address, &socketAddress); + + if (socketFd == InvalidSocketHandle) + { + socketFd = PlatformNetState::smReservedSocketList.activate(handleFd, AF_INET6, false); + } + + if (!::connect(socketFd, (struct sockaddr *) &socketAddress, sizeof(socketAddress))) + return NoError; } - socketAddress.sin_port = htons(port); - error = ::bind(socket, (sockaddr *) &socketAddress, sizeof(socketAddress)); - - if(!error) - return NoError; - return getLastError(); + return PlatformNetState::getLastError(); } -Net::Error Net::setBufferSize(NetSocket socket, S32 bufferSize) +Net::Error Net::listen(NetSocket handleFd, S32 backlog) +{ + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); + if (socketFd == InvalidSocketHandle) + return NotASocket; + + if(!::listen(socketFd, backlog)) + return NoError; + return PlatformNetState::getLastError(); +} + +NetSocket Net::accept(NetSocket handleFd, NetAddress *remoteAddress) +{ + sockaddr_storage addr; + socklen_t addrLen = sizeof(addr); + + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); + if (socketFd == InvalidSocketHandle) + return NetSocket::INVALID; + + SOCKET acceptedSocketFd = ::accept(socketFd, (sockaddr *)&addr, &addrLen); + if (acceptedSocketFd != InvalidSocketHandle) + { + if (addr.ss_family == AF_INET) + { + // ipv4 + IPSocketToNetAddress(((struct sockaddr_in*)&addr), remoteAddress); + } + else if (addr.ss_family == AF_INET6) + { + // ipv6 + IPSocket6ToNetAddress(((struct sockaddr_in6*)&addr), remoteAddress); + } + + NetSocket newHandleFd = PlatformNetState::smReservedSocketList.reserve(acceptedSocketFd); + return newHandleFd; + } + + return NetSocket::INVALID; +} + +Net::Error Net::bindAddress(const NetAddress &address, NetSocket handleFd, bool useUDP) +{ + int error = 0; + sockaddr_storage socketAddress; + + dMemset(&socketAddress, '\0', sizeof(socketAddress)); + + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); + if (socketFd == InvalidSocketHandle) + { + if (handleFd.getHandle() == -1) + return NotASocket; + } + + if (address.type == NetAddress::IPAddress) + { + socketFd = PlatformNetState::smReservedSocketList.activate(handleFd, AF_INET, useUDP); + NetAddressToIPSocket(&address, (struct sockaddr_in*)&socketAddress); + error = ::bind(socketFd, (struct sockaddr*)&socketAddress, sizeof(sockaddr_in)); + } + else if (address.type == NetAddress::IPV6Address) + { + socketFd = PlatformNetState::smReservedSocketList.activate(handleFd, AF_INET6, useUDP); + NetAddressToIPSocket6(&address, (struct sockaddr_in6*)&socketAddress); + error = ::bind(socketFd, (struct sockaddr*)&socketAddress, sizeof(sockaddr_in6)); + } + + if (!error) + return NoError; + return PlatformNetState::getLastError(); +} + +Net::Error Net::setBufferSize(NetSocket handleFd, S32 bufferSize) { S32 error; - error = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *) &bufferSize, sizeof(bufferSize)); + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); + if (socketFd == InvalidSocketHandle) + return NotASocket; + + error = ::setsockopt(socketFd, SOL_SOCKET, SO_RCVBUF, (char *) &bufferSize, sizeof(bufferSize)); if(!error) - error = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *) &bufferSize, sizeof(bufferSize)); + error = ::setsockopt(socketFd, SOL_SOCKET, SO_SNDBUF, (char *) &bufferSize, sizeof(bufferSize)); if(!error) return NoError; - return getLastError(); + return PlatformNetState::getLastError(); } -Net::Error Net::setBroadcast(NetSocket socket, bool broadcast) +Net::Error Net::setBroadcast(NetSocket handleFd, bool broadcast) { S32 bc = broadcast; - S32 error = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&bc, sizeof(bc)); + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); + if (socketFd == InvalidSocketHandle) + return NotASocket; + S32 error = ::setsockopt(socketFd, SOL_SOCKET, SO_BROADCAST, (char*)&bc, sizeof(bc)); if(!error) return NoError; - return getLastError(); + return PlatformNetState::getLastError(); } -Net::Error Net::setBlocking(NetSocket socket, bool blockingIO) +Net::Error Net::setBlocking(NetSocket handleFd, bool blockingIO) { + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); + if (socketFd == InvalidSocketHandle) + return NotASocket; + unsigned long notblock = !blockingIO; - S32 error = ioctl(socket, FIONBIO, ¬block); + S32 error = ioctl(socketFd, FIONBIO, ¬block); if(!error) return NoError; - return getLastError(); + return PlatformNetState::getLastError(); } -Net::Error Net::send(NetSocket socket, const U8 *buffer, S32 bufferSize) +bool Net::getListenAddress(const NetAddress::Type type, NetAddress *address, bool forceDefaults) { + if (type == NetAddress::IPAddress) + { + const char* serverIP = forceDefaults ? NULL : Con::getVariable("pref::Net::BindAddress"); + if (!serverIP || serverIP[0] == '\0') + { + address->type = type; + address->port = PlatformNetState::defaultPort; + *((U32*)address->address.ipv4.netNum) = INADDR_ANY; + return true; + } + else + { + return Net::stringToAddress(serverIP, address, false); + } + } + else if (type == NetAddress::IPBroadcastAddress) + { + address->type = type; + address->port = PlatformNetState::defaultPort; + *((U32*)address->address.ipv4.netNum) = INADDR_BROADCAST; + return true; + } + else if (type == NetAddress::IPV6Address) + { + const char* serverIP6 = forceDefaults ? NULL : Con::getVariable("pref::Net::BindAddress6"); + if (!serverIP6 || serverIP6[0] == '\0') + { + sockaddr_in6 addr; + dMemset(&addr, '\0', sizeof(addr)); + + addr.sin6_port = htons(PlatformNetState::defaultPort); + addr.sin6_addr = in6addr_any; + + IPSocket6ToNetAddress(&addr, address); + return true; + } + else + { + return Net::stringToAddress(serverIP6, address, false); + } + } + else if (type == NetAddress::IPV6MulticastAddress) + { + const char* multicastAddressValue = forceDefaults ? NULL : Con::getVariable("pref::Net::Multicast6Address"); + if (!multicastAddressValue || multicastAddressValue[0] == '\0') + { + multicastAddressValue = TORQUE_NET_DEFAULT_MULTICAST_ADDRESS; + } + + return Net::stringToAddress(multicastAddressValue, address, false); + } + else + { + return false; + } +} + +void Net::getIdealListenAddress(NetAddress *address) +{ + dMemset(address, '\0', sizeof(NetAddress)); + + if (Net::smIpv6Enabled) + { + if (Net::getListenAddress(NetAddress::IPV6Address, address) == NeedHostLookup) + { + Net::getListenAddress(NetAddress::IPV6Address, address, true); + } + } + else + { + if (Net::getListenAddress(NetAddress::IPAddress, address) == NeedHostLookup) + { + Net::getListenAddress(NetAddress::IPAddress, address, true); + } + } +} + +Net::Error Net::send(NetSocket handleFd, const U8 *buffer, S32 bufferSize) +{ + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); + if (socketFd == InvalidSocketHandle) + return NotASocket; + errno = 0; - S32 bytesWritten = ::send(socket, (const char*)buffer, bufferSize, 0); + S32 bytesWritten = ::send(socketFd, (const char*)buffer, bufferSize, 0); if(bytesWritten == -1) #if defined(TORQUE_USE_WINSOCK) Con::errorf("Could not write to socket. Error: %s",strerror_wsa( WSAGetLastError() )); @@ -925,126 +1551,202 @@ Net::Error Net::send(NetSocket socket, const U8 *buffer, S32 bufferSize) Con::errorf("Could not write to socket. Error: %s",strerror(errno)); #endif - return getLastError(); + return PlatformNetState::getLastError(); } -Net::Error Net::recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead) +Net::Error Net::recv(NetSocket handleFd, U8 *buffer, S32 bufferSize, S32 *bytesRead) { - *bytesRead = ::recv(socket, (char*)buffer, bufferSize, 0); + SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd); + if (socketFd == InvalidSocketHandle) + return NotASocket; + + *bytesRead = ::recv(socketFd, (char*)buffer, bufferSize, 0); if(*bytesRead == -1) - return getLastError(); + return PlatformNetState::getLastError(); return NoError; } bool Net::compareAddresses(const NetAddress *a1, const NetAddress *a2) { - if((a1->type != a2->type) || - (*((U32 *)a1->netNum) != *((U32 *)a2->netNum)) || - (a1->port != a2->port)) - return false; - - if(a1->type == NetAddress::IPAddress) - return true; - for(S32 i = 0; i < 6; i++) - if(a1->nodeNum[i] != a2->nodeNum[i]) - return false; - return true; + return a1->isSameAddressAndPort(*a2); } -bool Net::stringToAddress(const char *addressString, NetAddress *address) +Net::Error Net::stringToAddress(const char *addressString, NetAddress *address, bool hostLookup, int requiredFamily) { - if(!dStrnicmp(addressString, "ipx:", 4)) - // ipx support deprecated - return false; - - if(!dStrnicmp(addressString, "ip:", 3)) - addressString += 3; // eat off the ip: - - sockaddr_in ipAddr; - char remoteAddr[256]; - if(strlen(addressString) > 255) - return false; - - dStrcpy(remoteAddr, addressString); - - char *portString = dStrchr(remoteAddr, ':'); - if(portString) - *portString++ = '\0'; - - if(!dStricmp(remoteAddr, "broadcast")) - ipAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + char addr[256]; + int port = 0; + int actualFamily = AF_UNSPEC; + if (!PlatformNetState::extractAddressParts(addressString, addr, port, actualFamily)) + { + return WrongProtocolType; + } + + // Make sure family matches (in cast we have IP: stuff in address) + if (requiredFamily != AF_UNSPEC && actualFamily != AF_UNSPEC && (actualFamily != requiredFamily)) + { + return WrongProtocolType; + } + + if (actualFamily == AF_UNSPEC) + { + actualFamily = requiredFamily; + } + + addressString = addr; + dMemset(address, '\0', sizeof(NetAddress)); + + if (!dStricmp(addressString, "broadcast")) + { + address->type = NetAddress::IPBroadcastAddress; + if (!(actualFamily == AF_UNSPEC || actualFamily == AF_INET)) + return WrongProtocolType; + + if (port != 0) + address->port = port; + else + address->port = PlatformNetState::defaultPort; + } + else if (!dStricmp(addressString, "multicast")) + { + address->type = NetAddress::IPV6MulticastAddress; + if (!(actualFamily == AF_UNSPEC || actualFamily == AF_INET6)) + return WrongProtocolType; + + if (port != 0) + address->port = port; + else + address->port = PlatformNetState::defaultPort; + } else { - ipAddr.sin_addr.s_addr = inet_addr(remoteAddr); - - if (ipAddr.sin_addr.s_addr == INADDR_NONE) // error + sockaddr_in ipAddr; + sockaddr_in6 ipAddr6; + + dMemset(&ipAddr, 0, sizeof(ipAddr)); + dMemset(&ipAddr6, 0, sizeof(ipAddr6)); + + bool hasInterface = dStrchr(addressString, '%') != NULL; // if we have an interface, best use getaddrinfo to parse + + // Check if we've got a simple ipv4 / ipv6 + + if (inet_pton(AF_INET, addressString, &ipAddr.sin_addr) == 1) { - // On the Xbox, 'gethostbyname' does not exist so... -#ifndef TORQUE_OS_XENON - struct hostent *hp; - if((hp = gethostbyname(remoteAddr)) == 0) - return false; + if (!(actualFamily == AF_UNSPEC || actualFamily == AF_INET)) + return WrongProtocolType; + IPSocketToNetAddress(((struct sockaddr_in*)&ipAddr), address); + + if (port != 0) + address->port = port; else - memcpy(&ipAddr.sin_addr.s_addr, hp->h_addr, sizeof(in_addr)); -#else - // On the Xbox do XNetDnsLookup - XNDNS *pxndns = NULL; - HANDLE hEvent = CreateEvent(NULL, false, false, NULL); - XNetDnsLookup(remoteAddr, hEvent, &pxndns); - - // Wait for event (passing NULL as a handle to XNetDnsLookup will NOT - // cause it to behave synchronously, so do not remove the handle/wait - while(pxndns->iStatus == WSAEINPROGRESS) - WaitForSingleObject(hEvent, INFINITE); - - bool foundAddr = pxndns->iStatus == 0 && pxndns->cina > 0; - if(foundAddr) + address->port = PlatformNetState::defaultPort; + + return NoError; + } + else if (!hasInterface && inet_pton(AF_INET6, addressString, &ipAddr6.sin6_addr) == 1) + { + if (!(actualFamily == AF_UNSPEC || actualFamily == AF_INET6)) + return WrongProtocolType; + IPSocket6ToNetAddress(((struct sockaddr_in6*)&ipAddr6), address); + + if (port != 0) + address->port = port; + else + address->port = PlatformNetState::defaultPort; + + return NoError; + } + else + { + if (!hostLookup && !hasInterface) + return NeedHostLookup; + + int ret = 0; + struct addrinfo hint, *res = NULL; + dMemset(&hint, 0, sizeof(hint)); + hint.ai_family = actualFamily; + hint.ai_flags = hostLookup ? 0 : AI_NUMERICHOST; + + if (ret = getaddrinfo(addressString, NULL, &hint, &res) == 0) { - // Lets just grab the first address returned, for now - memcpy(&ipAddr.sin_addr, pxndns->aina, sizeof(IN_ADDR)); + if (actualFamily != AF_UNSPEC) + { + // Prefer desired protocol + res = PlatformNetState::pickAddressByProtocol(res, actualFamily); + } + + if (res && res->ai_family == AF_INET) + { + // ipv4 + IPSocketToNetAddress(((struct sockaddr_in*)res->ai_addr), address); + } + else if (res && res->ai_family == AF_INET6) + { + // ipv6 + IPSocket6ToNetAddress(((struct sockaddr_in6*)res->ai_addr), address); + } + else + { + // unknown + return UnknownError; + } + + if (port != 0) + address->port = port; + else + address->port = PlatformNetState::defaultPort; } - - XNetDnsRelease(pxndns); - CloseHandle(hEvent); - - // If we didn't successfully resolve the DNS lookup, bail after the - // handles are released - if(!foundAddr) - return false; -#endif } } - if(portString) - ipAddr.sin_port = htons(dAtoi(portString)); - else - ipAddr.sin_port = htons(defaultPort); - ipAddr.sin_family = AF_INET; - IPSocketToNetAddress(&ipAddr, address); - return true; + + return NoError; } void Net::addressToString(const NetAddress *address, char addressString[256]) { - if(address->type == NetAddress::IPAddress) + if(address->type == NetAddress::IPAddress || address->type == NetAddress::IPBroadcastAddress) { sockaddr_in ipAddr; - netToIPSocketAddress(address, &ipAddr); - - if(ipAddr.sin_addr.s_addr == htonl(INADDR_BROADCAST)) - dSprintf(addressString, 256, "IP:Broadcast:%d", ntohs(ipAddr.sin_port)); + NetAddressToIPSocket(address, &ipAddr); + + if (ipAddr.sin_addr.s_addr == htonl(INADDR_BROADCAST) || address->type == NetAddress::IPBroadcastAddress) + { + if (ipAddr.sin_port == 0) + dSprintf(addressString, 256, "IP:Broadcast"); + else + dSprintf(addressString, 256, "IP:Broadcast:%d", ntohs(ipAddr.sin_port)); + } else { -#ifndef TORQUE_OS_XENON - dSprintf(addressString, 256, "IP:%s:%d", inet_ntoa(ipAddr.sin_addr), - ntohs(ipAddr.sin_port)); -#else - dSprintf(addressString, 256, "IP:%d.%d.%d.%d:%d", ipAddr.sin_addr.s_net, - ipAddr.sin_addr.s_host, ipAddr.sin_addr.s_lh, - ipAddr.sin_addr.s_impno, ntohs( ipAddr.sin_port ) ); - -#endif + char buffer[256]; + buffer[0] = '\0'; + sockaddr_in ipAddr; + NetAddressToIPSocket(address, &ipAddr); + inet_ntop(AF_INET, &(ipAddr.sin_addr), buffer, sizeof(buffer)); + if (ipAddr.sin_port == 0) + dSprintf(addressString, 256, "IP:%s", buffer); + else + dSprintf(addressString, 256, "IP:%s:%i", buffer, ntohs(ipAddr.sin_port)); } } + else if (address->type == NetAddress::IPV6Address) + { + char buffer[256]; + buffer[0] = '\0'; + sockaddr_in6 ipAddr; + NetAddressToIPSocket6(address, &ipAddr); + inet_ntop(AF_INET6, &(ipAddr.sin6_addr), buffer, sizeof(buffer)); + if (ipAddr.sin6_port == 0) + dSprintf(addressString, 256, "IP6:%s", buffer); + else + dSprintf(addressString, 256, "IP6:[%s]:%i", buffer, ntohs(ipAddr.sin6_port)); + } + else if (address->type == NetAddress::IPV6MulticastAddress) + { + if (address->port == 0) + dSprintf(addressString, 256, "IP6:Multicast"); + else + dSprintf(addressString, 256, "IP6:Multicast:%d", address->port); + } else { *addressString = 0; @@ -1052,3 +1754,142 @@ void Net::addressToString(const NetAddress *address, char addressString[256]) } } +void Net::enableMulticast() +{ + SOCKET socketFd; + + if (Net::smIpv6Enabled) + { + socketFd = PlatformNetState::smReservedSocketList.resolve(PlatformNetState::udp6Socket); + + if (socketFd != InvalidSocketHandle) + { + PlatformNetState::multicast6Socket = PlatformNetState::udp6Socket; + + Net::Error error = NoError; + + if (error == NoError) + { + unsigned long multicastTTL = 1; + + if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + (char*)&multicastTTL, sizeof(multicastTTL)) < 0) + { + error = PlatformNetState::getLastError(); + } + } + + // Find multicast to bind to... + + NetAddress multicastAddress; + sockaddr_in6 multicastSocketAddress; + + const char *multicastAddressValue = Con::getVariable("pref::Net::Multicast6Address"); + if (!multicastAddressValue || multicastAddressValue[0] == '\0') + { + multicastAddressValue = TORQUE_NET_DEFAULT_MULTICAST_ADDRESS; + } + + error = Net::stringToAddress(multicastAddressValue, &multicastAddress, false); + + if (error == NoError) + { + dMemset(&PlatformNetState::multicast6Group, '\0', sizeof(&PlatformNetState::multicast6Group)); + NetAddressToIPSocket6(&multicastAddress, &multicastSocketAddress); + dMemcpy(&PlatformNetState::multicast6Group.ipv6mr_multiaddr, &multicastSocketAddress.sin6_addr, sizeof(PlatformNetState::multicast6Group.ipv6mr_multiaddr)); + } + + // Setup group + + if (error == NoError) + { + const char *multicastInterface = Con::getVariable("pref::Net::Multicast6Interface"); + + if (multicastInterface && multicastInterface[0] != '\0') + { +#ifdef TORQUE_USE_WINSOCK + PlatformNetState::multicast6Group.ipv6mr_interface = dAtoi(multicastInterface); +#else + PlatformNetState::multicast6Group.ipv6mr_interface = if_nametoindex(multicastInterface); +#endif + } + else + { + PlatformNetState::multicast6Group.ipv6mr_interface = 0; // 0 == accept from any interface + } + + if (PlatformNetState::multicast6Group.ipv6mr_interface && error == NoError) + { + if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *)&PlatformNetState::multicast6Group.ipv6mr_interface, sizeof(PlatformNetState::multicast6Group.ipv6mr_interface)) < 0) + { + error = PlatformNetState::getLastError(); + } + } + + if (error == NoError && setsockopt(socketFd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&PlatformNetState::multicast6Group, sizeof(PlatformNetState::multicast6Group)) < 0) + { + error = PlatformNetState::getLastError(); + } + } + + + if (error == NoError) + { + Con::printf("Multicast initialized on port %d", PlatformNetState::defaultPort); + } + else + { + PlatformNetState::multicast6Socket = NetSocket::INVALID; + Con::printf("Unable to multicast UDP - error %d", error); + } + } + } +} + +void Net::disableMulticast() +{ + if (PlatformNetState::multicast6Socket != NetSocket::INVALID) + { + PlatformNetState::multicast6Socket = NetSocket::INVALID; + } +} + +bool Net::isMulticastEnabled() +{ + return PlatformNetState::multicast6Socket != NetSocket::INVALID; +} + +U32 NetAddress::getHash() const +{ + U32 value = 0; + switch (type) + { + case NetAddress::IPAddress: + value = Torque::hash((const U8*)&address.ipv4.netNum, sizeof(address.ipv4.netNum), 0); + break; + case NetAddress::IPV6Address: + value = Torque::hash((const U8*)address.ipv6.netNum, sizeof(address.ipv6.netNum), 0); + break; + default: + value = 0; + break; + } + return value; +} + +bool Net::isAddressTypeAvailable(NetAddress::Type addressType) +{ + switch (addressType) + { + case NetAddress::IPAddress: + return PlatformNetState::udpSocket != NetSocket::INVALID; + case NetAddress::IPV6Address: + return PlatformNetState::udp6Socket != NetSocket::INVALID; + case NetAddress::IPBroadcastAddress: + return PlatformNetState::udpSocket != NetSocket::INVALID; + case NetAddress::IPV6MulticastAddress: + return PlatformNetState::multicast6Socket != NetSocket::INVALID; + default: + return false; + } +} diff --git a/Engine/source/platform/platformNet.h b/Engine/source/platform/platformNet.h index 16a809927..e98862182 100644 --- a/Engine/source/platform/platformNet.h +++ b/Engine/source/platform/platformNet.h @@ -31,6 +31,8 @@ #define MAXPACKETSIZE 1500 #endif +#define TORQUE_NET_DEFAULT_MULTICAST_ADDRESS "ff04::7467::656E::6574::776B" + typedef S32 NetConnectionId; /// Generic network address @@ -41,18 +43,130 @@ struct NetAddress S32 type; ///< Type of address (IPAddress currently) /// Acceptable NetAddress types. - enum + enum Type { IPAddress, + IPV6Address, + + IPBroadcastAddress, + IPV6MulticastAddress }; - U8 netNum[4]; ///< For IP: sin_addr
- U8 nodeNum[6]; ///< For IP: Not used.
- U16 port; ///< For IP: sin_port
+ union + { + struct { + U8 netNum[4]; + } ipv4; + + struct { + U8 netNum[16]; + U32 netFlow; + U32 netScope; + } ipv6; + + struct { + U8 netNum[16]; + U8 netFlow[4]; + U8 netScope[4]; + } ipv6_raw; + + } address; + + U16 port; + + bool isSameAddress(const NetAddress &other) const + { + if (type != other.type) + return false; + + switch (type) + { + case NetAddress::IPAddress: + return (dMemcmp(other.address.ipv4.netNum, address.ipv4.netNum, 4) == 0); + break; + case NetAddress::IPV6Address: + return (dMemcmp(other.address.ipv6.netNum, address.ipv6.netNum, 16) == 0); + break; + case NetAddress::IPBroadcastAddress: + return true; + break; + case NetAddress::IPV6MulticastAddress: + return true; + break; + } + + return false; + } + + bool isSameAddressAndPort(const NetAddress &other) const + { + if (type != other.type) + return false; + + switch (type) + { + case NetAddress::IPAddress: + return (dMemcmp(other.address.ipv4.netNum, address.ipv4.netNum, 4) == 0) && other.port == port; + break; + case NetAddress::IPV6Address: + return (dMemcmp(other.address.ipv6.netNum, address.ipv6.netNum, 16) == 0) && other.port == port; + break; + case NetAddress::IPBroadcastAddress: + return true; + break; + case NetAddress::IPV6MulticastAddress: + return true; + break; + } + + return false; + } + + bool isEqual(const NetAddress &other) const + { + if (type != other.type) + return false; + + switch (type) + { + case NetAddress::IPAddress: + return other.port == port && (dMemcmp(other.address.ipv4.netNum, address.ipv4.netNum, 4) == 0); + break; + case NetAddress::IPV6Address: + return other.port == port && other.address.ipv6.netFlow == address.ipv6.netFlow && other.address.ipv6.netScope == address.ipv6.netScope && (dMemcmp(other.address.ipv6.netNum, address.ipv6.netNum, 16) == 0); + break; + case NetAddress::IPBroadcastAddress: + return other.port == port; + break; + case NetAddress::IPV6MulticastAddress: + return other.port == port; + break; + } + + return false; + } + + U32 getHash() const; }; -typedef S32 NetSocket; -const NetSocket InvalidSocket = -1; +class NetSocket +{ +protected: + S32 mHandle; + +public: + NetSocket() : mHandle(-1) { ; } + + inline void setHandle(S32 handleId) { mHandle = handleId; } + inline S32 getHandle() const { return mHandle; } + inline U32 getHash() const { return mHandle; } + + bool operator==(const NetSocket &other) const { return mHandle == other.mHandle; } + bool operator!=(const NetSocket &other) const { return mHandle != other.mHandle; } + + static NetSocket fromHandle(S32 handleId) { NetSocket ret; ret.mHandle = handleId; return ret; } + static NetSocket INVALID; +}; /// void event(NetSocket sock, U32 state) typedef JournaledSignal ConnectionNotifyEvent; @@ -76,7 +190,8 @@ struct Net InvalidPacketProtocol, WouldBlock, NotASocket, - UnknownError + UnknownError, + NeedHostLookup }; enum ConnectionState { @@ -87,12 +202,6 @@ struct Net Disconnected }; - enum Protocol - { - UDPProtocol, - TCPProtocol - }; - static const S32 MaxPacketDataSize = MAXPACKETSIZE; static ConnectionNotifyEvent smConnectionNotify; @@ -100,6 +209,10 @@ struct Net static ConnectionReceiveEvent smConnectionReceive; static PacketReceiveEvent smPacketReceive; + static bool smMulticastEnabled; + static bool smIpv4Enabled; + static bool smIpv6Enabled; + static bool init(); static void shutdown(); @@ -116,13 +229,13 @@ struct Net // Reliable net functions (TCP) // all incoming messages come in on the Connected* events - static NetSocket openListenPort(U16 port); + static NetSocket openListenPort(U16 port, NetAddress::Type = NetAddress::IPAddress); static NetSocket openConnectTo(const char *stringAddress); // does the DNS resolve etc. static void closeConnectTo(NetSocket socket); static Error sendtoSocket(NetSocket socket, const U8 *buffer, S32 bufferSize); static bool compareAddresses(const NetAddress *a1, const NetAddress *a2); - static bool stringToAddress(const char *addressString, NetAddress *address); + static Net::Error stringToAddress(const char *addressString, NetAddress *address, bool hostLookup=true, int family=0); static void addressToString(const NetAddress *address, char addressString[256]); // lower level socked based network functions @@ -136,15 +249,27 @@ struct Net static Error listen(NetSocket socket, S32 maxConcurrentListens); static NetSocket accept(NetSocket acceptSocket, NetAddress *remoteAddress); - static Error bind(NetSocket socket, U16 port); + static Error bindAddress(const NetAddress &address, NetSocket socket, bool useUDP=false); static Error setBufferSize(NetSocket socket, S32 bufferSize); static Error setBroadcast(NetSocket socket, bool broadcastEnable); static Error setBlocking(NetSocket socket, bool blockingIO); + /// Gets the desired default listen address for a specified address type + static bool getListenAddress(const NetAddress::Type type, NetAddress *address, bool forceDefaults=false); + static void getIdealListenAddress(NetAddress *address); + + // Multicast for ipv6 local net browsing + static void enableMulticast(); + static void disableMulticast(); + static bool isMulticastEnabled(); + + // Protocol state + static bool isAddressTypeAvailable(NetAddress::Type addressType); private: static void process(); + static void processListenSocket(NetSocket socket); }; -#endif \ No newline at end of file +#endif diff --git a/Engine/source/platform/platformNetAsync.cpp b/Engine/source/platform/platformNetAsync.cpp index 701410e38..9341ace54 100644 --- a/Engine/source/platform/platformNetAsync.cpp +++ b/Engine/source/platform/platformNetAsync.cpp @@ -53,7 +53,7 @@ struct NetAsync::NameLookupRequest NameLookupRequest() { - sock = InvalidSocket; + sock = NetSocket::INVALID; remoteAddr[0] = 0; out_h_addr[0] = 0; out_h_length = -1; @@ -81,9 +81,12 @@ protected: virtual void execute() { #ifndef TORQUE_OS_XENON + + NetAddress address; + Net::Error error = Net::stringToAddress(mRequest.remoteAddr, &address, true); + // do it - struct hostent* hostent = gethostbyname(mRequest.remoteAddr); - if (hostent == NULL) + if (error != Net::NoError) { // oh well! leave the lookup data unmodified (h_length) should // still be -1 from initialization @@ -94,9 +97,9 @@ protected: // copy the stuff we need from the hostent dMemset(mRequest.out_h_addr, 0, sizeof(mRequest.out_h_addr)); - dMemcpy(mRequest.out_h_addr, hostent->h_addr, hostent->h_length); + dMemcpy(mRequest.out_h_addr, &address, sizeof(address)); - mRequest.out_h_length = hostent->h_length; + mRequest.out_h_length = sizeof(address); mRequest.complete = true; } #else @@ -159,7 +162,7 @@ void NetAsync::queueLookup(const char* remoteAddr, NetSocket socket) ThreadPool::GLOBAL().queueWorkItem( workItem ); } -bool NetAsync::checkLookup(NetSocket socket, char* out_h_addr, +bool NetAsync::checkLookup(NetSocket socket, void* out_h_addr, S32* out_h_length, S32 out_h_addr_size) { bool found = false; diff --git a/Engine/source/platform/platformNetAsync.h b/Engine/source/platform/platformNetAsync.h index dc59a1a87..ce33fc630 100644 --- a/Engine/source/platform/platformNetAsync.h +++ b/Engine/source/platform/platformNetAsync.h @@ -54,7 +54,7 @@ class NetAsync // out_h_length will be set appropriately. if out_h_length is -1, then // name could not be resolved. otherwise, it provides the number of // address bytes copied into out_h_addr. - bool checkLookup(NetSocket socket, char* out_h_addr, int* out_h_length, S32 out_h_addr_size); + bool checkLookup(NetSocket socket, void* out_h_addr, int* out_h_length, S32 out_h_addr_size); }; // the global net async object diff --git a/Engine/source/platform/test/netTest.cpp b/Engine/source/platform/test/netTest.cpp index 3765a79c2..889d150f2 100644 --- a/Engine/source/platform/test/netTest.cpp +++ b/Engine/source/platform/test/netTest.cpp @@ -52,7 +52,7 @@ struct TcpHandle else { Process::requestShutdown(); - mSocket = NULL; + mSocket = NetSocket::INVALID; ASSERT_EQ(Net::Disconnected, state) << "Ended with a network error!"; } @@ -72,7 +72,7 @@ TEST(Net, TCPRequest) { TcpHandle handler; - handler.mSocket = InvalidSocket; + handler.mSocket = NetSocket::INVALID; handler.mDataReceived = 0; // Hook into the signals. @@ -119,7 +119,7 @@ struct JournalHandle else { Process::requestShutdown(); - mSocket = NULL; + mSocket = NetSocket::INVALID; ASSERT_EQ(Net::Disconnected, state) << "Ended with a network error!"; } @@ -135,7 +135,7 @@ struct JournalHandle void makeRequest() { - mSocket = InvalidSocket; + mSocket = NetSocket::INVALID; mDataReceived = 0; // Hook into the signals. @@ -175,4 +175,4 @@ TEST(Net, JournalTCPRequest) << "Didn't get same data back from journal playback."; } -#endif \ No newline at end of file +#endif diff --git a/Engine/source/sim/netConnection.cpp b/Engine/source/sim/netConnection.cpp index 0608d3123..f12c8cde6 100644 --- a/Engine/source/sim/netConnection.cpp +++ b/Engine/source/sim/netConnection.cpp @@ -157,7 +157,7 @@ bool NetConnection::mFilesWereDownloaded = false; static inline U32 HashNetAddress(const NetAddress *addr) { - return *((U32 *)addr->netNum) % NetConnection::HashTableSize; + return addr->getHash() % NetConnection::HashTableSize; } NetConnection *NetConnection::lookup(const NetAddress *addr) @@ -1421,7 +1421,7 @@ DefineEngineMethod( NetConnection, connect, void, (const char* remoteAddress),, ) { NetAddress addr; - if(!Net::stringToAddress(remoteAddress, &addr)) + if (Net::stringToAddress(remoteAddress, &addr) != Net::NoError) { Con::errorf("NetConnection::connect: invalid address - %s", remoteAddress); return; diff --git a/Engine/source/sim/netInterface.cpp b/Engine/source/sim/netInterface.cpp index 759120bcf..6d312df8a 100644 --- a/Engine/source/sim/netInterface.cpp +++ b/Engine/source/sim/netInterface.cpp @@ -41,7 +41,7 @@ NetInterface::NetInterface() GNet = this; mLastTimeoutCheckTime = 0; - mAllowConnections = true; + mAllowConnections = false; } @@ -109,7 +109,7 @@ void NetInterface::processPacketReceiveEvent(NetAddress srcAddress, RawData pack pStream.read(&packetType); NetAddress *addr = &srcAddress; - if(packetType <= GameHeartbeat) + if(packetType <= GameHeartbeat || packetType == MasterServerExtendedListResponse) handleInfoPacket(addr, packetType, &pStream); #ifdef GGC_PLUGIN else if (packetType == GGCPacket) @@ -556,10 +556,7 @@ void NetInterface::computeNetMD5(const NetAddress *address, U32 connectSequence, U32 in[16]; in[0] = address->type; - in[1] = (U32(address->netNum[0]) << 24) | - (U32(address->netNum[1]) << 16) | - (U32(address->netNum[2]) << 8) | - (U32(address->netNum[3])); + in[1] = address->getHash(); in[2] = address->port; in[3] = connectSequence; for(U32 i = 0; i < 12; i++) diff --git a/Engine/source/sim/netInterface.h b/Engine/source/sim/netInterface.h index 2776c890a..73aa07ec6 100644 --- a/Engine/source/sim/netInterface.h +++ b/Engine/source/sim/netInterface.h @@ -46,7 +46,7 @@ public: GameInfoRequest = 18, GameInfoResponse = 20, GameHeartbeat = 22, - GGCPacket = 24, + GGCPacket = 24, ConnectChallengeRequest = 26, ConnectChallengeReject = 28, ConnectChallengeResponse = 30, @@ -54,6 +54,9 @@ public: ConnectReject = 34, ConnectAccept = 36, Disconnect = 38, + MasterServerExtendedListResponse = 40, + MasterServerChallenge = 42, + MasterServerExtendedListRequest = 44, }; protected: diff --git a/Templates/Empty/buildFiles/config/torque3D_dedicated.conf b/Templates/Empty/buildFiles/config/torque3D_dedicated.conf index ec0b6281d..18364e0a5 100644 --- a/Templates/Empty/buildFiles/config/torque3D_dedicated.conf +++ b/Templates/Empty/buildFiles/config/torque3D_dedicated.conf @@ -81,7 +81,7 @@ addProjectLibInput('ADVAPI32.LIB'); addProjectLibInput('GDI32.LIB'); addProjectLibInput('WINMM.LIB'); - addProjectLibInput('WSOCK32.LIB'); + addProjectLibInput('WS2_32.LIB.LIB'); addProjectLibInput('vfw32.lib'); addProjectLibInput('Imm32.lib'); addProjectLibInput('d3d9.lib'); diff --git a/Templates/Full/buildFiles/config/torque3D_dedicated.conf b/Templates/Full/buildFiles/config/torque3D_dedicated.conf index ec0b6281d..65db62b78 100644 --- a/Templates/Full/buildFiles/config/torque3D_dedicated.conf +++ b/Templates/Full/buildFiles/config/torque3D_dedicated.conf @@ -81,7 +81,7 @@ addProjectLibInput('ADVAPI32.LIB'); addProjectLibInput('GDI32.LIB'); addProjectLibInput('WINMM.LIB'); - addProjectLibInput('WSOCK32.LIB'); + addProjectLibInput('WS2_32.LIB'); addProjectLibInput('vfw32.lib'); addProjectLibInput('Imm32.lib'); addProjectLibInput('d3d9.lib'); diff --git a/Templates/Full/source/torqueConfig.h b/Templates/Full/source/torqueConfig.h index bdc06eaa2..365476b19 100644 --- a/Templates/Full/source/torqueConfig.h +++ b/Templates/Full/source/torqueConfig.h @@ -34,6 +34,8 @@ /// What's the name of your application? Used in a variety of places. #define TORQUE_APP_NAME "Full" +#define TORQUE_NET_DEFAULT_MULTICAST_ADDRESS "ff04::7467:656E:6574:776B" + /// What version of the application specific source code is this? /// /// Version number is major * 1000 + minor * 100 + revision * 10. diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 276ab712f..50a7e6c8c 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -616,7 +616,7 @@ endif() if(WIN32) # copy pasted from T3D build system, some might not be needed - set(TORQUE_EXTERNAL_LIBS "COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;d3d9.lib;d3dx9.lib;DxErr.lib;ole32.lib;shell32.lib;oleaut32.lib;version.lib" CACHE STRING "external libs to link against") + set(TORQUE_EXTERNAL_LIBS "COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WS2_32.LIB;vfw32.lib;Imm32.lib;d3d9.lib;d3dx9.lib;DxErr.lib;ole32.lib;shell32.lib;oleaut32.lib;version.lib" CACHE STRING "external libs to link against") mark_as_advanced(TORQUE_EXTERNAL_LIBS) addLib("${TORQUE_EXTERNAL_LIBS}") diff --git a/Tools/projectGenerator/classes/NPWebPlugin.php b/Tools/projectGenerator/classes/NPWebPlugin.php index ab01cf68b..1f674f644 100644 --- a/Tools/projectGenerator/classes/NPWebPlugin.php +++ b/Tools/projectGenerator/classes/NPWebPlugin.php @@ -118,7 +118,7 @@ class NPWebPlugin addProjectLibInput('ADVAPI32.LIB'); addProjectLibInput('GDI32.LIB'); addProjectLibInput('WINMM.LIB'); - addProjectLibInput('WSOCK32.LIB'); + addProjectLibInput('WS2_32.LIB'); addProjectLibInput('vfw32.lib'); addProjectLibInput('Imm32.lib'); addProjectLibInput('UnicoWS.lib'); diff --git a/Tools/projectGenerator/classes/Torque3D.php b/Tools/projectGenerator/classes/Torque3D.php index b682092c6..a009bcdb1 100644 --- a/Tools/projectGenerator/classes/Torque3D.php +++ b/Tools/projectGenerator/classes/Torque3D.php @@ -173,7 +173,7 @@ class Torque3D addProjectLibInput('ADVAPI32.LIB'); addProjectLibInput('GDI32.LIB'); addProjectLibInput('WINMM.LIB'); - addProjectLibInput('WSOCK32.LIB'); + addProjectLibInput('WS2_32.LIB'); addProjectLibInput('vfw32.lib'); addProjectLibInput('Imm32.lib'); addProjectLibInput('d3d9.lib'); diff --git a/Tools/projectGenerator/libs/Torque3D.conf b/Tools/projectGenerator/libs/Torque3D.conf index 83833c2d5..4f6a6f4cb 100644 --- a/Tools/projectGenerator/libs/Torque3D.conf +++ b/Tools/projectGenerator/libs/Torque3D.conf @@ -183,7 +183,7 @@ else addProjectLibInput('ADVAPI32.LIB'); addProjectLibInput('GDI32.LIB'); addProjectLibInput('WINMM.LIB'); - addProjectLibInput('WSOCK32.LIB'); + addProjectLibInput('WS2_32.LIB'); addProjectLibInput('vfw32.lib'); addProjectLibInput('Imm32.lib'); addProjectLibInput('d3d9.lib');