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; }
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; } }