Torque3D/Engine/source/platform/platformNet.cpp
Areloch 5e9d7d63a1 Adjusts handling for httpObject class so that if curl flag is off, files are not included and references are disabled to avoid compilation issues
Adds SMAA anti aliasing
Adjusts AA option setting to properly work and facilitate SMAA
Added defaults for AA to facilitate SMAA
Updated brightness and HDR values on Example Level to comply more closely to the Editor default to give better default results
Fixed issue where ShapeEditor wasn't initialized properly when we'd open it from the Asset Browser
2022-09-16 16:16:12 -05:00

2007 lines
57 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platformNet.h"
#include "platform/threads/mutex.h"
#include "core/strings/stringFunctions.h"
#include "core/util/hashFunction.h"
#include "console/consoleTypes.h"
#ifdef TORQUE_NET_CURL
#include "app/net/httpObject.h"
#endif
// jamesu - debug DNS
//#define TORQUE_DEBUG_LOOKUPS
#if defined (TORQUE_OS_WIN)
#define TORQUE_USE_WINSOCK
#include <errno.h>
#include <ws2tcpip.h>
#ifndef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif // EINPROGRESS
#define ioctl ioctlsocket
typedef S32 socklen_t;
#elif defined ( TORQUE_OS_MAC )
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
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 )
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
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
#define closesocket close
#endif
#include "core/util/tVector.h"
#include "platform/platformNetAsync.h"
#include "console/console.h"
#include "core/util/journal/process.h"
#include "core/util/journal/journal.h"
NetSocket NetSocket::INVALID = NetSocket::fromHandle(-1);
template<class T> class ReservedSocketList
{
public:
struct EntryType
{
T value;
bool used;
EntryType() : value(-1), used(false) { ; }
bool operator==(const EntryType &e1)
{
return value == e1.value && used == e1.used;
}
bool operator!=(const EntryType &e1)
{
return !(value == e1.value && used == e1.used);
}
};
Vector<EntryType> 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);
static void IPSocket6ToNetAddress(const struct sockaddr_in6 *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<SOCKET> smReservedSocketList;
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
int theError = errno;
if (errno == EAGAIN)
return Net::WouldBlock;
if (errno == 0)
return Net::NoError;
if (errno == EINPROGRESS)
return Net::WouldBlock;
return Net::UnknownError;
#endif
}
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;
#ifdef TORQUE_DISABLE_PC_CONNECTIVITY
// Xbox uses a VDP (voice/data protocol) socket for networking
protocol = IPPROTO_VDP;
useVDP = true;
#endif
return protocol;
}
struct addrinfo* pickAddressByProtocol(struct addrinfo* addr, int protocol)
{
for (; 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.
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, 256);
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, 256);
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;
}
Net::Error getSocketAddress(SOCKET socketFd, int requiredFamily, NetAddress *outAddress)
{
Net::Error error = Net::UnknownError;
if (requiredFamily == AF_INET)
{
sockaddr_in ipAddr;
socklen_t len = sizeof(ipAddr);
if (getsockname(socketFd, (struct sockaddr*)&ipAddr, &len) >= 0)
{
IPSocketToNetAddress(&ipAddr, outAddress);
error = Net::NoError;
}
else
{
error = getLastError();
}
}
else if (requiredFamily == AF_INET6)
{
sockaddr_in6 ipAddr;
socklen_t len = sizeof(ipAddr);
if (getsockname(socketFd, (struct sockaddr*)&ipAddr, &len) >= 0)
{
IPSocket6ToNetAddress(&ipAddr, outAddress);
error = Net::NoError;
}
else
{
error = getLastError();
}
}
return error;
}
};
template<class T> NetSocket ReservedSocketList<T>::reserve(SOCKET reserveId, bool doLock)
{
MutexHandle handle;
if (doLock)
{
handle.lock(mMutex, true);
}
S32 idx = mSocketList.find_next(EntryType());
if (idx == -1)
{
EntryType entry;
entry.value = reserveId;
entry.used = true;
mSocketList.push_back(entry);
return NetSocket::fromHandle(mSocketList.size() - 1);
}
else
{
EntryType &entry = mSocketList[idx];
entry.used = true;
entry.value = reserveId;
}
return NetSocket::fromHandle(idx);
}
template<class T> void ReservedSocketList<T>::remove(NetSocket socketToRemove, bool doLock)
{
MutexHandle handle;
if (doLock)
{
handle.lock(mMutex, true);
}
if ((U32)socketToRemove.getHandle() >= (U32)mSocketList.size())
return;
mSocketList[socketToRemove.getHandle()] = EntryType();
}
template<class T> T ReservedSocketList<T>::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;
EntryType &entry = mSocketList[socketToActivate.getHandle()];
if (!entry.used)
return -1;
T socketFd = entry.value;
if (socketFd == -1)
{
socketFd = ::socket(family, typeID, protocol);
if (socketFd == InvalidSocketHandle)
{
if (clearOnFail)
{
remove(socketToActivate, false);
}
return InvalidSocketHandle;
}
else
{
entry.used = true;
entry.value = socketFd;
return socketFd;
}
}
return socketFd;
}
template<class T> T ReservedSocketList<T>::resolve(NetSocket socketToResolve)
{
MutexHandle h;
h.lock(mMutex, true);
if ((U32)socketToResolve.getHandle() >= (U32)mSocketList.size())
return -1;
EntryType &entry = mSocketList[socketToResolve.getHandle()];
return entry.used ? entry.value : -1;
}
ConnectionNotifyEvent* Net::smConnectionNotify = NULL;
ConnectionAcceptedEvent* Net::smConnectionAccept = NULL;
ConnectionReceiveEvent* Net::smConnectionReceive = NULL;
PacketReceiveEvent* Net::smPacketReceive = NULL;
ConnectionNotifyEvent& Net::getConnectionNotifyEvent()
{
return *smConnectionNotify;
}
ConnectionAcceptedEvent& Net::getConnectionAcceptedEvent()
{
return *smConnectionAccept;
}
ConnectionReceiveEvent& Net::getConnectionReceiveEvent()
{
return *smConnectionReceive;
}
PacketReceiveEvent& Net::getPacketReceiveEvent()
{
return *smPacketReceive;
}
// 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 PolledSocket
{
// local enum for socket states for polled sockets
enum SocketState
{
InvalidState,
Connected,
ConnectionPending,
Listening,
NameLookupRequired
};
PolledSocket()
{
fd = -1;
handleFd = NetSocket::INVALID;
state = InvalidState;
remoteAddr[0] = 0;
remotePort = -1;
}
SOCKET fd;
NetSocket handleFd;
S32 state;
char remoteAddr[256];
S32 remotePort;
};
// list of polled sockets
static Vector<PolledSocket*> gPolledSockets( __FILE__, __LINE__ );
static PolledSocket* addPolledSocket(NetSocket handleFd, SOCKET fd, S32 state,
char* remoteAddr = NULL, S32 port = -1)
{
PolledSocket* sock = new PolledSocket();
sock->fd = fd;
sock->handleFd = handleFd;
sock->state = state;
if (remoteAddr)
dStrcpy(sock->remoteAddr, remoteAddr, 256);
if (port != -1)
sock->remotePort = port;
gPolledSockets.push_back(sock);
return sock;
}
bool netSocketWaitForWritable(NetSocket handleFd, S32 timeoutMs)
{
fd_set writefds;
timeval timeout;
SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd);
FD_ZERO( &writefds );
FD_SET( socketFd, &writefds );
timeout.tv_sec = timeoutMs / 1000;
timeout.tv_usec = ( timeoutMs % 1000 ) * 1000;
if( select(socketFd + 1, NULL, &writefds, NULL, &timeout) > 0 )
return true;
return false;
}
bool Net::init()
{
#if defined(TORQUE_USE_WINSOCK)
if(!PlatformNetState::initCount)
{
WSADATA stWSAData;
AssertISV( !WSAStartup( 0x0101, &stWSAData ), "Net::init - failed to init WinSock!" );
//logprintf("Winsock initialization %s", success ? "succeeded." : "failed!");
}
#endif
PlatformNetState::initCount++;
smConnectionNotify = new ConnectionNotifyEvent();
smConnectionAccept = new ConnectionAcceptedEvent();
smConnectionReceive = new ConnectionReceiveEvent();
smPacketReceive = new PacketReceiveEvent();
#ifdef TORQUE_NET_CURL
HTTPObject::init();
#endif
Process::notify(&Net::process, PROCESS_NET_ORDER);
return(true);
}
void Net::shutdown()
{
Process::remove(&Net::process);
#ifdef TORQUE_NET_CURL
HTTPObject::shutdown();
#endif
while (gPolledSockets.size() > 0)
{
if (gPolledSockets[0] == NULL)
gPolledSockets.erase(gPolledSockets.begin());
else
closeConnectTo(gPolledSockets[0]->handleFd);
}
closePort();
PlatformNetState::initCount--;
// Destroy event handlers
delete smConnectionNotify;
delete smConnectionAccept;
delete smConnectionReceive;
delete smPacketReceive;
#if defined(TORQUE_USE_WINSOCK)
if(!PlatformNetState::initCount)
{
WSACleanup();
}
#endif
}
// ipv4 version of name routines
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);
#if defined(TORQUE_OS_BSD) || defined(TORQUE_OS_MAC)
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)
{
address->type = NetAddress::IPAddress;
address->port = ntohs(sockAddr->sin_port);
dMemcpy(&address->address.ipv4.netNum[0], &sockAddr->sin_addr, 4);
}
// 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::fromHandle(ret);
}
Net::Error error = NoError;
NetAddress address;
if (Net::getListenAddress(addressType, &address) != Net::NoError)
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));
error = NotASocket;
handleFd = NetSocket::INVALID;
}
if (error == NoError)
{
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;
}
}
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(handleFd.getHandle()));
return handleFd;
}
NetSocket Net::openConnectTo(const char *addressString)
{
if (Journal::IsPlaying())
{
U32 ret;
Journal::Read(&ret);
return NetSocket::fromHandle(ret);
}
NetAddress address;
NetSocket handleFd = NetSocket::INVALID;
Net::Error error = NoError;
error = Net::stringToAddress(addressString, &address, false);
if (error == NoError && address.type != NetAddress::IPAddress && address.type != NetAddress::IPV6Address)
{
error = Net::WrongProtocolType;
}
// Open socket
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)
{
setBlocking(handleFd, false);
if (::connect(socketFd, (struct sockaddr *)&ipAddr, sizeof(ipAddr)) == -1)
{
Net::Error err = PlatformNetState::getLastError();
if (err != Net::WouldBlock)
{
Con::errorf("Error connecting to %s: %u",
addressString, err);
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 == NoError && address.type == NetAddress::IPV6Address)
{
sockaddr_in6 ipAddr6;
NetAddressToIPSocket6(&address, &ipAddr6);
SOCKET socketFd = PlatformNetState::smReservedSocketList.activate(handleFd, AF_INET6, false, true);
if (socketFd != InvalidSocketHandle)
{
setBlocking(handleFd, false);
if (::connect(socketFd, (struct sockaddr *)&ipAddr6, sizeof(ipAddr6)) == -1)
{
Net::Error err = PlatformNetState::getLastError();
if (err != Net::WouldBlock)
{
Con::errorf("Error connecting to %s: %u",
addressString, err);
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
char addr[256];
int port = 0;
int actualFamily = AF_UNSPEC;
if (PlatformNetState::extractAddressParts(addressString, addr, port, actualFamily))
{
addPolledSocket(handleFd, InvalidSocketHandle, PolledSocket::NameLookupRequired, addr, port);
// queue the lookup
gNetAsync.queueLookup(addressString, handleFd);
}
else
{
closeSocket(handleFd);
handleFd = NetSocket::INVALID;
}
}
else
{
closeSocket(handleFd);
handleFd = NetSocket::INVALID;
}
if (Journal::IsRecording())
Journal::Write(U32(handleFd.getHandle()));
return handleFd;
}
void Net::closeConnectTo(NetSocket handleFd)
{
if(Journal::IsPlaying())
return;
// if this socket is in the list of polled sockets, remove it
for (S32 i = 0; i < gPolledSockets.size(); ++i)
{
if (gPolledSockets[i] && gPolledSockets[i]->handleFd == handleFd)
{
delete gPolledSockets[i];
gPolledSockets[i] = NULL;
break;
}
}
closeSocket(handleFd);
}
Net::Error Net::sendtoSocket(NetSocket handleFd, const U8 *buffer, S32 bufferSize, S32 *outBufferWritten)
{
if(Journal::IsPlaying())
{
U32 e;
S32 outBytes;
Journal::Read(&e);
Journal::Read(&outBytes);
if (outBufferWritten)
*outBufferWritten = outBytes;
return (Net::Error) e;
}
S32 outBytes = 0;
Net::Error e = send(handleFd, buffer, bufferSize, &outBytes);
if (Journal::IsRecording())
{
Journal::Write(U32(e));
Journal::Write(outBytes);
}
if (outBufferWritten)
*outBufferWritten = outBytes;
return e;
}
bool Net::openPort(S32 port, bool doBind)
{
if (PlatformNetState::udpSocket != NetSocket::INVALID)
{
closeSocket(PlatformNetState::udpSocket);
PlatformNetState::udpSocket = NetSocket::INVALID;
}
if (PlatformNetState::udp6Socket != NetSocket::INVALID)
{
closeSocket(PlatformNetState::udp6Socket);
PlatformNetState::udp6Socket = NetSocket::INVALID;
}
// 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 = PlatformNetState::getDefaultGameProtocol();
SOCKET socketFd = InvalidSocketHandle;
NetAddress address;
NetAddress listenAddress;
char listenAddressStr[256];
if (Net::smIpv4Enabled)
{
if (Net::getListenAddress(NetAddress::IPAddress, &address) == Net::NoError)
{
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)
{
error = PlatformNetState::getSocketAddress(socketFd, AF_INET, &listenAddress);
if (error == NoError)
{
Net::addressToString(&listenAddress, listenAddressStr);
Con::printf("UDP initialized on ipv4 %s", listenAddressStr);
}
}
if (error != NoError)
{
closeSocket(PlatformNetState::udpSocket);
PlatformNetState::udpSocket = NetSocket::INVALID;
Con::printf("Unable to initialize UDP on ipv4 - error %d", error);
}
}
}
else
{
Con::errorf("Unable to initialize UDP on ipv4 - invalid address.");
PlatformNetState::udpSocket = NetSocket::INVALID;
}
}
if (Net::smIpv6Enabled)
{
if (Net::getListenAddress(NetAddress::IPV6Address, &address) == Net::NoError)
{
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)
{
error = PlatformNetState::getSocketAddress(socketFd, AF_INET6, &listenAddress);
if (error == NoError)
{
Net::addressToString(&listenAddress, listenAddressStr);
Con::printf("UDP initialized on ipv6 %s", listenAddressStr);
}
}
if (error != NoError)
{
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 PlatformNetState::udpSocket;
}
void Net::closePort()
{
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)
{
if(Journal::IsPlaying())
return NoError;
SOCKET socketFd;
if(address->type == NetAddress::IPAddress || address->type == NetAddress::IPBroadcastAddress)
{
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 NotASocket;
}
}
else if (address->type == NetAddress::IPV6Address || address->type == NetAddress::IPV6MulticastAddress)
{
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 NotASocket;
}
}
return WrongProtocolType;
}
void Net::process()
{
// Process listening sockets
processListenSocket(PlatformNetState::udpSocket);
processListenSocket(PlatformNetState::udp6Socket);
#ifdef TORQUE_NET_CURL
// process HTTPObject
HTTPObject::process();
#endif
// process the polled sockets. This blob of code performs functions
// similar to WinsockProc in winNet.cc
if (gPolledSockets.size() == 0)
return;
S32 optval;
socklen_t optlen = sizeof(S32);
S32 bytesRead;
Net::Error err;
bool removeSock = false;
PolledSocket *currentSock = NULL;
NetSocket incomingHandleFd = NetSocket::INVALID;
NetAddress out_h_addr;
S32 out_h_length = 0;
RawData readBuff;
NetSocket removeSockHandle;
for (S32 i = 0; i < gPolledSockets.size();
/* no increment, this is done at end of loop body */)
{
removeSock = false;
currentSock = gPolledSockets[i];
// Cleanup if we've removed it
if (currentSock == NULL)
{
gPolledSockets.erase(i);
continue;
}
switch (currentSock->state)
{
case PolledSocket::InvalidState:
Con::errorf("Error, InvalidState socket in polled sockets list");
break;
case PolledSocket::ConnectionPending:
// see if it is now connected
if (getsockopt(currentSock->fd, SOL_SOCKET, SO_ERROR,
(char*)&optval, &optlen) == -1)
{
Con::errorf("Error getting socket options: %s", strerror(errno));
removeSock = true;
removeSockHandle = currentSock->handleFd;
smConnectionNotify->trigger(currentSock->handleFd, Net::ConnectFailed);
}
else
{
if (optval == EINPROGRESS)
// still connecting...
break;
if (optval == 0)
{
// poll for writable status to be sure we're connected.
bool ready = netSocketWaitForWritable(currentSock->handleFd,0);
if(!ready)
break;
currentSock->state = PolledSocket::Connected;
smConnectionNotify->trigger(currentSock->handleFd, Net::Connected);
}
else
{
// some kind of error
Con::errorf("Error connecting: %s", strerror(errno));
removeSock = true;
removeSockHandle = currentSock->handleFd;
smConnectionNotify->trigger(currentSock->handleFd, Net::ConnectFailed);
}
}
break;
case PolledSocket::Connected:
// try to get some data
bytesRead = 0;
readBuff.alloc(MaxPacketDataSize);
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;
smConnectionReceive->trigger(currentSock->handleFd, readBuff);
}
else
{
// ack! this shouldn't happen
if (bytesRead < 0)
Con::errorf("Unexpected error on socket: %s", strerror(errno));
removeSock = true;
removeSockHandle = currentSock->handleFd;
// zero bytes read means EOF
smConnectionNotify->trigger(currentSock->handleFd, Net::Disconnected);
}
}
else if (err != Net::NoError && err != Net::WouldBlock)
{
Con::errorf("Error reading from socket: %s", strerror(errno));
removeSock = true;
removeSockHandle = currentSock->handleFd;
smConnectionNotify->trigger(currentSock->handleFd, Net::Disconnected);
}
break;
case PolledSocket::NameLookupRequired:
{
U32 newState = Net::NoError;
// is the lookup complete?
if (!gNetAsync.checkLookup(
currentSock->handleFd, &out_h_addr, &out_h_length,
sizeof(out_h_addr)))
break;
if (out_h_length == -1)
{
Con::errorf("DNS lookup failed: %s", currentSock->remoteAddr);
newState = Net::DNSFailed;
removeSock = true;
removeSockHandle = currentSock->handleFd;
}
else
{
// try to connect
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)
{
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
}
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
}
else
{
Con::errorf("Error connecting to %s: Invalid Protocol",
currentSock->remoteAddr);
newState = Net::ConnectFailed;
removeSock = true;
removeSockHandle = currentSock->handleFd;
}
if (ai_addr)
{
if (::connect(currentSock->fd, ai_addr,
ai_addrlen) == -1)
{
err = PlatformNetState::getLastError();
if (err != Net::WouldBlock)
{
Con::errorf("Error connecting to %s: %u",
currentSock->remoteAddr, err);
newState = Net::ConnectFailed;
removeSock = true;
removeSockHandle = currentSock->handleFd;
}
else
{
newState = Net::DNSResolved;
currentSock->state = PolledSocket::ConnectionPending;
}
}
else
{
newState = Net::Connected;
currentSock->state = Connected;
}
}
}
smConnectionNotify->trigger(currentSock->handleFd, newState);
break;
}
case PolledSocket::Listening:
NetAddress incomingAddy;
incomingHandleFd = Net::accept(currentSock->handleFd, &incomingAddy);
if(incomingHandleFd != NetSocket::INVALID)
{
setBlocking(incomingHandleFd, false);
addPolledSocket(incomingHandleFd, PlatformNetState::smReservedSocketList.resolve(incomingHandleFd), Connected);
smConnectionAccept->trigger(currentSock->handleFd, incomingHandleFd, incomingAddy);
}
break;
}
// only increment index if we're not removing the connection, since
// the removal will shift the indices down by one
if (removeSock)
closeConnectTo(removeSockHandle);
else
i++;
}
}
void Net::processListenSocket(NetSocket socketHandle)
{
if (socketHandle == NetSocket::INVALID)
return;
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;
smPacketReceive->trigger(srcAddress, tmpBuffer);
}
}
NetSocket Net::openSocket()
{
return PlatformNetState::smReservedSocketList.reserve();
}
Net::Error Net::closeSocket(NetSocket handleFd)
{
if(handleFd != NetSocket::INVALID)
{
SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd);
PlatformNetState::smReservedSocketList.remove(handleFd);
if(!::closesocket(socketFd))
return NoError;
else
return PlatformNetState::getLastError();
}
else
return NotASocket;
}
Net::Error Net::connect(NetSocket handleFd, const NetAddress *address)
{
if(!(address->type == NetAddress::IPAddress || address->type == NetAddress::IPV6Address))
return WrongProtocolType;
SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd);
if (address->type == NetAddress::IPAddress)
{
sockaddr_in socketAddress;
NetAddressToIPSocket(address, &socketAddress);
if (socketFd == InvalidSocketHandle)
{
socketFd = PlatformNetState::smReservedSocketList.activate(handleFd, AF_INET, false);
}
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;
}
return PlatformNetState::getLastError();
}
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;
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(socketFd, SOL_SOCKET, SO_SNDBUF, (char *) &bufferSize, sizeof(bufferSize));
if(!error)
return NoError;
return PlatformNetState::getLastError();
}
Net::Error Net::setBroadcast(NetSocket handleFd, bool broadcast)
{
S32 bc = broadcast;
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 PlatformNetState::getLastError();
}
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(socketFd, FIONBIO, &notblock);
if(!error)
return NoError;
return PlatformNetState::getLastError();
}
Net::Error 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 = 0;
*((U32*)address->address.ipv4.netNum) = INADDR_ANY;
return Net::NoError;
}
else
{
return Net::stringToAddress(serverIP, address, false);
}
}
else if (type == NetAddress::IPBroadcastAddress)
{
address->type = type;
address->port = 0;
*((U32*)address->address.ipv4.netNum) = INADDR_BROADCAST;
return Net::NoError;
}
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 = 0;
addr.sin6_addr = in6addr_any;
IPSocket6ToNetAddress(&addr, address);
return Net::NoError;
}
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 Net::WrongProtocolType;
}
}
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, S32 *outBytesWritten)
{
SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd);
if (socketFd == InvalidSocketHandle)
return NotASocket;
errno = 0;
S32 bytesWritten = ::send(socketFd, (const char*)buffer, bufferSize, 0);
if (outBytesWritten)
{
*outBytesWritten = *outBytesWritten < 0 ? 0 : bytesWritten;
}
return PlatformNetState::getLastError();
}
Net::Error Net::recv(NetSocket handleFd, U8 *buffer, S32 bufferSize, S32 *bytesRead)
{
SOCKET socketFd = PlatformNetState::smReservedSocketList.resolve(handleFd);
if (socketFd == InvalidSocketHandle)
return NotASocket;
*bytesRead = ::recv(socketFd, (char*)buffer, bufferSize, 0);
if(*bytesRead == -1)
return PlatformNetState::getLastError();
return NoError;
}
bool Net::compareAddresses(const NetAddress *a1, const NetAddress *a2)
{
return a1->isSameAddressAndPort(*a2);
}
Net::Error Net::stringToAddress(const char *addressString, NetAddress *address, bool hostLookup, int requiredFamily)
{
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
{
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)
{
if (!(actualFamily == AF_UNSPEC || actualFamily == AF_INET))
return WrongProtocolType;
IPSocketToNetAddress(((struct sockaddr_in*)&ipAddr), address);
if (port != 0)
address->port = port;
else
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;
struct addrinfo hint, *res = NULL;
dMemset(&hint, 0, sizeof(hint));
hint.ai_family = actualFamily;
hint.ai_flags = hostLookup ? 0 : AI_NUMERICHOST;
if (getaddrinfo(addressString, NULL, &hint, &res) == 0)
{
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;
}
}
}
return NoError;
}
void Net::addressToString(const NetAddress *address, char addressString[256])
{
if(address->type == NetAddress::IPAddress || address->type == NetAddress::IPBroadcastAddress)
{
sockaddr_in ipAddr;
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
{
char buffer[256];
buffer[0] = '\0';
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;
return;
}
}
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)
{
char listenAddressStr[256];
Net::addressToString(&multicastAddress, listenAddressStr);
Con::printf("Multicast initialized on %s", listenAddressStr);
}
if (error != NoError)
{
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;
}
}