void NetInterface::checkTimeouts() { U32 time = Platform::getVirtualMilliseconds(); if(time > mLastTimeoutCheckTime + TimeoutCheckInterval) { for(U32 i = 0; i < mPendingConnections.size();) { NetConnection *pending = mPendingConnections[i]; if(pending->getConnectionState() == NetConnection::AwaitingChallengeResponse && time > pending->mConnectLastSendTime + ChallengeRetryTime) { if(pending->mConnectSendCount > ChallengeRetryCount) { pending->onConnectTimedOut(); removePendingConnection(pending); pending->deleteObject(); continue; } else sendConnectChallengeRequest(pending); } else if(pending->getConnectionState() == NetConnection::AwaitingConnectResponse && time > pending->mConnectLastSendTime + ConnectRetryTime) { if(pending->mConnectSendCount > ConnectRetryCount) { pending->onConnectTimedOut(); removePendingConnection(pending); pending->deleteObject(); continue; } else sendConnectRequest(pending); } i++; } mLastTimeoutCheckTime = time; NetConnection *walk = NetConnection::getConnectionList(); while(walk) { NetConnection *next = walk->getNext(); if(walk->checkTimeout(time)) { // this baddie timed out walk->onTimedOut(); walk->deleteObject(); } walk = next; } } }
void NetInterface::handleConnectChallengeResponse(const Address &address, BitStream *stream) { NetConnection *conn = findPendingConnection(address); if(!conn || conn->getConnectionState() != NetConnection::AwaitingChallengeResponse) return; Nonce theNonce; theNonce.read(stream); ConnectionParameters &theParams = conn->getConnectionParameters(); if(theNonce != theParams.mNonce) return; stream->read(&theParams.mClientIdentity); // see if the server wants us to solve a client puzzle theParams.mServerNonce.read(stream); stream->read(&theParams.mPuzzleDifficulty); if(theParams.mPuzzleDifficulty > ClientPuzzleManager::MaxPuzzleDifficulty) return; // see if the connection needs to be authenticated or uses key exchange if(stream->readFlag()) { if(stream->readFlag()) { theParams.mCertificate = new Certificate(stream); if(!theParams.mCertificate->isValid() || !conn->validateCertficate(theParams.mCertificate, true)) return; theParams.mPublicKey = theParams.mCertificate->getPublicKey(); } else { theParams.mPublicKey = new AsymmetricKey(stream); if(!theParams.mPublicKey->isValid() || !conn->validatePublicKey(theParams.mPublicKey, true)) return; } if(mPrivateKey.isNull() || mPrivateKey->getKeySize() != theParams.mPublicKey->getKeySize()) { // we don't have a private key, so generate one for this connection theParams.mPrivateKey = new AsymmetricKey(theParams.mPublicKey->getKeySize()); } else theParams.mPrivateKey = mPrivateKey; theParams.mSharedSecret = theParams.mPrivateKey->computeSharedSecretKey(theParams.mPublicKey); //logprintf("shared secret (client) %s", theParams.mSharedSecret->encodeBase64()->getBuffer()); Random::read(theParams.mSymmetricKey, SymmetricCipher::KeySize); theParams.mUsingCrypto = true; } TNLLogMessageV(LogNetInterface, ("Received Challenge Response: %8x", theParams.mClientIdentity )); conn->setConnectionState(NetConnection::ComputingPuzzleSolution); conn->mConnectSendCount = 0; theParams.mPuzzleSolution = 0; conn->mConnectLastSendTime = getCurrentTime(); continuePuzzleSolution(conn); }
void NetInterface::handleConnectReject(const NetAddress *address, BitStream *stream) { U32 connectSequence; stream->read(&connectSequence); NetConnection *conn = findPendingConnection(address, connectSequence); if(!conn || (conn->getConnectionState() != NetConnection::AwaitingChallengeResponse && conn->getConnectionState() != NetConnection::AwaitingConnectResponse)) return; removePendingConnection(conn); char reason[256]; stream->readString(reason); conn->onConnectionRejected(reason); conn->deleteObject(); }
void NetInterface::handleConnectAccept(const Address &address, BitStream *stream) { Nonce nonce, serverNonce; nonce.read(stream); serverNonce.read(stream); U32 decryptPos = stream->getBytePosition(); stream->setBytePosition(decryptPos); // Make sure we're actually waiting for a connection. If not, then there's something wrong, and we bail. NetConnection *conn = findPendingConnection(address); if(!conn || conn->getConnectionState() != NetConnection::AwaitingConnectResponse) return; ConnectionParameters &theParams = conn->getConnectionParameters(); if(theParams.mNonce != nonce || theParams.mServerNonce != serverNonce) return; if(theParams.mUsingCrypto) { SymmetricCipher theCipher(theParams.mSharedSecret); if(!stream->decryptAndCheckHash(NetConnection::MessageSignatureBytes, decryptPos, &theCipher)) return; } U32 recvSequence; stream->read(&recvSequence); conn->setInitialRecvSequence(recvSequence); NetConnection::TerminationReason reason; if(!conn->readConnectAccept(stream, reason)) { removePendingConnection(conn); return; } if(theParams.mUsingCrypto) { stream->read(SymmetricCipher::KeySize, theParams.mInitVector); conn->setSymmetricCipher(new SymmetricCipher(theParams.mSymmetricKey, theParams.mInitVector)); } addConnection(conn); // First, add it as a regular connection, removePendingConnection(conn); // then remove it from the pending connection list conn->setConnectionState(NetConnection::Connected); conn->onConnectionEstablished(); // notify the connection that it has been established logprintf(LogConsumer::LogNetInterface, "Received Connect Accept - connection established."); }
void NetInterface::handleConnectAccept(const Address &address, BitStream *stream) { Nonce nonce, serverNonce; nonce.read(stream); serverNonce.read(stream); U32 decryptPos = stream->getBytePosition(); stream->setBytePosition(decryptPos); NetConnection *conn = findPendingConnection(address); if(!conn || conn->getConnectionState() != NetConnection::AwaitingConnectResponse) return; ConnectionParameters &theParams = conn->getConnectionParameters(); if(theParams.mNonce != nonce || theParams.mServerNonce != serverNonce) return; if(theParams.mUsingCrypto) { SymmetricCipher theCipher(theParams.mSharedSecret); if(!stream->decryptAndCheckHash(NetConnection::MessageSignatureBytes, decryptPos, &theCipher)) return; } U32 recvSequence; stream->read(&recvSequence); conn->setInitialRecvSequence(recvSequence); const char *errorString = NULL; if(!conn->readConnectAccept(stream, &errorString)) { removePendingConnection(conn); return; } if(theParams.mUsingCrypto) { stream->read(SymmetricCipher::KeySize, theParams.mInitVector); conn->setSymmetricCipher(new SymmetricCipher(theParams.mSymmetricKey, theParams.mInitVector)); } addConnection(conn); // first, add it as a regular connection removePendingConnection(conn); // remove from the pending connection list conn->setConnectionState(NetConnection::Connected); conn->onConnectionEstablished(); // notify the connection that it has been established TNLLogMessageV(LogNetInterface, ("Received Connect Accept - connection established.")); }
void NetInterface::handleConnectAccept(const NetAddress *address, BitStream *stream) { U32 connectSequence; stream->read(&connectSequence); NetConnection *conn = findPendingConnection(address, connectSequence); if(!conn || conn->getConnectionState() != NetConnection::AwaitingConnectResponse) return; const char *errorString = NULL; if(!conn->readConnectAccept(stream, &errorString)) { conn->handleStartupError(errorString); removePendingConnection(conn); conn->deleteObject(); return; } removePendingConnection(conn); // remove from the pending connection list conn->setNetworkConnection(true); conn->onConnectionEstablished(true); // notify the connection that it has been established conn->setEstablished(); // installs the connection in the connection table, and causes pings/timeouts to happen conn->setConnectSequence(connectSequence); }
void NetInterface::handleConnectChallengeResponse(const NetAddress *address, BitStream *stream) { Con::printf("Got Connect challenge Response"); U32 connectSequence; stream->read(&connectSequence); NetConnection *conn = findPendingConnection(address, connectSequence); if(!conn || conn->getConnectionState() != NetConnection::AwaitingChallengeResponse) return; U32 addressDigest[4]; stream->read(&addressDigest[0]); stream->read(&addressDigest[1]); stream->read(&addressDigest[2]); stream->read(&addressDigest[3]); conn->setAddressDigest(addressDigest); conn->setConnectionState(NetConnection::AwaitingConnectResponse); conn->mConnectSendCount = 0; Con::printf("Sending Connect Request"); sendConnectRequest(conn); }
void NetInterface::handleConnectReject(const Address &address, BitStream *stream) { Nonce nonce; Nonce serverNonce; nonce.read(stream); serverNonce.read(stream); NetConnection *conn = findPendingConnection(address); if(!conn || (conn->getConnectionState() != NetConnection::AwaitingChallengeResponse && conn->getConnectionState() != NetConnection::AwaitingConnectResponse)) return; ConnectionParameters &p = conn->getConnectionParameters(); if(p.mNonce != nonce || p.mServerNonce != serverNonce) return; NetConnection::TerminationReason reason = (NetConnection::TerminationReason) stream->readEnum(NetConnection::TerminationReasons); char reasonStr[256]; stream->readString(reasonStr); logprintf(LogConsumer::LogNetInterface, "Received Connect Reject - reason code %d (%s)", reason, reasonStr); // If the reason is a bad puzzle solution, try once more with a new nonce if(reason == NetConnection::ReasonPuzzle && !p.mPuzzleRetried) { p.mPuzzleRetried = true; conn->setConnectionState(NetConnection::AwaitingChallengeResponse); conn->mConnectSendCount = 0; p.mNonce.getRandom(); // Generate new nonce sendConnectChallengeRequest(conn); return; } conn->setConnectionState(NetConnection::ConnectRejected); conn->onConnectTerminated(reason, reasonStr); removePendingConnection(conn); }
void NetInterface::handleConnectReject(const Address &address, BitStream *stream) { Nonce nonce; Nonce serverNonce; nonce.read(stream); serverNonce.read(stream); NetConnection *conn = findPendingConnection(address); if(!conn || (conn->getConnectionState() != NetConnection::AwaitingChallengeResponse && conn->getConnectionState() != NetConnection::AwaitingConnectResponse)) return; ConnectionParameters &p = conn->getConnectionParameters(); if(p.mNonce != nonce || p.mServerNonce != serverNonce) return; char reason[256]; stream->readString(reason); TNLLogMessageV(LogNetInterface, ("Received Connect Reject - reason %s", reason)); // if the reason is a bad puzzle solution, try once more with a // new nonce. if(!strcmp(reason, "Puzzle") && !p.mPuzzleRetried) { p.mPuzzleRetried = true; conn->setConnectionState(NetConnection::AwaitingChallengeResponse); conn->mConnectSendCount = 0; p.mNonce.getRandom(); sendConnectChallengeRequest(conn); return; } conn->setConnectionState(NetConnection::ConnectRejected); conn->onConnectTerminated(NetConnection::ReasonRemoteHostRejectedConnection, reason); removePendingConnection(conn); }
void NetInterface::handlePunch(const Address &theAddress, BitStream *stream) { S32 i, j; NetConnection *conn; Nonce firstNonce; firstNonce.read(stream); ByteBuffer b(firstNonce.data, Nonce::NonceSize); TNLLogMessageV(LogNetInterface, ("Received punch packet from %s - %s", theAddress.toString(), b.encodeBase64()->getBuffer())); for(i = 0; i < mPendingConnections.size(); i++) { conn = mPendingConnections[i]; ConnectionParameters &theParams = conn->getConnectionParameters(); if(conn->getConnectionState() != NetConnection::SendingPunchPackets) continue; if((theParams.mIsInitiator && firstNonce != theParams.mServerNonce) || (!theParams.mIsInitiator && firstNonce != theParams.mNonce)) continue; // first see if the address is in the possible addresses list: for(j = 0; j < theParams.mPossibleAddresses.size(); j++) if(theAddress == theParams.mPossibleAddresses[j]) break; // if there was an exact match, just exit the loop, or // continue on to the next pending if this is not an initiator: if(j != theParams.mPossibleAddresses.size()) { if(theParams.mIsInitiator) break; else continue; } // if there was no exact match, we may have a funny NAT in the // middle. But since a packet got through from the remote host // we'll want to send a punch to the address it came from, as long // as only the port is not an exact match: for(j = 0; j < theParams.mPossibleAddresses.size(); j++) if(theAddress.isEqualAddress(theParams.mPossibleAddresses[j])) break; // if the address wasn't even partially in the list, just exit out if(j == theParams.mPossibleAddresses.size()) continue; // otherwise, as long as we don't have too many ping addresses, // add this one to the list: if(theParams.mPossibleAddresses.size() < 5) theParams.mPossibleAddresses.push_back(theAddress); // if this is the initiator of the arranged connection, then // process the punch packet from the remote host by issueing a // connection request. if(theParams.mIsInitiator) break; } if(i == mPendingConnections.size()) return; ConnectionParameters &theParams = conn->getConnectionParameters(); SymmetricCipher theCipher(theParams.mArrangedSecret); if(!stream->decryptAndCheckHash(NetConnection::MessageSignatureBytes, stream->getBytePosition(), &theCipher)) return; Nonce nextNonce; nextNonce.read(stream); if(nextNonce != theParams.mNonce) return; // see if the connection needs to be authenticated or uses key exchange if(stream->readFlag()) { if(stream->readFlag()) { theParams.mCertificate = new Certificate(stream); if(!theParams.mCertificate->isValid() || !conn->validateCertficate(theParams.mCertificate, true)) return; theParams.mPublicKey = theParams.mCertificate->getPublicKey(); } else { theParams.mPublicKey = new AsymmetricKey(stream); if(!theParams.mPublicKey->isValid() || !conn->validatePublicKey(theParams.mPublicKey, true)) return; } if(mPrivateKey.isNull() || mPrivateKey->getKeySize() != theParams.mPublicKey->getKeySize()) { // we don't have a private key, so generate one for this connection theParams.mPrivateKey = new AsymmetricKey(theParams.mPublicKey->getKeySize()); } else theParams.mPrivateKey = mPrivateKey; theParams.mSharedSecret = theParams.mPrivateKey->computeSharedSecretKey(theParams.mPublicKey); //logprintf("shared secret (client) %s", theParams.mSharedSecret->encodeBase64()->getBuffer()); Random::read(theParams.mSymmetricKey, SymmetricCipher::KeySize); theParams.mUsingCrypto = true; } conn->setNetAddress(theAddress); TNLLogMessageV(LogNetInterface, ("Punch from %s matched nonces - connecting...", theAddress.toString())); conn->setConnectionState(NetConnection::AwaitingConnectResponse); conn->mConnectSendCount = 0; conn->mConnectLastSendTime = getCurrentTime(); sendArrangedConnectRequest(conn); }
void NetInterface::processConnections() { mCurrentTime = Platform::getRealMilliseconds(); mPuzzleManager.tick(mCurrentTime); // first see if there are any delayed packets that need to be sent... while(mSendPacketList && mSendPacketList->sendTime < getCurrentTime()) { DelaySendPacket *next = mSendPacketList->nextPacket; mSocket.sendto(mSendPacketList->remoteAddress, mSendPacketList->packetData, mSendPacketList->packetSize); free(mSendPacketList); mSendPacketList = next; } NetObject::collapseDirtyList(); // collapse all the mask bits... for(S32 i = 0; i < mConnectionList.size(); i++) mConnectionList[i]->checkPacketSend(false, getCurrentTime()); if(getCurrentTime() > mLastTimeoutCheckTime + TimeoutCheckInterval) { for(S32 i = 0; i < mPendingConnections.size();) { NetConnection *pending = mPendingConnections[i]; if(pending->getConnectionState() == NetConnection::AwaitingChallengeResponse && getCurrentTime() > pending->mConnectLastSendTime + ChallengeRetryTime) { if(pending->mConnectSendCount > ChallengeRetryCount) { pending->setConnectionState(NetConnection::ConnectTimedOut); pending->onConnectTerminated(NetConnection::ReasonTimedOut, "Timeout"); removePendingConnection(pending); continue; } else sendConnectChallengeRequest(pending); } else if(pending->getConnectionState() == NetConnection::AwaitingConnectResponse && getCurrentTime() > pending->mConnectLastSendTime + ConnectRetryTime) { if(pending->mConnectSendCount > ConnectRetryCount) { pending->setConnectionState(NetConnection::ConnectTimedOut); pending->onConnectTerminated(NetConnection::ReasonTimedOut, "Timeout"); removePendingConnection(pending); continue; } else { if(pending->getConnectionParameters().mIsArranged) sendArrangedConnectRequest(pending); else sendConnectRequest(pending); } } else if(pending->getConnectionState() == NetConnection::SendingPunchPackets && getCurrentTime() > pending->mConnectLastSendTime + PunchRetryTime) { if(pending->mConnectSendCount > PunchRetryCount) { pending->setConnectionState(NetConnection::ConnectTimedOut); pending->onConnectTerminated(NetConnection::ReasonTimedOut, "Timeout"); removePendingConnection(pending); continue; } else sendPunchPackets(pending); } else if(pending->getConnectionState() == NetConnection::ComputingPuzzleSolution && getCurrentTime() > pending->mConnectLastSendTime + PuzzleSolutionTimeout) { pending->setConnectionState(NetConnection::ConnectTimedOut); pending->onConnectTerminated(NetConnection::ReasonTimedOut, "Timeout"); removePendingConnection(pending); } i++; } mLastTimeoutCheckTime = getCurrentTime(); for(S32 i = 0; i < mConnectionList.size();) { if(mConnectionList[i]->checkTimeout(getCurrentTime())) { mConnectionList[i]->setConnectionState(NetConnection::TimedOut); mConnectionList[i]->onConnectionTerminated(NetConnection::ReasonTimedOut, "Timeout"); removeConnection(mConnectionList[i]); } else i++; } } // check if we're trying to solve any client connection puzzles for(S32 i = 0; i < mPendingConnections.size(); i++) { if(mPendingConnections[i]->getConnectionState() == NetConnection::ComputingPuzzleSolution) { continuePuzzleSolution(mPendingConnections[i]); break; } } }
void NetInterface::handleArrangedConnectRequest(const Address &theAddress, BitStream *stream) { S32 i, j; NetConnection *conn; Nonce nonce, serverNonce; nonce.read(stream); // see if the connection is in the main connection table. // If the connection is in the connection table and it has // the same initiatorSequence, we'll just resend the connect // acceptance packet, assuming that the last time we sent it // it was dropped. NetConnection *oldConnection = findConnection(theAddress); if(oldConnection) { ConnectionParameters &cp = oldConnection->getConnectionParameters(); if(cp.mNonce == nonce) { sendConnectAccept(oldConnection); return; } } for(i = 0; i < mPendingConnections.size(); i++) { conn = mPendingConnections[i]; ConnectionParameters &theParams = conn->getConnectionParameters(); if(conn->getConnectionState() != NetConnection::SendingPunchPackets || theParams.mIsInitiator) continue; if(nonce != theParams.mNonce) continue; for(j = 0; j < theParams.mPossibleAddresses.size(); j++) if(theAddress.isEqualAddress(theParams.mPossibleAddresses[j])) break; if(j != theParams.mPossibleAddresses.size()) break; } if(i == mPendingConnections.size()) return; ConnectionParameters &theParams = conn->getConnectionParameters(); SymmetricCipher theCipher(theParams.mArrangedSecret); if(!stream->decryptAndCheckHash(NetConnection::MessageSignatureBytes, stream->getBytePosition(), &theCipher)) return; stream->setBytePosition(stream->getBytePosition()); serverNonce.read(stream); if(serverNonce != theParams.mServerNonce) return; if(stream->readFlag()) { if(mPrivateKey.isNull()) return; theParams.mUsingCrypto = true; theParams.mPublicKey = new AsymmetricKey(stream); theParams.mPrivateKey = mPrivateKey; U32 decryptPos = stream->getBytePosition(); stream->setBytePosition(decryptPos); theParams.mSharedSecret = theParams.mPrivateKey->computeSharedSecretKey(theParams.mPublicKey); SymmetricCipher theCipher(theParams.mSharedSecret); if(!stream->decryptAndCheckHash(NetConnection::MessageSignatureBytes, decryptPos, &theCipher)) return; // now read the first part of the connection's session (symmetric) key stream->read(SymmetricCipher::KeySize, theParams.mSymmetricKey); Random::read(theParams.mInitVector, SymmetricCipher::KeySize); } U32 connectSequence; theParams.mDebugObjectSizes = stream->readFlag(); stream->read(&connectSequence); TNLLogMessageV(LogNetInterface, ("Received Arranged Connect Request")); if(oldConnection) disconnect(oldConnection, NetConnection::ReasonSelfDisconnect, ""); conn->setNetAddress(theAddress); conn->setInitialRecvSequence(connectSequence); if(theParams.mUsingCrypto) conn->setSymmetricCipher(new SymmetricCipher(theParams.mSymmetricKey, theParams.mInitVector)); const char *errorString = NULL; if(!conn->readConnectRequest(stream, &errorString)) { sendConnectReject(&theParams, theAddress, errorString); removePendingConnection(conn); return; } addConnection(conn); removePendingConnection(conn); conn->setConnectionState(NetConnection::Connected); conn->onConnectionEstablished(); sendConnectAccept(conn); }