//----------------------------------------------------------------------------- // V12 Engine // // Copyright (c) 2001 GarageGames.Com // Portions Copyright (c) 2001 by Sierra Online, Inc. //----------------------------------------------------------------------------- //!!!!!!!!! NOTE !!!!!!!!! // Since the platform layers are currently separate, the Mac implementation takes // extreme advantage of knowing that all values are already in 'network order', and // that a byte array of an ip address is the same byte order as a U32 ip address. // dc - need to inlucde OT stuff first, as V12 does some nasty stuff with redeclaring // base new() implementation to take filename/linenum, and screws up the OT hdrs. #include #if __APPLE__ #include #include #define IP_BROADCAST kIP_BROADCAST #define IP_REUSEADDR kIP_REUSEADDR #else #include #endif #include "PlatformMacCarb/platformMacCarb.h" #include "Platform/platform.h" #include "Platform/event.h" #include "console/console.h" #include "Platform/gameInterface.h" #include "Core/fileStream.h" struct Connection { NetSocket socket; S32 state; S32 prevState; bool listenConnection; Connection *nextConnection; Connection *nextInTable; // HANDLE connectThreadHandle; U32 tag; }; struct Status { bool mSignal; OSErr mErr; void *mData; Status() { mSignal = false; mErr = kOTNoError; mData = NULL; } }; // main control vars. static Net::Error getLastError(); static S32 defaultPort = 28000; static S32 netPort = 0; static bool sendPackets = true; static U32 nextConnectionId = 1; static U32 nextAcceptId = 1000000000; static Connection *connectionList = NULL; enum { ConnectionTableSize = 256, ConnectionTableMask = 0xFF }; static Connection *connectionTable[ConnectionTableSize] = { 0, }; // OpenTransport init controls. static U32 OTReferenceCount = 0; //====================================================================== // NEW MAC GLOBALS //====================================================================== // structures we want/need to hang around. static EndpointRef mainEndpoint = NULL; static InetAddress mainPortAddr, destPortAddr; static TBind tbRequest, tbBound; static TUnitData tSend, tRecv; static OTFlags tFlags = 0L; // error holders. static OSErr macErr = kOTNoError; static const OSStatus kStatusFine = 0L; static OSStatus macStatus = kStatusFine; static OTResult macResult = kOTNoError; #define HOSTFROMBYTES(a) (*((InetHost*)(a))) static const InetHost broadcastIP = 0xFFFFFFFF; static const InetHost loopbackIP = 0x7F000001; // 127.0.0.1 #if V12_DEBUG static InetHost testloop = 0; static U8 test2[4]; #endif //====================================================================== static pascal void EventProc( void *_pSession, OTEventCode event, OTResult result, void *cookie ) { Status *status = (Status *) _pSession; switch ( event ) { case T_DATA: //pSess->m_dataToRead = 1; break; case T_BINDCOMPLETE: status->mErr = (OSErr) result; status->mSignal = true; break; case T_OPENCOMPLETE: status->mErr = (OSErr) result; status->mData = cookie; // the EndPoint status->mSignal = true; break; } } /* //====================================================================== void Net::setJournaling(bool jrn) { sendPackets = !jrn; } */ //====================================================================== bool Net::init() { if ( OTReferenceCount == 0 ) if (InitOpenTransport() != kOTNoError) return false; #if V12_DEBUG // this just validates the byte ordering of host ips in a byte array. OTInetStringToHost("127.0.2.1", &testloop); OTInetStringToHost("127.0.2.1", (InetHost*)test2); #endif // successfully opened Open Transport, inncrement the reference count OTReferenceCount++; return true; } //====================================================================== void Net::shutdown() { while(connectionList) Net::closeConnectTo(connectionList->tag); // make sure that we shut down the main open port. closePort(); if ( OTReferenceCount <= 0 ) return; // close Open Transport if there are no more references to it OTReferenceCount--; if ( OTReferenceCount == 0 ) CloseOpenTransport(); } //====================================================================== static void netToIPSocketAddress(const NetAddress *address, InetAddress *inAddr) { dMemset(inAddr, 0, sizeof(InetAddress)); inAddr->fAddressType = AF_INET; inAddr->fHost = HOSTFROMBYTES(address->netNum); inAddr->fPort = address->port; } //====================================================================== static void IPSocketToNetAddress(const InetAddress *inAddr, NetAddress *address) { address->type = NetAddress::IPAddress; address->port = inAddr->fPort; HOSTFROMBYTES(address->netNum) = inAddr->fHost; } //====================================================================== bool Net::openPort(S32 port) { // check if we have an endpoint already. if (mainEndpoint != NULL) // just unbind it - no need to close & recreate... OTUnbind(mainEndpoint); else // need to create a new endpoint. { mainEndpoint = OTOpenEndpoint(OTCreateConfiguration(kUDPName), 0L, NULL, &macStatus); if (mainEndpoint == NULL) { // !!!!!!TBD add error case. // macStatus may need to be converted... return false; } } // then, bind the endpoint to the specified address. // first, clear the bind value struct. tbBound.qlen = 0; tbBound.addr.maxlen = 0; tbBound.addr.len = 0; tbBound.addr.buf = NULL; // then, setup the bind request struct to point to a port addr struct. tbRequest.qlen = 4; tbRequest.addr.maxlen = sizeof(InetAddress); tbRequest.addr.len = sizeof(InetAddress); tbRequest.addr.buf = (U8*)(&mainPortAddr); // then, fill the port address structure for the given port. // this will bind to whichever net device it first finds. !!!!!TBD???? OTInitInetAddress(&mainPortAddr, port, kOTAnyInetAddress); // then do the bind. this is sync, obv. macStatus = OTBind(mainEndpoint, &tbRequest, &tbBound); if (macStatus != kStatusFine) { return false; } // now that we're bound, need to 'negotiate' for capabilities. U8 omBuffer[kOTFourByteOptionSize]; TOptMgmt omRequest; TOptMgmt omReply; // alias the buffer for cleaner structure access. TOption *omOpts = (TOption*)omBuffer; // set up the request and reply structures. omRequest.flags = T_NEGOTIATE; omRequest.opt.len = kOTFourByteOptionSize; omRequest.opt.buf = omBuffer; omReply.opt.maxlen = kOTFourByteOptionSize; omReply.opt.buf = omBuffer; // main thing we need is BROADCAST cap. omOpts->len = kOTFourByteOptionSize; omOpts->status = 0; omOpts->level = INET_IP; // we're dealing with IP protocols. omOpts->name = IP_BROADCAST; // we want BROADCAST. *(U32*)(omOpts->value) = TRUE; // we want broadcast to be ENABLED. macStatus = OTOptionManagement(mainEndpoint, &omRequest, &omReply); if(macStatus != kStatusFine) { // need to look deeper to see what really happened. if(omOpts->status != T_SUCCESS) { // !!!!!TBD convert/handle the error! } return false; } // we also want REUSE_ADDR omOpts->len = kOTFourByteOptionSize; omOpts->status = 0; omOpts->level = INET_IP; // we're dealing with IP protocols. omOpts->name = IP_REUSEADDR; // we want REUSE_ADDR. *(U32*)(omOpts->value) = TRUE; // we want broadcast to be ENABLED. macStatus = OTOptionManagement(mainEndpoint, &omRequest, &omReply); if(macStatus != kStatusFine) { // need to look deeper to see what really happened. if(omOpts->status != T_SUCCESS) { // !!!!!TBD convert/handle the error! } return false; } // we're ready. hold onto assigned portnum in a global... netPort = port; return true; } //====================================================================== void Net::closePort() { // check if we have an endpoint. if (mainEndpoint != NULL) { // for now, unbind it OTUnbind(mainEndpoint); netPort = 0; // we'll try this for nicer cleanup... OTCloseProvider(mainEndpoint); mainEndpoint = NULL; } } //====================================================================== Net::Error Net::sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize) { if (!sendPackets) return NoError; if (mainEndpoint == NULL) return NoError; // !!!!!TBD???? // clear any send struct fields that should be clean... tSend.opt.len = 0; // setup the send buffer. tSend.udata.maxlen = bufferSize; tSend.udata.len = bufferSize; tSend.udata.buf = (U8*)buffer; // setup the dest addr struct. tSend.addr.maxlen = sizeof(InetAddress); tSend.addr.len = sizeof(InetAddress); tSend.addr.buf = (U8*)(&mainPortAddr); // fill in the dest address. netToIPSocketAddress(address, &mainPortAddr); // then send it. macResult = OTSndUData(mainEndpoint, &tSend); if(macResult < 0) { //!!!!!!TBD handle error. return UnknownError; } return NoError; } //====================================================================== static PacketReceiveEvent receiveEvent; // why create on stack each time... //====================================================================== void Net::process() { S32 bytesRead; if (mainEndpoint==NULL) return; macResult = kOTNoError; while(1) { bytesRead = 0L; // clear any send struct fields that should be clean... tRecv.opt.len = 0; // setup the recv buffer to point to the receive event packet.. tRecv.udata.maxlen = MaxPacketDataSize; tRecv.udata.len = MaxPacketDataSize; tRecv.udata.buf = receiveEvent.data; // setup the dest addr struct. tRecv.addr.maxlen = sizeof(InetAddress); tRecv.addr.len = sizeof(InetAddress); tRecv.addr.buf = (U8*)(&destPortAddr); tFlags = 0L; macResult = OTRcvUData(mainEndpoint, &tRecv, &tFlags); if (macResult == kOTNoError) bytesRead = tRecv.udata.len; else if (macResult < 0) { if (macResult == kOTNoDataErr) break; // clean return. else { // !!!!TBD error handling. break; } } // else // !!!!TBD????? what to do with positive error? // { // } IPSocketToNetAddress(&destPortAddr, &receiveEvent.sourceAddress); // check for loopback. 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; // validate that we read something, else loop again. if (bytesRead <= 0) continue; // post the received data. receiveEvent.size = PacketReceiveEventHeaderSize + bytesRead; Game->postEvent(receiveEvent); } // !!!!!!TBD // may need to convert macResult into another variable // for referencing error later on... } // THE NEXT SET OF METHODS ARE FOR HANDLING TCP-STREAM PORTS. //====================================================================== //DWORD WINAPI connectThreadFunction(LPVOID param) //{ // Connection *con = (Connection *) param; // con; // return 0; //} //====================================================================== static Connection *newConnection(U32 id) { Connection *conn = new Connection; conn->nextConnection = connectionList; conn->tag = id; connectionList = conn; conn->nextInTable = connectionTable[conn->tag & ConnectionTableMask]; connectionTable[conn->tag & ConnectionTableMask] = conn; return conn; } //====================================================================== NetSocket Net::openListenPort(U16 port) { nextConnectionId++; if(!sendPackets) return nextConnectionId; Connection *conn = newConnection(nextConnectionId); conn->listenConnection = true; conn->socket = openSocket(); bind(conn->socket, port); listen(conn->socket, 4); setBlocking(conn->socket, false); return conn->tag; } //====================================================================== NetSocket Net::openConnectTo(const char *stringAddress) { nextConnectionId++; if(!sendPackets) return nextConnectionId; Connection *conn = newConnection(nextConnectionId); conn->listenConnection = false; conn->prevState = ConnectedNotifyEvent::DNSResolved; conn->state = ConnectedNotifyEvent::DNSResolved; conn->socket = openSocket(); NetAddress netaddr; connect(conn->socket, &netaddr); setBlocking(conn->socket, false); //threadHandle = CreateThread(NULL, 0, connectThreadFunction, (LPVOID) conn, 0, &threadId); //CloseHandle(threadHandle); conn->state = ConnectedNotifyEvent::Connected; return conn->tag; } #if later //====================================================================== void Net::processConnected() { Connection **walk = &connectionList; Connection *con; while((con = *walk) != NULL) { bool del = false; if(con->listenConnection) { NetSocket newSocket; ConnectedAcceptEvent event; newSocket = accept(con->socket, &event.address); if(newSocket != InvalidSocket) { Connection *nc = newConnection(nextAcceptId++); nc->listenConnection = false; nc->prevState = ConnectedNotifyEvent::Connected; nc->state = ConnectedNotifyEvent::Connected; nc->socket = newSocket; setBlocking(nc->socket, false); event.portTag = con->tag; event.connectionTag = nc->tag; Game->postEvent(event); } walk = &con->nextConnection; } else { if(con->state != con->prevState) { ConnectedNotifyEvent event; event.tag = con->tag; event.state = con->state; Game->postEvent(event); con->prevState = con->state; } if(con->state == ConnectedNotifyEvent::Connected) { ConnectedReceiveEvent event; Net::Error err; S32 bytesRead; event.tag = con->tag; do { // err = recv(con->socket, event.data, MaxPacketDataSize, &bytesRead); if(err == NoError && bytesRead != 0) { event.size = ConnectedReceiveEventHeaderSize + bytesRead; Game->postEvent(event); } else if(err != WouldBlock) { // bad news... this disconnected ConnectedNotifyEvent event; event.tag = con->tag; event.state = ConnectedNotifyEvent::Disconnected; Game->postEvent(event); del = true; } } while(err == NoError && bytesRead != 0); } if(del) { *walk = con->nextConnection; closeSocket(con->socket); for(Connection **tbWalk = &connectionTable[con->tag & ConnectionTableMask]; *tbWalk != NULL; tbWalk = &(*tbWalk)->nextInTable) { Connection *dc = *tbWalk; if(dc->tag == con->tag) { *tbWalk = dc->nextInTable; break; } } delete con; } else walk = &con->nextConnection; } } } #endif //====================================================================== void Net::closeConnectTo(NetSocket sock) { if(Game->isJournalReading()) return; Connection **walk; for(walk = &connectionList; *walk != NULL; walk = &(*walk)->nextConnection) { Connection *con = *walk; if(con->tag == sock) { *walk = con->nextConnection; closeSocket(con->socket); break; } } for(walk = &connectionTable[sock & ConnectionTableMask]; *walk != NULL; walk = &(*walk)->nextInTable) { Connection *con = *walk; if(con->tag == sock) { *walk = con->nextInTable; delete con; return; } } } //====================================================================== 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; } //====================================================================== NetSocket Net::openSocket() { return InvalidSocket; #if later SOCKET retSocket; retSocket = socket(AF_INET, SOCK_STREAM, 0); if(retSocket == INVALID_SOCKET) return InvalidSocket; else return retSocket; #endif } //====================================================================== Net::Error Net::closeSocket(NetSocket socket) { if(socket != InvalidSocket) { #if later if(!closesocket(socket)) return NoError; else #endif return getLastError(); } else return NotASocket; } //====================================================================== Net::Error Net::connect(NetSocket socket, const NetAddress *address) { if(address->type != NetAddress::IPAddress) return WrongProtocolType; #if later SOCKADDR_IN socketAddress; netToIPSocketAddress(address, &socketAddress); if(!::connect(socket, (PSOCKADDR) &socketAddress, sizeof(socketAddress))) return NoError; #endif return getLastError(); } Net::Error Net::listen(NetSocket socket, S32 backlog) { #if later if(!::listen(socket, backlog)) return NoError; #endif return getLastError(); } //====================================================================== NetSocket Net::accept(NetSocket acceptSocket, NetAddress *remoteAddress) { #if later SOCKADDR_IN socketAddress; S32 addrLen = sizeof(socketAddress); SOCKET retVal = ::accept(acceptSocket, (PSOCKADDR) &socketAddress, &addrLen); if(retVal != INVALID_SOCKET) { IPSocketToNetAddress(&socketAddress, remoteAddress); return retVal; } #endif return InvalidSocket; } //====================================================================== Net::Error Net::bind(NetSocket socket, U16 port) { #if later 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, (PSOCKADDR) &socketAddress, sizeof(socketAddress)); if(!error) return NoError; #endif return getLastError(); } //====================================================================== Net::Error Net::setBufferSize(NetSocket socket, S32 bufferSize) { socket, 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) { socket, 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) { socket, 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) { Net::Error e; // e = ::send(socket, (const char*)buffer, bufferSize, 0); return getLastError(); // return e; } //====================================================================== Net::Error Net::recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead) { socket, buffer, bufferSize, 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) return false; if(*((U32 *)a1->netNum) != *((U32 *)a2->netNum)) return false; /* this was for IPX only. !!!!tbd 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) { char sRemote[256]; // temporary space so we can muck with the inString. char *sPort; // clear destination. dMemset((void*)address, 0, sizeof(NetAddress)); // assume IP if it doesn't have ipx: at the front. if (dStrnicmp(addressString, "ipx:", 4)) { if (!dStrnicmp(addressString, "ip:", 3)) addressString += 3; // eat off the ip: if (dStrlen(addressString) > 255) // waaay too long. return false; dStrcpy(sRemote, addressString); sPort = dStrchr(sRemote, ':'); if (sPort) // if non-null... *sPort++ = 0; // null the : and set ahead past the : // first, look for broadcast. if(!dStricmp(sRemote, "broadcast")) { *((InetHost*)address->netNum) = broadcastIP; } else { // try to do a simple string convert, in hopes it's a dotted-numeral address. OTInetStringToHost(sRemote, ((InetHost*)(address->netNum))); // if host numbers are all zero, safe bet that we need to do DNS lookup... if(*((InetHost*)address->netNum) == 0) { /* THIS WAS OLD CODE... NEED TO VERIFY OR REWRITE!!!!TBD U8 dnsBuffer[1024]; TLookupRequest dnsRequest; TLookupReply dnsReply; MapperRef dns; // create a mapper for DNS lookup dns = OTOpenMapper(OTCreateConfiguration(kDNRName), 0, &macStatus); // set the mapper to be sync and blocking, so we don't need callbacks // !!!!TBD change to async handling of this! if (macStatus == kOTNoError) oterr = OTSetSynchronous(dns); if (macStatus == kOTNoError) oterr = OTSetBlocking(dns); // then, if we haven't yet hit an error, do the DNS lookup. if (macStatus == kOTNoError) { dMemset((void*)&dnsRequest, 0, sizeof(TLookupRequest)); dnsRequest.maxcnt = 1; // just looking for a single IP dnsRequest.timeout = 5000; // # ms before we timeout. dnsRequest.name.buf = sRemote; dnsRequest.name.len = dStrlen(sRemote); dMemset((char *)&dnsReply, 0, sizeof(TLookupReply)); dnsReply.names.maxlen = 1024; dnsReply.names.buf = dnsBuffer; macStatus = OTLookupName(dns, &dnsRequest, &dnsReply); if (macStatus == kOTNoError) { if (!dnsReply.rspcount) { // !!!!TBD handle error of not found. hopefully, macStatus was set already. } else // copy over the result. { // address->netNumIP = ((InetAddress*)(((TLookupBuffer*)dnsBuffer)->fAddressBuffer)); } } } // HERE we handle ALL drop through error cases. // this way, it's handled in one place, and cleanup // is simpler as well... if (macStatus != kOTNoError) { //!!!!TBD -- handle the error by setting global lookup val. } // close down dns if valid. if (dns != kOTInvalidMapperRef) { macStatus = OTCloseProvider(dns); if (macStatus != kOTNoError) { // !!!! NOT SURE. we don't want to necessarily blow away // the failure above with this one. //!!!!TBD -- handle the error by setting global lookup val. } } */ // if((hp = gethostbyname(remoteAddr)) == NULL) return false; } } if (sPort) address->port = dAtoi(sPort); else address->port = defaultPort; address->type = NetAddress::IPAddress; return true; } return false; } //====================================================================== void Net::addressToString(const NetAddress *address, char addressString[256]) { addressString[0] = 0; // clear string... if(address->type == NetAddress::IPAddress) { if (HOSTFROMBYTES(address->netNum) == broadcastIP) dSprintf(addressString, 256, "IP:Broadcast:%d", address->port); else dSprintf(addressString, 256, "IP:%d.%d.%d.%d:%d", address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3], address->port); } } //====================================================================== Net::Error getLastError() { return Net::UnknownError; // S32 err = WSAGetLastError(); // switch(err) // { // case WSAEWOULDBLOCK: // return Net::WouldBlock; // default: // return Net::UnknownError; // } }