//----------------------------------------------------------------------------- // V12 Engine // // Copyright (c) 2001 GarageGames.Com // Portions Copyright (c) 2001 by Sierra Online, Inc. //----------------------------------------------------------------------------- #include "platformX86UNIX/platformX86UNIX.h" #include "platform/platform.h" #include "platform/event.h" //#include //#include #include #include #include #include #include /* for PROTO_IPX */ #include #include #include #include "console/console.h" #include "platform/gameInterface.h" #include "core/fileStream.h" struct NameLookup { struct hostent *hostEnt; int socket; U16 port; NameLookup *nextLookup; }; static NameLookup *lookupList = NULL; // process receives data and posts it to the game. static Net::Error getLastError(); static S32 defaultPort = 28000; static S32 netPort = 0; static int ipxSocket = NULL; static int udpSocket = NULL; enum { MaxConnections = 1024, }; /* 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; 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; bool wserr = ::connect(temp->socket, (PSOCKADDR) &ipAddr, sizeof(ipAddr)); // always errors out if(wserr && WSAGetLastError() != WSAEWOULDBLOCK) { notifyEvent.state = ConnectedNotifyEvent::DNSFailed; ::closesocket(temp->socket); } else { WSAAsyncSelect(temp->socket, winsockWindow, WM_USER, FD_READ | FD_CONNECT | FD_CLOSE); notifyEvent.state = ConnectedNotifyEvent::DNSResolved; } Game->postEvent(notifyEvent); *walk = temp->nextLookup; delete temp; break; } } break; default: return DefWindowProc( hWnd, message, wParam, lParam ); } return 0; } */ bool Net::init() { /* nothing to do */ } void Net::shutdown() { while (lookupList) { NameLookup *temp = lookupList; lookupList = temp->nextLookup; delete temp; } closePort(); } static void netToIPSocketAddress(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]; sprintf(tAddr, "%d.%d.%d.%d\n", 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); // sockAddr->sin_addr.s_addr = address->netNum[0]; // hopefully this will work. } static void IPSocketToNetAddress(const struct sockaddr_in *sockAddr, NetAddress *address) { address->type = NetAddress::IPAddress; address->port = htons(sockAddr->sin_port); 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]; } static void netToIPXSocketAddress(const NetAddress *address, sockaddr_ipx *sockAddr) { dMemset(sockAddr, 0, sizeof(sockaddr_ipx)); sockAddr->sipx_family = AF_INET; sockAddr->sipx_port = htons(address->port); sockAddr->sipx_network = address->netNum[0]; sockAddr->sipx_node[0] = address->nodeNum[0]; sockAddr->sipx_node[1] = address->nodeNum[1]; sockAddr->sipx_node[2] = address->nodeNum[2]; sockAddr->sipx_node[3] = address->nodeNum[3]; sockAddr->sipx_node[4] = address->nodeNum[4]; sockAddr->sipx_node[5] = address->nodeNum[5]; } static void IPXSocketToNetAddress(const sockaddr_ipx *sockAddr, NetAddress *address) { address->type = NetAddress::IPXAddress; address->port = htons(sockAddr->sipx_port); address->netNum[0] = sockAddr->sipx_network; address->nodeNum[0] = sockAddr->sipx_node[0]; address->nodeNum[1] = sockAddr->sipx_node[1]; address->nodeNum[2] = sockAddr->sipx_node[2]; address->nodeNum[3] = sockAddr->sipx_node[3]; address->nodeNum[4] = sockAddr->sipx_node[4]; address->nodeNum[5] = sockAddr->sipx_node[5]; } NetSocket Net::openListenPort(U16 port) { if(Game->isJournalReading()) { U32 ret; Game->journalRead(&ret); return NetSocket(ret); } NetSocket sock = openSocket(); fd_set sockFDSet; FD_ZERO(&sockFDSet); FD_SET(sock, &sockFDSet); bind(sock, port); listen(sock, 4); setBlocking(sock, false); if (select(sock, &sockFDSet, &sockFDSet, &sockFDSet, NULL) == -1) Con::printf("Error: error in select()\n"); 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; if(::connect(sock, (struct sockaddr *)&ipAddr, sizeof(ipAddr)) == -1) { Con::printf("Couldn't connect()"); ::close(sock); sock = InvalidSocket; } if(sock != InvalidSocket) { fd_set sockFDSet; FD_ZERO(&sockFDSet); FD_SET(sock, &sockFDSet); select(sock, &sockFDSet, &sockFDSet, &sockFDSet, NULL); } } else { NameLookup *lookup = new NameLookup; lookup->socket = sock; lookup->port = port; lookup->hostEnt = gethostbyname(remoteAddr); if(lookup->hostEnt == NULL) { delete lookup; ::close(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) { close(lookup->socket); *walk = lookup->nextLookup; delete lookup; return; } } close(sock); } Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, int 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 != InvalidSocket) close(udpSocket); if(ipxSocket != InvalidSocket) close(ipxSocket); udpSocket = socket(AF_INET, SOCK_DGRAM, 0); ipxSocket = socket(AF_IPX, SOCK_DGRAM, 0); if(udpSocket != InvalidSocket) { 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 { close(udpSocket); udpSocket = InvalidSocket; Con::printf("Unable to initialize UDP - error %d", error); } } if(ipxSocket != InvalidSocket) { Net::Error error = NoError; sockaddr_ipx ipxAddress; memset((char *)&ipxAddress, 0, sizeof(ipxAddress)); ipxAddress.sipx_family = AF_IPX; ipxAddress.sipx_port = htons(port); S32 err = ::bind(ipxSocket, (struct sockaddr *)&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 { close(ipxSocket); ipxSocket = InvalidSocket; Con::printf("Unable to initialize IPX - error %d", error); } } netPort = port; return ipxSocket != InvalidSocket || udpSocket != InvalidSocket; } void Net::closePort() { if(ipxSocket != InvalidSocket) close(ipxSocket); if(udpSocket != InvalidSocket) close(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, (sockaddr *) &ipxAddr, sizeof(sockaddr_ipx)) == -1) return getLastError(); else return NoError; } else { sockaddr_in ipAddr; netToIPSocketAddress(address, &ipAddr); if(::sendto(udpSocket, (const char*)buffer, bufferSize, 0, (sockaddr *) &ipAddr, sizeof(sockaddr_in)) == -1) return getLastError(); else return NoError; } } void Net::process() { sockaddr sa; PacketReceiveEvent receiveEvent; for(;;) { S32 addrLen = sizeof(sa); S32 bytesRead = -1; if(udpSocket != InvalidSocket) bytesRead = recvfrom(udpSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen); if(bytesRead == -1 && ipxSocket != InvalidSocket) { addrLen = sizeof(sa); bytesRead = recvfrom(ipxSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen); } if(bytesRead == -1) 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() { int retSocket; retSocket = socket(AF_INET, SOCK_STREAM, 0); if(retSocket == InvalidSocket) return InvalidSocket; else return retSocket; } Net::Error Net::closeSocket(NetSocket socket) { if(socket != InvalidSocket) { if(!close(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, (sockaddr *) &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); int retVal = ::accept(acceptSocket, (sockaddr *) &socketAddress, &addrLen); if(retVal != InvalidSocket) { 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( "Host::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, (sockaddr *) &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) { int notblock = !blockingIO; S32 error = ioctl(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 == -1) 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:%s:%d", inet_ntoa(ipAddr.sin_addr), ntohs(ipAddr.sin_port)); // dSprintf(addressString, 256, "IP:%d:%d", ipAddr.sin_addr.s_addr, // 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() { return Net::UnknownError; }