void FlowWriter::writeResponseHeader(BinaryWriter& writer,const string& name,double callbackHandle) { writer.write8(Message::AMF_WITH_HANDLER);writer.write32(0); writer.write8(AMF_STRING);writer.writeString16(name); writer.write8(AMF_NUMBER); // marker writer << callbackHandle; writer.write8(AMF_NULL); }
void RTMFPWriter::flush(BinaryWriter& writer,UInt64 stage,UInt8 flags,bool header,const UInt8* data,UInt16 size) { if(_stageAck==0 && header) flags |= MESSAGE_HEADER; if(size==0) flags |= MESSAGE_ABANDONMENT; if(state()==CLOSED && _messages.size()==1) // On LAST message flags |= MESSAGE_END; // TRACE("RTMFPWriter ",id," stage ",stage); writer.write8(flags); if(header) { writer.write7BitLongValue(id); writer.write7BitLongValue(stage); writer.write7BitLongValue(stage-_stageAck); // signature if(_stageAck==0) { writer.writeString8(signature); // No write this in the case where it's a new flow! if(flowId>0) { writer.write8(1+Util::Get7BitValueSize(flowId)); // following size writer.write8(0x0a); // Unknown! writer.write7BitLongValue(flowId); } writer.write8(0); // marker of end for this part } } if (size > 0) writer.writeRaw(data, size); }
void RTMFPWriter::flush(BinaryWriter& writer,UInt64 stage,UInt8 flags,bool header,const RTMFPMessage& message, UInt32 offset, UInt16 size) { if(_stageAck==0 && header) flags |= MESSAGE_HEADER; if(size==0) flags |= MESSAGE_ABANDONMENT; if(state()==CLOSED && _messages.size()==1) // On LAST message flags |= MESSAGE_END; // TRACE("RTMFPWriter ",id," stage ",stage); writer.write8(flags); if(header) { writer.write7BitLongValue(id); writer.write7BitLongValue(stage); writer.write7BitLongValue(stage-_stageAck); // signature if(_stageAck==0) { writer.write8((UInt8)signature.size()).write(signature); // No write this in the case where it's a new flow! if(flowId>0) { writer.write8(1+Util::Get7BitValueSize(flowId)); // following size writer.write8(0x0a); // Unknown! writer.write7BitLongValue(flowId); } writer.write8(0); // marker of end for this part } } if (size == 0) return; if (offset < message.frontSize()) { UInt8 count = message.frontSize()-offset; if (size<count) count = (UInt8)size; writer.write(message.front()+offset,count); size -= count; if (size == 0) return; offset += count; } writer.write(message.body()+offset-message.frontSize(), size); }
UInt8 WS::WriteHeader(MessageType type,UInt32 size,BinaryWriter& writer) { if(type==TYPE_NIL) return 0; writer.write8(type|0x80); if (size < 126) { writer.write8(size); return 2; } if (size < 65536) { writer.write8(126); writer.write16(size); return 4; } writer.write8(127); writer.write64(size); return 10; }
// Writer audio or video packet void FLV::write(BinaryWriter& writer,UInt8 track,UInt32 time,const UInt8* data,UInt32 size) { /// 11 bytes of header writer.write8(track&AUDIO ? AMF::AUDIO : AMF::VIDEO); // size on 3 bytes writer.write24(size); // time on 3 bytes writer.write24(time); // unknown 4 bytes set to 0 writer.write32(0); /// playload writer.writeRaw(data, size); /// footer writer.write32(11+size); }
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; }
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