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;
}
Beispiel #2
0
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();
}
Beispiel #5
0
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();
}
Beispiel #6
0
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();
}
Beispiel #7
0
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;
}
Beispiel #10
0
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;
}
Beispiel #11
0
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();
	}
}