//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "platformWin32/platformWin32.h" #include "platform/platform.h" #include "platform/event.h" #include #if !defined(USE_IPX) || defined(TORQUE_COMPILER_MINGW) # define NO_IPX_SUPPORT #endif #if !defined(NO_IPX_SUPPORT) # include #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, ¬block); 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; } }