bool WSSession::buildPacket(PacketReader& packet) { if (packet.available()<2) return false; UInt8 type = packet.read8() & 0x0F; UInt8 lengthByte = packet.read8(); UInt32 size=lengthByte&0x7f; if (size==127) { if (packet.available()<8) return false; size = (UInt32)packet.read64(); } else if (size==126) { if (packet.available()<2) return false; size = packet.read16(); } if(lengthByte&0x80) size += 4; if (packet.available()<size) return false; packet.shrink(size); if (lengthByte & 0x80) { shared_ptr<WSUnmasking> pWSUnmasking(new WSUnmasking(invoker, packet.current(),packet.available(), type)); decode<WSUnmasking>(pWSUnmasking); } else { packet.reset(packet.position()-1); *(UInt8*)packet.current() = type; } return true; }
void Handshake::packetHandler(PacketReader& packet) { UInt8 marker = packet.read8(); if(marker!=0x0b) { ERROR("Marker handshake wronk : must be '0B' and not '%02x'",marker); return; } UInt16 time = packet.read16(); UInt8 id = packet.read8(); packet.shrink(packet.read16()); // length PacketWriter& packetOut = writer(); UInt8 idResponse=0; { PacketWriter response(packetOut,3); idResponse = handshakeHandler(id,packet,response); if(idResponse==0) return; } packetOut << (UInt8)idResponse; packetOut << (UInt16)(packetOut.length()-packetOut.position()-2); send(true); // reset farid to 0! _farId=0; }
void Handshake::packetHandler(PacketReader& packet) { UInt8 marker = packet.read8(); if(marker!=0x0b) { ERROR("Marker handshake wrong : should be 0b and not %u",marker); return; } UInt16 time = packet.read16(); UInt8 id = packet.read8(); packet.shrink(packet.read16()); // length PacketWriter& response(writer()); UInt32 pos = response.position(); response.next(3); UInt8 idResponse = handshakeHandler(id,packet,response); response.reset(pos); if(idResponse>0) { response.write8(idResponse); response.write16(response.length()-response.position()-2); flush(); } // reset farid to 0! (UInt32&)farId=0; }
void ServerConnection::packetHandler(PacketReader& packet) { UInt8 marker = packet.read8(); if(marker!=0x0b) { ERROR("ServerConnection with an unknown %u marker, it should be 0x0b",marker); return; } packet.next(2); UInt8 id = packet.read8(); switch(id) { case 0x71: { packet.next(2); string tag; packet.readString8(tag); map<string,P2PHandshakerAddress>::iterator it = _p2pHandshakers.find(tag); if(it==_p2pHandshakers.end()) { ERROR("Unknown ServerConnection tag %s on P2P handshake",tag.c_str()); break; } (SocketAddress&)_handshake.peer.address = it->second; packet.reset(0); PacketWriter writer(packet.current(),packet.available()+16); // +16 for futur 0xFFFF padding writer.clear(packet.available()); _handshake.send(writer); _p2pHandshakers.erase(it); break; } case 0x40: { if(!_connected) { // Edge hello response _connected=true; return; } // Edge keepalive PacketWriter& packet(writer()); packet.write8(0x41); packet.write16(0); flush(); INFO("Keepalive RTMFP server"); break; } case 0x45: { // Server is death! (bool&)died=true; break; } default: ERROR("Unkown ServerConnection packet id %u",id); } }
AMF::ContentType RTMFPFlow::unpack(PacketReader& packet,UInt32& time) { if(packet.available()==0) return AMF::EMPTY; AMF::ContentType type = (AMF::ContentType)packet.read8(); switch(type) { // amf content case AMF::INVOCATION_AMF3: packet.next(1); case AMF::INVOCATION: packet.next(4); return AMF::INVOCATION; case AMF::AUDIO: case AMF::VIDEO: time = packet.read32(); break; case AMF::DATA: packet.next(1); case AMF::RAW: packet.next(4); case AMF::CHUNKSIZE: break; default: ERROR("Unpacking type '",Format<UInt8>("%02x",(UInt8)type),"' unknown"); break; } return type; }
Message::Type Flow::unpack(PacketReader& reader) { if(reader.available()==0) return Message::EMPTY; Message::Type type = (Message::Type)reader.read8(); switch(type) { // amf content case 0x11: reader.next(1); case Message::AMF_WITH_HANDLER: reader.next(4); return Message::AMF_WITH_HANDLER; case Message::AMF: reader.next(5); case Message::AUDIO: case Message::VIDEO: break; // raw data case 0x04: reader.next(4); case 0x01: break; default: ERROR("Unpacking type '%02x' unknown",type); break; } return type; }
void RTMFPHandshake::packetHandler(PacketReader& packet) { UInt8 marker = packet.read8(); if(marker!=0x0b) { ERROR("Marker handshake wrong : should be 0b and not ",Format<UInt8>("%.2x",marker)); return; } UInt16 time = packet.read16(); UInt8 id = packet.read8(); packet.shrink(packet.read16()); // length PacketWriter& response(this->packet()); UInt32 oldSize(response.size()); response.next(3); // type and size UInt8 idResponse = handshakeHandler(id,packet,response); if(idResponse>0) BinaryWriter(response,oldSize).write8(idResponse).write16(response.size()-oldSize-3); else response.clear(oldSize); }
void RTMFPSession::packetHandler(PacketReader& packet) { _recvTimestamp.update(); // 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.readString8(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"); ((UInt64&)_pFlowNull->id) = idFlow; pFlow = _pFlowNull; } } case 0x11 : { ++stage; ++deltaNAck; // has Header? if(type==0x11) flags = message.read8(); // Process request if (pFlow) pFlow->fragmentHandler(stage, deltaNAck, message, flags); break; } default : ERROR("RTMFPMessage type '", Format<UInt8>("%02x", type), "' unknown"); } // Next packet.next(size); type = packet.available()>0 ? packet.read8() : 0xFF; // Commit RTMFPFlow if(pFlow && type!= 0x11) { pFlow->commit(); if(pFlow->consumed()) { _flows.erase(pFlow->id); delete pFlow; } pFlow=NULL; } }
void WSSession::packetHandler(PacketReader& packet) { UInt8 type = 0; Exception ex; if(peer.connected) { type = packet.read8(); switch(type) { case WS::TYPE_BINARY: { RawReader reader(packet); peer.onMessage(ex, "onMessage",reader,WS::TYPE_BINARY); break; } case WS::TYPE_TEXT: { if(!JSONReader::IsValid(packet)) { RawReader reader(packet); peer.onMessage(ex, "onMessage",reader); break; } JSONReader reader(packet); if(reader.followingType()!=JSONReader::STRING) { peer.onMessage(ex, "onMessage",reader); break; } string name; reader.readString(name); if(name=="__publish") { if(reader.followingType()!=JSONReader::STRING) { ex.set(Exception::PROTOCOL, "__publish method takes a stream name in first parameter",WS::CODE_MALFORMED_PAYLOAD); break; } reader.readString(name); if(_pPublication) invoker.unpublish(peer,_pPublication->name()); _pPublication = invoker.publish(ex, peer,name); } else if(name=="__play") { if(reader.followingType()!=JSONReader::STRING) { ex.set(Exception::PROTOCOL, "__play method takes a stream name in first parameter",WS::CODE_MALFORMED_PAYLOAD); break; } reader.readString(name); closeSusbcription(); } else if(name=="__closePublish") { closePublication(); } else if(name=="__closePlay") { closeSusbcription(); } else if (name == "__close") { closePublication(); closeSusbcription(); } else if(_pPublication) { reader.reset(); _pPublication->pushData(reader); } else peer.onMessage(ex, name,reader); break; } case WS::TYPE_CLOSE: _writer.close(packet.available() ? packet.read16() : 0); break; case WS::TYPE_PING: _writer.writePong(packet.current(),packet.available()); break; case WS::TYPE_PONG: peer.setPing(_writer.ping = (UInt16)(_time.elapsed()/1000)); break; default: ex.set(Exception::PROTOCOL, Format<UInt8>("Type %#x unknown", type), WS::CODE_MALFORMED_PAYLOAD); break; } if (ex) { ERROR(ex.error()); _writer.close((ex.code()==Exception::APPLICATION || ex.code() == Exception::SOFTWARE) ? WS::CODE_PROTOCOL_ERROR : ex.code()); } } if(!peer.connected || type==WS::TYPE_CLOSE) kill(); else _writer.flush(); }
void Middle::targetPacketHandler(PacketReader& packet) { if(_firstResponse) ((Timestamp&)peer.lastReceptionTime).update(); // To emulate a long ping corresponding, otherwise client send multiple times each packet _firstResponse = false; UInt8 marker = packet.read8(); _timeSent = packet.read16(); // time UInt16 timeEcho(0); if((marker|0xF0) == 0xFE) timeEcho = packet.read16(); // time echo PacketWriter& packetOut = writer(); int pos = packetOut.position(); UInt8 type = packet.available()>0 ? packet.read8() : 0xFF; UInt64 idFlow,stage; UInt8 nbPeerSent = 0; while(type!=0xFF) { int posType = packetOut.position(); packetOut.write8(type); UInt16 size = packet.read16(); PacketReader content(packet.current(),size);packetOut.write16(size); if(type==0x10 || type==0x11) { UInt8 flag = content.read8();packetOut.write8(flag); if(type==0x10) { idFlow = content.read7BitLongValue();packetOut.write7BitLongValue(idFlow); stage = content.read7BitLongValue();packetOut.write7BitLongValue(stage); packetOut.write7BitLongValue(content.read7BitLongValue()); } else ++stage; if(!(flag&MESSAGE_WITH_BEFOREPART)) { if(flag&MESSAGE_HEADER) { UInt8 len = content.read8(); packetOut.write8(len); while(len!=0) { packetOut.writeRaw(content.current(),len); content.next(len); len = content.read8(); packetOut.write8(len); } } UInt8 flagType = content.read8(); packetOut.write8(flagType); if(flagType==0x09) { UInt32 time = content.read32(); packetOut.write32(time); TRACE("Timestamp/Flag video : %u/%2x",time,*content.current()); } else if(flagType==0x08) { UInt32 time = content.read32(); packetOut.write32(time); TRACE("Timestamp/Flag audio : %u/%2x",time,*content.current()); } else if(flagType==0x04) { packetOut.write32(content.read32()); UInt16 a = content.read16();packetOut.write16(a); UInt32 b = content.read32(); packetOut.write32(b); if(content.available()>0) { UInt32 c = content.read32(); packetOut.write32(c); if(a!=0x22) { DEBUG("Raw %llu : %.2x %u %u",idFlow,a,b,c) } else { TRACE("Bound %llu : %.2x %u %u",idFlow,a,b,c); } } else DEBUG("Raw %llu : %.2x %u",idFlow,a,b) /* if(a==0x1F) { packetOut.reset(posType); content.next(content.available()); }*/ }
void Middle::packetHandler(PacketReader& packet) { if(!_pMiddleAesEncrypt) manage(); // to wait the target handshake response // Middle to target PacketWriter& request = requester(); UInt8 marker = packet.read8(); request << marker; request << packet.read16(); if((marker|0xF0) == 0xFD) request.write16(packet.read16()); // time echo int pos = request.position(); UInt8 type = packet.available()>0 ? packet.read8() : 0xFF; while(type!=0xFF) { UInt16 size = packet.read16(); PacketReader content(packet.current(),size); PacketWriter out(request.begin()+request.position(),request.available()); // 3 for future type and size out.clear(3); if(type==0x10) { out.write8(content.read8()); UInt64 idFlow = content.read7BitLongValue();out.write7BitLongValue(idFlow); UInt64 stage = content.read7BitLongValue();out.write7BitLongValue(stage); if(idFlow==0x02 && stage==0x01) { if(!_isPeer) { /// Replace NetConnection infos out.writeRaw(content.current(),14);content.next(14); // first string string tmp; content.readString16(tmp);out.writeString16(tmp); AMFWriter writer(out); writer.amf0Preference=true; AMFReader reader(content); writer.writeNumber(reader.readNumber()); // double AMFSimpleObject obj; reader.readSimpleObject(obj); /// Replace tcUrl if(obj.has("tcUrl")) obj.setString("tcUrl",_queryUrl); writer.writeSimpleObject(obj); } else { out.writeRaw(content.current(),3);content.next(3); UInt16 netGroupHeader = content.read16();out.write16(netGroupHeader); if(netGroupHeader==0x4752) { map<string,string>::const_iterator it = peer.properties.find("groupspec"); if(it!=peer.properties.end()) { out.writeRaw(content.current(),71);content.next(71); UInt8 result1[AES_KEY_SIZE]; UInt8 result2[AES_KEY_SIZE]; HMAC(EVP_sha256(),&_sharedSecret[0],_sharedSecret.size(),&_targetNonce[0],_targetNonce.size(),result1,NULL); HMAC(EVP_sha256(),it->second.c_str(),it->second.size(),result1,AES_KEY_SIZE,result2,NULL); out.writeRaw(result2,AES_KEY_SIZE);content.next(AES_KEY_SIZE); out.writeRaw(content.current(),4);content.next(4); out.writeRaw(_target.peerId,ID_SIZE);content.next(ID_SIZE); } else WARN("No groupspec client property indicated to make working the 'man-in-the-middle' mode between peers in a NetGroup case"); } } } } else if(type == 0x4C) { kill(); return; } else if(type == 0x51) { //printf("%s\n",Util::FormatHex(content.current(),content.available()).c_str()); } out.writeRaw(content.current(),content.available()); packet.next(size); if(out.length()>=3) { request<<type; size = out.length()-3; request.write16(size);request.next(size); } type = packet.available()>0 ? packet.read8() : 0xFF; } if(request.length()>pos) sendToTarget(); }
UInt8 RTMFPHandshake::handshakeHandler(UInt8 id,PacketReader& request,PacketWriter& response) { switch(id){ case 0x30: { request.next(1); UInt8 epdLen = request.read8()-1; UInt8 type = request.read8(); string epd; request.readRaw(epdLen,epd); string tag; request.readRaw(16,tag); response.writeString8(tag); if(type == 0x0f) { const UInt8* peerId = (const UInt8*)epd.c_str(); RTMFPSession* pSessionWanted = _sessions.find<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 || peer.address.host() == pSessionWanted->peer.address.host()) pSession = _sessions.find<RTMFPSession>(peer.address); pSessionWanted->p2pHandshake(tag,peer.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) { Exception ex; SocketAddress address; string addr; SocketAddress::Split(pSession->peer.serverAddress,addr); bool success(false); EXCEPTION_TO_LOG(success=address.set(ex,addr, 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(peer.address == *it) WARN("A client tries to connect to himself (same ",peer.address.toString()," address)"); RTMFP::WriteAddress(response,*it,RTMFP::ADDRESS_REDIRECTION); DEBUG("P2P address initiator exchange, ",it->toString()); } return addresses.empty() ? 0 : 0x71; }
UInt8 Handshake::handshakeHandler(UInt8 id,PacketReader& request,PacketWriter& response) { switch(id){ case 0x30: { request.read8(); // passer un caractere (boite dans boite) UInt8 epdLen = request.read8()-1; UInt8 type = request.read8(); string epd; request.readRaw(epdLen,epd); string tag; request.readRaw(16,tag); response.writeString8(tag); if(type == 0x0f) return _gateway.p2pHandshake(tag,response,peer.address,(const UInt8*)epd.c_str()); if(type == 0x0a){ /// Handshake HelloAttempt& attempt = helloAttempt<HelloAttempt>(tag); if(edges().size()>0 && (_invoker.edgesAttemptsBeforeFallback==0 || attempt.count <_invoker.edgesAttemptsBeforeFallback)) { if(_invoker.edgesAttemptsBeforeFallback>0) { try { URI uri(epd); response.writeAddress(SocketAddress(uri.getHost(),uri.getPort()),false); // TODO check with true! } catch(Exception& ex) { ERROR("Parsing %s URL problem in hello attempt : %s",epd.c_str(),ex.displayText().c_str()); } } map<int,list<Edge*> > edgesSortedByCount; map<string,Edge*>::const_iterator it; for(it=edges().begin();it!=edges().end();++it) edgesSortedByCount[it->second->count].push_back(it->second); UInt8 count=0; map<int,list<Edge*> >::const_iterator it2; for(it2=edgesSortedByCount.begin();it2!=edgesSortedByCount.end();++it2) { list<Edge*>::const_iterator it3; for(it3=it2->second.begin();it3!=it2->second.end();++it3) { response.writeAddress((*it3)->address,false); if((++count)==6) // 6 redirections maximum break; } if(it3!=it2->second.end()) break; } return 0x71; } if(edges().size()>0) WARN("After %u hello attempts, impossible to connect to edges. Edges are busy? or unreachable?",_invoker.edgesAttemptsBeforeFallback); // New Cookie createCookie(response,attempt,tag,epd); // instance id (certificat in the middle) response.writeRaw(_certificat,sizeof(_certificat)); return 0x70; } else ERROR("Unkown handshake first way with '%02x' type",type); break; } case 0x39: case 0x38: { (UInt32&)farId = request.read32(); if(request.read7BitLongValue()!=COOKIE_SIZE) { ERROR("Bad handshake cookie '%s': its size should be 64 bytes",Util::FormatHex(request.current(),COOKIE_SIZE).c_str()); return 0; } map<const UInt8*,Cookie*,CompareCookies>::iterator itCookie = _cookies.find(request.current()); if(itCookie==_cookies.end()) { if(id!=0x39) { ERROR("Handshake cookie '%s' unknown",Util::FormatHex(request.current(),COOKIE_SIZE).c_str()); return 0; } Cookie* pCookie = new Cookie(); UInt32 pos = request.position(); request.readRaw((UInt8*)pCookie->value,COOKIE_SIZE); request >> (string&)pCookie->queryUrl; request.reset(pos); itCookie = _cookies.insert(pair<const UInt8*,Cookie*>(pCookie->value,pCookie)).first; } Cookie& cookie(*itCookie->second); if(cookie.id==0) { UInt8 decryptKey[AES_KEY_SIZE];UInt8* pDecryptKey=&decryptKey[0]; UInt8 encryptKey[AES_KEY_SIZE];UInt8* pEncryptKey=&encryptKey[0]; if(id==0x38) { request.next(COOKIE_SIZE); size_t size = (size_t)request.read7BitLongValue(); // peerId = SHA256(farPubKey) EVP_Digest(request.current(),size,(UInt8*)peer.id,NULL,EVP_sha256(),NULL); vector<UInt8> publicKey(request.read7BitValue()-2); request.next(2); // unknown request.readRaw(&publicKey[0],publicKey.size()); size = request.read7BitValue(); cookie.computeKeys(&publicKey[0],publicKey.size(),request.current(),(UInt16)size,decryptKey,encryptKey); } else { // edge pDecryptKey=NULL; pEncryptKey=NULL; memcpy((UInt8*)peer.id,request.current(),ID_SIZE); request.next(COOKIE_SIZE); request.next(request.read7BitEncoded()); } // Fill peer infos Util::UnpackUrl(cookie.queryUrl,(string&)peer.path,(map<string,string>&)peer.properties); // RESPONSE Session& session = _gateway.createSession(farId,peer,pDecryptKey,pEncryptKey,cookie); (UInt32&)cookie.id = session.id; string address; if(id==0x39) { // Session by edge session.flags |= SESSION_BY_EDGE; Edge* pEdge = _invoker.edges(peer.address); if(!pEdge) ERROR("Edge session creation by an unknown server edge %s",peer.address.toString().c_str()) else pEdge->addSession(session); request >> address; } else // Session direct address = session.peer.address.toString(); session.peer.addresses.clear(); session.peer.addresses.push_back(address); cookie.write(); } else
void Session::packetHandler(PacketReader& packet) { _recvTimestamp.update(); // Read packet UInt8 marker = packet.read8()|0xF0; _timeSent = packet.read16(); // with time echo if(marker == 0xFD) _peer.setPing(RTMFP::Time(_recvTimestamp.epochMicroseconds())-packet.read16()); else if(marker != 0xF9) WARN("Packet marker unknown : %02x",marker); // Variables for request (0x10 and 0x11) UInt8 flags; Flow* pFlow=NULL; UInt32 stage=0; UInt32 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("Session failed on the client side"); break; case 0x4c : /// Session death! kill(); break; /// KeepAlive case 0x01 : writeMessage(0x41,0); case 0x41 : _timesKeepalive=0; break; case 0x5e : { // Flow exception! UInt32 id = message.read7BitValue(); FlowWriter* pFlowWriter = flowWriter(id); if(pFlowWriter) pFlowWriter->fail("receiver has rejected the flow"); else WARN("FlowWriter %u unfound for failed signal",id); 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 UInt32 id = message.read7BitValue(); FlowWriter* pFlowWriter = flowWriter(id); if(pFlowWriter) { UInt8 ack = message.read8(); while(ack==0xFF) ack = message.read8(); if(ack>0) pFlowWriter->acknowledgment(message.read7BitValue()); else { // In fact here, we should send a 0x18 message (with id flow), // but it can create a loop... We prefer the following behavior pFlowWriter->fail("ack negative from client"); } } else WARN("FlowWriter %u unfound for acknowledgment",id); break; } /// Request // 0x10 normal request // 0x11 special request, in repeat case (following stage request) case 0x10 : { flags = message.read8(); UInt32 idFlow = message.read7BitValue(); stage = message.read7BitValue()-1; deltaNAck = message.read7BitValue()-1; map<UInt32,Flow*>::const_iterator it = _flows.find(idFlow); pFlow = it==_flows.end() ? NULL : it->second; // Header part if present if(flags & MESSAGE_HEADER) { string signature; message.readString8(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 '%u'",idFlow) else message.read7BitValue(); // Fullduplex useless here! Because we are creating a new Flow! // Useless header part UInt8 length=message.read8(); while(length>0 && message.available()) { WARN("Unknown message part on flow '%u'",idFlow); message.next(length); length=message.read8(); } if(length>0) ERROR("Bad header message part, finished before scheduled"); } } if(!pFlow) { WARN("Flow %u unfound",idFlow); ((UInt32&)_pFlowNull->id) = idFlow; pFlow = _pFlowNull; } } case 0x11 : { ++stage; ++deltaNAck; // has Header? if(type==0x11) flags = message.read8(); // Process request pFlow->fragmentHandler(stage,deltaNAck,message,flags); if(!pFlow->error().empty()) fail(pFlow->error()); // send fail message immediatly break; } default : ERROR("Message type '%02x' unknown",type); } // Next packet.next(size); type = packet.available()>0 ? packet.read8() : 0xFF; // Commit Flow if(pFlow && stage>0 && type!= 0x11) { pFlow->commit(); if(pFlow->consumed()) { _flows.erase(pFlow->id); delete pFlow; } pFlow=NULL; } }
void Middle::targetPacketHandler(PacketReader& packet) { if(_firstResponse) _recvTimestamp.update(); // To emulate a long ping corresponding, otherwise client send multiple times each packet _firstResponse = false; UInt8 marker = packet.read8(); UInt16 timestamp = packet.read16(); // time if((marker|0xF0) == 0xFE) _timeSent = packet.read16(); // time echo PacketWriter& packetOut = writer(); int pos = packetOut.position(); UInt8 type = packet.available()>0 ? packet.read8() : 0xFF; UInt32 idFlow,stage; UInt8 nbPeerSent = 0; while(type!=0xFF) { int posType = packetOut.position(); packetOut.write8(type); UInt16 size = packet.read16(); PacketReader content(packet.current(),size);packetOut.write16(size); if(type==0x10 || type==0x11) { UInt8 flag = content.read8();packetOut.write8(flag); if(type==0x10) { idFlow = content.read7BitValue();packetOut.write7BitValue(idFlow); stage = content.read7BitValue();packetOut.write7BitValue(stage); } else ++stage; packetOut.write7BitValue(content.read7BitValue()); if(!(flag&MESSAGE_WITH_BEFOREPART)) { if(flag&MESSAGE_HEADER) { UInt8 len = content.read8(); packetOut.write8(len); while(len!=0) { packetOut.writeRaw(content.current(),len); content.next(len); len = content.read8(); packetOut.write8(len); } } UInt8 flagType = content.read8(); packetOut.write8(flagType); if(flagType==0x09) { UInt32 time = content.read32(); packetOut.write32(time); //TRACE("Timestamp/Flag video : %u/%2x",time,*content.current()); } else if(flagType==0x08) { UInt32 time = content.read32(); packetOut.write32(time); //TRACE("Timestamp/Flag audio : %u/%2x",time,*content.current()); } else if(flagType==0x04) { packetOut.write32(content.read32()); UInt16 a = content.read16(); packetOut.write16(a); UInt32 b = content.read32(); packetOut.write32(b); UInt32 c = content.read32(); packetOut.write32(c); //TRACE("Bound %u : %u %u %u",idFlow,a,b,c); } if(flagType==0x0b && stage==0x01 && ((marker==0x4e && idFlow==0x03) || (marker==0x8e && idFlow==0x05))) { /// Replace "middleId" by "peerId" UInt8 middlePeerIdWanted[ID_SIZE]; content.readRaw(middlePeerIdWanted,ID_SIZE); ++nbPeerSent; Sessions::Iterator it; for(it=_sessions.begin();it!=_sessions.end();++it) { Middle* pMiddle = (Middle*)it->second; if(pMiddle->middlePeer() == middlePeerIdWanted) { memcpy(middlePeerIdWanted,pMiddle->peer.id,ID_SIZE); break; } } packetOut.writeRaw(middlePeerIdWanted,ID_SIZE); } else if(flagType == 0x01) { packetOut.writeRaw(content.current(),68);content.next(68); Entities<Group>::Iterator it; for(it = _invoker.groups.begin();it!=_invoker.groups.end();++it) { Group& group = *it->second; Group::Iterator itP; for(itP=group.begin();itP!=group.end();++itP) { if((**itP)==_target.id) { UInt8 result1[AES_KEY_SIZE]; UInt8 result2[AES_KEY_SIZE]; HMAC(EVP_sha256(),_target.sharedSecret,KEY_SIZE,&_target.initiatorNonce[0],_target.initiatorNonce.size(),result1,NULL); HMAC(EVP_sha256(),group.id,ID_SIZE,result1,AES_KEY_SIZE,result2,NULL); packetOut.writeRaw(result2,AES_KEY_SIZE);content.next(AES_KEY_SIZE); packetOut.writeRaw(content.current(),4);content.next(4); packetOut.writeRaw(peer.id,ID_SIZE);content.next(ID_SIZE); break; } } if(itP!=group.end()) break; } if(it==_invoker.groups.end()) ERROR("Handshake NetGroup packet between peers without corresponding Group"); } } } else if(type == 0x0F) { packetOut.writeRaw(content.current(),3);content.next(3); UInt8 peerId[ID_SIZE]; content.readRaw(peerId,ID_SIZE); if(memcmp(peerId,peer.id,ID_SIZE)!=0 && memcmp(peerId,_middlePeer.id,ID_SIZE)!=0) WARN("The p2pHandshake target packet doesn't match the peerId (or the middlePeerId)"); // Replace by the peer.id packetOut.writeRaw(peer.id,ID_SIZE); } packetOut.writeRaw(content.current(),content.available()); packet.next(size); type = packet.available()>0 ? packet.read8() : 0xFF; } if(nbPeerSent>0) INFO("%02x peers sending",nbPeerSent); if(packetOut.length()>pos) flush(); }
void Middle::packetHandler(PacketReader& packet) { if(!_pMiddleAesEncrypt) { DEBUG("500ms sleeping to wait target handshaking"); Thread::sleep(500); // to wait the target handshake response manage(); } // Middle to target PacketWriter& request = requester(); UInt8 marker = packet.read8(); request << marker; request << packet.read16(); if((marker|0xF0) == 0xFD) request.write16(packet.read16()); // time echo int pos = request.position(); UInt8 type = packet.available()>0 ? packet.read8() : 0xFF; while(type!=0xFF) { UInt16 size = packet.read16(); PacketReader content(packet.current(),size); PacketWriter out(request.begin()+request.position(),request.available()); // 3 for future type and size out.clear(3); if(type==0x10) { out.write8(content.read8()); UInt32 idFlow = content.read7BitValue();out.write7BitValue(idFlow); UInt32 stage = content.read7BitValue();out.write7BitValue(stage); if(idFlow==0x02 && stage==0x01) { if(!_isPeer) { /// Replace NetConnection infos out.writeRaw(content.current(),14);content.next(14); // first string string tmp; content.readString16(tmp);out.writeString16(tmp); AMFWriter writer(out); writer.amf0Preference=true; AMFReader reader(content); writer.writeNumber(reader.readNumber()); // double AMFSimpleObject obj; reader.readSimpleObject(obj); /// Replace tcUrl if(obj.has("tcUrl")) obj.setString("tcUrl",_queryUrl); writer.writeSimpleObject(obj); } else { out.writeRaw(content.current(),3);content.next(3); UInt16 netGroupHeader = content.read16();out.write16(netGroupHeader); if(netGroupHeader==0x4752) { out.writeRaw(content.current(),71);content.next(71); Entities<Group>::Iterator it; for(it = _invoker.groups.begin();it!=_invoker.groups.end();++it) { Group& group = *it->second; Group::Iterator itP; for(itP=group.begin();itP!=group.end();++itP) { if((**itP)==_target.id) { UInt8 result1[AES_KEY_SIZE]; UInt8 result2[AES_KEY_SIZE]; HMAC(EVP_sha256(),_sharedSecret,KEY_SIZE,&_targetNonce[0],_targetNonce.size(),result1,NULL); HMAC(EVP_sha256(),group.id,ID_SIZE,result1,AES_KEY_SIZE,result2,NULL); out.writeRaw(result2,AES_KEY_SIZE);content.next(AES_KEY_SIZE); out.writeRaw(content.current(),4);content.next(4); out.writeRaw(_target.peerId,ID_SIZE);content.next(ID_SIZE); break; } } if(itP!=group.end()) break; } if(it==_invoker.groups.end()) ERROR("Handshake NetGroup packet between peers without corresponding Group"); } } } } else if(type == 0x4C) { kill(); } else if(type == 0x51) { //printf("%s\n",Util::FormatHex(content.current(),content.available()).c_str()); } out.writeRaw(content.current(),content.available()); packet.next(size); if(out.length()>=3) { request<<type; size = out.length()-3; request.write16(size);request.next(size); } type = packet.available()>0 ? packet.read8() : 0xFF; } if(request.length()>pos) sendToTarget(); }
// TODO make perhaps a FlowHandshake UInt8 Handshake::handshakeHandler(UInt8 id,PacketReader& request,PacketWriter& response) { switch(id){ case 0x30: { request.read8(); // passer un caractere (boite dans boite) UInt8 epdLen = request.read8()-1; UInt8 type = request.read8(); string epd; request.readRaw(epdLen,epd); string tag; request.readRaw(16,tag); response.writeString8(tag); // UDP hole punching if(type == 0x0f) return _gateway.p2pHandshake(tag,response,peer().address,(const UInt8*)epd.c_str()); if(type == 0x0a){ /// Handshake // RESPONSE 38 // New Cookie char cookie[65]; RandomInputStream ris; ris.read(cookie,64); cookie[64]='\0'; response.write8(64); response.writeRaw(cookie,64); _cookies[cookie] = new Cookie(epd); // instance id (certificat in the middle) response.writeRaw(_certificat,sizeof(_certificat)); return 0x70; } else { ERROR("Unkown handshake first way with '%02x' type",type); } break; } case 0x38: { _farId = request.read32(); string cookie; request.readRaw(request.read8(),cookie); map<string,Cookie*>::iterator itCookie = _cookies.find(cookie.c_str()); if(itCookie==_cookies.end()) { ERROR("Handshake cookie '%s' unknown",cookie.c_str()); return 0; } request.read8(); // why 0x81? // signature string farSignature; request.readString8(farSignature); // 81 02 1D 02 stable // farPubKeyPart UInt8 farPubKeyPart[128]; request.readRaw((char*)farPubKeyPart,sizeof(farPubKeyPart)); string farCertificat; request.readString8(farCertificat); // peerId = SHA256(farSignature+farPubKey) string temp(farSignature); temp.append((char*)farPubKeyPart,sizeof(farPubKeyPart)); EVP_Digest(temp.c_str(),temp.size(),(UInt8*)peer().id,NULL,EVP_sha256(),NULL); // Compute Diffie-Hellman secret UInt8 pubKey[128]; UInt8 sharedSecret[128]; RTMFP::EndDiffieHellman(RTMFP::BeginDiffieHellman(pubKey),farPubKeyPart,sharedSecret); // Compute Keys UInt8 requestKey[AES_KEY_SIZE]; UInt8 responseKey[AES_KEY_SIZE]; RTMFP::ComputeAsymetricKeys(sharedSecret,pubKey,_signature,farCertificat,requestKey,responseKey); // RESPONSE Util::UnpackUrl(itCookie->second->queryUrl,(string&)peer().path,(map<string,string>&)peer().parameters); response << _gateway.createSession(_farId,peer(),requestKey,responseKey); response.write8(0x81); response.writeString8(_signature); response.writeRaw((char*)pubKey,sizeof(pubKey)); response.write8(0x58); // remove cookie _cookies.erase(itCookie); // db_add_public(s); // TODO return 0x78; } default: ERROR("Unkown handshake packet id '%02x'",id); } return 0; }
UInt8 Handshake::handshakeHandler(UInt8 id,PacketReader& request,PacketWriter& response) { switch(id){ case 0x30: { request.read8(); // passer un caractere (boite dans boite) UInt8 epdLen = request.read8()-1; UInt8 type = request.read8(); string epd; request.readRaw(epdLen,epd); string tag; request.readRaw(16,tag); response.writeString8(tag); if(type == 0x0f) return _gateway.p2pHandshake(tag,response,peer.address,(const UInt8*)epd.c_str()); if(type == 0x0a){ /// Handshake HelloAttempt& attempt = helloAttempt<HelloAttempt>(tag); // Fill peer infos UInt16 port; string host; Util::UnpackUrl(epd,host,port,(string&)peer.path,(map<string,string>&)peer.properties); set<string> addresses; peer.onHandshake(attempt.count+1,addresses); if(!addresses.empty()) { set<string>::iterator it; for(it=addresses.begin();it!=addresses.end();++it) { try { if((*it)=="again") ((string&)*it).assign(format("%s:%hu",host,port)); SocketAddress address(*it); response.writeAddress(address,it==addresses.begin()); } catch(Exception& ex) { ERROR("Bad redirection address %s in hello attempt, %s",(*it)=="again" ? epd.c_str() : (*it).c_str(),ex.displayText().c_str()); } } return 0x71; } // New Cookie createCookie(response,attempt,tag,epd); // instance id (certificat in the middle) response.writeRaw(_certificat,sizeof(_certificat)); return 0x70; } else ERROR("Unkown handshake first way with '%02x' type",type); break; } case 0x38: { (UInt32&)farId = request.read32(); if(request.read7BitLongValue()!=COOKIE_SIZE) { ERROR("Bad handshake cookie '%s': its size should be 64 bytes",Util::FormatHex(request.current(),COOKIE_SIZE).c_str()); return 0; } map<const UInt8*,Cookie*,CompareCookies>::iterator itCookie = _cookies.find(request.current()); if(itCookie==_cookies.end()) { WARN("Cookie %s unknown, maybe already connected (udpBuffer congested?)",Util::FormatHex(request.current(),COOKIE_SIZE).c_str()); return 0; } Cookie& cookie(*itCookie->second); (SocketAddress&)cookie.peerAddress = peer.address; if(cookie.farId==0) { ((UInt32&)cookie.farId) = farId; request.next(COOKIE_SIZE); size_t size = (size_t)request.read7BitLongValue(); // peerId = SHA256(farPubKey) EVP_Digest(request.current(),size,(UInt8*)cookie.peerId,NULL,EVP_sha256(),NULL); cookie.initiatorKey().resize(request.read7BitValue()-2); request.next(2); // unknown request.readRaw(&cookie.initiatorKey()[0],cookie.initiatorKey().size()); cookie.initiatorNonce().resize(request.read7BitValue()); request.readRaw(&cookie.initiatorNonce()[0],cookie.initiatorNonce().size()); cookie.computeKeys(); } else if(cookie.id>0) { // Repeat cookie reponse! cookie.read(response); return 0x78; } // else Keys are computing (multi-thread) break; } default: ERROR("Unkown handshake packet id %u",id); } return 0; }