/* GameSpy GT2 SDK Dan "Mr. Pants" Schoenblum dan@gamespy.com Copyright 2002 GameSpy Industries, Inc devsupport@gamespy.com */ #include "gt2Message.h" #include "gt2Buffer.h" #include "gt2Connection.h" #include "gt2Socket.h" #include "gt2Callback.h" #include "gt2Utility.h" #include static unsigned short gti2UShortFromBuffer(const GT2Byte * buffer, int pos) { unsigned short s; s = (unsigned short)((buffer[pos] << 8) & 0xFF00); pos++; s |= buffer[pos]; return s; } static void gti2UShortToBuffer(GT2Byte * buffer, int pos, unsigned short s) { buffer[pos++] = (GT2Byte)((s >> 8) & 0xFF); buffer[pos] = (GT2Byte)(s & 0xFF); } static int gti2SNDiff(unsigned short SN1, unsigned short SN2) { return (short)(SN1 - SN2); } static GT2Bool gti2ConnectionError(GT2Connection connection, GT2Result result, GT2CloseReason reason) { // first check if we're still connecting if(connection->state < GTI2Connected) { // check if the local side is the initiator if(connection->initiated) { // mark it as closed gti2ConnectionClosed(connection); // call the callback if(!gti2ConnectedCallback(connection, result, NULL, 0)) return GT2False; } else { // are we waiting for accept/reject? if(connection->state == GTI2AwaitingAcceptReject) connection->freeAtAcceptReject = GT2True; // mark it as closed gti2ConnectionClosed(connection); } } // report the close, as long as we're not already closed else if(connection->state != GTI2Closed) { // mark it as closed gti2ConnectionClosed(connection); // call the callback if(!gti2ClosedCallback(connection, reason)) return GT2False; } return GT2True; } static GT2Bool gti2ConnectionCommunicationError(GT2Connection connection) { return gti2ConnectionError(connection, GT2NegotiationError, GT2CommunicationError); } static GT2Bool gti2ConnectionMemoryError(GT2Connection connection) { // let the other side know if(!gti2SendClosed(connection)) return GT2False; return gti2ConnectionError(connection, GT2OutOfMemory, GT2NotEnoughMemory); } static GT2Bool gti2HandleESN(GT2Connection connection, unsigned short ESN) { int len; int i; GTI2OutgoingBufferMessage * message; int shortenBy; // get the number of messages in the outgoing queue len = ArrayLength(connection->outgoingBufferMessages); if(!len) return GT2True; // loop through until we hit one we can't remove for(i = 0 ; i < len ; i++) { // get the message message = (GTI2OutgoingBufferMessage *)ArrayNth(connection->outgoingBufferMessages, i); // don't stop until we get to the ESN if(gti2SNDiff(message->serialNumber, ESN) >= 0) break; } // check for not removing any if(i == 0) return GT2True; // remove the message info structs while(i--) ArrayDeleteAt(connection->outgoingBufferMessages, i); // check how many messages are left len = ArrayLength(connection->outgoingBufferMessages); if(!len) { // buffer is empty connection->outgoingBuffer.len = 0; return GT2True; } // figure out how much to move everything forward message = (GTI2OutgoingBufferMessage *)ArrayNth(connection->outgoingBufferMessages, 0); shortenBy = message->start; // do the move on the info structs for(i = 0 ; i < len ; i++) { message = (GTI2OutgoingBufferMessage *)ArrayNth(connection->outgoingBufferMessages, i); message->start -= shortenBy; } // move the actual data gti2BufferShorten(&connection->outgoingBuffer, 0, shortenBy); return GT2True; } static GT2Bool gti2HandleAppUnreliable(GT2Connection connection, GT2Byte * message, int len) { // check the state if((connection->state != GTI2Connected) && (connection->state != GTI2Closing)) return GT2True; // do we need to filter it? if(ArrayLength(connection->receiveFilters)) { if(!gti2ReceiveFilterCallback(connection, 0, message, len, GT2False)) return GT2False; return GT2True; } // we received data, call the callback if(!gti2ReceivedCallback(connection, message, len, GT2False)) return GT2False; return GT2True; } static GT2Bool gti2HandleAppReliable(GT2Connection connection, GT2Byte * message, int len) { // check the state if((connection->state != GTI2Connected) && (connection->state != GTI2Closing)) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; } else { // do we need to filter it? if(ArrayLength(connection->receiveFilters)) { if(!gti2ReceiveFilterCallback(connection, 0, message, len, GT2True)) return GT2False; return GT2True; } // we received data, call the callback if(!gti2ReceivedCallback(connection, message, len, GT2True)) return GT2False; } return GT2True; } static GT2Bool gti2HandleClientChallenge(GT2Connection connection, GT2Byte * message, int len) { char response[GTI2_RESPONSE_LEN]; char challenge[GTI2_CHALLENGE_LEN]; // check the state if(connection->state != GTI2AwaitingClientChallenge) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // make sure the message is long enough if(len < GTI2_CHALLENGE_LEN) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // generate a response to the challenge gti2GetResponse((GT2Byte *)response, message); // generate our own challenge gti2GetChallenge((GT2Byte *)challenge); // store what our response will be gti2GetResponse((GT2Byte *)connection->response, (GT2Byte *)challenge); // send our own challenge if(!gti2SendServerChallenge(connection, response, challenge)) return GT2False; // new state connection->state = GTI2AwaitingClientResponse; return GT2True; } static GT2Bool gti2HandleServerChallenge(GT2Connection connection, GT2Byte * message, int len) { char response[GTI2_RESPONSE_LEN]; // check the state if(connection->state != GTI2AwaitingServerChallenge) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // make sure the message is long enough if(len < (GTI2_RESPONSE_LEN + GTI2_CHALLENGE_LEN)) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // check the response if(!gti2CheckResponse(message, (GT2Byte *)connection->response)) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // generate our response to the server's challenge gti2GetResponse((GT2Byte *)response, message + GTI2_RESPONSE_LEN); // send the response, including our intial message if(!gti2SendClientResponse(connection, response, connection->initialMessage, connection->initialMessageLen)) return GT2False; // free the initial message if(connection->initialMessage) { gsifree(connection->initialMessage); connection->initialMessage = NULL; } // new state connection->state = GTI2AwaitingAcceptance; return GT2True; } static GT2Bool gti2HandleClientResponse(GT2Connection connection, GT2Byte * message, int len) { int latency; // check the state if(connection->state != GTI2AwaitingClientResponse) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // make sure the message is long enough if(len < (GTI2_RESPONSE_LEN)) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // check the response if(!gti2CheckResponse(message, (GT2Byte *)connection->response)) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // need to make sure the connection didn't just stop listening if(!connection->socket->connectAttemptCallback) { // send them a closed if(!gti2SendClosed(connection)) return GT2False; // mark it as closed gti2ConnectionClosed(connection); return GT2True; } // new state connection->state = GTI2AwaitingAcceptReject; // calculate the approx. latency latency = (int)(current_time() - connection->challengeTime); // it's up to the app now if(!gti2ConnectAttemptCallback(connection->socket, connection, connection->ip, connection->port, latency, message + GTI2_RESPONSE_LEN, len - GTI2_RESPONSE_LEN)) return GT2False; return GT2True; } static GT2Bool gti2HandleAccept(GT2Connection connection) { // check the state if(connection->state != GTI2AwaitingAcceptance) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // new state connection->state = GTI2Connected; // call the callback if(!gti2ConnectedCallback(connection, GT2Success, NULL, 0)) return GT2False; return GT2True; } static GT2Bool gti2HandleReject(GT2Connection connection, GT2Byte * message, int len) { // check the state if(connection->state != GTI2AwaitingAcceptance) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // mark it as closed gti2ConnectionClosed(connection); // send a closed reply if(!gti2SendClosed(connection)) return GT2False; // call the callback if(!gti2ConnectedCallback(connection, GT2Rejected, message, len)) return GT2False; return GT2True; } static GT2Bool gti2HandleClose(GT2Connection connection) { GT2Bool localClose; // send a closed reply if(!gti2SendClosed(connection)) return GT2False; // were we attempting to close this connection? localClose = (connection->state == GTI2Closing); // handle it as an error (so the right callbacks are called) if(!gti2ConnectionError(connection, GT2Rejected, localClose?GT2LocalClose:GT2RemoteClose)) return GT2False; return GT2True; } static GT2Bool gti2DeliverReliableMessage(GT2Connection connection, GTI2MessageType type, GT2Byte * message, int len) { // bump our ESN connection->expectedSerialNumber++; if(type == GTI2MsgAppReliable) { if(!gti2HandleAppReliable(connection, message, len)) return GT2False; } else if(type == GTI2MsgClientChallenge) { if(!gti2HandleClientChallenge(connection, message, len)) return GT2False; } else if(type == GTI2MsgServerChallenge) { if(!gti2HandleServerChallenge(connection, message, len)) return GT2False; } else if(type == GTI2MsgClientResponse) { if(!gti2HandleClientResponse(connection, message, len)) return GT2False; } else if(type == GTI2MsgAccept) { if(!gti2HandleAccept(connection)) return GT2False; } else if(type == GTI2MsgReject) { if(!gti2HandleReject(connection, message, len)) return GT2False; } else if(type == GTI2MsgClose) { if(!gti2HandleClose(connection)) return GT2False; } else if(type == GTI2MsgKeepAlive) { // ignore } return GT2True; } #ifdef WIN32 static int __cdecl gti2IncomingBufferMessageCompare(const void * elem1, const void * elem2) #else static int gti2IncomingBufferMessageCompare(const void * elem1, const void * elem2) #endif { const GTI2IncomingBufferMessage * message1 = (GTI2IncomingBufferMessage *)elem1; const GTI2IncomingBufferMessage * message2 = (GTI2IncomingBufferMessage *)elem2; return gti2SNDiff(message1->serialNumber, message2->serialNumber); } static GT2Bool gti2BufferIncomingMessage(GT2Connection connection, GTI2MessageType type, unsigned short SN, GT2Byte * message, int len, GT2Bool * overflow) { GTI2IncomingBufferMessage messageInfo; GTI2IncomingBufferMessage * bufferedMessage; int num; int i; // check the number of messages being held num = ArrayLength(connection->incomingBufferMessages); // check if this message is already buffered for(i = 0 ; i < num ; i++) { // get the message bufferedMessage = (GTI2IncomingBufferMessage *)ArrayNth(connection->incomingBufferMessages, i); // check if this is the same message if(bufferedMessage->serialNumber == SN) { *overflow = GT2False; return GT2True; } // check if we've already past the target SN if(gti2SNDiff(bufferedMessage->serialNumber, SN) > 0) break; } // check that there's enough space to store the message if(gti2GetBufferFreeSpace(&connection->incomingBuffer) < len) { *overflow = GT2True; return GT2True; } // setup the message info messageInfo.start = connection->incomingBuffer.len; messageInfo.len = len; messageInfo.type = type; messageInfo.serialNumber = SN; // add it to the list ArrayInsertSorted(connection->incomingBufferMessages, &messageInfo, gti2IncomingBufferMessageCompare); // make sure the length is one more if(ArrayLength(connection->incomingBufferMessages) != (num + 1)) { *overflow = GT2True; return GT2True; } // copy the message into the buffer gti2BufferWriteData(&connection->incomingBuffer, message, len); // check for sending a nack // we want to send one when we think a message or messages were probably dropped if(num == 0) { // if we're the only message in the hold, send a nack if(!gti2SendNack(connection, connection->expectedSerialNumber, (unsigned short)(SN - 1))) return GT2False; } else { GTI2IncomingBufferMessage * msg; // are we the highest message? msg = (GTI2IncomingBufferMessage *)ArrayNth(connection->incomingBufferMessages, num); if(msg->serialNumber == SN) { GTI2IncomingBufferMessage * prev; unsigned short diff; // if we're not right after the second-highest SN, the ones in between were probably dropped prev = (GTI2IncomingBufferMessage *)ArrayNth(connection->incomingBufferMessages, num - 1); diff = (unsigned short)gti2SNDiff(SN, prev->serialNumber); if(diff > 1) { if(!gti2SendNack(connection, (unsigned short)(prev->serialNumber + 1), (unsigned short)(SN - 1))) return GT2False; } } } *overflow = GT2False; return GT2True; } static void gti2RemoveHoldMessage(GT2Connection connection, GTI2IncomingBufferMessage * message, int index) { int moveAfter; int shortenBy; int moveEnd = 0; int num; int i; // save off info about it moveAfter = message->start; shortenBy = message->len; // delete it ArrayDeleteAt(connection->incomingBufferMessages, index); // loop through and fix up message's stored after this one in the buffer // also figure out exactly how much data we'll need to move num = ArrayLength(connection->incomingBufferMessages); for(i = 0 ; i < num ; i++) { // check if this message needs to be moved forward message = (GTI2IncomingBufferMessage *)ArrayNth(connection->incomingBufferMessages, i); if(message->start > moveAfter) { message->start -= shortenBy; moveEnd = max(moveEnd, message->start + message->len); } } // fix up the buffer itself gti2BufferShorten(&connection->incomingBuffer, moveAfter, shortenBy); } static GT2Bool gti2DeliverHoldMessages(GT2Connection connection) { GTI2IncomingBufferMessage * message; int num; int i; restart: // loop through the buffered messages, checking if there are any that can now be delivered // loop through backwards to ease removal num = ArrayLength(connection->incomingBufferMessages); for(i = (num - 1) ; i >= 0 ; i--) { message = (GTI2IncomingBufferMessage *)ArrayNth(connection->incomingBufferMessages, i); // we should deliver this if it's what we're expecting if(message->serialNumber == connection->expectedSerialNumber) { // deliver it if(!gti2DeliverReliableMessage(connection, message->type, connection->incomingBuffer.buffer + message->start, message->len)) return GT2False; // remove it gti2RemoveHoldMessage(connection, message, i); // we need to go through this loop again. // goto's are evil, but a little evil is good here goto restart; } } return GT2True; } static void gti2SetPendingAck(GT2Connection connection) { // if there's not a pending ack, set one if(!connection->pendingAck) { connection->pendingAck = GT2True; connection->pendingAckTime = current_time(); } } static GT2Bool gti2HandleReliableMessage(GT2Connection connection, GTI2MessageType type, GT2Byte * message, int len) { unsigned short SN; unsigned short ESN; const int headerLength = connection->socket->protocolOffset + GTI2_MAGIC_STRING_LEN + 1 + 2 + 2; GT2Bool overflow; // check if it's long enough if(len < (connection->socket->protocolOffset + GTI2_MAGIC_STRING_LEN + 1 + 2 + 2)) // magic string + type + SN + ESN { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // get the SN SN = gti2UShortFromBuffer(message + connection->socket->protocolOffset, GTI2_MAGIC_STRING_LEN + 1); // get the ESN ESN = gti2UShortFromBuffer(message + connection->socket->protocolOffset, GTI2_MAGIC_STRING_LEN + 3); // update the message and length to point to the actual message if (connection->socket->protocolType == GTI2VdpProtocol && type == GTI2MsgAppReliable) { message[connection->socket->protocolOffset + GTI2_MAGIC_STRING_LEN + 3] = message[0]; message[connection->socket->protocolOffset + GTI2_MAGIC_STRING_LEN + 4] = message[1]; message += headerLength - connection->socket->protocolOffset; len -= headerLength - connection->socket->protocolOffset; } else { message += headerLength; len -= headerLength; } // handle the ESN if(!gti2HandleESN(connection, ESN)) return GT2False; // check if it's the SN we expected if(SN == connection->expectedSerialNumber) { // make sure we ack this message // do this before delivering, because we might send an ack as part of a reliable reply gti2SetPendingAck(connection); // deliver the message if(!gti2DeliverReliableMessage(connection, type, message, len)) return GT2False; // check if there are any messages in the hold that can now be delivered if(!gti2DeliverHoldMessages(connection)) return GT2False; return GT2True; } // check if the message is a duplicate if(gti2SNDiff(SN, connection->expectedSerialNumber) < 0) { // it's a duplicate, ack it again gti2SetPendingAck(connection); // ignore it return GT2True; } // we can't deliver this yet, so put it in the hold if(!gti2BufferIncomingMessage(connection, type, SN, message, len, &overflow)) return GT2False; // check for a buffer overflow if(overflow) { if(!gti2ConnectionMemoryError(connection)) return GT2False; } return GT2True; } static GT2Bool gti2HandleAck(GT2Connection connection, const GT2Byte * message, int len) { unsigned short ESN; // make sure it has enough space for the ESN if(len != 2) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // get the ESN ESN = gti2UShortFromBuffer(message, 0); // handle it if(!gti2HandleESN(connection, ESN)) return GT2False; return GT2True; } static GT2Bool gti2HandleNack(GT2Connection connection, const GT2Byte * message, int len) { unsigned short SNMin; unsigned short SNMax; int num; int i; GTI2OutgoingBufferMessage * messageInfo; // read based on length. SNMin = gti2UShortFromBuffer(message, 0); if(len == 2) { SNMax = SNMin; } else if(len == 4) { SNMax = gti2UShortFromBuffer(message, 2); } else { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; } // loop through the messages, resending any specified ones num = ArrayLength(connection->outgoingBufferMessages); for(i = 0 ; i < num ; i++) { messageInfo = (GTI2OutgoingBufferMessage *)ArrayNth(connection->outgoingBufferMessages, i); if((gti2SNDiff(messageInfo->serialNumber, SNMin) >= 0) && (gti2SNDiff(messageInfo->serialNumber, SNMax) <= 0)) { if(!gti2ResendMessage(connection, messageInfo)) return GT2False; } } return GT2True; } static GT2Bool gti2HandlePing(GT2Connection connection, GT2Byte * message, int len) { // send it right back return gti2SendPong(connection, message, len); } static GT2Bool gti2HandlePong(GT2Connection connection, const GT2Byte * message, int len) { gsi_time startTime; // do we care about this? if(!connection->callbacks.ping) return GT2True; // is this a pong we're interested in? // "time" + ping-sent-time if(len != (4 + sizeof(gsi_time))) return GT2True; if(memcmp(message, "time", 4) != 0) return GT2True; // get the start time memcpy(&startTime, message + 4, sizeof(gsi_time)); // call the callback if(!gti2PingCallback(connection, (int)(current_time() - startTime))) return GT2False; return GT2True; } static GT2Bool gti2HandleClosed(GT2Connection connection) { GT2Bool localClose; // are we already closed? if(connection->state == GTI2Closed) return GT2True; // were we attempting to close this connection? localClose = (connection->state == GTI2Closing); // handle it as an error (so the right callbacks are called) if(!gti2ConnectionError(connection, GT2Rejected, localClose?GT2LocalClose:GT2RemoteClose)) return GT2False; return GT2True; } static GT2Bool gti2HandleUnreliableMessage(GT2Connection connection, GTI2MessageType type, GT2Byte * message, int len) { int headerLength; GT2Byte * dataStart; int dataLen; // most unreliable messages don't need the header headerLength = (connection->socket->protocolOffset + GTI2_MAGIC_STRING_LEN + 1); dataStart = (message + headerLength); dataLen = (len - headerLength); // handle unreliable messages based on type if(type == GTI2MsgAck) { if(!gti2HandleAck(connection, dataStart, dataLen)) return GT2False; } else if(type == GTI2MsgNack) { if(!gti2HandleNack(connection, dataStart, dataLen)) return GT2False; } else if(type == GTI2MsgPing) { if(!gti2HandlePing(connection, message, len)) return GT2False; } else if(type == GTI2MsgPong) { if(!gti2HandlePong(connection, dataStart, dataLen)) return GT2False; } else if(type == GTI2MsgClosed) { if(!gti2HandleClosed(connection)) return GT2False; } return GT2True; } // VDP sockets have data length which needs to be stripped off static GT2Bool gti2HandleMessage(GT2Socket socket, GT2Byte * message, int len, unsigned int ip, unsigned short port) { GT2Connection connection; GT2Bool magicString; GT2Result result; GTI2MessageType type; GT2Bool handled; int actualLength = len - socket->protocolOffset; // VDP messages have 2 byte header which is removed based on protocol GT2Byte *actualMessage = message + socket->protocolOffset; // find out if we have an existing connection for this address connection = gti2SocketFindConnection(socket, ip, port); // let the dump handle this if(socket->receiveDumpCallback) { if(!gti2DumpCallback(socket, connection, ip, port, GT2False, message, len, GT2False)) return GT2False; } // check if the message starts with the magic string // use greater than for the len compare because it also must have a type magicString = ((actualLength > GTI2_MAGIC_STRING_LEN) && (memcmp(actualMessage, GTI2_MAGIC_STRING, GTI2_MAGIC_STRING_LEN) == 0)); // check if we don't have a connection if(!connection) { // if we don't know who this is from, let the unrecognized message callback have first crack at it if(!gti2UnrecognizedMessageCallback(socket, ip, port, message, len, &handled)) return GT2False; // if they handled it, we don't care about it. if(handled) return GT2True; // if this isn't a connection request, tell them the connection is closed if(!magicString || (actualMessage[GTI2_MAGIC_STRING_LEN] != GTI2MsgClientChallenge)) { // if this is a closed message, don't send one back (to avoid recursion) if(!magicString || (actualMessage[GTI2_MAGIC_STRING_LEN] != GTI2MsgClosed)) { if(!gti2SendClosedOnSocket(socket, ip, port)) return GT2False; } return GT2True; } // if we're not listening, we just ignore this if(!socket->connectAttemptCallback) return GT2True; // create a connection result = gti2NewIncomingConnection(socket, &connection, ip, port); if(result != GT2Success) { // as long as this wasn't a duplicate address error, tell them we're closed // in the case of duplicates, we don't want to close the existing connection if(result != GT2DuplicateAddress) { if(!gti2SendClosedOnSocket(socket, ip, port)) return GT2False; } return GT2True; } } // is the connection already closed? if(connection->state == GTI2Closed) { // if this is a closed message, don't send one back (to avoid recursion) if(!magicString || (actualMessage[GTI2_MAGIC_STRING_LEN] != GTI2MsgClosed)) { if(!gti2SendClosed(connection)) return GT2False; } return GT2True; } // check if this is an unreliable app message with a magic string header if(magicString && ((actualLength >= (GTI2_MAGIC_STRING_LEN * 2)) && (memcmp(actualMessage + GTI2_MAGIC_STRING_LEN, GTI2_MAGIC_STRING, GTI2_MAGIC_STRING_LEN) == 0))) { message[3] = message[1]; message[2] = message[0]; message = actualMessage; actualMessage += socket->protocolOffset; actualLength -= socket->protocolOffset; len -= GTI2_MAGIC_STRING_LEN; magicString = GT2False; } // if it doesn't have a magic string it's an app unreliable if(!magicString) { // First determine if the connection found has gone throught the internal challenge response if (connection->state < GTI2Connected) { // pass any message that doesn't have a magic string to // the app so that the SDK doesn't drop them if(!gti2UnrecognizedMessageCallback(socket, ip, port, message, len, &handled)) return GT2False; } else { if(!gti2HandleAppUnreliable(connection, message, len)) return GT2False; } return GT2True; } // get the message type type = (GTI2MessageType)actualMessage[GTI2_MAGIC_STRING_LEN]; // check for a bad type /*if(type < 0) { if(!gti2ConnectionCommunicationError(connection)) return GT2False; return GT2True; }*/ // check if it's reliable if(type < GTI2NumReliableMessages) { // handle it if(!gti2HandleReliableMessage(connection, type, message, len)) return GT2False; return GT2True; } // handle unreliable messages if(!gti2HandleUnreliableMessage(connection, type, message, len)) return GT2False; return GT2True; } GT2Bool gti2HandleConnectionReset(GT2Socket socket, unsigned int ip, unsigned short port) { GT2Connection connection; // find the connection for the reset connection = gti2SocketFindConnection(socket, ip, port); // let the dump know about this if(socket->receiveDumpCallback) { if(!gti2DumpCallback(socket, connection, ip, port, GT2True, NULL, 0, GT2False)) return GT2False; } // there's no connection, so ignore it if(!connection) return GT2True; // are we waiting for a response from the server? if(connection->state == GTI2AwaitingServerChallenge) { // are we still within the timeout time? if(!connection->timeout || ((current_time() - connection->startTime) < connection->timeout)) return GT2True; // report this as a timeout if(!gti2ConnectionError(connection, GT2TimedOut, GT2RemoteClose)) return GT2False; } else { // report the error if(!gti2ConnectionError(connection, GT2Rejected, GT2RemoteClose)) return GT2False; } return GT2True; } GT2Bool gti2HandleHostUnreachable(GT2Socket socket, unsigned int ip, unsigned short port, GT2Bool send) { GT2Connection connection; // find the connection for the reset connection = gti2SocketFindConnection(socket, ip, port); // let the dump know about this if(socket->receiveDumpCallback) { if(!gti2DumpCallback(socket, connection, ip, port, GT2True, NULL, 0, send)) return GT2False; } // there's no connection, so ignore it if(!connection) return GT2True; // report the error if(!gti2ConnectionError(connection, GT2TimedOut, GT2RemoteClose)) return GT2False; return GT2True; } #ifdef GSI_ADHOC // return length if successful // <=0 on error gsi_bool _NetworkAdHocSocketRecv(int socket_id, char *buf, int bufferlen, int flags, char *saddr, //struct SceNetEtherAddr = char[6]; gsi_u16 *sport); // return 0 if no data, -1 if error, >0 if data to read int _NetworkAdHocCanReceiveOnSocket(int socket_id); GT2Bool gti2ReceiveAdHocMessages(GT2Socket socket,char *buffer, int buffersize) { int rcode; SOCKADDR_IN address; int addressLen;//, datasize; // check for messages while (1) { int datasize = _NetworkAdHocCanReceiveOnSocket(socket->socket); if (datasize < 0) // error { gti2SocketError(socket); return GT2False; } if (datasize == 0) break; // no data { // We have data to recv // receive the message char mac[6]; gsi_u16 port; //gsi_u32 ip; addressLen = sizeof(address); rcode = _NetworkAdHocSocketRecv(socket->socket, buffer,buffersize , 0, mac,&port); if(rcode < 0) // fatal socket error { #if(0) // notes if(0)//rcode == WSAECONNRESET) { // handle the reset if(!gti2HandleConnectionReset(socket, address.sin_addr.s_addr, ntohs(address.sin_port))) return GT2False; } else if (rcode == WSAEHOSTUNREACH) { if (!gti2HandleHostUnreachable(socket, address.sin_addr.s_addr, ntohs(address.sin_port), GT2False)) return GT2False; } else #endif { gti2SocketError(socket); return GT2False; } } if(rcode == 0) // no data { return GT2False; } // at this point we have valid data // change ethernet to IP address address.sin_addr.s_addr = gt2MacToIp(mac); address.sin_port = port; #ifdef RECV_LOG // log it gti2LogMessage(address.sin_addr.s_addr, ntohs(address.sin_port), socket->ip, socket->port, buffer, rcode); #endif // handle the message if(!gti2HandleMessage(socket, (GT2Byte *)buffer, rcode, address.sin_addr.s_addr, address.sin_port)) return GT2False; } } return GT2True; } #endif GT2Bool gti2ReceiveMessages(GT2Socket socket) { int rcode; SOCKADDR_IN address; int addressLen; // avoid overflowing stack #if (GTI2_STACK_RECV_BUFFER_SIZE > 1600) static char buffer[GTI2_STACK_RECV_BUFFER_SIZE]; #else char buffer[GTI2_STACK_RECV_BUFFER_SIZE]; #endif #ifdef GSI_ADHOC if(socket->protocolType == GTI2AdHocProtocol) { return gti2ReceiveAdHocMessages(socket,buffer,GTI2_STACK_RECV_BUFFER_SIZE); } #endif // check for messages while (CanReceiveOnSocket(socket->socket)) { // mj todo: get this plat specific stuff out of here. Belongs in play specific layer. // abstract recvfrom // receive the message addressLen = sizeof(address); rcode = recvfrom(socket->socket, buffer, sizeof(buffer), 0, (SOCKADDR *)&address, &addressLen); if (gsiSocketIsError(rcode)) { rcode = GOAGetLastError(socket->socket); if(rcode == WSAECONNRESET) { // handle the reset if(!gti2HandleConnectionReset(socket, address.sin_addr.s_addr, ntohs(address.sin_port))) return GT2False; } #ifndef SN_SYSTEMS else if (rcode == WSAEHOSTUNREACH) { if (!gti2HandleHostUnreachable(socket, address.sin_addr.s_addr, ntohs(address.sin_port), GT2False)) return GT2False; } #endif else if(rcode != WSAEMSGSIZE) { // fatal socket error gti2SocketError(socket); return GT2False; } } else { #ifdef RECV_LOG // log it gti2LogMessage(address.sin_addr.s_addr, ntohs(address.sin_port), socket->ip, socket->port, buffer, rcode); #endif // handle the message if(!gti2HandleMessage(socket, (GT2Byte *)buffer, rcode, address.sin_addr.s_addr, ntohs(address.sin_port))) return GT2False; } } return GT2True; } static GT2Bool gti2StoreOutgoingReliableMessageInfo(GT2Connection connection, unsigned short SN, int len) { GTI2OutgoingBufferMessage messageInfo; int num; // setup the message info memset(&messageInfo, 0, sizeof(messageInfo)); messageInfo.start = connection->outgoingBuffer.len; messageInfo.len = len; messageInfo.serialNumber = SN; messageInfo.lastSend = current_time(); // check the number of messages before we do the add num = ArrayLength(connection->outgoingBufferMessages); // add it to the list ArrayAppend(connection->outgoingBufferMessages, &messageInfo); // make sure the length is one more if(ArrayLength(connection->outgoingBufferMessages) != (num + 1)) return GT2False; return GT2True; } static GT2Bool gti2BeginReliableMessage(GT2Connection connection, GTI2MessageType type, int len, GT2Bool * overflow) { int freeSpace; // VDP data length needed in the front of every packet unsigned short vdpDataLength = (unsigned short)(len - connection->socket->protocolOffset); // check how much free space is in the outgoing buffer freeSpace = gti2GetBufferFreeSpace(&connection->outgoingBuffer); // do we have the space to hold it? if(freeSpace < len) { if(!gti2ConnectionMemoryError(connection)) return GT2False; *overflow = GT2True; return GT2True; } // store the message's info if(!gti2StoreOutgoingReliableMessageInfo(connection, connection->serialNumber, len)) { if(!gti2ConnectionMemoryError(connection)) return GT2False; *overflow = GT2True; return GT2True; } // setup the header if (connection->socket->protocolType == GTI2VdpProtocol) gti2BufferWriteData(&connection->outgoingBuffer, (const GT2Byte *)&vdpDataLength, connection->socket->protocolOffset); gti2BufferWriteData(&connection->outgoingBuffer, (const GT2Byte *)GTI2_MAGIC_STRING, GTI2_MAGIC_STRING_LEN); gti2BufferWriteByte(&connection->outgoingBuffer, (GT2Byte)type); gti2BufferWriteUShort(&connection->outgoingBuffer, connection->serialNumber++); gti2BufferWriteUShort(&connection->outgoingBuffer, connection->expectedSerialNumber); *overflow = GT2False; return GT2True; } static GT2Bool gti2EndReliableMessage(GT2Connection connection) { GTI2OutgoingBufferMessage * message; int len; // the message we're sending is the last one len = ArrayLength(connection->outgoingBufferMessages); assert(len > 0); message = (GTI2OutgoingBufferMessage *)ArrayNth(connection->outgoingBufferMessages, len - 1); // send it if(!gti2ConnectionSendData(connection, connection->outgoingBuffer.buffer + message->start, message->len)) return GT2False; // we just did an ack (as part of the message) connection->pendingAck = GT2False; return GT2True; } GT2Bool gti2SendAppReliable(GT2Connection connection, const GT2Byte * message, int len) { int totalLen; GT2Bool overflow; // magic string + type + SN + ESN + message totalLen = (GTI2_MAGIC_STRING_LEN + 1 + 2 + 2 + len); // begin the message if(!gti2BeginReliableMessage(connection, GTI2MsgAppReliable, totalLen, &overflow)) return GT2False; if(overflow) return GT2True; // write the message gti2BufferWriteData(&connection->outgoingBuffer, message, len); // end the message if(!gti2EndReliableMessage(connection)) return GT2False; return GT2True; } GT2Bool gti2SendClientChallenge(GT2Connection connection, const char challenge[GTI2_CHALLENGE_LEN]) { // magic string + type + SN + ESN + challenge int totalLen = (GTI2_MAGIC_STRING_LEN + 1 + 2 + 2 + GTI2_CHALLENGE_LEN); GT2Bool overflow; // begin the message if(!gti2BeginReliableMessage(connection, GTI2MsgClientChallenge, totalLen + connection->socket->protocolOffset, &overflow)) return GT2False; if(overflow) return GT2True; // write the challenge gti2BufferWriteData(&connection->outgoingBuffer, (const GT2Byte *)challenge, GTI2_CHALLENGE_LEN); // end the message if(!gti2EndReliableMessage(connection)) return GT2False; return GT2True; } GT2Bool gti2SendServerChallenge(GT2Connection connection, const char response[GTI2_RESPONSE_LEN], const char challenge[GTI2_CHALLENGE_LEN]) { // magic string + type + SN + ESN + response + challenge int totalLen = (GTI2_MAGIC_STRING_LEN + 1 + 2 + 2 + GTI2_RESPONSE_LEN + GTI2_CHALLENGE_LEN); GT2Bool overflow; // begin the message if(!gti2BeginReliableMessage(connection, GTI2MsgServerChallenge, totalLen + connection->socket->protocolOffset, &overflow)) return GT2False; if(overflow) return GT2True; // write the response gti2BufferWriteData(&connection->outgoingBuffer, (const GT2Byte *)response, GTI2_RESPONSE_LEN); // write the challenge gti2BufferWriteData(&connection->outgoingBuffer, (const GT2Byte *)challenge, GTI2_CHALLENGE_LEN); // end the message if(!gti2EndReliableMessage(connection)) return GT2False; // save the time connection->challengeTime = connection->lastSend; return GT2True; } GT2Bool gti2SendClientResponse(GT2Connection connection, const char response[GTI2_RESPONSE_LEN], const char * message, int len) { // magic string + type + SN + ESN + response + message int totalLen = (GTI2_MAGIC_STRING_LEN + 1 + 2 + 2 + GTI2_RESPONSE_LEN + len); GT2Bool overflow; // begin the message if(!gti2BeginReliableMessage(connection, GTI2MsgClientResponse, totalLen + connection->socket->protocolOffset, &overflow)) return GT2False; if(overflow) return GT2True; // write the response gti2BufferWriteData(&connection->outgoingBuffer, (const GT2Byte *)response, GTI2_RESPONSE_LEN); // write the message gti2BufferWriteData(&connection->outgoingBuffer, (const GT2Byte *)message, len); // end the message if(!gti2EndReliableMessage(connection)) return GT2False; return GT2True; } GT2Bool gti2SendAccept(GT2Connection connection) { // magic string + type + SN + ESN int totalLen = (GTI2_MAGIC_STRING_LEN + 1 + 2 + 2); GT2Bool overflow; // begin the message if(!gti2BeginReliableMessage(connection, GTI2MsgAccept, totalLen + connection->socket->protocolOffset, &overflow)) return GT2False; if(overflow) return GT2True; // end the message if(!gti2EndReliableMessage(connection)) return GT2False; return GT2True; } GT2Bool gti2SendReject(GT2Connection connection, const GT2Byte * message, int len) { // magic string + type + SN + ESN + message int totalLen = (GTI2_MAGIC_STRING_LEN + 1 + 2 + 2 + len); GT2Bool overflow; // begin the message if(!gti2BeginReliableMessage(connection, GTI2MsgReject, totalLen + connection->socket->protocolOffset, &overflow)) return GT2False; if(overflow) return GT2True; // write the message gti2BufferWriteData(&connection->outgoingBuffer, message, len); // end the message if(!gti2EndReliableMessage(connection)) return GT2False; return GT2True; } GT2Bool gti2SendClose(GT2Connection connection) { // magic string + type + SN + ESN int totalLen = (GTI2_MAGIC_STRING_LEN + 1 + 2 + 2); GT2Bool overflow; // begin the message if(!gti2BeginReliableMessage(connection, GTI2MsgClose, totalLen + connection->socket->protocolOffset, &overflow)) return GT2False; if(overflow) return GT2True; // end the message if(!gti2EndReliableMessage(connection)) return GT2False; return GT2True; } GT2Bool gti2SendKeepAlive(GT2Connection connection) { // magic string + type + SN + ESN int totalLen = (GTI2_MAGIC_STRING_LEN + 1 + 2 + 2); GT2Bool overflow; // begin the message if(!gti2BeginReliableMessage(connection, GTI2MsgKeepAlive, totalLen + connection->socket->protocolOffset, &overflow)) return GT2False; if(overflow) return GT2True; // end the message if(!gti2EndReliableMessage(connection)) return GT2False; return GT2True; } GT2Bool gti2SendAppUnreliable(GT2Connection connection, const GT2Byte * message, int len) { int freeSpace; int totalLen; GT2Byte * start; // check if we can send it right away (unreliable that doesn't start with the magic string) if((len < GTI2_MAGIC_STRING_LEN) || (memcmp(message + connection->socket->protocolOffset, GTI2_MAGIC_STRING, GTI2_MAGIC_STRING_LEN) != 0)) { if(!gti2ConnectionSendData(connection, message, len)) return GT2False; return GT2True; } // magic string + message totalLen = (GTI2_MAGIC_STRING_LEN + len); // check how much free space is in the outgoing buffer freeSpace = gti2GetBufferFreeSpace(&connection->outgoingBuffer); // do we have the space to hold it? if(freeSpace < totalLen) { // just drop it return GT2True; } // store the start of the actual message in the buffer start = (connection->outgoingBuffer.buffer + connection->outgoingBuffer.len); // Copy the VDP data length if necessary if (connection->socket->protocolType == GTI2VdpProtocol) gti2BufferWriteData(&connection->outgoingBuffer, message, 2); // copy it in, repeating the magic string at the beginning gti2BufferWriteData(&connection->outgoingBuffer, (const GT2Byte *)GTI2_MAGIC_STRING, GTI2_MAGIC_STRING_LEN); // copy the data at the starting position + offset based on the protocol gti2BufferWriteData(&connection->outgoingBuffer, message + connection->socket->protocolOffset, (int)(len - connection->socket->protocolOffset)); // do the send if(!gti2ConnectionSendData(connection, start, totalLen)) return GT2False; // we don't need to save the message gti2BufferShorten(&connection->outgoingBuffer, -1, totalLen); return GT2True; } GT2Bool gti2SendAck(GT2Connection connection) { // always allocate data length + magic string + type + ESN // part of the buffer may not be used but more efficience to be on stack char buffer[MAX_PROTOCOL_OFFSET + GTI2_MAGIC_STRING_LEN + 1 + 2]; int pos = 0; // write the VDP data length if (connection->socket->protocolType == GTI2VdpProtocol) { short dataLength = (GTI2_MAGIC_STRING_LEN + 1 + 2); memcpy(buffer, &dataLength, 2); pos += 2; } // write the magic string memcpy(buffer + pos, GTI2_MAGIC_STRING, GTI2_MAGIC_STRING_LEN); pos += GTI2_MAGIC_STRING_LEN; // write the type buffer[pos++] = GTI2MsgAck; // write the ESN gti2UShortToBuffer((GT2Byte *)buffer, pos, connection->expectedSerialNumber); pos += 2; // send it if(!gti2ConnectionSendData(connection, (const GT2Byte *)buffer, pos)) return GT2False; // we just did an ack connection->pendingAck = GT2False; return GT2True; } GT2Bool gti2SendNack(GT2Connection connection, unsigned short SNMin, unsigned short SNMax) { // data length + magic string + type + SNMin [+ SNMax] // part of the buffer may not be used but more efficience to be on stack char buffer[MAX_PROTOCOL_OFFSET + GTI2_MAGIC_STRING_LEN + 1 + 2 + 2]; int pos = 0; // write the VDP data length if (connection->socket->protocolType == GTI2VdpProtocol) { short dataLength = (GTI2_MAGIC_STRING_LEN + 1 + 2 + 2); memcpy(buffer, &dataLength, 2); pos += 2; } // write the magic string memcpy(buffer + pos, GTI2_MAGIC_STRING, GTI2_MAGIC_STRING_LEN); pos += GTI2_MAGIC_STRING_LEN; // write the type buffer[pos++] = GTI2MsgNack; // write the SNMin gti2UShortToBuffer((GT2Byte *)buffer, pos, SNMin); pos += 2; // write the SNMax if it's different if(SNMin != SNMax) { gti2UShortToBuffer((GT2Byte *)buffer, pos, SNMax); pos += 2; } // send it if(!gti2ConnectionSendData(connection, (const GT2Byte *)buffer, pos)) return GT2False; return GT2True; } GT2Bool gti2SendPing(GT2Connection connection) { // data length + magic string + type + "time" + current time // part of the buffer may not be used but more efficience to be on stack char buffer[MAX_PROTOCOL_OFFSET + GTI2_MAGIC_STRING_LEN + 1 + 4 + sizeof(gsi_time)]; int pos = 0; gsi_time now; // write the VDP data length if (connection->socket->protocolType == GTI2VdpProtocol) { short dataLength = (GTI2_MAGIC_STRING_LEN + 1 + 4 + sizeof(gsi_time)); memcpy(buffer, &dataLength, 2); pos += 2; } // write the magic string memcpy(buffer + pos, GTI2_MAGIC_STRING, GTI2_MAGIC_STRING_LEN); pos += GTI2_MAGIC_STRING_LEN; // write the type buffer[pos++] = GTI2MsgPing; // copy the time id memcpy(buffer + pos, "time", 4); pos += 4; // write the current time now = current_time(); memcpy(buffer + pos, &now, sizeof(gsi_time)); pos += (int)sizeof(gsi_time); // send it if(!gti2ConnectionSendData(connection, (const GT2Byte *)buffer, pos)) return GT2False; return GT2True; } GT2Bool gti2SendPong(GT2Connection connection, GT2Byte * message, int len) { // change the ping to a pong message[GTI2_MAGIC_STRING_LEN] = GTI2MsgPong; // send it return gti2ConnectionSendData(connection, message, len); } GT2Bool gti2SendClosed(GT2Connection connection) { // normal close return gti2SendClosedOnSocket(connection->socket, connection->ip, connection->port); } GT2Bool gti2SendClosedOnSocket(GT2Socket socket, unsigned int ip, unsigned short port) { // Vdp data length (not including voice) + magic string + type // part of the buffer may not be used but more efficience to be on stack char buffer[MAX_PROTOCOL_OFFSET + GTI2_MAGIC_STRING_LEN + 1]; int pos = 0; // write the data length if (socket->protocolType == GTI2VdpProtocol) { short dataLength = GTI2_MAGIC_STRING_LEN + 1; memcpy(buffer, &dataLength, 2); pos += 2; } // write the magic string memcpy(buffer + pos, GTI2_MAGIC_STRING, GTI2_MAGIC_STRING_LEN); pos += GTI2_MAGIC_STRING_LEN; // write the type buffer[pos++] = GTI2MsgClosed; // send it if(!gti2SocketSend(socket, ip, port, (const GT2Byte *)buffer, pos)) return GT2False; return GT2True; } GT2Bool gti2ResendMessage(GT2Connection connection, GTI2OutgoingBufferMessage * message) { GTI2MessageType type; int pos; // replace the ESN (it's after the magic string, the type, and the SN) pos = (message->start + connection->socket->protocolOffset + GTI2_MAGIC_STRING_LEN + 1 + 2); gti2UShortToBuffer(connection->outgoingBuffer.buffer, pos, connection->expectedSerialNumber); // send the message if(!gti2ConnectionSendData(connection, connection->outgoingBuffer.buffer + message->start, message->len)) return GT2False; // update the last time sent message->lastSend = connection->lastSend; // if it was a server challenge, update that time too type = (GTI2MessageType)connection->outgoingBuffer.buffer[message->start + connection->socket->protocolOffset + GTI2_MAGIC_STRING_LEN]; if(type == GTI2MsgServerChallenge) connection->challengeTime = connection->lastSend; return GT2True; } GT2Bool gti2Send(GT2Connection connection, const GT2Byte * message, int len, GT2Bool reliable) { if (reliable) return gti2SendAppReliable(connection, message, len); //send unreliable messages return gti2SendAppUnreliable(connection, message, len); } GT2Bool gti2WasMessageIDConfirmed(const GT2Connection connection, GT2MessageID messageID) { GTI2OutgoingBufferMessage * message; int len; // if there are no reliable messages waiting confirmation, then this has already been confirmed len = ArrayLength(connection->outgoingBufferMessages); if(!len) return GT2True; // get the oldest message waiting confirmation message = (GTI2OutgoingBufferMessage *)ArrayNth(connection->outgoingBufferMessages, 0); // if the message id we are looking for is older than the first one waiting confirmation, // then it has already been confirmed if(gti2SNDiff(messageID, message->serialNumber) < 0) return GT2True; // the message hasn't been confirmed yet return GT2False; }