bool NATTraversalProtocol::SignalInputData(IOBuffer &buffer, sockaddr_in *pPeerAddress) { //FINEST("_inputBuffer:\n%s", STR(buffer)); buffer.IgnoreAll(); if (_pOutboundAddress == NULL) return true; if (_pOutboundAddress->sin_addr.s_addr != pPeerAddress->sin_addr.s_addr) { WARN("Attempt to divert traffic. DoS attack!?"); return true; } string ipAddress = inet_ntoa(_pOutboundAddress->sin_addr); if (_pOutboundAddress->sin_port == pPeerAddress->sin_port) { INFO("The client has public endpoint: %s:%"PRIu16, STR(ipAddress), ENTOHS(_pOutboundAddress->sin_port)); } else { INFO("The client is behind firewall: %s:%"PRIu16" -> %s:%"PRIu16, STR(ipAddress), ENTOHS(_pOutboundAddress->sin_port), STR(ipAddress), ENTOHS(pPeerAddress->sin_port)); _pOutboundAddress->sin_port = pPeerAddress->sin_port; } _pOutboundAddress = NULL; return true; }
bool HTTP4CLIProtocol::EnqueueForOutbound() { //1. Empty our local buffer _localOutputBuffer.IgnoreAll(); //2. Get the HTTP protocol InboundHTTPProtocol *pHTTP = (InboundHTTPProtocol *) GetFarProtocol(); //3. Prepare the HTTP headers //pHTTP->SetOutboundHeader(HTTP_HEADERS_CONTENT_TYPE, "application/json"); pHTTP->SetOutboundHeader(HTTP_HEADERS_CONTENT_TYPE, "text/plain"); //4. Get the buffer from PT_INBOUND_JSONCLI IOBuffer *pBuffer = GetNearProtocol()->GetOutputBuffer(); if (pBuffer == NULL) return true; //5. Put the data inside the local buffer and empty the buffer from //the PT_INBOUND_JSONCLI _localOutputBuffer.ReadFromBuffer(GETIBPOINTER(*pBuffer), GETAVAILABLEBYTESCOUNT(*pBuffer)); pBuffer->IgnoreAll(); //6. Trigger EnqueueForOutbound down the stack return pHTTP->EnqueueForOutbound(); }
bool InboundMJPGHTTPStreamProtocol::SignalInputData(IOBuffer &buffer) { //1. Is the stream name acquired? if (_streamNameAcquired) { buffer.IgnoreAll(); return true; } if (!AcquireStreamName(buffer)) { FATAL("Unable to get the stream name"); return false; } if (!_streamNameAcquired) { return true; } //7. Search for the stream called streamName and pick the first one map<uint32_t, BaseStream *> inStreams = GetApplication()->GetStreamsManager()->FindByTypeByName( ST_IN_CAM_MJPG, _streamName, false, true); if (inStreams.size() == 0) { if (lowerCase(_streamName) == "crossdomain.xml") { return SendCrossDomain(); } else { FATAL("Stream %s not found", STR(_streamName)); return Send404NotFound(); } } BaseInStream *pInStream = (BaseInStream *) MAP_VAL(inStreams.begin()); //8. Create our raw outbound stream _pOutStream = new OutNetMJPGHTTPStream(this, GetApplication()->GetStreamsManager(), _streamName); //9. Link it to the in stream if (!pInStream->Link(_pOutStream)) { FATAL("Unable to link to the in stream"); return false; } //10. All done. Ignore all the traffic buffer.IgnoreAll(); //11. Done return true; }
bool BaseHTTPProtocol::EnqueueForOutbound() { //1. Get the output buffer if (_pNearProtocol == NULL) { FATAL("No near protocol"); return false; } IOBuffer *pBuffer = _pNearProtocol->GetOutputBuffer(); uint32_t bufferLength = 0; if (pBuffer != NULL) { bufferLength = GETAVAILABLEBYTESCOUNT(*pBuffer); } //3. add or replace X-Powered-By attribute _outboundHeaders[HTTP_HEADERS_X_POWERED_BY] = HTTP_HEADERS_X_POWERED_BY_US; //4. add or replace the Server attribute if (GetType() == PT_INBOUND_HTTP) { _outboundHeaders[HTTP_HEADERS_SERVER] = HTTP_HEADERS_SERVER_US; } //5. Get rid of the Content-Length attribute and add it only if necessary _outboundHeaders.RemoveKey(HTTP_HEADERS_CONTENT_LENGTH); if (bufferLength > 0) { _outboundHeaders[HTTP_HEADERS_CONTENT_LENGTH] = format("%u", bufferLength); } //6. Get rid of Transfer-Encoding attribute _outboundHeaders.RemoveKey(HTTP_HEADERS_TRANSFER_ENCODING); //7. Write the first line of the request/response _outputBuffer.ReadFromString(GetOutputFirstLine() + "\r\n"); //8. Write the headers and the final '\r\n' FOR_MAP(_outboundHeaders, string, Variant, i) { if (MAP_VAL(i) != V_STRING) { FATAL("Invalid HTTP headers:\n%s", STR(_outboundHeaders.ToString())); return false; } _outputBuffer.ReadFromString(format("%s: %s\r\n", STR(MAP_KEY(i)), STR(MAP_VAL(i)))); } _outboundHeaders.Reset(); _outboundHeaders.IsArray(false); _outputBuffer.ReadFromString("\r\n"); //9. Write the actual content if necessary if (bufferLength > 0) { _outputBuffer.ReadFromBuffer(GETIBPOINTER(*pBuffer), GETAVAILABLEBYTESCOUNT(*pBuffer)); //10. Empty the upper output buffer pBuffer->IgnoreAll(); } //11. Let the request flow further return BaseProtocol::EnqueueForOutbound(); }
bool BaseSSLProtocol::SignalInputData(IOBuffer &buffer) { //1. get the SSL input buffer BIO *pInBio = SSL_get_rbio(_pSSL); //2. dump all the data from the network inside the ssl input BIO_write(pInBio, GETIBPOINTER(buffer), GETAVAILABLEBYTESCOUNT(buffer)); buffer.IgnoreAll(); //3. Do we have to do some handshake? if (!_sslHandshakeCompleted) { if (!DoHandshake()) { FATAL("Unable to do the SSL handshake"); return false; } if (!_sslHandshakeCompleted) { return true; } } //4. Read the actual data an put it in the descrypted input buffer int32_t read = 0; while ((read = SSL_read(_pSSL, _pReadBuffer, MAX_SSL_READ_BUFFER)) > 0) { _inputBuffer.ReadFromBuffer(_pReadBuffer, (uint32_t) read); } if (read < 0) { int32_t error = SSL_get_error(_pSSL, read); if (error != SSL_ERROR_WANT_READ && error != SSL_ERROR_WANT_WRITE) { FATAL("Unable to read data: %d", error); return false; } } //6. If we have pending data inside the decrypted buffer, bubble it up on the protocol stack if (GETAVAILABLEBYTESCOUNT(_inputBuffer) > 0) { if (_pNearProtocol != NULL) { if (!_pNearProtocol->SignalInputData(_inputBuffer)) { FATAL("Unable to signal near protocol for new data"); return false; } } } //7. After the data was sent on the upper layers, we might have outstanding //data that needs to be sent. return PerformIO(); }
bool InboundHTTP4RTMP::ProcessIdle(vector<string> &parts) { BaseProtocol *pProtocol = Bind(parts[2]); if (pProtocol == NULL) { FATAL("Unable to bind protocol"); return false; } _outputBuffer.ReadFromByte(1); IOBuffer *pBuffer = pProtocol->GetOutputBuffer(); if (pBuffer != NULL) { _outputBuffer.ReadFromBuffer(GETIBPOINTER(*pBuffer), GETAVAILABLEBYTESCOUNT(*pBuffer)); pBuffer->IgnoreAll(); } return BaseProtocol::EnqueueForOutbound(); }
bool BaseSSLProtocol::EnqueueForOutbound() { //1. Is the SSL handshake completed? if (!_sslHandshakeCompleted) { return DoHandshake(); } //2. Do we have some outstanding data? IOBuffer *pBuffer = _pNearProtocol->GetOutputBuffer(); if (pBuffer == NULL) return true; //3. Encrypt the outstanding data if (SSL_write(_pSSL, GETIBPOINTER(*pBuffer), GETAVAILABLEBYTESCOUNT(*pBuffer)) != (int32_t) GETAVAILABLEBYTESCOUNT(*pBuffer)) { FATAL("Unable to write %u bytes", GETAVAILABLEBYTESCOUNT(*pBuffer)); return false; } pBuffer->IgnoreAll(); //4. Do the actual I/O return PerformIO(); }
bool BaseInFileStream::SendCodecs() { //1. Read the first frame MediaFrame frame1; if (!_pSeekFile->SeekTo(_framesBaseOffset + 0 * sizeof (MediaFrame))) { FATAL("Unablt to seek inside seek file"); return false; } if (!_pSeekFile->ReadBuffer((uint8_t *) & frame1, sizeof (MediaFrame))) { FATAL("Unable to read frame from seeking file"); return false; } //2. Read the second frame MediaFrame frame2; if (!_pSeekFile->SeekTo(_framesBaseOffset + 1 * sizeof (MediaFrame))) { FATAL("Unablt to seek inside seek file"); return false; } if (!_pSeekFile->ReadBuffer((uint8_t *) & frame2, sizeof (MediaFrame))) { FATAL("Unable to read frame from seeking file"); return false; } //3. Read the current frame to pickup the timestamp from it MediaFrame currentFrame; if (!_pSeekFile->SeekTo(_framesBaseOffset + _currentFrameIndex * sizeof (MediaFrame))) { FATAL("Unablt to seek inside seek file"); return false; } if (!_pSeekFile->ReadBuffer((uint8_t *) & currentFrame, sizeof (MediaFrame))) { FATAL("Unable to read frame from seeking file"); return false; } //4. Is the first frame a codec setup? //If not, the second is not a codec setup for sure if (!frame1.isBinaryHeader) { _audioVideoCodecsSent = true; return true; } //5. Build the buffer for the first frame IOBuffer buffer; if (!BuildFrame(_pFile, frame1, buffer)) { FATAL("Unable to build the frame"); return false; } //6. Do the feedeng with the first frame if (!_pOutStreams->info->FeedData( GETIBPOINTER(buffer), //pData GETAVAILABLEBYTESCOUNT(buffer), //dataLength 0, //processedLength GETAVAILABLEBYTESCOUNT(buffer), //totalLength currentFrame.absoluteTime, //absoluteTimestamp frame1.type == MEDIAFRAME_TYPE_AUDIO //isAudio )) { FATAL("Unable to feed audio data"); return false; } //7. Is the second frame a codec setup? if (!frame2.isBinaryHeader) { _audioVideoCodecsSent = true; return true; } //8. Build the buffer for the second frame buffer.IgnoreAll(); if (!BuildFrame(_pFile, frame2, buffer)) { FATAL("Unable to build the frame"); return false; } //9. Do the feedeng with the second frame if (!_pOutStreams->info->FeedData( GETIBPOINTER(buffer), //pData GETAVAILABLEBYTESCOUNT(buffer), //dataLength 0, //processedLength GETAVAILABLEBYTESCOUNT(buffer), //totalLength currentFrame.absoluteTime, //absoluteTimestamp frame2.type == MEDIAFRAME_TYPE_AUDIO //isAudio )) { FATAL("Unable to feed audio data"); return false; } //10. Done _audioVideoCodecsSent = true; return true; }
bool InboundRTMPProtocol::PerformComplexHandshake(IOBuffer &buffer, bool encrypted) { //get the buffers uint8_t *pInputBuffer = GETIBPOINTER(buffer); if (_pOutputBuffer == NULL) { _pOutputBuffer = new uint8_t[3072]; } else { delete[] _pOutputBuffer; _pOutputBuffer = new uint8_t[3072]; } //timestamp EHTONLP(_pOutputBuffer, (uint32_t) time(NULL)); //version EHTONLP(_pOutputBuffer + 4, (uint32_t) 0x00000000); //generate random data for (uint32_t i = 8; i < 3072; i++) { _pOutputBuffer[i] = rand() % 256; } for (uint32_t i = 0; i < 10; i++) { uint32_t index = rand() % (3072 - HTTP_HEADERS_SERVER_US_LEN); memcpy(_pOutputBuffer + index, HTTP_HEADERS_SERVER_US, HTTP_HEADERS_SERVER_US_LEN); } //**** FIRST 1536 bytes from server response ****// //compute DH key position uint32_t serverDHOffset = GetDHOffset(_pOutputBuffer, _handshakeScheme); uint32_t clientDHOffset = GetDHOffset(pInputBuffer, _handshakeScheme); //generate DH key DHWrapper dhWrapper(1024); if (!dhWrapper.Initialize()) { FATAL("Unable to initialize DH wrapper"); return false; } if (!dhWrapper.CreateSharedKey(pInputBuffer + clientDHOffset, 128)) { FATAL("Unable to create shared key"); return false; } if (!dhWrapper.CopyPublicKey(_pOutputBuffer + serverDHOffset, 128)) { FATAL("Couldn't write public key!"); return false; } if (encrypted) { uint8_t secretKey[128]; if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) { FATAL("Unable to copy shared key"); return false; } _pKeyIn = new RC4_KEY; _pKeyOut = new RC4_KEY; InitRC4Encryption( secretKey, (uint8_t*) & pInputBuffer[clientDHOffset], (uint8_t*) & _pOutputBuffer[serverDHOffset], _pKeyIn, _pKeyOut); //bring the keys to correct cursor uint8_t data[1536]; RC4(_pKeyIn, 1536, data, data); RC4(_pKeyOut, 1536, data, data); } //generate the digest uint32_t serverDigestOffset = GetDigestOffset(_pOutputBuffer, _handshakeScheme); uint8_t *pTempBuffer = new uint8_t[1536 - 32]; memcpy(pTempBuffer, _pOutputBuffer, serverDigestOffset); memcpy(pTempBuffer + serverDigestOffset, _pOutputBuffer + serverDigestOffset + 32, 1536 - serverDigestOffset - 32); uint8_t *pTempHash = new uint8_t[512]; HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pTempHash); //put the digest in place memcpy(_pOutputBuffer + serverDigestOffset, pTempHash, 32); //cleanup delete[] pTempBuffer; delete[] pTempHash; //**** SECOND 1536 bytes from server response ****// //Compute the chalange index from the initial client request uint32_t keyChallengeIndex = GetDigestOffset(pInputBuffer, _handshakeScheme); //compute the key pTempHash = new uint8_t[512]; HMACsha256(pInputBuffer + keyChallengeIndex, //pData 32, //dataLength BaseRTMPProtocol::genuineFMSKey, //key 68, //keyLength pTempHash //pResult ); //generate the hash uint8_t *pLastHash = new uint8_t[512]; HMACsha256(_pOutputBuffer + 1536, //pData 1536 - 32, //dataLength pTempHash, //key 32, //keyLength pLastHash //pResult ); //put the hash where it belongs memcpy(_pOutputBuffer + 1536 * 2 - 32, pLastHash, 32); //cleanup delete[] pTempHash; delete[] pLastHash; //***** DONE BUILDING THE RESPONSE ***// //wire the response if (encrypted) _outputBuffer.ReadFromByte(6); else _outputBuffer.ReadFromByte(3); _outputBuffer.ReadFromBuffer(_pOutputBuffer, 3072); //final cleanup delete[] _pOutputBuffer; _pOutputBuffer = NULL; if (!buffer.IgnoreAll()) { FATAL("Unable to ignore input buffer"); return false; } //signal outbound data if (!EnqueueForOutbound()) { FATAL("Unable to signal outbound data"); return false; } //move to the next stage in the handshake _rtmpState = RTMP_STATE_SERVER_RESPONSE_SENT; return true; }
bool RTCPProtocol::SignalInputData(IOBuffer &buffer, sockaddr_in *pPeerAddress) { //0. Save the last known address if (&_lastAddress != pPeerAddress) { _lastAddress = *pPeerAddress; _validLastAddress = true; } //1. Parse the SR uint8_t *pBuffer = GETIBPOINTER(buffer); uint32_t bufferLength = GETAVAILABLEBYTESCOUNT(buffer); while (bufferLength > 0) { if (bufferLength < 4) { buffer.IgnoreAll(); return true; } uint8_t PT = pBuffer[1]; uint16_t len = ENTOHSP(pBuffer + 2); len = (len + 1)*4; if (len > bufferLength) { buffer.IgnoreAll(); return true; } switch (PT) { case 200: //SR { if (len < 28) { buffer.IgnoreAll(); return true; } uint32_t ntpSec = ENTOHLP(pBuffer + 8) - 2208988800UL; uint32_t ntpFrac = ENTOHLP(pBuffer + 12); uint64_t ntpMicroseconds = (uint32_t) (((double) ntpFrac / (double) (0x100000000LL))*1000000.0); ntpMicroseconds += ((uint64_t) ntpSec)*1000000; uint32_t rtpTimestamp = ENTOHLP(pBuffer + 16); if (_pConnectivity == NULL) { FATAL("No connectivity, unable to send SR"); return false; } _pConnectivity->ReportSR(ntpMicroseconds, rtpTimestamp, _isAudio); _lsr = ENTOHLP(pBuffer + 10); if (!_pConnectivity->SendRR(_isAudio)) { FATAL("Unable to send RR"); _pConnectivity->EnqueueForDelete(); _pConnectivity = NULL; return false; } break; } case 203: //BYE { if (_pConnectivity == NULL) { FATAL("No connectivity, BYE packet ignored"); return false; } _pConnectivity->EnqueueForDelete(); _pConnectivity = NULL; break; } default: { break; } } buffer.Ignore(len); pBuffer = GETIBPOINTER(buffer); bufferLength = GETAVAILABLEBYTESCOUNT(buffer); } return true; }
bool EchoProtocol::SignalInputData(IOBuffer &buffer) { //PREAMBLE //First, check the transport type. If it is a //http transport, wait for it to finish the request //before doing stuff with it. This is not mandatory. //We can start consume the buffer right away, but since //we do a echo protocol, let's keep the things simple //If the transport is direct TCP, we will echo back only after //getting a new line character. //I will try to keep the things extremely simple in this function //sacrificing "user input sanitize" for simplicity. Anyway, I think //we all know how an echo protocol should behave... //1. Check and see if the protocol is HTTP or not if (GetFarProtocol()->GetType() == PT_INBOUND_HTTP) { //2. This has HTTP protocol as carrier. Get it and //wait for it to complete InboundHTTPProtocol *pHTTP = (InboundHTTPProtocol *) GetFarProtocol(); if (!pHTTP->TransferCompleted()) { FINEST("HTTP transfer not completed yet"); return true; } //3. Ok, it is complete. Get the data and put it inside the output buffer //Actually, we are going to add the string "ECHO " first, and after that //the actual data. Just for fun... _outputBuffer.ReadFromString("ECHO "); _outputBuffer.ReadFromBuffer( GETIBPOINTER(buffer), GETAVAILABLEBYTESCOUNT(buffer)); //3.1. Let's also dump the complete HTTP request. You might want to pick //up a thing or 2 from it. Just for fun... FINEST("HTTP request:\n%s", STR(pHTTP->GetHeaders().ToString())); //4. Ignore the input buffer now. buffer.IgnoreAll(); //5. Add some fancy mime type and some custom HTTP headers... Just for fun... pHTTP->SetOutboundHeader(HTTP_HEADERS_CONTENT_TYPE, "text/plain"); pHTTP->SetOutboundHeader("My-fancy-http-header", "aloha from C++ RTMP Server"); //6. We are done. Enqueue the stack for outbound I/O return EnqueueForOutbound(); } else { //7. Get the data inside a string string data = string((const char *) GETIBPOINTER(buffer), GETAVAILABLEBYTESCOUNT(buffer)); //8. extremely minimal and dangerous test to see if we have a new-line, //but for the sake of simplicity, I'm just going to keep it like that if (data.length() == 0 || data[data.length() - 1] != '\n') { FINEST("Not enough data. So far I have %s. Wait for more...", STR(data)); return true; } //9. Ok, it is complete. Get the data and put it inside the output buffer //Actually, we are going to add the string "ECHO " first, and after that //the actual data. Just for fun... _outputBuffer.ReadFromString("ECHO "); _outputBuffer.ReadFromBuffer( GETIBPOINTER(buffer), GETAVAILABLEBYTESCOUNT(buffer)); //10. Ignore the input buffer now. buffer.IgnoreAll(); //11. for demonstration purposes, whenever I get the string "testHttpRequest" //I'm just going to do a request to "http://www.rtmpd.com" and print out //the page on console. This should illustrate how to use the outbound HTTP protocol HTTPDownloadProtocol::DoSimpleGETRequestWithSomePayload("http://www.rtmpd.com/resources","Some data.... Hello World!"); //11. We are done. Enqueue the stack for outbound I/O return EnqueueForOutbound(); } }