Esempio n. 1
0
void RTMFPHandshake::receive(const SocketAddress& address, BinaryReader& request) {

	if(!Session::receive(address, request))
		return;

	UInt8 marker = request.read8();
	if(marker!=0x0b) {
		ERROR("Marker handshake wrong : should be 0b and not ",Format<UInt8>("%.2x",marker));
		return;
	}

	UInt16 time = request.read16();
	UInt8 id = request.read8();
	request.shrink(request.read16()); // length

	BinaryWriter response(packet(),RTMFP_MAX_PACKET_SIZE);
	response.clear(RTMFP_HEADER_SIZE+3); // header + type and size
	UInt8 idResponse = handshakeHandler(id,address, request,response);
	if (!idResponse)
		return;
	
	BinaryWriter(response.data() + RTMFP_HEADER_SIZE, 3).write8(idResponse).write16(response.size() - RTMFP_HEADER_SIZE - 3);
	(UInt32&)farId = 0;
	flush(0x0b, response.size());
}
Esempio n. 2
0
	void ackMessageHandler(UInt32 ackCount,UInt32 lostCount,BinaryReader& content,UInt32 available,UInt32 size) {
		if(available==0 || content.read8()!=_type)
			return;
		qos.add(content.read32(),ackCount,lostCount,size);
	}
