2017-04-17 06:17:10 -06:00

827 lines
24 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "platform/platform.h"
#include "platform/event.h"
#include <winsock.h>
#if !defined(USE_IPX) || defined(TORQUE_COMPILER_MINGW)
# define NO_IPX_SUPPORT
#endif
#if !defined(NO_IPX_SUPPORT)
# include <wsipx.h>
#else
typedef void* SOCKADDR_IPX;
#endif
#include "console/console.h"
#include "platform/gameInterface.h"
#include "core/fileStream.h"
struct NameLookup
{
U8 hostEntStruct[MAXGETHOSTSTRUCT];
HANDLE lookupHandle;
SOCKET socket;
U16 port;
NameLookup *nextLookup;
};
static NameLookup *lookupList = NULL;
static Net::Error getLastError();
static S32 defaultPort = 28000;
static S32 netPort = 0;
static SOCKET ipxSocket = INVALID_SOCKET;
static SOCKET udpSocket = INVALID_SOCKET;
enum WinNetConstants {
MaxConnections = 1024, ///< Maximum allowed number of connections.
};
HWND winsockWindow = NULL;
static LRESULT PASCAL WinsockProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
U32 error;
U32 bufLen;
U32 event;
SOCKET socket;
Net::Error err;
S32 bytesRead;
static ConnectedNotifyEvent notifyEvent;
static ConnectedReceiveEvent receiveEvent;
static ConnectedAcceptEvent acceptEvent;
switch(message)
{
case WM_USER:
error = WSAGETSELECTERROR(lParam);
event = WSAGETSELECTEVENT(lParam);
socket = wParam;
switch(event)
{
case FD_READ:
err = Net::recv(socket, receiveEvent.data, MaxPacketDataSize, &bytesRead);
if(err == Net::NoError && bytesRead != 0)
{
receiveEvent.tag = socket;
receiveEvent.size = ConnectedReceiveEventHeaderSize + bytesRead;
Game->postEvent(receiveEvent);
}
break;
case FD_CONNECT:
notifyEvent.tag = socket;
if(error)
notifyEvent.state = ConnectedNotifyEvent::ConnectFailed;
else
notifyEvent.state = ConnectedNotifyEvent::Connected;
Game->postEvent(notifyEvent);
break;
case FD_CLOSE:
// see first if there is anything to read:
for(;;)
{
err = Net::recv(socket, receiveEvent.data, MaxPacketDataSize, &bytesRead);
if(err != Net::NoError || bytesRead == 0)
break;
receiveEvent.tag = socket;
receiveEvent.size = ConnectedReceiveEventHeaderSize + bytesRead;
Game->postEvent(receiveEvent);
}
notifyEvent.tag = socket;
notifyEvent.state = ConnectedNotifyEvent::Disconnected;
Game->postEvent(notifyEvent);
break;
case FD_ACCEPT:
acceptEvent.portTag = socket;
acceptEvent.connectionTag = Net::accept(socket, &acceptEvent.address);
if(acceptEvent.connectionTag != InvalidSocket)
{
Net::setBlocking(acceptEvent.connectionTag, false);
WSAAsyncSelect(acceptEvent.connectionTag, winsockWindow, WM_USER, FD_READ | FD_CONNECT | FD_CLOSE);
Game->postEvent(acceptEvent);
}
break;
}
break;
case WM_USER + 1:
error = WSAGETASYNCERROR(lParam);
bufLen = WSAGETASYNCBUFLEN(lParam);
HANDLE handle;
handle = HANDLE(wParam);
NameLookup **walk;
for(walk = &lookupList; *walk; walk = &((*walk)->nextLookup))
{
if((*walk)->lookupHandle == handle)
{
NameLookup *temp = *walk;
struct hostent *hp = (struct hostent *) temp->hostEntStruct;
if(error)
{
notifyEvent.state = ConnectedNotifyEvent::DNSFailed;
notifyEvent.tag = temp->socket;
::closesocket(temp->socket);
}
else
{
SOCKADDR_IN ipAddr;
memcpy(&ipAddr.sin_addr.s_addr, hp->h_addr, sizeof(IN_ADDR));
ipAddr.sin_port = temp->port;
ipAddr.sin_family = AF_INET;
notifyEvent.tag = temp->socket;
WSAAsyncSelect(temp->socket, winsockWindow, WM_USER, FD_READ | FD_CONNECT | FD_CLOSE);
bool wserr = ::connect(temp->socket, (PSOCKADDR) &ipAddr, sizeof(ipAddr)); // always errors out
if (wserr && WSAGetLastError() == WSAEWOULDBLOCK)
notifyEvent.state = ConnectedNotifyEvent::DNSResolved;
else {
Con::printf("Connect error: %d", WSAGetLastError());
::closesocket(temp->socket);
notifyEvent.state = ConnectedNotifyEvent::ConnectFailed;
}
}
Game->postEvent(notifyEvent);
*walk = temp->nextLookup;
delete temp;
break;
}
}
break;
default:
return DefWindowProc( hWnd, message, wParam, lParam );
}
return 0;
}
static void InitNetWindow()
{
WNDCLASS wc;
dMemset(&wc, 0, sizeof(wc));
wc.style = 0;
wc.lpfnWndProc = WinsockProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = winState.appInstance;
wc.hIcon = 0;
wc.hCursor = 0;
wc.hbrBackground = 0;
wc.lpszMenuName = 0;
wc.lpszClassName = dT("WinSockClass");
RegisterClass( &wc );
winsockWindow = CreateWindowEx(
0,
dT("WinSockClass"),
dT(""),
0,
0, 0, 0, 0,
NULL, NULL,
winState.appInstance,
NULL);
}
bool Net::init()
{
WSADATA stWSAData;
InitNetWindow();
return !WSAStartup(0x0101, &stWSAData);
}
void Net::shutdown()
{
while(lookupList)
{
NameLookup *temp = lookupList;
lookupList = temp->nextLookup;
WSACancelAsyncRequest ( temp->lookupHandle );
delete temp;
}
DestroyWindow(winsockWindow);
closePort();
WSACleanup();
}
static void netToIPSocketAddress(const NetAddress *address, SOCKADDR_IN *sockAddr)
{
dMemset(sockAddr, 0, sizeof(SOCKADDR_IN));
sockAddr->sin_family = AF_INET;
sockAddr->sin_port = htons(address->port);
sockAddr->sin_addr.s_net = address->netNum[0];
sockAddr->sin_addr.s_host = address->netNum[1];
sockAddr->sin_addr.s_lh = address->netNum[2];
sockAddr->sin_addr.s_impno = address->netNum[3];
}
static void IPSocketToNetAddress(const SOCKADDR_IN *sockAddr, NetAddress *address)
{
address->type = NetAddress::IPAddress;
address->port = htons(sockAddr->sin_port);
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;
}
static void netToIPXSocketAddress(const NetAddress *address, SOCKADDR_IPX *sockAddr)
{
#if !defined(NO_IPX_SUPPORT)
dMemset(sockAddr, 0, sizeof(SOCKADDR_IPX));
sockAddr->sa_family = AF_INET;
sockAddr->sa_socket = htons(address->port);
sockAddr->sa_netnum[0] = address->netNum[0];
sockAddr->sa_netnum[1] = address->netNum[1];
sockAddr->sa_netnum[2] = address->netNum[2];
sockAddr->sa_netnum[3] = address->netNum[3];
sockAddr->sa_nodenum[0] = address->nodeNum[0];
sockAddr->sa_nodenum[1] = address->nodeNum[1];
sockAddr->sa_nodenum[2] = address->nodeNum[2];
sockAddr->sa_nodenum[3] = address->nodeNum[3];
sockAddr->sa_nodenum[4] = address->nodeNum[4];
sockAddr->sa_nodenum[5] = address->nodeNum[5];
#endif
}
static void IPXSocketToNetAddress(const SOCKADDR_IPX *sockAddr, NetAddress *address)
{
#if !defined(NO_IPX_SUPPORT)
address->type = NetAddress::IPXAddress;
address->port = htons(sockAddr->sa_socket);
address->netNum[0] = sockAddr->sa_netnum[0] ;
address->netNum[1] = sockAddr->sa_netnum[1] ;
address->netNum[2] = sockAddr->sa_netnum[2] ;
address->netNum[3] = sockAddr->sa_netnum[3] ;
address->nodeNum[0] = sockAddr->sa_nodenum[0];
address->nodeNum[1] = sockAddr->sa_nodenum[1];
address->nodeNum[2] = sockAddr->sa_nodenum[2];
address->nodeNum[3] = sockAddr->sa_nodenum[3];
address->nodeNum[4] = sockAddr->sa_nodenum[4];
address->nodeNum[5] = sockAddr->sa_nodenum[5];
#endif
}
NetSocket Net::openListenPort(U16 port)
{
if(Game->isJournalReading())
{
U32 ret;
Game->journalRead(&ret);
return NetSocket(ret);
}
NetSocket sock = openSocket();
bind(sock, port);
listen(sock, 4);
setBlocking(sock, false);
if(WSAAsyncSelect ( sock, winsockWindow, WM_USER, FD_ACCEPT ))
Con::printf("Connect error: %d", WSAGetLastError());
if(Game->isJournalWriting())
Game->journalWrite(U32(sock));
return sock;
}
NetSocket Net::openConnectTo(const char *addressString)
{
if(!dStrnicmp(addressString, "ipx:", 4))
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(Game->isJournalReading())
{
U32 ret;
Game->journalRead(&ret);
return NetSocket(ret);
}
NetSocket sock = openSocket();
setBlocking(sock, false);
SOCKADDR_IN ipAddr;
ipAddr.sin_addr.s_addr = inet_addr(remoteAddr);
if(ipAddr.sin_addr.s_addr != INADDR_NONE)
{
ipAddr.sin_port = port;
ipAddr.sin_family = AF_INET;
WSAAsyncSelect(sock, winsockWindow, WM_USER, FD_READ | FD_CONNECT | FD_CLOSE);
if(::connect(sock, (PSOCKADDR) &ipAddr, sizeof(ipAddr) ) )
{
if(WSAGetLastError() != WSAEWOULDBLOCK)
{
Con::printf("Connect error: %d", WSAGetLastError());
::closesocket(sock);
sock = InvalidSocket;
}
}
}
else
{
NameLookup *lookup = new NameLookup;
lookup->socket = sock;
lookup->port = port;
lookup->lookupHandle = WSAAsyncGetHostByName (winsockWindow, WM_USER + 1, remoteAddr, (char *) lookup->hostEntStruct, MAXGETHOSTSTRUCT );
if(!lookup->lookupHandle)
{
delete lookup;
::closesocket(sock);
sock = InvalidSocket;
}
else
{
lookup->nextLookup = lookupList;
lookupList = lookup;
}
}
if(Game->isJournalWriting())
Game->journalWrite(U32(sock));
return sock;
}
void Net::closeConnectTo(NetSocket sock)
{
if(Game->isJournalReading())
return;
for(NameLookup **walk = &lookupList; *walk; walk = &((*walk)->nextLookup) )
{
NameLookup *lookup = *walk;
if(lookup->socket == sock)
{
WSACancelAsyncRequest ( lookup->lookupHandle );
closesocket(lookup->socket);
*walk = lookup->nextLookup;
delete lookup;
return;
}
}
closesocket(sock);
}
Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, S32 bufferSize)
{
if(Game->isJournalReading())
{
U32 e;
Game->journalRead(&e);
return (Net::Error) e;
}
Net::Error e = send(socket, buffer, bufferSize);
if(Game->isJournalWriting())
Game->journalWrite(U32(e));
return e;
}
bool Net::openPort(S32 port)
{
if(udpSocket != INVALID_SOCKET)
closesocket(udpSocket);
if(ipxSocket != INVALID_SOCKET)
closesocket(ipxSocket);
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
#if !defined(NO_IPX_SUPPORT)
ipxSocket = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
#else
ipxSocket = INVALID_SOCKET;
#endif
if(udpSocket != INVALID_SOCKET)
{
Net::Error error;
error = bind(udpSocket, port);
if(error == NoError)
error = setBufferSize(udpSocket, 32768);
if(error == NoError)
error = setBroadcast(udpSocket, true);
if(error == NoError)
error = setBlocking(udpSocket, false);
if(error == NoError)
Con::printf("UDP initialized on port %d", port);
else
{
closesocket(udpSocket);
udpSocket = INVALID_SOCKET;
Con::printf("Unable to initialize UDP - error %d", error);
}
}
#if !defined(NO_IPX_SUPPORT)
if(ipxSocket != INVALID_SOCKET)
{
Net::Error error = NoError;
SOCKADDR_IPX ipxAddress;
memset((char *)&ipxAddress, 0, sizeof(ipxAddress));
ipxAddress.sa_family = AF_IPX;
ipxAddress.sa_socket = htons(port);
S32 err = ::bind(ipxSocket, (PSOCKADDR) &ipxAddress, sizeof(ipxAddress));
if(err)
error = getLastError();
if(error == NoError)
error = setBufferSize(ipxSocket, 32768);
if(error == NoError)
error = setBroadcast(ipxSocket, true);
if(error == NoError)
error = setBlocking(ipxSocket, false);
if(error == NoError)
Con::printf("IPX initialized on port %d", port);
else
{
closesocket(ipxSocket);
ipxSocket = INVALID_SOCKET;
Con::printf("Unable to initialize IPX - error %d", error);
}
}
#endif
netPort = port;
return ipxSocket != INVALID_SOCKET || udpSocket != INVALID_SOCKET;
}
void Net::closePort()
{
if(ipxSocket != INVALID_SOCKET)
closesocket(ipxSocket);
if(udpSocket != INVALID_SOCKET)
closesocket(udpSocket);
}
Net::Error Net::sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize)
{
if(Game->isJournalReading())
return NoError;
if(address->type == NetAddress::IPXAddress)
{
SOCKADDR_IPX ipxAddr;
netToIPXSocketAddress(address, &ipxAddr);
if(::sendto(ipxSocket, (const char*)buffer, bufferSize, 0,
(PSOCKADDR) &ipxAddr, sizeof(SOCKADDR_IPX)) == SOCKET_ERROR)
return getLastError();
else
return NoError;
}
else
{
SOCKADDR_IN ipAddr;
netToIPSocketAddress(address, &ipAddr);
if(::sendto(udpSocket, (const char*)buffer, bufferSize, 0,
(PSOCKADDR) &ipAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
return getLastError();
else
return NoError;
}
}
void Net::process()
{
SOCKADDR sa;
PacketReceiveEvent receiveEvent;
for(;;)
{
S32 addrLen = sizeof(sa);
S32 bytesRead = SOCKET_ERROR;
if(udpSocket != INVALID_SOCKET)
bytesRead = recvfrom(udpSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen);
if(bytesRead == SOCKET_ERROR && ipxSocket != INVALID_SOCKET)
{
addrLen = sizeof(sa);
bytesRead = recvfrom(ipxSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen);
}
if(bytesRead == SOCKET_ERROR)
break;
if(sa.sa_family == AF_INET)
IPSocketToNetAddress((SOCKADDR_IN *) &sa, &receiveEvent.sourceAddress);
else if(sa.sa_family == AF_IPX)
IPXSocketToNetAddress((SOCKADDR_IPX *) &sa, &receiveEvent.sourceAddress);
else
continue;
NetAddress &na = receiveEvent.sourceAddress;
if(na.type == NetAddress::IPAddress &&
na.netNum[0] == 127 &&
na.netNum[1] == 0 &&
na.netNum[2] == 0 &&
na.netNum[3] == 1 &&
na.port == netPort)
continue;
if(bytesRead <= 0)
continue;
receiveEvent.size = PacketReceiveEventHeaderSize + bytesRead;
Game->postEvent(receiveEvent);
}
}
NetSocket Net::openSocket()
{
SOCKET retSocket;
retSocket = socket(AF_INET, SOCK_STREAM, 0);
if(retSocket == INVALID_SOCKET)
return InvalidSocket;
else
return retSocket;
}
Net::Error Net::closeSocket(NetSocket socket)
{
if(socket != InvalidSocket)
{
if(!closesocket(socket))
return NoError;
else
return getLastError();
}
else
return NotASocket;
}
Net::Error Net::connect(NetSocket socket, const NetAddress *address)
{
if(address->type != NetAddress::IPAddress)
return WrongProtocolType;
SOCKADDR_IN socketAddress;
netToIPSocketAddress(address, &socketAddress);
if(!::connect(socket, (PSOCKADDR) &socketAddress, sizeof(socketAddress)))
return NoError;
return getLastError();
}
Net::Error Net::listen(NetSocket socket, S32 backlog)
{
if(!::listen(socket, backlog))
return NoError;
return getLastError();
}
NetSocket Net::accept(NetSocket acceptSocket, NetAddress *remoteAddress)
{
SOCKADDR_IN socketAddress;
S32 addrLen = sizeof(socketAddress);
SOCKET retVal = ::accept(acceptSocket, (PSOCKADDR) &socketAddress, &addrLen);
if(retVal != INVALID_SOCKET)
{
IPSocketToNetAddress(&socketAddress, remoteAddress);
return retVal;
}
return InvalidSocket;
}
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;
}
} else {
Con::printf( "Binding server port to default IP" );
socketAddress.sin_addr.s_addr = INADDR_ANY;
}
socketAddress.sin_port = htons(port);
error = ::bind(socket, (PSOCKADDR) &socketAddress, sizeof(socketAddress));
if(!error)
return NoError;
return getLastError();
}
Net::Error Net::setBufferSize(NetSocket socket, S32 bufferSize)
{
S32 error;
error = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *) &bufferSize, sizeof(bufferSize));
if(!error)
error = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *) &bufferSize, sizeof(bufferSize));
if(!error)
return NoError;
return getLastError();
}
Net::Error Net::setBroadcast(NetSocket socket, bool broadcast)
{
S32 bc = broadcast;
S32 error = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&bc, sizeof(bc));
if(!error)
return NoError;
return getLastError();
}
Net::Error Net::setBlocking(NetSocket socket, bool blockingIO)
{
DWORD notblock = !blockingIO;
S32 error = ioctlsocket(socket, FIONBIO, &notblock);
if(!error)
return NoError;
return getLastError();
}
Net::Error Net::send(NetSocket socket, const U8 *buffer, S32 bufferSize)
{
S32 error = ::send(socket, (const char*)buffer, bufferSize, 0);
if(!error)
return NoError;
return getLastError();
}
Net::Error Net::recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead)
{
*bytesRead = ::recv(socket, (char*)buffer, bufferSize, 0);
if(*bytesRead == SOCKET_ERROR)
return 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;
}
bool Net::stringToAddress(const char *addressString, NetAddress *address)
{
if(dStrnicmp(addressString, "ipx:", 4))
{
// assume IP if it doesn't have ipx: at the front.
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;
struct hostent *hp;
if(!dStricmp(remoteAddr, "broadcast"))
ipAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
else
{
ipAddr.sin_addr.s_addr = inet_addr(remoteAddr);
if(ipAddr.sin_addr.s_addr == INADDR_NONE)
{
if((hp = gethostbyname(remoteAddr)) == NULL)
return false;
else
memcpy(&ipAddr.sin_addr.s_addr, hp->h_addr, sizeof(IN_ADDR));
}
}
if(portString)
ipAddr.sin_port = htons(dAtoi(portString));
else
ipAddr.sin_port = htons(defaultPort);
ipAddr.sin_family = AF_INET;
IPSocketToNetAddress(&ipAddr, address);
return true;
}
else
{
S32 i;
S32 port;
address->type = NetAddress::IPXAddress;
for(i = 0; i < 6; i++)
address->nodeNum[i] = 0xFF;
// it's an IPX string
addressString += 4;
if(!dStricmp(addressString, "broadcast"))
{
address->port = defaultPort;
return true;
}
else if(sscanf(addressString, "broadcast:%d", &port) == 1)
{
address->port = port;
return true;
}
else
{
S32 nodeNum[6];
S32 netNum[4];
S32 count = dSscanf(addressString, "%2x%2x%2x%2x:%2x%2x%2x%2x%2x%2x:%d",
&netNum[0], &netNum[1], &netNum[2], &netNum[3],
&nodeNum[0], &nodeNum[1], &nodeNum[2], &nodeNum[3], &nodeNum[4], &nodeNum[5],
&port);
if(count == 10)
{
port = defaultPort;
count++;
}
if(count != 11)
return false;
for(i = 0; i < 6; i++)
address->nodeNum[i] = nodeNum[i];
for(i = 0; i < 4; i++)
address->netNum[i] = netNum[i];
address->port = port;
return true;
}
}
}
void Net::addressToString(const NetAddress *address, char addressString[256])
{
if(address->type == NetAddress::IPAddress)
{
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));
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));
}
else
{
dSprintf(addressString, 256, "IPX:%.2X%.2X%.2X%.2X:%.2X%.2X%.2X%.2X%.2X%.2X:%d",
address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3],
address->nodeNum[0], address->nodeNum[1], address->nodeNum[2], address->nodeNum[3], address->nodeNum[4], address->nodeNum[5],
address->port);
}
}
Net::Error getLastError()
{
S32 err = WSAGetLastError();
switch(err)
{
case WSAEWOULDBLOCK:
return Net::WouldBlock;
default:
return Net::UnknownError;
}
}