Esempio n. 3
0
UInt8 RTMFPHandshake::handshakeHandler(UInt8 id,const SocketAddress& address, BinaryReader& request,BinaryWriter& response) {

	switch(id){
		case 0x30: {
			
			request.read7BitValue(); // = epdLen + 2 (useless)
			UInt16 epdLen = request.read7BitValue()-1;
			UInt8 type = request.read8();
			string epd;
			request.read(epdLen,epd);

			string tag;
			request.read(16,tag);
			response.write7BitValue(tag.size()).write(tag);
		
			if(type == 0x0f) {

				const UInt8* peerId((const UInt8*)epd.c_str());
				
				RTMFPSession* pSessionWanted = _sessions.findByPeer<RTMFPSession>(peerId);
	
				if(pSessionWanted) {
					if(pSessionWanted->failed())
						return 0x00; // TODO no way in RTMFP to tell "died!"
					/// Udp hole punching
					UInt32 times = attempt(tag);
		
					RTMFPSession* pSession(NULL);
					if(times > 0 || address.host() == pSessionWanted->peer.address.host()) // try in first just with public address (excepting if the both peer are on the same machine)
						pSession = _sessions.findByAddress<RTMFPSession>(address,Socket::DATAGRAM);
					
					bool hasAnExteriorPeer(pSessionWanted->p2pHandshake(tag,address,times,pSession));
					
					// public address
					RTMFP::WriteAddress(response,pSessionWanted->peer.address, RTMFP::ADDRESS_PUBLIC);
					DEBUG("P2P address initiator exchange, ",pSessionWanted->peer.address.toString());

					if (hasAnExteriorPeer && pSession->peer.serverAddress.host()!=pSessionWanted->peer.address.host()) {
						// the both peer see the server in a different way (and serverAddress.host()!= public address host written above),
						// Means an exterior peer, but we can't know which one is the exterior peer
						// so add an interiorAddress build with how see eachone the server on the both side
						SocketAddress interiorAddress(pSession->peer.serverAddress.host(), pSessionWanted->peer.address.port());
						RTMFP::WriteAddress(response,interiorAddress, RTMFP::ADDRESS_PUBLIC);
						DEBUG("P2P address initiator exchange, ",interiorAddress.toString());
					}	

					// local address
					for(const SocketAddress& address : pSessionWanted->peer.localAddresses) {
						RTMFP::WriteAddress(response,address, RTMFP::ADDRESS_LOCAL);
						DEBUG("P2P address initiator exchange, ",address.toString());
					}

					// add the turn address (RelayServer) if possible and required
					if (pSession && times>0) {
						UInt8 timesBeforeTurn(0);
						if(pSession->peer.parameters().getNumber("timesBeforeTurn",timesBeforeTurn) && timesBeforeTurn>=times) {
							UInt16 port = invoker.relayer.relay(pSession->peer.address,pSessionWanted->peer.address,20); // 20 sec de timeout is enough for RTMFP!
							if (port > 0) {
								SocketAddress address(pSession->peer.serverAddress.host(), port);
								RTMFP::WriteAddress(response, address, RTMFP::ADDRESS_REDIRECTION);
							} // else ERROR already display by RelayServer class
						}
					}
					return 0x71;
				}


				DEBUG("UDP Hole punching, session ", Util::FormatHex(peerId, ID_SIZE, LOG_BUFFER), " wanted not found")
				set<SocketAddress> addresses;
				peer.onRendezVousUnknown(peerId,addresses);
				set<SocketAddress>::const_iterator it;
				for(it=addresses.begin();it!=addresses.end();++it) {
					if(it->host().isWildcard())
						continue;
					if(address == *it)
						WARN("A client tries to connect to himself (same ", address.toString()," address)");
					RTMFP::WriteAddress(response,*it,RTMFP::ADDRESS_REDIRECTION);
					DEBUG("P2P address initiator exchange, ",it->toString());
				}
				return addresses.empty() ? 0 : 0x71;
			}

			if(type == 0x0a){
				/// RTMFPHandshake
				HelloAttempt& attempt = AttemptCounter::attempt<HelloAttempt>(tag);

				Peer& peer(*_pPeer);

				// Fill peer infos
				peer.properties().clear();
				string serverAddress;
				Util::UnpackUrl(epd, serverAddress, (string&)peer.path,(string&)peer.query);
				peer.setServerAddress(serverAddress);
				Util::UnpackQuery(peer.query, peer.properties());

				Exception ex;

				set<SocketAddress> addresses;
				peer.onHandshake(attempt.count+1,addresses);
				if(!addresses.empty()) {
					set<SocketAddress>::iterator it;
					for(it=addresses.begin();it!=addresses.end();++it) {
						if (it->host().isWildcard())
							RTMFP::WriteAddress(response, peer.serverAddress, RTMFP::ADDRESS_REDIRECTION);
						else
							RTMFP::WriteAddress(response, *it, RTMFP::ADDRESS_REDIRECTION);
					}
					return 0x71;
				}


				// New RTMFPCookie
				RTMFPCookie* pCookie = attempt.pCookie;
				if (!pCookie) {
					pCookie = new RTMFPCookie(*this, invoker, tag, _pPeer);
					if (!pCookie->run(ex)) {
						delete pCookie;
						ERROR("RTMFPCookie creation, ", ex.error())
						return 0;
					}
					_pPeer.reset(new Peer((Handler&)invoker)); // reset peer
					_cookies.emplace(pCookie->value(), pCookie);
					attempt.pCookie = pCookie;
				}

				// response
				response.write8(COOKIE_SIZE);
				response.write(pCookie->value(),COOKIE_SIZE);
				// instance id (certificat in the middle)
				response.write(_certificat,sizeof(_certificat));
				return 0x70;

			} else
Esempio n. 4
0
UInt8 RTMFPHandshake::handshakeHandler(UInt8 id,const SocketAddress& address, BinaryReader& request,BinaryWriter& response) {

	switch(id){
		case 0x30: {
			
			request.next(1);
			UInt8 epdLen = request.read8()-1;

			UInt8 type = request.read8();

			string epd;
			request.read(epdLen,epd);

			string tag;
			request.read(16,tag);
			response.write8(tag.size()).write(tag);
		
			if(type == 0x0f) {

				const UInt8* peerId = (const UInt8*)epd.c_str();
				
				RTMFPSession* pSessionWanted = _sessions.findByPeer<RTMFPSession>(peerId);
	
				if(pSessionWanted) {
					if(pSessionWanted->failed())
						return 0x00; // TODO no way in RTMFP to tell "died!"
					/// Udp hole punching
					UInt32 times = attempt(tag);

					RTMFPSession* pSession(NULL);
					if(times > 0 || address.host() == pSessionWanted->peer.address.host())
						pSession = _sessions.findByAddress<RTMFPSession>(address,Socket::DATAGRAM);
					
					pSessionWanted->p2pHandshake(tag,address,times,pSession);

					RTMFP::WriteAddress(response,pSessionWanted->peer.address, RTMFP::ADDRESS_PUBLIC);
					DEBUG("P2P address initiator exchange, ",pSessionWanted->peer.address.toString());
					for(const SocketAddress& address : pSessionWanted->peer.localAddresses) {
						RTMFP::WriteAddress(response,address, RTMFP::ADDRESS_LOCAL);
						DEBUG("P2P address initiator exchange, ",address.toString());
					}

					// add the turn address (RelayServer) if possible and required
					if (pSession && times>0) {
						UInt8 timesBeforeTurn(0);
						if(pSession->peer.parameters().getNumber("timesBeforeTurn",timesBeforeTurn) && timesBeforeTurn>=times) {
							UInt16 port = invoker.relayer.relay(pSession->peer.address,pSessionWanted->peer.address,20); // 20 sec de timeout is enough for RTMFP!
							if (port > 0) {
								string host;
								SocketAddress::SplitLiteral(pSession->peer.serverAddress, host);
								bool success(false);
								Exception ex;
								SocketAddress address;
								EXCEPTION_TO_LOG(success=address.set(ex,host, port),"RTMFP turn impossible")
								if (success)
									RTMFP::WriteAddress(response, address, RTMFP::ADDRESS_REDIRECTION);
							} // else ERROR already display by RelayServer class
						}
					}
					return 0x71;
				}


				DEBUG("UDP Hole punching, session ", Util::FormatHex(peerId, ID_SIZE, LOG_BUFFER), " wanted not found")
				set<SocketAddress> addresses;
				peer.onRendezVousUnknown(peerId,addresses);
				set<SocketAddress>::const_iterator it;
				for(it=addresses.begin();it!=addresses.end();++it) {
					if(it->host().isWildcard())
						continue;
					if(address == *it)
						WARN("A client tries to connect to himself (same ", address.toString()," address)");
					RTMFP::WriteAddress(response,*it,RTMFP::ADDRESS_REDIRECTION);
					DEBUG("P2P address initiator exchange, ",it->toString());
				}
				return addresses.empty() ? 0 : 0x71;
			}
Esempio n. 5
0
void RTMFPSession::receive(const SocketAddress& address, BinaryReader& packet) {

	if (!Session::receive(address, packet))
		return;

	// Read packet
	UInt8 marker = packet.read8()|0xF0;
	
	_timeSent = packet.read16();

	// with time echo
	if(marker == 0xFD) {
		UInt16 time = RTMFP::TimeNow();
		UInt16 timeEcho = packet.read16();
		if(timeEcho>time) {
			if(timeEcho-time<30)
				time=0;
			else
				time += 0xFFFF-timeEcho;
			timeEcho = 0;
		}
		peer.setPing((time-timeEcho)*RTMFP_TIMESTAMP_SCALE);
	}
	else if(marker != 0xF9)
		WARN("RTMFPPacket marker unknown : ", Format<UInt8>("%02x",marker));


	// Variables for request (0x10 and 0x11)
	UInt8 flags;
	RTMFPFlow* pFlow=NULL;
	UInt64 stage=0;
	UInt64 deltaNAck=0;

	UInt8 type = packet.available()>0 ? packet.read8() : 0xFF;
	bool answer = false;

	// Can have nested queries
	while(type!=0xFF) {

		UInt16 size = packet.read16();

		PacketReader message(packet.current(),size);		

		switch(type) {
			case 0x0c :
				fail("failed on client side");
				break;

			case 0x4c :
				/// Session death!
				_failed = true; // to avoid the fail signal!!
				kill();
				return;

			/// KeepAlive
			case 0x01 :
				if(!peer.connected)
					fail("Timeout connection client");
				else
					writeMessage(0x41,0);
			case 0x41 :
				_timesKeepalive=0;
				break;

			case 0x5e : {
				// RTMFPFlow exception!
				UInt64 id = message.read7BitLongValue();
				
				RTMFPWriter* pRTMFPWriter = writer(id);
				if(pRTMFPWriter)
					pRTMFPWriter->fail("Writer rejected on session ",name());
				else
					WARN("RTMFPWriter ", id, " unfound for failed signal on session ", name());
				break;

			}
			case 0x18 :
				/// This response is sent when we answer with a Acknowledgment negative
				// It contains the id flow
				// I don't unsertand the usefulness...
				//pFlow = &flow(message.read8());
				//stage = pFlow->stageSnd();
				// For the moment, we considerate it like a exception
				fail("ack negative from server"); // send fail message immediatly
				break;

			case 0x51 : {
				/// Acknowledgment
				UInt64 id = message.read7BitLongValue();
				RTMFPWriter* pRTMFPWriter = writer(id);
				if(pRTMFPWriter)
					pRTMFPWriter->acknowledgment(message);
				else
					WARN("RTMFPWriter ",id," unfound for acknowledgment on session ",name());
				break;
			}
			/// Request
			// 0x10 normal request
			// 0x11 special request, in repeat case (following stage request)
			case 0x10 : {
				flags = message.read8();
				UInt64 idFlow = message.read7BitLongValue();
				stage = message.read7BitLongValue()-1;
				deltaNAck = message.read7BitLongValue()-1;
				
				if (_failed)
					break;

				map<UInt64,RTMFPFlow*>::const_iterator it = _flows.find(idFlow);
				pFlow = it==_flows.end() ? NULL : it->second;

				// Header part if present
				if(flags & MESSAGE_HEADER) {
					string signature;
					message.read(message.read8(),signature);

					if(!pFlow)
						pFlow = createFlow(idFlow,signature);

					if(message.read8()>0) {

						// Fullduplex header part
						if(message.read8()!=0x0A)
							WARN("Unknown fullduplex header part for the flow ",idFlow)
						else 
							message.read7BitLongValue(); // Fullduplex useless here! Because we are creating a new RTMFPFlow!

						// Useless header part 
						UInt8 length=message.read8();
						while(length>0 && message.available()) {
							WARN("Unknown message part on flow ",idFlow);
							message.next(length);
							length=message.read8();
						}
						if(length>0)
							ERROR("Bad header message part, finished before scheduled");
					}

				}
				
				if(!pFlow) {
					WARN("RTMFPFlow ",idFlow," unfound");
					if (_pFlowNull)
						((UInt64&)_pFlowNull->id) = idFlow;
					pFlow = _pFlowNull;
				}

			}	
			case 0x11 : {
				++stage;
				++deltaNAck;

				// has Header?
				if(type==0x11)
					flags = message.read8();

				// Process request
				if (pFlow && !_failed) {
					bool wasConnected(peer.connected);
					pFlow->receive(stage, deltaNAck, message, flags);
					if (!wasConnected && peer.connected) {
						for (auto& it : _flowWriters)
							it.second->open();
					}
				}
				break;
			}
			default :
				ERROR("RTMFPMessage type '", Format<UInt8>("%02x", type), "' unknown");
		}

		// Next
		packet.next(size);
		type = packet.available()>0 ? packet.read8() : 0xFF;

		// Commit RTMFPFlow (pFlow means 0x11 or 0x10 message)
		if(pFlow && type!= 0x11) {
			pFlow->commit();
			if(pFlow->consumed()) {
				if (pFlow->critical()) {
					if (!peer.connected) {
						// without connection, nothing must be sent!
						for (auto& it : _flowWriters)
							it.second->clear();
					}
					fail(); // If connection fails, log is already displayed, and so fail the whole session!
				} else {
					_flows.erase(pFlow->id);
					delete pFlow;
				}
			}
			pFlow=NULL;
		}
	}