diff --git a/build_scripts/Debian+Ubuntu/changelog b/build_scripts/Debian+Ubuntu/changelog index 70bbeb0e0..a84fe88b6 100644 --- a/build_scripts/Debian+Ubuntu/changelog +++ b/build_scripts/Debian+Ubuntu/changelog @@ -1851,7 +1851,7 @@ retroshare (0.5.4-0.6685~precise) precise; urgency=low * Bug fixes - - Added missing location from cert when addign new friend + - Added missing location from cert when adding new friend - Added missing IndicateConfigChanged to p3PeerMgrIMPL::setDynDNS - Fixed crash when closing the main window without the setting "Minimize to Tray Icon" - Renamed the setting "Do not Minimize to Tray Icon" to "Minimize to Tray @@ -2925,7 +2925,7 @@ retroshare (0.5.3-0.4953~natty) natty; urgency=low - Fixed Download toaster (utf8 in file name, use QDesktopServices vs RsUrlHandler for collections, fixed crash after opening a collection) (Patch from AsamK). - fixed utf8 chars in certificate links (Patch from AsamK) - - removed cache adding strategy to DL queue that was O(n^2). Now addign cache at the end of the queue + - removed cache adding strategy to DL queue that was O(n^2). Now adding cache at the end of the queue - Fixed bug when the user clicks on a link without http:// in a QTextBrowser. This link was oppened directly in RetroShare. - Attempted fix for maintaining External Port in Manual Forwarded Mode. diff --git a/libresapi/src/api/ChatHandler.cpp b/libresapi/src/api/ChatHandler.cpp index f464af4ab..b9f7bda4b 100644 --- a/libresapi/src/api/ChatHandler.cpp +++ b/libresapi/src/api/ChatHandler.cpp @@ -91,7 +91,7 @@ StreamBase& operator << (StreamBase& left, ChatHandler::ChatInfo& info) left << makeKeyValueReference("remote_author_id", info.remote_author_id) << makeKeyValueReference("remote_author_name", info.remote_author_name) << makeKeyValueReference("is_broadcast", info.is_broadcast) - << makeKeyValueReference("is_gxs_id", info.is_gxs_id) + << makeKeyValueReference("is_distant_chat_id", info.is_distant_chat_id) << makeKeyValueReference("is_lobby", info.is_lobby) << makeKeyValueReference("is_peer", info.is_peer); return left; @@ -183,7 +183,6 @@ void ChatHandler::tick() l.auto_subscribe = false; l.is_private = false; l.is_broadcast = true; - l.gxs_id = id.toGxsId(); lobbies.push_back(l); } @@ -225,12 +224,20 @@ void ChatHandler::tick() author_id = msg.broadcast_peer_id.toStdString(); author_name = mRsPeers->getPeerName(msg.broadcast_peer_id); } - else if(msg.chat_id.isGxsId()) + else if(msg.chat_id.isDistantChatId()) { - author_id = msg.chat_id.toGxsId().toStdString(); - RsIdentityDetails details; - if(!gxs_id_failed && mRsIdentity->getIdDetails(msg.chat_id.toGxsId(), details)) + DistantChatPeerInfo dcpinfo ; + + if(!rsMsgs->getDistantChatStatus(msg.chat_id.toDistantChatId(),dcpinfo)) { + std::cerr << "(EE) cannot get info for distant chat peer " << msg.chat_id.toDistantChatId() << std::endl; + continue ; + } + + RsIdentityDetails details; + if(!gxs_id_failed && mRsIdentity->getIdDetails(msg.incoming? dcpinfo.to_id: dcpinfo.own_id, details)) + { + author_id = details.mId.toStdString(); author_name = details.mNickname; } else @@ -278,7 +285,7 @@ void ChatHandler::tick() { ChatInfo info; info.is_broadcast = msg.chat_id.isBroadcast(); - info.is_gxs_id = msg.chat_id.isGxsId(); + info.is_distant_chat_id = msg.chat_id.isDistantChatId(); info.is_lobby = msg.chat_id.isLobbyId(); info.is_peer = msg.chat_id.isPeerId(); if(msg.chat_id.isLobbyId()) @@ -289,12 +296,15 @@ void ChatHandler::tick() info.remote_author_name = vit->name; } } - else if(msg.chat_id.isGxsId()) + else if(msg.chat_id.isDistantChatId()) { RsIdentityDetails details; - if(!gxs_id_failed && mRsIdentity->getIdDetails(msg.chat_id.toGxsId(), details)) + DistantChatPeerInfo dcpinfo ; + + if(!gxs_id_failed && rsMsgs->getDistantChatStatus(msg.chat_id.toDistantChatId(),dcpinfo) + && mRsIdentity->getIdDetails(msg.incoming? dcpinfo.to_id: dcpinfo.own_id, details)) { - info.remote_author_id = msg.chat_id.toGxsId().toStdString(); + info.remote_author_id = details.mId.toStdString(); info.remote_author_name = details.mNickname; } else diff --git a/libresapi/src/api/ChatHandler.h b/libresapi/src/api/ChatHandler.h index 257192d29..bb15cf2f1 100644 --- a/libresapi/src/api/ChatHandler.h +++ b/libresapi/src/api/ChatHandler.h @@ -84,7 +84,7 @@ public: class ChatInfo{ public: bool is_broadcast; - bool is_gxs_id; + bool is_distant_chat_id; bool is_lobby; bool is_peer; std::string remote_author_id; diff --git a/libretroshare/src/chat/distantchat.cc b/libretroshare/src/chat/distantchat.cc index 8b1dbdabc..e311b9233 100644 --- a/libretroshare/src/chat/distantchat.cc +++ b/libretroshare/src/chat/distantchat.cc @@ -32,6 +32,7 @@ #include "util/rsaes.h" #include "util/rsmemory.h" +#include "util/rsprint.h" #include @@ -54,113 +55,52 @@ static const uint32_t RS_DISTANT_CHAT_DH_STATUS_UNINITIALIZED = 0x0000 ; static const uint32_t RS_DISTANT_CHAT_DH_STATUS_HALF_KEY_DONE = 0x0001 ; static const uint32_t RS_DISTANT_CHAT_DH_STATUS_KEY_AVAILABLE = 0x0002 ; -void DistantChatService::connectToTurtleRouter(p3turtle *tr) +static const uint32_t DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID = 0xa0001 ; + +typedef RsGxsTunnelService::RsGxsTunnelId RsGxsTunnelId; + +void DistantChatService::connectToGxsTunnelService(RsGxsTunnelService *tr) { - mTurtle = tr ; - tr->registerTunnelService(this) ; + mGxsTunnels = tr ; + tr->registerClientService(DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID,this) ; } -void DistantChatService::flush() -{ - // Flush items that could not be sent, probably because of a Mutex protected zone. - // - while(!pendingDistantChatItems.empty()) - { - sendTurtleData( pendingDistantChatItems.front() ) ; - pendingDistantChatItems.pop_front() ; - } - - // TODO: also sweep GXS id map and disable any ID with no virtual peer id in the list. - - RS_STACK_MUTEX(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - time_t now = time(NULL) ; - - for(std::map::iterator it(_distant_chat_contacts.begin());it!=_distant_chat_contacts.end();++it) - { - if(it->second.last_contact+20+DISTANT_CHAT_KEEP_ALIVE_TIMEOUT < now && it->second.status == RS_DISTANT_CHAT_STATUS_CAN_TALK) - { - std::cerr << "(II) DistantChatService:: connexion interrupted with peer." << std::endl; - it->second.status = RS_DISTANT_CHAT_STATUS_TUNNEL_DN ; - it->second.virtual_peer_id.clear() ; - - // Also reset turtle router monitoring so as to make the tunnel handling more responsive. If we don't do that, - // the TR will wait 60 secs for the tunnel to die, which causes a significant waiting time in the chat window. - - if(it->second.direction == RsTurtleGenericTunnelItem::DIRECTION_SERVER) - { - std::cerr << "(II) DistantChatService:: forcing new tunnel campain." << std::endl; - - mTurtle->forceReDiggTunnels( hashFromGxsId(it->first) ); - } - } - if(it->second.last_keep_alive_sent + DISTANT_CHAT_KEEP_ALIVE_TIMEOUT < now && it->second.status == RS_DISTANT_CHAT_STATUS_CAN_TALK) - { - RsChatStatusItem *cs = new RsChatStatusItem ; - - cs->status_string.clear() ; - cs->flags = RS_CHAT_FLAG_PRIVATE | RS_CHAT_FLAG_KEEP_ALIVE; - cs->PeerId(RsPeerId(it->first)) ; - - // we send off-mutex to avoid deadlock. - - pendingDistantChatItems.push_back(cs) ; - - it->second.last_keep_alive_sent = now ; -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "(II) DistantChatService:: Sending keep alive packet to gxs id " << it->first << std::endl; -#endif - } - } -} - -bool DistantChatService::handleRecvItem(RsChatItem *item) -{ - if(item == NULL) - return false ; - - switch(item->PacketSubType()) - { - case RS_PKT_SUBTYPE_DISTANT_CHAT_DH_PUBLIC_KEY: handleRecvDHPublicKey(dynamic_cast(item)) ; break ; - return true ; - - case RS_PKT_SUBTYPE_CHAT_STATUS: - { - // Keep alive packets should not be forwarded to the GUI. It's just for keeping the tunnel up. - - RsChatStatusItem *csi = dynamic_cast(item) ; - if(csi != NULL && csi->flags & RS_CHAT_FLAG_KEEP_ALIVE) - return true ; - } - - default: - return false ; - } - - return false ; -} bool DistantChatService::handleOutgoingItem(RsChatItem *item) { + RsGxsTunnelId tunnel_id ; + { RS_STACK_MUTEX(mDistantChatMtx) ; - std::map::const_iterator it=_distant_chat_contacts.find(RsGxsId(item->PeerId())); + std::map::const_iterator it=mDistantChatContacts.find(DistantChatPeerId(item->PeerId())); - if(it == _distant_chat_contacts.end()) + if(it == mDistantChatContacts.end()) return false ; } #ifdef CHAT_DEBUG std::cerr << "p3ChatService::handleOutgoingItem(): sending to " << item->PeerId() << ": interpreted as a distant chat virtual peer id." << std::endl; #endif - sendTurtleData(item) ; + + uint32_t size = item->serial_size() ; + RsTemporaryMemory mem(size) ; + + if(!item->serialise(mem,size)) + { + std::cerr << "(EE) serialisation error. Something's really wrong!" << std::endl; + return false; + } + + std::cerr << " sending: " << RsUtil::BinToHex(mem,size) << std::endl; + + mGxsTunnels->sendData( RsGxsTunnelId(item->PeerId()),DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID,mem,size); return true; } void DistantChatService::handleRecvChatStatusItem(RsChatStatusItem *cs) { if(cs->flags & RS_CHAT_FLAG_CLOSING_DISTANT_CONNECTION) - markDistantChatAsClosed(RsGxsId(cs->PeerId())) ; + markDistantChatAsClosed(DistantChatPeerId(cs->PeerId())) ; // nothing more to do, because the decryption routing will update the last_contact time when decrypting. @@ -168,940 +108,139 @@ void DistantChatService::handleRecvChatStatusItem(RsChatStatusItem *cs) std::cerr << "DistantChatService::handleRecvChatStatusItem(): received keep alive packet for inactive chat! peerId=" << cs->PeerId() << std::endl; } -bool DistantChatService::handleTunnelRequest(const RsFileHash& hash,const RsPeerId& /*peer_id*/) +void DistantChatService::notifyTunnelStatus(const RsGxsTunnelService::RsGxsTunnelId &tunnel_id, uint32_t tunnel_status) { - RsStackMutex stack(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - // look into owned GXS ids, and see if the hash corresponds to the expected hash - // - std::list own_id_list ; - rsIdentity->getOwnIds(own_id_list) ; - - // re-computing the hash from the GXS id allows to dynamically change the hash. That will allow - // the use of a contact passphrase, if needed. - - for(std::list::const_iterator it(own_id_list.begin());it!=own_id_list.end();++it) - if(hashFromGxsId(*it) == hash) - { -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "DistantChatService::handleTunnelRequest: received tunnel request for hash " << hash << std::endl; - std::cerr << " answering true!" << std::endl; -#endif - return true ; - } - - return false ; -} - -void DistantChatService::addVirtualPeer(const TurtleFileHash& hash,const TurtleVirtualPeerId& virtual_peer_id,RsTurtleGenericTunnelItem::Direction dir) -{ -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "DistantChatService:: received new virtual peer " << virtual_peer_id << " for hash " << hash << ", dir=" << dir << std::endl; -#endif - RsGxsId own_gxs_id ; - + std::cerr << "DistantChatService::notifyTunnelStatus(): got notification " << std::hex << tunnel_status << std::dec << " for tunnel " << tunnel_id << std::endl; + + switch(tunnel_status) { - RS_STACK_MUTEX(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - DistantChatDHInfo& dhinfo( _distant_chat_virtual_peer_ids[virtual_peer_id] ) ; - dhinfo.gxs_id.clear() ; - - if(dhinfo.dh != NULL) - DH_free(dhinfo.dh) ; - - dhinfo.dh = NULL ; - dhinfo.direction = dir ; - dhinfo.hash = hash ; - dhinfo.status = RS_DISTANT_CHAT_DH_STATUS_UNINITIALIZED ; - - // if( _distant_chat_virtual_peer_ids.find(virtual_peer_id) != _distant_chat_virtual_peer_ids.end()) - // { - // std::cerr << " Tunnel already registered for " << hash << " and virtual peer " << virtual_peer_id << ". Doing nothing." << std::endl; - // return ; - // } - - if(dir == RsTurtleGenericTunnelItem::DIRECTION_CLIENT) - { - // check that a tunnel is not already working for this hash. If so, give up. - - own_gxs_id = gxsIdFromHash(hash) ; - - } - else // client side - { - RsGxsId to_gxs_id = gxsIdFromHash(hash) ; - std::map::const_iterator it = _distant_chat_contacts.find(to_gxs_id) ; - - if(it == _distant_chat_contacts.end()) - { - std::cerr << "(EE) no pre-registered peer for hash " << hash << " on client side. This is a bug." << std::endl; - return ; - } - - if(it->second.status == RS_DISTANT_CHAT_STATUS_CAN_TALK) - { - std::cerr << " virtual peer is for a distant chat session that is already openned and alive. Giving it up." << std::endl; - return ; - } - - own_gxs_id = it->second.own_gxs_id ; - } - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Creating new virtual peer ID entry and empty DH session key." << std::endl; -#endif - - } - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Adding virtual peer " << virtual_peer_id << " for chat hash " << hash << std::endl; -#endif - - // Start a new DH session for this tunnel - RS_STACK_MUTEX(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - locked_restartDHSession(virtual_peer_id,own_gxs_id) ; -} - -void DistantChatService::locked_restartDHSession(const RsPeerId& virtual_peer_id,const RsGxsId& own_gxs_id) -{ -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "Starting new DH session." << std::endl; -#endif - DistantChatDHInfo& dhinfo = _distant_chat_virtual_peer_ids[virtual_peer_id] ; - - dhinfo.status = RS_DISTANT_CHAT_DH_STATUS_UNINITIALIZED ; - - if(!locked_initDHSessionKey(dhinfo.dh)) - { - std::cerr << " (EE) Cannot start DH session. Something went wrong." << std::endl; - return ; - } - dhinfo.status = RS_DISTANT_CHAT_DH_STATUS_HALF_KEY_DONE ; - - if(!locked_sendDHPublicKey(dhinfo.dh,own_gxs_id,virtual_peer_id)) - std::cerr << " (EE) Cannot send DH public key. Something went wrong." << std::endl; -} - -void DistantChatService::removeVirtualPeer(const TurtleFileHash& hash,const TurtleVirtualPeerId& virtual_peer_id) -{ - bool tunnel_dn = false ; - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "Distant chat: Removing virtual peer " << virtual_peer_id << " for hash " << hash << std::endl; -#else - /* remove unused parameter warnings */ - (void) hash; -#endif - { - RsStackMutex stack(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - RsGxsId gxs_id ; - std::map::iterator it = _distant_chat_virtual_peer_ids.find(virtual_peer_id) ; - - if(it == _distant_chat_virtual_peer_ids.end()) - { - std::cerr << "(EE) Cannot remove virtual peer " << virtual_peer_id << ": not found in chat list!!" << std::endl; - return ; - } - - gxs_id = it->second.gxs_id ; - - if(it->second.dh != NULL) - DH_free(it->second.dh) ; - _distant_chat_virtual_peer_ids.erase(it) ; - - std::map::iterator it2 = _distant_chat_contacts.find(gxs_id) ; - - if(it2 == _distant_chat_contacts.end()) - { - std::cerr << "(EE) Cannot find GXS id " << gxs_id << " in contact list. Weird." << std::endl; - return ; - } - if(it2->second.virtual_peer_id == virtual_peer_id) - { - it2->second.status = RS_DISTANT_CHAT_STATUS_TUNNEL_DN ; - it2->second.virtual_peer_id.clear() ; - tunnel_dn = true ; - } - } - - if(tunnel_dn) - { - RsServer::notify()->notifyChatStatus(ChatId(RsGxsId(virtual_peer_id)),"tunnel is down...") ; - RsServer::notify()->notifyPeerStatusChanged(virtual_peer_id.toStdString(),RS_STATUS_OFFLINE) ; + default: + case RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_UNKNOWN: std::cerr << "(EE) don't know how to handle RS_GXS_TUNNEL_STATUS_UNKNOWN !" << std::endl; + break ; + + case RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_CAN_TALK: RsServer::notify()->notifyChatStatus(ChatId(DistantChatPeerId(tunnel_id)),"Tunnel is secured. You can talk!") ; + RsServer::notify()->notifyPeerStatusChanged(tunnel_id.toStdString(),RS_STATUS_ONLINE) ; + break ; + + case RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_TUNNEL_DN: RsServer::notify()->notifyChatStatus(ChatId(DistantChatPeerId(tunnel_id)),"tunnel is down...") ; + RsServer::notify()->notifyPeerStatusChanged(tunnel_id.toStdString(),RS_STATUS_OFFLINE) ; + break ; + + case RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED: RsServer::notify()->notifyChatStatus(ChatId(DistantChatPeerId(tunnel_id)),"tunnel is down...") ; + RsServer::notify()->notifyPeerStatusChanged(tunnel_id.toStdString(),RS_STATUS_OFFLINE) ; + break ; } } -#ifdef DEBUG_DISTANT_CHAT -static void printBinaryData(void *data,uint32_t size) +void DistantChatService::receiveData(const RsGxsTunnelService::RsGxsTunnelId &tunnel_id, unsigned char *data, uint32_t data_size) { - static const char outl[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' } ; - - uint32_t mx = std::min(50u,size) ; - - for(uint32_t j = 0; j < mx; j++) - { - std::cerr << outl[ ( ((uint8_t*)data)[j]>>4) ] ; - std::cerr << outl[ ((uint8_t*)data)[j] & 0xf ] ; - } - if(size > 50) - std::cerr << "..." ; -} -#endif - -void DistantChatService::receiveTurtleData(RsTurtleGenericTunnelItem *gitem,const RsFileHash& hash, - const RsPeerId& virtual_peer_id,RsTurtleGenericTunnelItem::Direction direction) -{ -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "DistantChatService::receiveTurtleData(): Received turtle data. " << std::endl; - std::cerr << " hash = " << hash << std::endl; - std::cerr << " vpid = " << virtual_peer_id << std::endl; - std::cerr << " acting as = " << direction << std::endl; -#else - /* remove unused parameter warnings */ - (void) direction; -#endif - - RsTurtleGenericDataItem *item = dynamic_cast(gitem) ; - - if(item == NULL) + std::cerr << "DistantChatService::receiveData(): got data of size " << data_size << " for tunnel " << tunnel_id << std::endl; + std::cerr << " received: " << RsUtil::BinToHex(data,data_size) << std::endl; + std::cerr << " deserialising..." << std::endl; + + RsItem *item = RsChatSerialiser().deserialise(data,&data_size) ; + + // always make the contact up to date. This is useful for server side, which doesn't know about the chat until it + // receives the first item. { - std::cerr << "(EE) item is not a data item. That is an error." << std::endl; - return ; + RS_STACK_MUTEX(mDistantChatMtx) ; + + RsGxsTunnelService::GxsTunnelInfo tinfo; + if(!mGxsTunnels->getTunnelInfo(tunnel_id,tinfo)) + return ; + + DistantChatContact& contact(mDistantChatContacts[DistantChatPeerId(tunnel_id)]) ; + + contact.to_id = tinfo.destination_gxs_id ; + contact.from_id = tinfo.source_gxs_id ; } - // Call the AES crypto module - // - the IV is the first 8 bytes of item->data_bytes - - if(item->data_size < 8) + + if(item != NULL) { - std::cerr << "(EE) item encrypted data stream is too small: size = " << item->data_size << std::endl; - return ; - } - if(*((uint64_t*)item->data_bytes) != 0) - { -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Item is encrypted." << std::endl; -#endif - - // if cannot decrypt, it means the key is wrong. We need to re-negociate a new key. - - handleEncryptedData((uint8_t*)item->data_bytes,item->data_size,hash,virtual_peer_id) ; + item->PeerId(RsPeerId(tunnel_id)) ; // just in case, but normally this is already done. + + handleIncomingItem(item) ; + RsServer::notify()->notifyListChange(NOTIFY_LIST_PRIVATE_INCOMING_CHAT, NOTIFY_TYPE_ADD); } else - { -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Item is not encrypted." << std::endl; -#endif - - // Now try deserialise the decrypted data to make an RsItem out of it. - // - RsItem *citem = RsChatSerialiser().deserialise(&((uint8_t*)item->data_bytes)[8],&item->data_size) ; - - if(citem == NULL) - { - std::cerr << "(EE) item could not be de-serialized. That is an error." << std::endl; - return ; - } - - // DH key items are sent even before we know who we speak to, so the virtual peer id is used in this - // case only. - - if(dynamic_cast(citem) != NULL) - { - citem->PeerId(virtual_peer_id) ; - handleIncomingItem(citem) ; - } - else - std::cerr << "(EE) Deserialiased item has unexpected type." << std::endl; - } + std::cerr << " (EE) item could not be deserialised!" << std::endl; } - -bool DistantChatService::handleEncryptedData(const uint8_t *data_bytes,uint32_t data_size,const TurtleFileHash& hash,const RsPeerId& virtual_peer_id) +void DistantChatService::markDistantChatAsClosed(const DistantChatPeerId& dcpid) { -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "handleEncryptedDataItem()" << std::endl; - std::cerr << " size = " << data_size << std::endl; - std::cerr << " data = " << (void*)data_bytes << std::endl; - std::cerr << " IV = " << std::hex << *(uint64_t*)data_bytes << std::dec << std::endl; - std::cerr << " data = " ; - - printBinaryData((uint8_t*)data_bytes,data_size) ; - std::cerr << std::endl; -#endif - - RsItem *citem = NULL; - - { - RS_STACK_MUTEX(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - std::map::iterator it = _distant_chat_virtual_peer_ids.find(virtual_peer_id) ; - - if(it == _distant_chat_virtual_peer_ids.end()) - { - std::cerr << "(EE) item is not coming out of a registered tunnel. Weird. hash=" << hash << ", peer id = " << virtual_peer_id << std::endl; - return true ; - } - - RsGxsId gxs_id = it->second.gxs_id ; - std::map::iterator it2 = _distant_chat_contacts.find(gxs_id) ; - - if(it2 == _distant_chat_contacts.end()) - { - std::cerr << "(EE) no GXS id data for ID=" << gxs_id << ". This is a bug." << std::endl; - return true ; - } - uint8_t aes_key[DISTANT_CHAT_AES_KEY_SIZE] ; - memcpy(aes_key,it2->second.aes_key,DISTANT_CHAT_AES_KEY_SIZE) ; - - uint32_t decrypted_size = RsAES::get_buffer_size(data_size-8); - uint8_t *decrypted_data = new uint8_t[decrypted_size]; -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Using IV: " << std::hex << *(uint64_t*)data_bytes << std::dec << std::endl; - std::cerr << " Decrypted buffer size: " << decrypted_size << std::endl; - std::cerr << " key : " ; printBinaryData(aes_key,16) ; std::cerr << std::endl; - std::cerr << " data : " ; printBinaryData((uint8_t*)data_bytes,data_size) ; std::cerr << std::endl; -#endif - - if(!RsAES::aes_decrypt_8_16((uint8_t*)data_bytes+8,data_size-8,aes_key,(uint8_t*)data_bytes,decrypted_data,decrypted_size)) - { - std::cerr << "(EE) packet decryption failed." << std::endl; - std::cerr << "(EE) resetting new DH session." << std::endl; - - delete[] decrypted_data ; - - locked_restartDHSession(virtual_peer_id,it2->second.own_gxs_id) ; - - return false ; - } - it2->second.status = RS_DISTANT_CHAT_STATUS_CAN_TALK ; - it2->second.last_contact = time(NULL) ; - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "(II) Decrypted data: size=" << decrypted_size << std::endl; -#endif - - // Now try deserialise the decrypted data to make an RsItem out of it. - // - citem = RsChatSerialiser().deserialise(decrypted_data,&decrypted_size) ; - delete[] decrypted_data ; - - if(citem == NULL) - { - std::cerr << "(EE) item could not be de-serialized. That is an error." << std::endl; - return true; - } - - // DH key items are sent even before we know who we speak to, so the virtual peer id is used in this - // case only. - - citem->PeerId(RsPeerId(gxs_id)) ; - } - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "(II) Setting peer id to " << citem->PeerId() << std::endl; -#endif - handleIncomingItem(citem) ; // Treats the item, and deletes it - - return true ; + mGxsTunnels->closeExistingTunnel(RsGxsTunnelService::RsGxsTunnelId(dcpid),DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID) ; + + RS_STACK_MUTEX(mDistantChatMtx) ; + + std::map::iterator it = mDistantChatContacts.find(dcpid) ; + + if(it != mDistantChatContacts.end()) + mDistantChatContacts.erase(it) ; } -void DistantChatService::handleRecvDHPublicKey(RsChatDHPublicKeyItem *item) +bool DistantChatService::initiateDistantChatConnexion(const RsGxsId& to_gxs_id, const RsGxsId& from_gxs_id, DistantChatPeerId& dcpid, uint32_t& error_code) { - if (!item) - { - return; - } + RsGxsTunnelId tunnel_id ; -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "DistantChatService: Received DH public key." << std::endl; - item->print(std::cerr, 0) ; -#endif + if(!mGxsTunnels->requestSecuredTunnel(to_gxs_id,from_gxs_id,tunnel_id,DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID,error_code)) + return false ; - // Look for the current state of the key agreement. + dcpid = DistantChatPeerId(tunnel_id) ; - TurtleVirtualPeerId vpid = item->PeerId() ; + DistantChatContact& dc_contact(mDistantChatContacts[dcpid]) ; - RS_STACK_MUTEX(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - std::map::iterator it = _distant_chat_virtual_peer_ids.find(vpid) ; - - if(it == _distant_chat_virtual_peer_ids.end()) - { - std::cerr << " (EE) Cannot find hash in distant chat peer list!!" << std::endl; - return ; - } - - // Now check the signature of the DH public key item. - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Checking signature. " << std::endl; -#endif - - uint32_t pubkey_size = BN_num_bytes(item->public_key) ; - RsTemporaryMemory data(pubkey_size) ; - BN_bn2bin(item->public_key, data) ; - - RsTlvSecurityKey signature_key ; - - // We need to get the key of the sender, but if the key is not cached, we - // need to get it first. So we let the system work for 2-3 seconds before - // giving up. Normally this would only cause a delay for uncached keys, - // which is rare. To force the system to cache the key, we first call for - // getIdDetails(). - // - RsIdentityDetails details ; - RsGxsId senders_id( item->signature.keyId ) ; - - for(int i=0;i<6;++i) - if(!mGixs->getKey(senders_id,signature_key) || signature_key.keyData.bin_data == NULL) - { -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Cannot get key. Waiting for caching. try " << i << "/6" << std::endl; -#endif - usleep(500 * 1000) ; // sleep for 500 msec. - } - else - break ; - - if(signature_key.keyData.bin_data == NULL) - { - std::cerr << " (EE) Key unknown for checking signature from " << senders_id << ", can't verify signature. Using key provided in DH packet (without adding to the keyring)." << std::endl; - - // check GXS key for defects. - - if(!GxsSecurity::checkPublicKey(item->gxs_key)) - { - std::cerr << "(SS) Security error in distant chat DH handshake: supplied key " << item->gxs_key.keyId << " is inconsistent. Refusing chat!" << std::endl; - return ; - } - if(item->gxs_key.keyId != item->signature.keyId) - { - std::cerr << "(SS) Security error in distant chat DH handshake: supplied key " << item->gxs_key.keyId << " is not the same than the item's signature key " << item->signature.keyId << ". Refusing chat!" << std::endl; - return ; - } - - signature_key = item->gxs_key ; - } - - if(!GxsSecurity::validateSignature((char*)(uint8_t*)data,pubkey_size,signature_key,item->signature)) - { - std::cerr << "(SS) Signature was verified and it doesn't check! This is a security issue!" << std::endl; - return ; - } - mGixs->timeStampKey(item->signature.keyId) ; - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Signature checks! Sender's ID = " << senders_id << std::endl; - std::cerr << " Computing AES key" << std::endl; -#endif - - if(it->second.dh == NULL) - { - std::cerr << " (EE) no DH information for that peer. This is an error." << std::endl; - return ; - } - if(it->second.status == RS_DISTANT_CHAT_DH_STATUS_KEY_AVAILABLE) - { -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " DH Session already set for this tunnel. Re-initing a new session!" << std::endl; -#endif - - locked_restartDHSession(vpid,_distant_chat_contacts[senders_id].own_gxs_id) ; - } - - // gets current key params. By default, should contain all null pointers. - // - it->second.gxs_id = senders_id ; - - // Looks for the DH params. If not there yet, create them. - // - int size = DH_size(it->second.dh) ; - unsigned char *key_buff = new unsigned char[size] ; - - if(size != DH_compute_key(key_buff,item->public_key,it->second.dh)) - { - std::cerr << " (EE) DH computation failed. Probably a bug. Error code=" << ERR_get_error() << std::endl; - return ; - } - it->second.status = RS_DISTANT_CHAT_DH_STATUS_KEY_AVAILABLE ; - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " DH key computation successed. New key in place." << std::endl; -#endif - DistantChatPeerInfo& pinfo(_distant_chat_contacts[senders_id]) ; - - // Now hash the key buffer into a 16 bytes key. - - assert(DISTANT_CHAT_AES_KEY_SIZE <= Sha1CheckSum::SIZE_IN_BYTES) ; - memcpy(pinfo.aes_key, RsDirUtil::sha1sum(key_buff,size).toByteArray(),DISTANT_CHAT_AES_KEY_SIZE) ; - delete[] key_buff ; - - pinfo.last_contact = time(NULL) ; - pinfo.last_keep_alive_sent = time(NULL) ; - pinfo.status = RS_DISTANT_CHAT_STATUS_CAN_TALK ; - pinfo.virtual_peer_id = vpid ; - pinfo.direction = it->second.direction ; - - if(pinfo.direction == RsTurtleGenericTunnelItem::DIRECTION_CLIENT) - pinfo.own_gxs_id = gxsIdFromHash(it->second.hash) ; - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " DH key computed. Tunnel is now secured!" << std::endl; - std::cerr << " Key computed: " ; printBinaryData(pinfo.aes_key,16) ; std::cerr << std::endl; - std::cerr << " Sending a ACK packet." << std::endl; -#endif - - // then we send an ACK packet to notify that the tunnel works. That's useful - // because it makes the peer at the other end of the tunnel know that all - // intermediate peer in the tunnel are able to transmit the data. - // However, it is not possible here to call sendTurtleData(), without dead-locking - // the turtle router, so we store the item is a list of items to be sent. - - RsChatStatusItem *cs = new RsChatStatusItem ; - - cs->status_string = "Tunnel is secured with PFS session. ACK received. You can talk!" ; - cs->flags = RS_CHAT_FLAG_PRIVATE | RS_CHAT_FLAG_ACK_DISTANT_CONNECTION; - cs->PeerId(RsPeerId(senders_id)); - - pendingDistantChatItems.push_back(cs) ; - - RsServer::notify()->notifyListChange(NOTIFY_LIST_PRIVATE_INCOMING_CHAT, NOTIFY_TYPE_ADD); -} - -bool DistantChatService::locked_sendDHPublicKey(const DH *dh,const RsGxsId& own_gxs_id,const RsPeerId& virtual_peer_id) -{ - if(dh == NULL) - { - std::cerr << " (EE) DH struct is not initialised! Error." << std::endl; - return false ; - } - - RsChatDHPublicKeyItem *dhitem = new RsChatDHPublicKeyItem ; - dhitem->public_key = BN_dup(dh->pub_key) ; - - // we should also sign the data and check the signature on the other end. - // - RsTlvKeySignature signature ; - RsTlvSecurityKey signature_key ; - RsTlvSecurityKey signature_key_public ; - - uint32_t error_status ; - - uint32_t size = BN_num_bytes(dhitem->public_key) ; - RsTemporaryMemory data(size) ; - BN_bn2bin(dhitem->public_key, data) ; - - if(!mGixs->signData(data,size,own_gxs_id,signature,error_status)) - { - switch(error_status) - { - case RsGixs::RS_GIXS_ERROR_KEY_NOT_AVAILABLE: std::cerr << "(EE) Key is not available. Cannot sign." << std::endl; - break ; - default: std::cerr << "(EE) Unknown error when signing" << std::endl; - break ; - } - delete(dhitem); - return false; - } - - if(!mGixs->getKey(own_gxs_id,signature_key_public)) - { - std::cerr << " (EE) Could not retrieve own public key for ID = " << own_gxs_id << ". Giging up sending DH session params." << std::endl; - return false ; - } - - - assert(!(signature_key_public.keyFlags & RSTLV_KEY_TYPE_FULL)) ; - - dhitem->signature = signature ; - dhitem->gxs_key = signature_key_public ; - dhitem->PeerId(RsPeerId(virtual_peer_id)) ; // special case for DH items - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Pushing DH session key item to pending distant messages..." << std::endl; - dhitem->print(std::cerr, 2) ; - std::cerr << std::endl; -#endif - pendingDistantChatItems.push_back(dhitem) ; // sent off-mutex to avoid deadlocking. - - return true ; -} - -bool DistantChatService::locked_initDHSessionKey(DH *& dh) -{ - static const std::string dh_prime_2048_hex = "B3B86A844550486C7EA459FA468D3A8EFD71139593FE1C658BBEFA9B2FC0AD2628242C2CDC2F91F5B220ED29AAC271192A7374DFA28CDDCA70252F342D0821273940344A7A6A3CB70C7897A39864309F6CAC5C7EA18020EF882693CA2C12BB211B7BA8367D5A7C7252A5B5E840C9E8F081469EBA0B98BCC3F593A4D9C4D5DF539362084F1B9581316C1F80FDAD452FD56DBC6B8ED0775F596F7BB22A3FE2B4753764221528D33DB4140DE58083DB660E3E105123FC963BFF108AC3A268B7380FFA72005A1515C371287C5706FFA6062C9AC73A9B1A6AC842C2764CDACFC85556607E86611FDF486C222E4896CDF6908F239E177ACC641FCBFF72A758D1C10CBB" ; - - if(dh != NULL) - { - DH_free(dh) ; - dh = NULL ; - } - - dh = DH_new() ; - - if(!dh) - { - std::cerr << " (EE) DH_new() failed." << std::endl; - return false ; - } - - BN_hex2bn(&dh->p,dh_prime_2048_hex.c_str()) ; - BN_hex2bn(&dh->g,"5") ; - - int codes = 0 ; - - if(!DH_check(dh, &codes) || codes != 0) - { - std::cerr << " (EE) DH check failed!" << std::endl; - return false ; - } - - if(!DH_generate_key(dh)) - { - std::cerr << " (EE) DH generate_key() failed! Error code = " << ERR_get_error() << std::endl; - return false ; - } -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " (II) DH Session key inited." << std::endl; -#endif - return true ; -} - -void DistantChatService::sendTurtleData(RsChatItem *item) -{ -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "DistantChatService::sendTurtleData(): try sending item " << (void*)item << " to peer " << item->PeerId() << std::endl; -#endif - - if(dynamic_cast(item) != NULL) - { - // make a TurtleGenericData item out of it, and send it in clear. - // - RsTurtleGenericDataItem *gitem = new RsTurtleGenericDataItem ; - - uint32_t rssize = item->serial_size() ; - - gitem->data_size = rssize + 8 ; - gitem->data_bytes = malloc(rssize+8) ; - - memset(gitem->data_bytes,0,8) ; - - if(!item->serialise(&((uint8_t*)gitem->data_bytes)[8],rssize)) - { - std::cerr << "(EE) Could not serialise item!!!" << std::endl; - delete gitem ; - delete item ; - return ; - } - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " DistantChatService::sendTurtleData(): Sending clear data to virtual peer: " << item->PeerId() << std::endl; - std::cerr << " gitem->data_size = " << gitem->data_size << std::endl; - std::cerr << " data = " ; - printBinaryData(gitem->data_bytes,gitem->data_size) ; - std::cerr << std::endl; -#endif - mTurtle->sendTurtleData(item->PeerId(),gitem) ; - } - else - { - uint32_t rssize = item->serial_size(); - RsTemporaryMemory buff(rssize) ; - - if(!item->serialise(buff,rssize)) - { - std::cerr << "(EE) DistantChatService::sendTurtleData(): Could not serialise item!" << std::endl; - delete item ; - return ; - } - - sendEncryptedTurtleData(buff,rssize,RsGxsId(item->PeerId())) ; - } - delete item ; -} - -void DistantChatService::sendEncryptedTurtleData(const uint8_t *buff,uint32_t rssize,const RsGxsId& gxs_id) -{ - uint8_t aes_key[DISTANT_CHAT_AES_KEY_SIZE] ; - uint64_t IV = 0; - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "Sending encrypted data to virtual gxs id " << gxs_id << std::endl; -#endif - RsStackMutex stack(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - std::map::iterator it = _distant_chat_contacts.find(gxs_id) ; - - if(it == _distant_chat_contacts.end()) - { - std::cerr << "(EE) Cannot find contact key info for ID " << gxs_id << ". Cannot send message!" << std::endl; - return ; - } - if(it->second.status != RS_DISTANT_CHAT_STATUS_CAN_TALK) - { - std::cerr << "(EE) Cannot talk to " << gxs_id << ". Tunnel status is: " << it->second.status << std::endl; - return ; - } - - memcpy(aes_key,it->second.aes_key,DISTANT_CHAT_AES_KEY_SIZE) ; - RsPeerId virtual_peer_id = it->second.virtual_peer_id ; - - while(IV == 0) IV = RSRandom::random_u64() ; // make a random 8 bytes IV, that is not 0 - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "DistantChatService::sendTurtleData(): tunnel found. Encrypting data." << std::endl; -#endif - - // Now encrypt this data using AES. - // - uint8_t *encrypted_data = new uint8_t[RsAES::get_buffer_size(rssize)]; - uint32_t encrypted_size = RsAES::get_buffer_size(rssize); - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Using IV: " << std::hex << IV << std::dec << std::endl; - std::cerr << " Using Key: " ; printBinaryData(aes_key,16) ; std::cerr << std::endl; -#endif - if(!RsAES::aes_crypt_8_16(buff,rssize,aes_key,(uint8_t*)&IV,encrypted_data,encrypted_size)) - { - std::cerr << "(EE) packet encryption failed." << std::endl; - delete[] encrypted_data ; - return ; - } - - // make a TurtleGenericData item out of it: - // - RsTurtleGenericDataItem *gitem = new RsTurtleGenericDataItem ; - - gitem->data_size = encrypted_size + 8 ; - gitem->data_bytes = malloc(gitem->data_size) ; - - memcpy(gitem->data_bytes ,&IV,8) ; - memcpy(& (((uint8_t*)gitem->data_bytes)[8]),encrypted_data,encrypted_size) ; - - delete[] encrypted_data ; - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "DistantChatService::sendTurtleData(): Sending encrypted data to virtual peer: " << virtual_peer_id << std::endl; - std::cerr << " gitem->data_size = " << gitem->data_size << std::endl; - std::cerr << " data = " ; - - printBinaryData(gitem->data_bytes,gitem->data_size) ; - std::cerr << std::endl; -#endif - - mTurtle->sendTurtleData(virtual_peer_id,gitem) ; -} - -bool DistantChatService::initiateDistantChatConnexion(const RsGxsId& to_gxs_id,const RsGxsId& from_gxs_id,uint32_t& error_code) -{ - // should be a parameter. - - std::list lst ; - mGixs->getOwnIds(lst) ; - - bool found = false ; - for(std::list::const_iterator it = lst.begin();it!=lst.end();++it) - if(*it == from_gxs_id) - { - found=true; - break ; - } - - if(!found) - { - std::cerr << " (EE) Cannot start distant chat, since GXS id " << from_gxs_id << " is not available." << std::endl; - return false ; - } - RsGxsId own_gxs_id = from_gxs_id ; - - startClientDistantChatConnection(to_gxs_id,own_gxs_id) ; + dc_contact.from_id = from_gxs_id ; + dc_contact.to_id = to_gxs_id ; error_code = RS_DISTANT_CHAT_ERROR_NO_ERROR ; - return true ; -} - -void DistantChatService::startClientDistantChatConnection(const RsGxsId& to_gxs_id,const RsGxsId& from_gxs_id) -{ - { - RsStackMutex stack(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - if(_distant_chat_contacts.find(to_gxs_id) != _distant_chat_contacts.end()) - { - std::cerr << "DistantChatService:: asking distant chat connexion to a peer who's already in a chat. Ignoring." << std::endl; - return ; - } - } - DistantChatPeerInfo info ; - - time_t now = time(NULL) ; - - info.last_contact = now ; - info.last_keep_alive_sent = now ; - info.status = RS_DISTANT_CHAT_STATUS_TUNNEL_DN ; - info.own_gxs_id = from_gxs_id ; - info.direction = RsTurtleGenericTunnelItem::DIRECTION_SERVER ; - info.virtual_peer_id.clear(); - - memset(info.aes_key,0,DISTANT_CHAT_AES_KEY_SIZE) ; - - { - RsStackMutex stack(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - _distant_chat_contacts[to_gxs_id] = info ; - } - - // Now ask the turtle router to manage a tunnel for that hash. - - RsFileHash hash = hashFromGxsId(to_gxs_id) ; -#ifdef DEBUG_DISTANT_CHAT - std::cerr << "Starting distant chat to " << to_gxs_id << ", hash = " << hash << ", from " << from_gxs_id << std::endl; - std::cerr << "Asking turtle router to monitor tunnels for hash " << hash << std::endl; -#endif - - mTurtle->monitorTunnels(hash,this,false) ; - - // spawn a status item so as to open the chat window. + // Make a self message to raise the chat window + RsChatMsgItem *item = new RsChatMsgItem; item->message = "[Starting distant chat. Please wait for secure tunnel to be established]" ; item->chatFlags = RS_CHAT_FLAG_PRIVATE ; - item->PeerId(RsPeerId(to_gxs_id)) ; + item->PeerId(RsPeerId(tunnel_id)) ; handleRecvChatMsgItem(item) ; - -} -TurtleFileHash DistantChatService::hashFromGxsId(const RsGxsId& destination) -{ - // This is in prevision for the "secured GXS tunnel" service, which will need a service ID to register, - // just like GRouter does. - - static const uint32_t client = RS_SERVICE_TYPE_DISTANT_CHAT ; - - assert( destination.SIZE_IN_BYTES == 16) ; - assert(Sha1CheckSum::SIZE_IN_BYTES == 20) ; - - uint8_t bytes[20] ; - memcpy(bytes,destination.toByteArray(),16) ; - bytes[16] = 0 ; - bytes[17] = 0 ; - bytes[18] = (client >> 8) & 0xff ; - bytes[19] = client & 0xff ; - - // We could rehash this, with a secret key to get a HMAC. That would allow to publish secret distant chat - // passphrases. I'll do this later if needed. - - return Sha1CheckSum(bytes) ; -} -RsGxsId DistantChatService::gxsIdFromHash(const TurtleFileHash& sum) -{ - assert( RsGxsId::SIZE_IN_BYTES == 16) ; - assert(Sha1CheckSum::SIZE_IN_BYTES == 20) ; - - uint32_t client_id = sum.toByteArray()[19] + (sum.toByteArray()[18] << 8) ; - - if(client_id != RS_SERVICE_TYPE_DISTANT_CHAT) - std::cerr << "WARNING: DistantChatService::gxsIdFromHash(). Hash is not a distant file hash. This should not happen." << std::endl; - - return RsGxsId(sum.toByteArray());// takes the first 16 bytes -} -bool DistantChatService::getDistantChatStatus(const RsGxsId& gxs_id,uint32_t& status, RsGxsId *from_gxs_id) -{ - RsStackMutex stack(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - - std::map::const_iterator it = _distant_chat_contacts.find(gxs_id) ; - - if(it != _distant_chat_contacts.end()) - { - status = it->second.status ; - - if(from_gxs_id != NULL) - *from_gxs_id = it->second.own_gxs_id ; - - return true ; - } - - status = RS_DISTANT_CHAT_STATUS_UNKNOWN ; - - return false ; -} - -bool DistantChatService::closeDistantChatConnexion(const RsGxsId& gxs_id) -{ - // two cases: - // - client needs to stop asking for tunnels => remove the hash from the list of tunnelled files - // - server needs to only close the window and let the tunnel die. But the window should only open - // if a message arrives. - - { - RsStackMutex stack(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - std::map::const_iterator it = _distant_chat_contacts.find(gxs_id) ; - - if(it == _distant_chat_contacts.end()) - { - std::cerr << "(EE) Cannot close distant chat connection. No connection openned for gxs id " << gxs_id << std::endl; - - // We don't know if we are server or client side, but mTurtle will not complain if the hash doesn't exist. - - mTurtle->stopMonitoringTunnels( hashFromGxsId(gxs_id) ); - - return false ; - } - if(it->second.direction == RsTurtleGenericTunnelItem::DIRECTION_CLIENT) // nothing more to do for server side. - return true ; - } - - // send a status item saying that we're closing the connection -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " Sending a ACK to close the tunnel since we're managing it. Peer id=." << gxs_id << std::endl; -#endif - - RsChatStatusItem *cs = new RsChatStatusItem ; - - cs->status_string = "" ; - cs->flags = RS_CHAT_FLAG_PRIVATE | RS_CHAT_FLAG_CLOSING_DISTANT_CONNECTION; - cs->PeerId(RsPeerId(gxs_id)); - - sendTurtleData(cs) ; // that needs to be done off-mutex and before we close the tunnel. - -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " This is client side. Stopping tunnel manageement for gxs_id " << gxs_id << std::endl; -#endif - mTurtle->stopMonitoringTunnels( hashFromGxsId(gxs_id) ); - { - RsStackMutex stack(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - std::map::iterator it = _distant_chat_contacts.find(gxs_id) ; - - if(it == _distant_chat_contacts.end()) // server side. Nothing to do. - { - std::cerr << "(EE) Cannot close chat associated to GXS id " << gxs_id << ": not found." << std::endl; - return false ; - } - - _distant_chat_contacts.erase(it) ; - - // DistantChatService::removeVirtualPeerId() will be called by the turtle service. - } + return true ; } -void DistantChatService::markDistantChatAsClosed(const RsGxsId& gxs_id) +bool DistantChatService::getDistantChatStatus(const DistantChatPeerId& tunnel_id, DistantChatPeerInfo& cinfo) { - RS_STACK_MUTEX(mDistantChatMtx); /********** STACK LOCKED MTX ******/ + RsStackMutex stack(mDistantChatMtx); /********** STACK LOCKED MTX ******/ - std::map::iterator it = _distant_chat_contacts.find(gxs_id) ; + RsGxsTunnelService::GxsTunnelInfo tinfo ; - if(it == _distant_chat_contacts.end()) - { - std::cerr << "(EE) Cannot mark distant chat connection as closed. No connection openned for gxs id " << gxs_id << ". Unexpected situation." << std::endl; - return ; - } + if(!mGxsTunnels->getTunnelInfo(RsGxsTunnelId(tunnel_id),tinfo)) + return false; - if(it->second.direction == RsTurtleGenericDataItem::DIRECTION_CLIENT) + cinfo.to_id = tinfo.destination_gxs_id; + cinfo.own_id = tinfo.source_gxs_id; + cinfo.peer_id = tunnel_id; + + switch(tinfo.tunnel_status) { -#ifdef DEBUG_DISTANT_CHAT - std::cerr << " This is server side. Marking distant chat as remotely closed for GXS id " << gxs_id << std::endl; -#endif - it->second.status = RS_DISTANT_CHAT_STATUS_REMOTELY_CLOSED ; + case RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_CAN_TALK : cinfo.status = RS_DISTANT_CHAT_STATUS_CAN_TALK; + break ; + case RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_TUNNEL_DN: cinfo.status = RS_DISTANT_CHAT_STATUS_TUNNEL_DN ; + break ; + case RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED: cinfo.status = RS_DISTANT_CHAT_STATUS_REMOTELY_CLOSED ; + break ; + default: + case RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_UNKNOWN: cinfo.status = RS_DISTANT_CHAT_STATUS_UNKNOWN; + break ; } + + return true ; } +bool DistantChatService::closeDistantChatConnexion(const DistantChatPeerId &tunnel_id) +{ + mGxsTunnels->closeExistingTunnel(RsGxsTunnelId(tunnel_id), DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID) ; + + // also remove contact. Or do we wait for the notification? + + return true ; +} + + diff --git a/libretroshare/src/chat/distantchat.h b/libretroshare/src/chat/distantchat.h index 04d3f2817..20435dc63 100644 --- a/libretroshare/src/chat/distantchat.h +++ b/libretroshare/src/chat/distantchat.h @@ -25,35 +25,38 @@ #pragma once -#include #include #include +#include class RsGixs ; static const uint32_t DISTANT_CHAT_AES_KEY_SIZE = 16 ; -class DistantChatService: public RsTurtleClientService +class DistantChatService: public RsGxsTunnelService::RsGxsTunnelClientService { public: - DistantChatService(RsGixs *pids) - : mGixs(pids), mDistantChatMtx("distant chat") + // So, public interface only uses DistandChatPeerId, but internally, this is converted into a RsGxsTunnelService::RsGxsTunnelId + + + DistantChatService() : mDistantChatMtx("distant chat") { - mTurtle = NULL ; + mGxsTunnels = NULL ; } - void flush() ; - - virtual void connectToTurtleRouter(p3turtle *) ; + // Overloaded methods from RsGxsTunnelClientService + + virtual void connectToGxsTunnelService(RsGxsTunnelService *tunnel_service) ; // Creates the invite if the public key of the distant peer is available. // Om success, stores the invite in the map above, so that we can respond to tunnel requests. // - bool initiateDistantChatConnexion(const RsGxsId& to_gxs_id,const RsGxsId &from_gxs_id, uint32_t &error_code) ; - bool closeDistantChatConnexion(const RsGxsId& pid) ; - virtual bool getDistantChatStatus(const RsGxsId &gxs_id,uint32_t &status, RsGxsId *from_gxs_id=NULL) ; + bool initiateDistantChatConnexion(const RsGxsId& to_gxs_id, const RsGxsId &from_gxs_id, DistantChatPeerId& dcpid, uint32_t &error_code) ; + bool closeDistantChatConnexion(const DistantChatPeerId &tunnel_id) ; + + virtual bool getDistantChatStatus(const DistantChatPeerId &tunnel_id, DistantChatPeerInfo& cinfo) ; - // derived in p3ChatService + // derived in p3ChatService, so as to pass down some info virtual void handleIncomingItem(RsItem *) = 0; virtual bool handleRecvChatMsgItem(RsChatMsgItem *ci)=0 ; @@ -62,78 +65,25 @@ public: void handleRecvChatStatusItem(RsChatStatusItem *cs) ; private: - class DistantChatPeerInfo + struct DistantChatContact { - public: - DistantChatPeerInfo() : last_contact(0), last_keep_alive_sent(0), status(0), direction(0) - { - memset(aes_key, 0, DISTANT_CHAT_AES_KEY_SIZE); - } - - time_t last_contact ; // used to keep track of working connexion - time_t last_keep_alive_sent ; // last time we sent a keep alive packet. - - unsigned char aes_key[DISTANT_CHAT_AES_KEY_SIZE] ; - - uint32_t status ; // info: do we have a tunnel ? - RsPeerId virtual_peer_id; // given by the turtle router. Identifies the tunnel. - RsGxsId own_gxs_id ; // gxs id we're using to talk. - RsTurtleGenericTunnelItem::Direction direction ; // specifiec wether we are client(managing the tunnel) or server. + RsGxsId from_id ; + RsGxsId to_id ; }; - - class DistantChatDHInfo - { - public: - DistantChatDHInfo() : dh(0), direction(0), status(0) {} - - DH *dh ; - RsGxsId gxs_id ; - RsTurtleGenericTunnelItem::Direction direction ; - uint32_t status ; - TurtleFileHash hash ; - }; - // This maps contains the current peers to talk to with distant chat. // - std::map _distant_chat_contacts ; // current peers we can talk to - std::map _distant_chat_virtual_peer_ids ; // current virtual peers. Used to figure out tunnels, etc. + std::map mDistantChatContacts ; // current peers we can talk to - // List of items to be sent asap. Used to store items that we cannot pass directly to - // sendTurtleData(), because of Mutex protection. + // Overloaded from RsGxsTunnelClientService + + virtual void notifyTunnelStatus(const RsGxsTunnelService::RsGxsTunnelId& tunnel_id,uint32_t tunnel_status) ; + virtual void receiveData(const RsGxsTunnelService::RsGxsTunnelId& id,unsigned char *data,uint32_t data_size) ; - std::list pendingDistantChatItems ; + // Utility functions. + + void markDistantChatAsClosed(const DistantChatPeerId& dcpid) ; - // Overloaded from RsTurtleClientService - - virtual bool handleTunnelRequest(const RsFileHash &hash,const RsPeerId& peer_id) ; - virtual void receiveTurtleData(RsTurtleGenericTunnelItem *item,const RsFileHash& hash,const RsPeerId& virtual_peer_id,RsTurtleGenericTunnelItem::Direction direction) ; - void addVirtualPeer(const TurtleFileHash&, const TurtleVirtualPeerId&,RsTurtleGenericTunnelItem::Direction dir) ; - void removeVirtualPeer(const TurtleFileHash&, const TurtleVirtualPeerId&) ; - void markDistantChatAsClosed(const RsGxsId &gxs_id) ; - void startClientDistantChatConnection(const RsGxsId &to_gxs_id,const RsGxsId& from_gxs_id) ; - void locked_restartDHSession(const RsPeerId &virtual_peer_id, const RsGxsId &own_gxs_id) ; - - //bool getHashFromVirtualPeerId(const TurtleVirtualPeerId& pid,RsFileHash& hash) ; - - static TurtleFileHash hashFromGxsId(const RsGxsId& destination) ; - static RsGxsId gxsIdFromHash(const TurtleFileHash& sum) ; - - void handleRecvDHPublicKey(RsChatDHPublicKeyItem *item) ; - bool locked_sendDHPublicKey(const DH *dh, const RsGxsId &own_gxs_id, const RsPeerId &virtual_peer_id) ; - bool locked_initDHSessionKey(DH *&dh); - DistantChatPeerId virtualPeerIdFromHash(const TurtleFileHash& hash ) ; // ... and to a hash for p3turtle - - - // Utility functions - - void sendTurtleData(RsChatItem *) ; - void sendEncryptedTurtleData(const uint8_t *buff,uint32_t rssize,const RsGxsId &gxs_id) ; - bool handleEncryptedData(const uint8_t *data_bytes,uint32_t data_size,const TurtleFileHash& hash,const RsPeerId& virtual_peer_id) ; - - static TurtleFileHash hashFromVirtualPeerId(const DistantChatPeerId& peerId) ; // converts IDs so that we can talk to RsPeerId from outside - - p3turtle *mTurtle ; - RsGixs *mGixs ; + RsGxsTunnelService *mGxsTunnels ; RsMutex mDistantChatMtx ; }; diff --git a/libretroshare/src/chat/distributedchat.cc b/libretroshare/src/chat/distributedchat.cc index 72a3f2a93..d0fdaab0b 100644 --- a/libretroshare/src/chat/distributedchat.cc +++ b/libretroshare/src/chat/distributedchat.cc @@ -1288,7 +1288,7 @@ void DistributedChatService::handleRecvLobbyInvite(RsChatLobbyInviteItem *item) else std::cerr << " : Match!" << std::endl; - std::cerr << " Addign new friend " << item->PeerId() << " to lobby." << std::endl; + std::cerr << " Adding new friend " << item->PeerId() << " to lobby." << std::endl; #endif it->second.participating_friends.insert(item->PeerId()) ; diff --git a/libretroshare/src/chat/p3chatservice.cc b/libretroshare/src/chat/p3chatservice.cc index 308cbc3b2..9aa02b0e0 100644 --- a/libretroshare/src/chat/p3chatservice.cc +++ b/libretroshare/src/chat/p3chatservice.cc @@ -54,7 +54,7 @@ static const uint32_t MAX_AVATAR_JPEG_SIZE = 32767; // Maximum size // Images are 96x96, which makes approx. 27000 bytes uncompressed. p3ChatService::p3ChatService(p3ServiceControl *sc,p3IdService *pids, p3LinkMgr *lm, p3HistoryMgr *historyMgr) - :DistantChatService(pids),DistributedChatService(getServiceInfo().mServiceType,sc,historyMgr,pids), mChatMtx("p3ChatService"),mServiceCtrl(sc), mLinkMgr(lm) , mHistoryMgr(historyMgr) + : DistributedChatService(getServiceInfo().mServiceType,sc,historyMgr,pids), mChatMtx("p3ChatService"),mServiceCtrl(sc), mLinkMgr(lm) , mHistoryMgr(historyMgr) { _serializer = new RsChatSerialiser() ; @@ -86,7 +86,7 @@ int p3ChatService::tick() receiveChatQueue(); DistributedChatService::flush() ; - DistantChatService::flush() ; + //DistantChatService::flush() ; return 0; } @@ -217,24 +217,25 @@ void p3ChatService::sendStatusString(const ChatId& id , const std::string& statu sendLobbyStatusString(id.toLobbyId(),status_string) ; else if(id.isBroadcast()) sendGroupChatStatusString(status_string); - else if(id.isPeerId() || id.isGxsId()) - { - RsChatStatusItem *cs = new RsChatStatusItem ; + else if(id.isPeerId() || id.isDistantChatId()) + { + RsChatStatusItem *cs = new RsChatStatusItem ; - cs->status_string = status_string ; - cs->flags = RS_CHAT_FLAG_PRIVATE ; - RsPeerId vpid; - if(id.isGxsId()) - vpid = RsPeerId(id.toGxsId()); - else - vpid = id.toPeerId(); - cs->PeerId(vpid); + cs->status_string = status_string ; + cs->flags = RS_CHAT_FLAG_PRIVATE ; + RsPeerId vpid; + if(id.isDistantChatId()) + vpid = RsPeerId(id.toDistantChatId()); + else + vpid = id.toPeerId(); + + cs->PeerId(vpid); #ifdef CHAT_DEBUG - std::cerr << "sending chat status packet:" << std::endl ; - cs->print(std::cerr) ; + std::cerr << "sending chat status packet:" << std::endl ; + cs->print(std::cerr) ; #endif - sendChatItem(cs); + sendChatItem(cs); } else { @@ -284,12 +285,14 @@ void p3ChatService::checkSizeAndSendMessage(RsChatMsgItem *msg) bool p3ChatService::isOnline(const RsPeerId& pid) { - // check if the id is a tunnel id or a peer id. - uint32_t status ; - if(getDistantChatStatus(RsGxsId(pid),status)) - return status == RS_DISTANT_CHAT_STATUS_CAN_TALK ; + // check if the id is a tunnel id or a peer id. + + DistantChatPeerInfo dcpinfo; + + if(getDistantChatStatus(DistantChatPeerId(pid),dcpinfo)) + return dcpinfo.status == RS_DISTANT_CHAT_STATUS_CAN_TALK ; else - return mServiceCtrl->isPeerConnected(getServiceInfo().mServiceType, pid); + return mServiceCtrl->isPeerConnected(getServiceInfo().mServiceType, pid); } bool p3ChatService::sendChat(ChatId destination, std::string msg) @@ -301,7 +304,7 @@ bool p3ChatService::sendChat(ChatId destination, std::string msg) sendPublicChat(msg); return true; } - else if(destination.isPeerId()==false && destination.isGxsId()==false) + else if(destination.isPeerId()==false && destination.isDistantChatId()==false) { std::cerr << "p3ChatService::sendChat() Error: chat id type not handled. Is it empty?" << std::endl; return false; @@ -313,8 +316,8 @@ bool p3ChatService::sendChat(ChatId destination, std::string msg) #endif RsPeerId vpid; - if(destination.isGxsId()) - vpid = RsPeerId(destination.toGxsId()); // convert to virtual peer id + if(destination.isDistantChatId()) + vpid = RsPeerId(destination.toDistantChatId()); // convert to virtual peer id else vpid = destination.toPeerId(); @@ -378,7 +381,12 @@ bool p3ChatService::sendChat(ChatId destination, std::string msg) #endif RsServer::notify()->notifyChatMessage(message); - mHistoryMgr->addMessage(message); + + // cyril: history is temporarily diabled for distant chat, since we need to store the full tunnel ID, but then + // at loading time, the ID is not known so that chat window shows 00000000 as a peer. + + if(!message.chat_id.isDistantChatId()) + mHistoryMgr->addMessage(message); checkSizeAndSendMessage(ci); @@ -497,11 +505,11 @@ void p3ChatService::handleIncomingItem(RsItem *item) return ; // don't delete! It's handled by handleRecvChatMsgItem in some specific cases only. } - if(DistantChatService::handleRecvItem(dynamic_cast(item))) - { - delete item ; - return ; - } +// if(DistantChatService::handleRecvItem(dynamic_cast(item))) +// { +// delete item ; +// return ; +// } if(DistributedChatService::handleRecvItem(dynamic_cast(item))) { @@ -749,7 +757,13 @@ bool p3ChatService::handleRecvChatMsgItem(RsChatMsgItem *ci) cm.incoming = true; cm.online = true; RsServer::notify()->notifyChatMessage(cm); - mHistoryMgr->addMessage(cm); + + // cyril: history is temporarily diabled for distant chat, since we need to store the full tunnel ID, but then + // at loading time, the ID is not known so that chat window shows 00000000 as a peer. + + if(!cm.chat_id.isDistantChatId()) + mHistoryMgr->addMessage(cm); + return true ; } @@ -766,7 +780,7 @@ void p3ChatService::handleRecvChatStatusItem(RsChatStatusItem *cs) std::cerr << "Received status string \"" << cs->status_string << "\"" << std::endl ; #endif - uint32_t status; + DistantChatPeerInfo dcpinfo; if(cs->flags & RS_CHAT_FLAG_REQUEST_CUSTOM_STATE) // no state here just a request. sendCustomState(cs->PeerId()) ; @@ -782,9 +796,9 @@ void p3ChatService::handleRecvChatStatusItem(RsChatStatusItem *cs) #endif sendCustomStateRequest(cs->PeerId()) ; } - else if(DistantChatService::getDistantChatStatus(RsGxsId(cs->PeerId()), status)) + else if(DistantChatService::getDistantChatStatus(DistantChatPeerId(cs->PeerId()), dcpinfo)) { - RsServer::notify()->notifyChatStatus(ChatId(RsGxsId(cs->PeerId())), cs->status_string) ; + RsServer::notify()->notifyChatStatus(ChatId(DistantChatPeerId(cs->PeerId())), cs->status_string) ; } else if(cs->flags & RS_CHAT_FLAG_PRIVATE) { @@ -816,9 +830,9 @@ void p3ChatService::initChatMessage(RsChatMsgItem *c, ChatMessage &m) return; } - uint32_t status; - if(DistantChatService::getDistantChatStatus(RsGxsId(c->PeerId()), status)) - m.chat_id = ChatId(RsGxsId(c->PeerId())); + DistantChatPeerInfo dcpinfo; + if(DistantChatService::getDistantChatStatus(DistantChatPeerId(c->PeerId()), dcpinfo)) + m.chat_id = ChatId(DistantChatPeerId(c->PeerId())); if (c -> chatFlags & RS_CHAT_FLAG_PRIVATE) m.chatflags |= RS_CHAT_PRIVATE; diff --git a/libretroshare/src/chat/rschatitems.cc b/libretroshare/src/chat/rschatitems.cc index e44102bc4..1074eee1c 100644 --- a/libretroshare/src/chat/rschatitems.cc +++ b/libretroshare/src/chat/rschatitems.cc @@ -51,18 +51,6 @@ std::ostream& RsChatMsgItem::print(std::ostream &out, uint16_t indent) printRsItemEnd(out, "RsChatMsgItem", indent); return out; } -std::ostream& RsChatDHPublicKeyItem::print(std::ostream &out, uint16_t indent) -{ - printRsItemBase(out, "RsChatDHPublicKeyItem", indent); - uint16_t int_Indent = indent + 2; - - printIndent(out, int_Indent); - out << " Signature Key ID: " << signature.keyId << std::endl ; - out << " Public Key ID: " << gxs_key.keyId << std::endl ; - - printRsItemEnd(out, "RsChatMsgItem", indent); - return out; -} std::ostream& RsChatLobbyListItem::print(std::ostream &out, uint16_t indent) { @@ -277,7 +265,6 @@ RsItem *RsChatSerialiser::deserialise(void *data, uint32_t *pktsize) case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST_REQUEST: return new RsChatLobbyListRequestItem(data,*pktsize) ; case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST: return new RsChatLobbyListItem(data,*pktsize) ; case RS_PKT_SUBTYPE_CHAT_LOBBY_CONFIG: return new RsChatLobbyConfigItem(data,*pktsize) ; - case RS_PKT_SUBTYPE_DISTANT_CHAT_DH_PUBLIC_KEY: return new RsChatDHPublicKeyItem(data,*pktsize) ; default: std::cerr << "Unknown packet type in chat!" << std::endl ; return NULL ; @@ -437,17 +424,6 @@ uint32_t RsChatLobbyConfigItem::serial_size() return s; } -uint32_t RsChatDHPublicKeyItem::serial_size() -{ - uint32_t s = 8 ; // header - s += 4 ; // BN size - s += BN_num_bytes(public_key) ; // public_key - s += signature.TlvSize() ; // signature - s += gxs_key.TlvSize() ; // gxs_key - - return s ; -} - /*************************************************************************/ RsChatAvatarItem::~RsChatAvatarItem() @@ -459,41 +435,6 @@ RsChatAvatarItem::~RsChatAvatarItem() } } -bool RsChatDHPublicKeyItem::serialise(void *data,uint32_t& pktsize) -{ - uint32_t tlvsize = serial_size() ; - uint32_t offset = 0; - - if (pktsize < tlvsize) - return false; /* not enough space */ - - pktsize = tlvsize; - - bool ok = true; - - ok &= setRsItemHeader(data, tlvsize, PacketId(), tlvsize); - - /* skip the header */ - offset += 8; - - uint32_t s = BN_num_bytes(public_key) ; - - ok &= setRawUInt32(data, tlvsize, &offset, s); - - BN_bn2bin(public_key,&((unsigned char *)data)[offset]) ; - offset += s ; - - ok &= signature.SetTlv(data, tlvsize, &offset); - ok &= gxs_key.SetTlv(data, tlvsize, &offset); - - if (offset != tlvsize) - { - ok = false; - std::cerr << "RsChatDHPublicKeyItem::serialiseItem() Size Error! offset=" << offset << ", tlvsize=" << tlvsize << std::endl; - } - return ok ; -} - /* serialise the data to the buffer */ bool RsChatMsgItem::serialise(void *data, uint32_t& pktsize) { @@ -995,28 +936,6 @@ bool RsChatLobbyConfigItem::serialise(void *data, uint32_t& pktsize) return ok; } -RsChatDHPublicKeyItem::RsChatDHPublicKeyItem(void *data,uint32_t /*size*/) - : RsChatItem(RS_PKT_SUBTYPE_DISTANT_CHAT_DH_PUBLIC_KEY) -{ - uint32_t offset = 8; // skip the header - uint32_t rssize = getRsItemSize(data); - bool ok = true ; - - uint32_t s=0 ; - /* get mandatory parts first */ - ok &= getRawUInt32(data, rssize, &offset, &s); - - public_key = BN_bin2bn(&((unsigned char *)data)[offset],s,NULL) ; - offset += s ; - - ok &= signature.GetTlv(data, rssize, &offset) ; - ok &= gxs_key.GetTlv(data, rssize, &offset) ; - - if (offset != rssize) - std::cerr << "RsChatDHPublicKeyItem::() Size error while deserializing." << std::endl ; - if (!ok) - std::cerr << "RsChatDHPublicKeyItem::() Unknown error while deserializing." << std::endl ; -} RsChatMsgItem::RsChatMsgItem(void *data,uint32_t /*size*/,uint8_t subtype) : RsChatItem(subtype) { diff --git a/libretroshare/src/grouter/p3grouter.cc b/libretroshare/src/grouter/p3grouter.cc index 8ed41178c..ddb6dd004 100644 --- a/libretroshare/src/grouter/p3grouter.cc +++ b/libretroshare/src/grouter/p3grouter.cc @@ -316,6 +316,7 @@ bool p3GRouter::registerKey(const RsGxsId& authentication_key,const GRouterServi grouter_debug() << " Auth GXS Id : " << authentication_key << std::endl; grouter_debug() << " Client id : " << std::hex << client_id << std::dec << std::endl; grouter_debug() << " Description : " << info.description_string << std::endl; + grouter_debug() << " Hash : " << hash << std::endl; #endif return true ; @@ -510,6 +511,12 @@ void p3GRouter::receiveTurtleData(RsTurtleGenericTunnelItem *gitem,const RsFileH RsItem *itm = RsGRouterSerialiser().deserialise(item->data_bytes,&item->data_size) ; +if(itm == NULL) +{ + std::cerr << "(EE) p3GRouter::receiveTurtleData(): cannot de-serialise data. Somthing wrong in the format. Item data (size="<< item->data_size << "): " << RsUtil::BinToHex((char*)item->data_bytes,item->data_size) << std::endl; + return ; +} + itm->PeerId(virtual_peer_id) ; // At this point we can have either a transaction chunk, or a transaction ACK. @@ -1674,10 +1681,14 @@ bool p3GRouter::locked_getClientAndServiceId(const TurtleFileHash& hash, const R { client = NULL ; service_id = 0; - RsGxsId gxs_id ; - makeGxsIdAndClientId(hash,gxs_id,service_id) ; - + + if(!locked_getGxsIdAndClientId(hash,gxs_id,service_id)) + { + std::cerr << " p3GRouter::ERROR: locked_getGxsIdAndClientId(): no key registered for hash " << hash << std::endl; + return false ; + } + if(gxs_id != destination_key) { std::cerr << " ERROR: verification (destination) GXS key " << destination_key << " does not match key from hash " << gxs_id << std::endl; @@ -2013,15 +2024,25 @@ Sha1CheckSum p3GRouter::makeTunnelHash(const RsGxsId& destination,const GRouterS bytes[18] = (client >> 8) & 0xff ; bytes[19] = client & 0xff ; - return Sha1CheckSum(bytes) ; + return RsDirUtil::sha1sum(bytes,20) ; } -void p3GRouter::makeGxsIdAndClientId(const TurtleFileHash& sum,RsGxsId& gxs_id,GRouterServiceId& client_id) +bool p3GRouter::locked_getGxsIdAndClientId(const TurtleFileHash& sum,RsGxsId& gxs_id,GRouterServiceId& client_id) { assert( gxs_id.SIZE_IN_BYTES == 16) ; assert(Sha1CheckSum::SIZE_IN_BYTES == 20) ; - gxs_id = RsGxsId(sum.toByteArray());// takes the first 16 bytes - client_id = sum.toByteArray()[19] + (sum.toByteArray()[18] << 8) ; + //gxs_id = RsGxsId(sum.toByteArray());// takes the first 16 bytes + //client_id = sum.toByteArray()[19] + (sum.toByteArray()[18] << 8) ; + + std::map::const_iterator it = _owned_key_ids.find(sum); + + if(it == _owned_key_ids.end()) + return false ; + + gxs_id = it->second.authentication_key ; + client_id = it->second.service_id ; + + return true ; } bool p3GRouter::loadList(std::list& items) { diff --git a/libretroshare/src/grouter/p3grouter.h b/libretroshare/src/grouter/p3grouter.h index 98beac792..6a73253cf 100644 --- a/libretroshare/src/grouter/p3grouter.h +++ b/libretroshare/src/grouter/p3grouter.h @@ -269,8 +269,8 @@ private: bool decryptDataItem(RsGRouterGenericDataItem *item) ; static Sha1CheckSum makeTunnelHash(const RsGxsId& destination,const GRouterServiceId& client); - static void makeGxsIdAndClientId(const TurtleFileHash &sum,RsGxsId& gxs_id,GRouterServiceId& client_id); + bool locked_getGxsIdAndClientId(const TurtleFileHash &sum,RsGxsId& gxs_id,GRouterServiceId& client_id); bool locked_sendTransactionData(const RsPeerId& pid,const RsGRouterTransactionItem& item); void locked_collectAvailableFriends(const GRouterKeyId &gxs_id,std::list& friend_peers, const std::set& incoming_routes,bool is_origin); diff --git a/libretroshare/src/gxstunnel/p3gxstunnel.cc b/libretroshare/src/gxstunnel/p3gxstunnel.cc new file mode 100644 index 000000000..f7be21db7 --- /dev/null +++ b/libretroshare/src/gxstunnel/p3gxstunnel.cc @@ -0,0 +1,1582 @@ +/* + * libretroshare/src/chat: distantchat.cc + * + * Services for RetroShare. + * + * Copyright 2014 by Cyril Soler + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License Version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + * + * Please report all bugs and problems to "csoler@users.sourceforge.net". + * + */ + + +#include + +#include "openssl/rand.h" +#include "openssl/dh.h" +#include "openssl/err.h" + +#include "util/rsaes.h" +#include "util/rsprint.h" +#include "util/rsmemory.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "p3gxstunnel.h" + +//#define DEBUG_GXS_TUNNEL + +static const uint32_t GXS_TUNNEL_KEEP_ALIVE_TIMEOUT = 6 ; // send keep alive packet so as to avoid tunnel breaks. + +static const uint32_t RS_GXS_TUNNEL_DH_STATUS_UNINITIALIZED = 0x0000 ; +static const uint32_t RS_GXS_TUNNEL_DH_STATUS_HALF_KEY_DONE = 0x0001 ; +static const uint32_t RS_GXS_TUNNEL_DH_STATUS_KEY_AVAILABLE = 0x0002 ; + +static const uint32_t RS_GXS_TUNNEL_DELAY_BETWEEN_RESEND = 10 ; // re-send every 10 secs. + +static const uint32_t GXS_TUNNEL_ENCRYPTION_HMAC_SIZE = SHA_DIGEST_LENGTH ; +static const uint32_t GXS_TUNNEL_ENCRYPTION_IV_SIZE = 8 ; + +static const uint32_t INTERVAL_BETWEEN_DEBUG_DUMP = 10 ; + +static std::string GXS_TUNNEL_APP_NAME = "GxsTunnels" ; + +static const uint8_t GXS_TUNNEL_APP_MAJOR_VERSION = 0x01 ; +static const uint8_t GXS_TUNNEL_APP_MINOR_VERSION = 0x00 ; +static const uint8_t GXS_TUNNEL_MIN_MAJOR_VERSION = 0x01 ; +static const uint8_t GXS_TUNNEL_MIN_MINOR_VERSION = 0x00 ; + +RsGxsTunnelService *rsGxsTunnel = NULL ; + +p3GxsTunnelService::p3GxsTunnelService(RsGixs *pids) + : mGixs(pids), mGxsTunnelMtx("GXS tunnel") +{ + mTurtle = NULL ; + + // any value is fine here, even 0, since items in different RS sessions will use different AES keys. + global_item_counter = 0;//RSRandom::random_u64() ; +} + +void p3GxsTunnelService::connectToTurtleRouter(p3turtle *tr) +{ + mTurtle = tr ; + tr->registerTunnelService(this) ; +} + +bool p3GxsTunnelService::registerClientService(uint32_t service_id,RsGxsTunnelService::RsGxsTunnelClientService *service) +{ + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + if(mRegisteredServices.find(service_id) != mRegisteredServices.end()) + { + std::cerr << "(EE) p3GxsTunnelService::registerClientService(): trying to register client " << std::hex << service_id << std::dec << ", which is already registered!" << std::endl; + return false; + } +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "p3GxsTunnelService::registerClientService(): registering client service " << std::hex << service_id << std::dec << std::endl; +#endif + + mRegisteredServices[service_id] = service ; + return true ; +} + +int p3GxsTunnelService::tick() +{ + static time_t last_dump = 0 ; + +#ifdef DEBUG_GXS_TUNNEL + time_t now = time(NULL); + + if(now > last_dump + INTERVAL_BETWEEN_DEBUG_DUMP ) + { + last_dump = now ; + debug_dump() ; + } +#endif + + flush() ; + + return 0 ; +} + +RsServiceInfo p3GxsTunnelService::getServiceInfo() +{ + return RsServiceInfo(RS_SERVICE_TYPE_GXS_TUNNEL, + GXS_TUNNEL_APP_NAME, + GXS_TUNNEL_APP_MAJOR_VERSION, + GXS_TUNNEL_APP_MINOR_VERSION, + GXS_TUNNEL_MIN_MAJOR_VERSION, + GXS_TUNNEL_MIN_MINOR_VERSION); +} + +void p3GxsTunnelService::flush() +{ + // Flush pending DH items. This is a higher priority, so we deal with them first. + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "p3GxsTunnelService::flush() flushing pending items." << std::endl; +#endif + { + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + for(std::list::iterator it=pendingDHItems.begin();it!=pendingDHItems.end();) + if(locked_sendClearTunnelData(*it) ) + it = pendingDHItems.erase(it) ; + else + ++it ; + } + + // Flush items that could not be sent, probably because of a Mutex protected zone. + // + { + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + for(std::list::iterator it=pendingGxsTunnelItems.begin();it!=pendingGxsTunnelItems.end();) + if(locked_sendEncryptedTunnelData(*it) ) + it = pendingGxsTunnelItems.erase(it) ; + else + { + ++it ; +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "Cannot send encrypted data item to tunnel " << (*it)->PeerId() << std::endl; +#endif + } + } + + // Look at pending data item, and re-send them if necessary. + + { + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + time_t now = time(NULL) ; + + for(std::map::iterator it = pendingGxsTunnelDataItems.begin();it != pendingGxsTunnelDataItems.end();++it) + if(now > RS_GXS_TUNNEL_DELAY_BETWEEN_RESEND + it->second.last_sending_attempt) + { + if(locked_sendEncryptedTunnelData(it->second.data_item)) + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " sending data item #" << std::hex << it->first << std::dec << std::endl; +#endif + it->second.last_sending_attempt = now ; + } +#ifdef DEBUG_GXS_TUNNEL + else + std::cerr << " Cannot send item " << std::hex << it->first << std::dec << std::endl; +#endif + } + } + + // TODO: also sweep GXS id map and disable any ID with no virtual peer id in the list. + + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + time_t now = time(NULL) ; + + for(std::map::iterator it(_gxs_tunnel_contacts.begin());it!=_gxs_tunnel_contacts.end();) + { + // Remove any tunnel that was remotely closed, since we cannot use it anymore. + + if(it->second.status == RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED && it->second.last_contact + 20 < now) + { + std::map::iterator tmp = it ; + ++tmp ; + _gxs_tunnel_contacts.erase(it) ; + it=tmp ; + continue ; + } + + if(it->second.last_contact+20+GXS_TUNNEL_KEEP_ALIVE_TIMEOUT < now && it->second.status == RS_GXS_TUNNEL_STATUS_CAN_TALK) + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "(II) GxsTunnelService:: connexion interrupted with peer." << std::endl; +#endif + + it->second.status = RS_GXS_TUNNEL_STATUS_TUNNEL_DN ; + it->second.virtual_peer_id.clear() ; + + // Also reset turtle router monitoring so as to make the tunnel handling more responsive. If we don't do that, + // the TR will wait 60 secs for the tunnel to die, which causes a significant waiting time in the chat window. + + if(it->second.direction == RsTurtleGenericTunnelItem::DIRECTION_SERVER) + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "(II) GxsTunnelService:: forcing new tunnel campain." << std::endl; +#endif + + mTurtle->forceReDiggTunnels( randomHashFromDestinationGxsId(it->second.to_gxs_id) ); + } + } + if(it->second.last_keep_alive_sent + GXS_TUNNEL_KEEP_ALIVE_TIMEOUT < now && it->second.status == RS_GXS_TUNNEL_STATUS_CAN_TALK) + { + RsGxsTunnelStatusItem *cs = new RsGxsTunnelStatusItem ; + + cs->status = RS_GXS_TUNNEL_FLAG_KEEP_ALIVE; + cs->PeerId(RsPeerId(it->first)) ; + + // we send off-mutex to avoid deadlock. + + pendingGxsTunnelItems.push_back(cs) ; + + it->second.last_keep_alive_sent = now ; +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "(II) GxsTunnelService:: Sending keep alive packet to gxs id " << it->first << std::endl; +#endif + } + ++it ; + } +} + +// In this function the PeerId is the GXS tunnel ID. + +void p3GxsTunnelService::handleIncomingItem(const RsGxsTunnelId& tunnel_id,RsGxsTunnelItem *item) +{ + if(item == NULL) + return ; + + // We have 3 things to do: + // + // 1 - if it's a data item, send an ACK + // 2 - if it's an ack item, mark the item as properly received, and remove it from the queue + // 3 - if it's a status item, act accordingly. + + switch(item->PacketSubType()) + { + + case RS_PKT_SUBTYPE_GXS_TUNNEL_DATA: handleRecvTunnelDataItem(tunnel_id,dynamic_cast(item)) ; + break ; + + case RS_PKT_SUBTYPE_GXS_TUNNEL_DATA_ACK: handleRecvTunnelDataAckItem(tunnel_id,dynamic_cast(item)) ; + break ; + + case RS_PKT_SUBTYPE_GXS_TUNNEL_STATUS: handleRecvStatusItem(tunnel_id,dynamic_cast(item)) ; + break ; + + default: + std::cerr << "(EE) impossible situation. DH items should be handled at the service level" << std::endl; + } + + delete item ; +} + +void p3GxsTunnelService::handleRecvTunnelDataAckItem(const RsGxsTunnelId& id,RsGxsTunnelDataAckItem *item) +{ + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "p3GxsTunnelService::handling RecvTunnelDataAckItem()" << std::endl; + std::cerr << " item counter = " << std::hex << item->unique_item_counter << std::dec << std::endl; +#endif + + // remove it from the queue. + + std::map::iterator it = pendingGxsTunnelDataItems.find(item->unique_item_counter) ; + + if(it == pendingGxsTunnelDataItems.end()) + { + std::cerr << " (EE) item number " << std::hex << item->unique_item_counter << " is unknown. This is unexpected." << std::endl; + return ; + } + + delete it->second.data_item ; + pendingGxsTunnelDataItems.erase(it) ; +} + +void p3GxsTunnelService::handleRecvTunnelDataItem(const RsGxsTunnelId& tunnel_id,RsGxsTunnelDataItem *item) +{ + // imediately send an ACK for this item + + RsGxsTunnelDataAckItem *ackitem = new RsGxsTunnelDataAckItem ; + + ackitem->unique_item_counter = item->unique_item_counter ; + ackitem->PeerId(RsPeerId(tunnel_id)) ; + + { + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + pendingGxsTunnelItems.push_back(ackitem) ; // we use the queue that does not need an ACK, in order to avoid an infinite loop ;-) + } + + // notify the client for the received data + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "p3GxsTunnelService::handleRecvTunnelDataItem()" << std::endl; + std::cerr << " data size = " << item->data_size << std::endl; + std::cerr << " service id = " << std::hex << item->service_id << std::dec << std::endl; + std::cerr << " counter id = " << std::hex << item->unique_item_counter << std::dec << std::endl; +#endif + + RsGxsTunnelClientService *service = NULL ; + { + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + std::map::const_iterator it = mRegisteredServices.find(item->service_id) ; + + if(it == mRegisteredServices.end()) + { + std::cerr << " (EE) no registered service with ID " << std::hex << item->service_id << std::dec << ". Rejecting item." << std::endl; + return ; + } + service = it->second ; + + std::map::iterator it2 = _gxs_tunnel_contacts.find(tunnel_id) ; + + if(it2 != _gxs_tunnel_contacts.end()) + it2->second.client_services.insert(item->service_id) ; + } + + service->receiveData(tunnel_id,item->data,item->data_size) ; + + item->data = NULL ; // avoids deletion, since the client has the memory now + item->data_size = 0 ; +} + +void p3GxsTunnelService::handleRecvStatusItem(const RsGxsTunnelId& tunnel_id, RsGxsTunnelStatusItem *cs) +{ + std::vector notifications ; + std::set clients ; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "p3GxsTunnelService::handleRecvStatusItem(): tunnel_id=" << tunnel_id << " status=" << cs->status << std::endl; +#endif + + switch(cs->status) + { + case RS_GXS_TUNNEL_FLAG_CLOSING_DISTANT_CONNECTION: + { + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + std::map::iterator it = _gxs_tunnel_contacts.find(tunnel_id) ; + + if(it == _gxs_tunnel_contacts.end()) + { + std::cerr << "(EE) Cannot mark tunnel connection as closed. No connection openned for tunnel id " << tunnel_id << ". Unexpected situation." << std::endl; + return ; + } + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Marking distant chat as remotely closed for tunnel id " << tunnel_id << std::endl; +#endif + if(it->second.direction == RsTurtleGenericDataItem::DIRECTION_CLIENT) + { + it->second.status = RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED ; +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " This is server side. The tunnel cannot be re-openned, so we give it up." << std::endl; +#endif + } + else + { + it->second.status = RS_GXS_TUNNEL_STATUS_TUNNEL_DN ; +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " This is client side. The tunnel will be re-openned automatically." << std::endl; +#endif + } + + notifications.push_back(RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED) ; + } // nothing more to do, because the decryption routing will update the last_contact time when decrypting. + break ; + + case RS_GXS_TUNNEL_FLAG_KEEP_ALIVE: +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService::handleRecvGxsTunnelStatusItem(): received keep alive packet for inactive tunnel! peerId=" << cs->PeerId() << " tunnel=" << tunnel_id << std::endl; +#endif + break ; + + case RS_GXS_TUNNEL_FLAG_ACK_DISTANT_CONNECTION: + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "Received ACK item from the distant peer!" << std::endl; +#endif + + // in this case we notify the clients using this tunnel. + + notifications.push_back(RS_GXS_TUNNEL_STATUS_CAN_TALK) ; + } + break ; + + default: + std::cerr << "(EE) unhandled tunnel status " << std::hex << cs->status << std::dec << std::endl; + break ; + } + + // notify all clients + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " notifying clients. Prending notifications: " << notifications.size() << std::endl; +#endif + + if(notifications.size() > 0) + { + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + std::map::iterator it = _gxs_tunnel_contacts.find(tunnel_id) ; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " " << it->second.client_services.size() << " client services for tunnel id " << tunnel_id << std::endl; +#endif + + for(std::set::const_iterator it2(it->second.client_services.begin());it2!=it->second.client_services.end();++it2) + { + std::map::const_iterator it3=mRegisteredServices.find(*it2) ; + + if(it3 != mRegisteredServices.end()) + clients.insert(it3->second) ; + } + } + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " notifying " << clients.size() << " clients." << std::endl; +#endif + + for(std::set::const_iterator it(clients.begin());it!=clients.end();++it) + for(uint32_t i=0;inotifyTunnelStatus(tunnel_id,notifications[i]) ; +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " notifying client " << (void*)(*it) << " of status " << notifications[i] << std::endl; +#endif + } +} + +bool p3GxsTunnelService::handleTunnelRequest(const RsFileHash& hash,const RsPeerId& /*peer_id*/) +{ + RsStackMutex stack(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + // look into owned GXS ids, and see if the hash corresponds to the expected hash + // + std::list own_id_list ; + rsIdentity->getOwnIds(own_id_list) ; + + // extract the GXS id from the hash + + RsGxsId destination_id = destinationGxsIdFromHash(hash) ; + + // linear search. Not costly because we have typically a low number of IDs. Otherwise, this really should be avoided! + + for(std::list::const_iterator it(own_id_list.begin());it!=own_id_list.end();++it) + if(*it == destination_id) + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService::handleTunnelRequest: received tunnel request for hash " << hash << std::endl; + std::cerr << " answering true!" << std::endl; +#endif + return true ; + } + + return false ; +} + +void p3GxsTunnelService::addVirtualPeer(const TurtleFileHash& hash,const TurtleVirtualPeerId& virtual_peer_id,RsTurtleGenericTunnelItem::Direction dir) +{ +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService:: received new virtual peer " << virtual_peer_id << " for hash " << hash << ", dir=" << dir << std::endl; +#endif + RsGxsId own_gxs_id ; + + { + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + GxsTunnelDHInfo& dhinfo( _gxs_tunnel_virtual_peer_ids[virtual_peer_id] ) ; + dhinfo.gxs_id.clear() ; + + if(dhinfo.dh != NULL) + DH_free(dhinfo.dh) ; + + dhinfo.dh = NULL ; + dhinfo.direction = dir ; + dhinfo.hash = hash ; + dhinfo.status = RS_GXS_TUNNEL_DH_STATUS_UNINITIALIZED ; + dhinfo.tunnel_id.clear(); + + if(dir == RsTurtleGenericTunnelItem::DIRECTION_CLIENT) // server side + { + // check that a tunnel is not already working for this hash. If so, give up. + + own_gxs_id = destinationGxsIdFromHash(hash) ; + } + else // client side + { + std::map::const_iterator it = _gxs_tunnel_contacts.begin(); + + while(it != _gxs_tunnel_contacts.end() && it->second.hash != hash) ++it ; + + if(it == _gxs_tunnel_contacts.end()) + { + std::cerr << "(EE) no pre-registered peer for hash " << hash << " on client side. This is a bug." << std::endl; + return ; + } + + if(it->second.status == RS_GXS_TUNNEL_STATUS_CAN_TALK) + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " virtual peer is for a distant chat session that is already openned and alive. Giving it up." << std::endl; +#endif + return ; + } + + own_gxs_id = it->second.own_gxs_id ; + } + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Creating new virtual peer ID entry and empty DH session key." << std::endl; +#endif + + } + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Adding virtual peer " << virtual_peer_id << " for chat hash " << hash << std::endl; +#endif + + // Start a new DH session for this tunnel + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + locked_restartDHSession(virtual_peer_id,own_gxs_id) ; +} + +void p3GxsTunnelService::locked_restartDHSession(const RsPeerId& virtual_peer_id,const RsGxsId& own_gxs_id) +{ +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "Starting new DH session." << std::endl; +#endif + GxsTunnelDHInfo& dhinfo = _gxs_tunnel_virtual_peer_ids[virtual_peer_id] ; // creates it, if necessary + + dhinfo.status = RS_GXS_TUNNEL_DH_STATUS_UNINITIALIZED ; + dhinfo.own_gxs_id = own_gxs_id ; + + if(!locked_initDHSessionKey(dhinfo.dh)) + { + std::cerr << " (EE) Cannot start DH session. Something went wrong." << std::endl; + return ; + } + dhinfo.status = RS_GXS_TUNNEL_DH_STATUS_HALF_KEY_DONE ; + + if(!locked_sendDHPublicKey(dhinfo.dh,own_gxs_id,virtual_peer_id)) + std::cerr << " (EE) Cannot send DH public key. Something went wrong." << std::endl; +} + +void p3GxsTunnelService::removeVirtualPeer(const TurtleFileHash& hash,const TurtleVirtualPeerId& virtual_peer_id) +{ + bool tunnel_dn = false ; + std::set client_services ; + RsGxsTunnelId tunnel_id ; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService: Removing virtual peer " << virtual_peer_id << " for hash " << hash << std::endl; +#else + /* remove unused parameter warnings */ + (void) hash; +#endif + { + RsStackMutex stack(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + RsGxsId gxs_id ; + std::map::iterator it = _gxs_tunnel_virtual_peer_ids.find(virtual_peer_id) ; + + if(it == _gxs_tunnel_virtual_peer_ids.end()) + { + std::cerr << "(EE) Cannot remove virtual peer " << virtual_peer_id << ": not found in tunnel list!!" << std::endl; + return ; + } + + tunnel_id = it->second.tunnel_id ; + + if(it->second.dh != NULL) + DH_free(it->second.dh) ; + + _gxs_tunnel_virtual_peer_ids.erase(it) ; + + std::map::iterator it2 = _gxs_tunnel_contacts.find(tunnel_id) ; + + if(it2 == _gxs_tunnel_contacts.end()) + { + std::cerr << "(EE) Cannot find tunnel id " << tunnel_id << " in contact list. Weird." << std::endl; + return ; + } + if(it2->second.virtual_peer_id == virtual_peer_id) + { + it2->second.status = RS_GXS_TUNNEL_STATUS_TUNNEL_DN ; + it2->second.virtual_peer_id.clear() ; + tunnel_dn = true ; + } + + for(std::set::const_iterator it(it2->second.client_services.begin());it!=it2->second.client_services.end();++it) + { + std::map::const_iterator it2 = mRegisteredServices.find(*it) ; + + if(it2 != mRegisteredServices.end()) + client_services.insert(it2->second) ; + } + } + + if(tunnel_dn) + { + // notify all client services that this tunnel is down + + for(std::set::const_iterator it(client_services.begin());it!=client_services.end();++it) + (*it)->notifyTunnelStatus(tunnel_id,RS_GXS_TUNNEL_STATUS_TUNNEL_DN) ; + } +} + +void p3GxsTunnelService::receiveTurtleData(RsTurtleGenericTunnelItem *gitem,const RsFileHash& hash, const RsPeerId& virtual_peer_id,RsTurtleGenericTunnelItem::Direction direction) +{ +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService::receiveTurtleData(): Received turtle data. " << std::endl; + std::cerr << " hash = " << hash << std::endl; + std::cerr << " vpid = " << virtual_peer_id << std::endl; + std::cerr << " acting as = " << direction << std::endl; +#else + /* remove unused parameter warnings */ + (void) direction; +#endif + + RsTurtleGenericDataItem *item = dynamic_cast(gitem) ; + + if(item == NULL) + { + std::cerr << "(EE) item is not a data item. That is an error." << std::endl; + return ; + } + // Call the AES crypto module + // - the IV is the first 8 bytes of item->data_bytes + + if(item->data_size < 8) + { + std::cerr << "(EE) item encrypted data stream is too small: size = " << item->data_size << std::endl; + return ; + } + if(*((uint64_t*)item->data_bytes) != 0) // WTF?? we should use flags + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Item is encrypted." << std::endl; +#endif + + // if cannot decrypt, it means the key is wrong. We need to re-negociate a new key. + + handleEncryptedData((uint8_t*)item->data_bytes,item->data_size,hash,virtual_peer_id) ; + } + else + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Item is not encrypted." << std::endl; +#endif + + // Now try deserialise the decrypted data to make an RsItem out of it. + // + uint32_t pktsize = item->data_size-8; + RsItem *citem = RsGxsTunnelSerialiser().deserialise(&((uint8_t*)item->data_bytes)[8],&pktsize) ; + + if(citem == NULL) + { + std::cerr << "(EE) item could not be de-serialized. That is an error." << std::endl; + return ; + } + + // DH key items are sent even before we know who we speak to, so the virtual peer id is used in this + // case only. + RsGxsTunnelDHPublicKeyItem *dhitem = dynamic_cast(citem) ; + + if(dhitem != NULL) + { + dhitem->PeerId(virtual_peer_id) ; + handleRecvDHPublicKey(dhitem) ; + } + else + std::cerr << "(EE) Deserialiased item has unexpected type." << std::endl; + } +} + +// This function encrypts the given data and adds a MAC and an IV into a serialised memory chunk that is then sent through the tunnel. + +bool p3GxsTunnelService::handleEncryptedData(const uint8_t *data_bytes,uint32_t data_size,const TurtleFileHash& hash,const RsPeerId& virtual_peer_id) +{ +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "p3GxsTunnelService::handleEncryptedDataItem()" << std::endl; + std::cerr << " size = " << data_size << std::endl; + std::cerr << " data = " << (void*)data_bytes << std::endl; + std::cerr << " IV = " << std::hex << *(uint64_t*)data_bytes << std::dec << std::endl; + std::cerr << " data = " << RsUtil::BinToHex((char*)data_bytes,data_size) ; + std::cerr << std::endl; +#endif + + RsGxsTunnelItem *citem = NULL; + RsGxsTunnelId tunnel_id; + + { + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + uint32_t encrypted_size = data_size - GXS_TUNNEL_ENCRYPTION_IV_SIZE - GXS_TUNNEL_ENCRYPTION_HMAC_SIZE; + uint32_t decrypted_size = RsAES::get_buffer_size(encrypted_size); + uint8_t *encrypted_data = (uint8_t*)data_bytes+GXS_TUNNEL_ENCRYPTION_IV_SIZE+GXS_TUNNEL_ENCRYPTION_HMAC_SIZE; + uint8_t *decrypted_data = new uint8_t[decrypted_size]; + uint8_t aes_key[GXS_TUNNEL_AES_KEY_SIZE] ; + + std::map::iterator it = _gxs_tunnel_virtual_peer_ids.find(virtual_peer_id) ; + + if(it == _gxs_tunnel_virtual_peer_ids.end()) + { + std::cerr << "(EE) item is not coming out of a registered tunnel. Weird. hash=" << hash << ", peer id = " << virtual_peer_id << std::endl; + return true ; + } + + tunnel_id = it->second.tunnel_id ; + std::map::iterator it2 = _gxs_tunnel_contacts.find(tunnel_id) ; + + if(it2 == _gxs_tunnel_contacts.end()) + { + std::cerr << "(EE) no tunnel data for tunnel ID=" << tunnel_id << ". This is a bug." << std::endl; + return true ; + } + memcpy(aes_key,it2->second.aes_key,GXS_TUNNEL_AES_KEY_SIZE) ; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Using IV: " << std::hex << *(uint64_t*)data_bytes << std::dec << std::endl; + std::cerr << " Decrypted buffer size: " << decrypted_size << std::endl; + std::cerr << " key : " << RsUtil::BinToHex((char*)aes_key,GXS_TUNNEL_AES_KEY_SIZE) << std::endl; + std::cerr << " hmac : " << RsUtil::BinToHex((char*)data_bytes+GXS_TUNNEL_ENCRYPTION_IV_SIZE,GXS_TUNNEL_ENCRYPTION_HMAC_SIZE) << std::endl; + std::cerr << " data : " << RsUtil::BinToHex((char*)data_bytes,data_size) << std::endl; +#endif + // first, check the HMAC + + unsigned char *hm = HMAC(EVP_sha1(),aes_key,GXS_TUNNEL_AES_KEY_SIZE,encrypted_data,encrypted_size,NULL,NULL) ; + + if(memcmp(hm,&data_bytes[GXS_TUNNEL_ENCRYPTION_IV_SIZE],GXS_TUNNEL_ENCRYPTION_HMAC_SIZE)) + { + std::cerr << "(EE) packet HMAC does not match. Computed HMAC=" << RsUtil::BinToHex((char*)hm,GXS_TUNNEL_ENCRYPTION_HMAC_SIZE) << std::endl; + std::cerr << "(EE) resetting new DH session." << std::endl; + + delete[] decrypted_data ; + + locked_restartDHSession(virtual_peer_id,it2->second.own_gxs_id) ; + + return false ; + } + + if(!RsAES::aes_decrypt_8_16(encrypted_data,encrypted_size, aes_key,(uint8_t*)data_bytes,decrypted_data,decrypted_size)) + { + std::cerr << "(EE) packet decryption failed." << std::endl; + std::cerr << "(EE) resetting new DH session." << std::endl; + + delete[] decrypted_data ; + + locked_restartDHSession(virtual_peer_id,it2->second.own_gxs_id) ; + + return false ; + } + it2->second.status = RS_GXS_TUNNEL_STATUS_CAN_TALK ; + it2->second.last_contact = time(NULL) ; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "(II) Decrypted data: size=" << decrypted_size << std::endl; +#endif + + // Now try deserialise the decrypted data to make an RsItem out of it. + // + citem = dynamic_cast(RsGxsTunnelSerialiser().deserialise(decrypted_data,&decrypted_size)) ; + + delete[] decrypted_data ; + + if(citem == NULL) + { + std::cerr << "(EE) item could not be de-serialized. That is an error." << std::endl; + return true; + } + + it2->second.total_received += decrypted_size ; + + // DH key items are sent even before we know who we speak to, so the virtual peer id is used in this + // case only. + + citem->PeerId(virtual_peer_id) ; + } + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "(II) Setting peer id to " << citem->PeerId() << std::endl; +#endif + handleIncomingItem(tunnel_id,citem) ; // Treats the item, and deletes it + + return true ; +} + +void p3GxsTunnelService::handleRecvDHPublicKey(RsGxsTunnelDHPublicKeyItem *item) +{ + if (!item) + { + std::cerr << "p3GxsTunnelService: Received null DH public key item. This should not happen." << std::endl; + return; + } + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService: Received DH public key." << std::endl; + item->print(std::cerr, 0) ; +#endif + + // Look for the current state of the key agreement. + + TurtleVirtualPeerId vpid = item->PeerId() ; + + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + std::map::iterator it = _gxs_tunnel_virtual_peer_ids.find(vpid) ; + + if(it == _gxs_tunnel_virtual_peer_ids.end()) + { + std::cerr << " (EE) Cannot find hash in gxs_tunnel peer list!!" << std::endl; + return ; + } + + // Now check the signature of the DH public key item. + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Checking signature. " << std::endl; +#endif + + uint32_t pubkey_size = BN_num_bytes(item->public_key) ; + unsigned char *data = (unsigned char *)malloc(pubkey_size) ; + BN_bn2bin(item->public_key, data) ; + + RsTlvSecurityKey signature_key ; + + // We need to get the key of the sender, but if the key is not cached, we + // need to get it first. So we let the system work for 2-3 seconds before + // giving up. Normally this would only cause a delay for uncached keys, + // which is rare. To force the system to cache the key, we first call for + // getIdDetails(). + // + RsIdentityDetails details ; + RsGxsId senders_id( item->signature.keyId ) ; + + for(int i=0;i<6;++i) + if(!mGixs->getKey(senders_id,signature_key) || signature_key.keyData.bin_data == NULL) + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Cannot get key. Waiting for caching. try " << i << "/6" << std::endl; +#endif + usleep(500 * 1000) ; // sleep for 500 msec. + } + else + break ; + + if(signature_key.keyData.bin_data == NULL) + { + std::cerr << " (EE) Key unknown for checking signature from " << senders_id << ", can't verify signature. Using key provided in DH packet (without adding to the keyring)." << std::endl; + + // check GXS key for defects. + + if(!GxsSecurity::checkPublicKey(item->gxs_key)) + { + std::cerr << "(SS) Security error in distant chat DH handshake: supplied key " << item->gxs_key.keyId << " is inconsistent. Refusing chat!" << std::endl; + return ; + } + if(item->gxs_key.keyId != item->signature.keyId) + { + std::cerr << "(SS) Security error in distant chat DH handshake: supplied key " << item->gxs_key.keyId << " is not the same than the item's signature key " << item->signature.keyId << ". Refusing chat!" << std::endl; + return ; + } + + signature_key = item->gxs_key ; + } + + if(!GxsSecurity::validateSignature((char*)data,pubkey_size,signature_key,item->signature)) + { + std::cerr << "(SS) Signature was verified and it doesn't check! This is a security issue!" << std::endl; + return ; + } + mGixs->timeStampKey(item->signature.keyId) ; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Signature checks! Sender's ID = " << senders_id << std::endl; + std::cerr << " Computing AES key" << std::endl; +#endif + + if(it->second.dh == NULL) + { + std::cerr << " (EE) no DH information for that peer. This is an error." << std::endl; + return ; + } + if(it->second.status == RS_GXS_TUNNEL_DH_STATUS_KEY_AVAILABLE) + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " DH Session already set for this tunnel. Re-initing a new session!" << std::endl; +#endif + + locked_restartDHSession(vpid,it->second.own_gxs_id) ; + } + + // gets current key params. By default, should contain all null pointers. + // + RsGxsId own_id = it->second.own_gxs_id ; + + RsGxsTunnelId tunnel_id = makeGxsTunnelId(own_id,senders_id) ; + + it->second.tunnel_id = tunnel_id ; + it->second.gxs_id = senders_id ; + + // Looks for the DH params. If not there yet, create them. + // + int size = DH_size(it->second.dh) ; + unsigned char *key_buff = new unsigned char[size] ; + + if(size != DH_compute_key(key_buff,item->public_key,it->second.dh)) + { + std::cerr << " (EE) DH computation failed. Probably a bug. Error code=" << ERR_get_error() << std::endl; + return ; + } + it->second.status = RS_GXS_TUNNEL_DH_STATUS_KEY_AVAILABLE ; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " DH key computation successed. New key in place." << std::endl; +#endif + // make a hash of destination and source GXS ids in order to create the tunnel name + + GxsTunnelPeerInfo& pinfo(_gxs_tunnel_contacts[tunnel_id]) ; + + // Now hash the key buffer into a 16 bytes key. + + assert(GXS_TUNNEL_AES_KEY_SIZE <= Sha1CheckSum::SIZE_IN_BYTES) ; + memcpy(pinfo.aes_key, RsDirUtil::sha1sum(key_buff,size).toByteArray(),GXS_TUNNEL_AES_KEY_SIZE) ; + delete[] key_buff ; + + pinfo.last_contact = time(NULL) ; + pinfo.last_keep_alive_sent = time(NULL) ; + pinfo.status = RS_GXS_TUNNEL_STATUS_CAN_TALK ; + pinfo.virtual_peer_id = vpid ; + pinfo.direction = it->second.direction ; + pinfo.own_gxs_id = own_id ; + pinfo.to_gxs_id = item->signature.keyId; // this is already set for client side but not for server side. + + // note: the hash might still be nn initialised on server side. + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " DH key computed. Tunnel is now secured!" << std::endl; + std::cerr << " Key computed: " << RsUtil::BinToHex((char*)pinfo.aes_key,16) << std::cerr << std::endl; + std::cerr << " Sending a ACK packet." << std::endl; +#endif + + // then we send an ACK packet to notify that the tunnel works. That's useful + // because it makes the peer at the other end of the tunnel know that all + // intermediate peer in the tunnel are able to transmit the data. + // However, it is not possible here to call sendTurtleData(), without dead-locking + // the turtle router, so we store the item is a list of items to be sent. + + RsGxsTunnelStatusItem *cs = new RsGxsTunnelStatusItem ; + + cs->status = RS_GXS_TUNNEL_FLAG_ACK_DISTANT_CONNECTION; + cs->PeerId(RsPeerId(tunnel_id)) ; + + pendingGxsTunnelItems.push_back(cs) ; +} + +// Note: for some obscure reason, the typedef does not work here. Looks like a compiler error. So I use the primary type. + +GXSTunnelId p3GxsTunnelService::makeGxsTunnelId(const RsGxsId &own_id, const RsGxsId &distant_id) const // creates a unique ID from two GXS ids. +{ + unsigned char mem[RsGxsId::SIZE_IN_BYTES * 2] ; + + // Always sort the ids, as a matter to avoid confusion between the two. Also that generates the same tunnel ID on both sides + // which helps debugging. If the code is right this is not needed anyway. + + if(own_id < distant_id) + { + memcpy(mem, own_id.toByteArray(), RsGxsId::SIZE_IN_BYTES) ; + memcpy(mem+RsGxsId::SIZE_IN_BYTES, distant_id.toByteArray(), RsGxsId::SIZE_IN_BYTES) ; + } + else + { + memcpy(mem, distant_id.toByteArray(), RsGxsId::SIZE_IN_BYTES) ; + memcpy(mem+RsGxsId::SIZE_IN_BYTES, own_id.toByteArray(), RsGxsId::SIZE_IN_BYTES) ; + } + + assert( RsGxsTunnelId::SIZE_IN_BYTES <= Sha1CheckSum::SIZE_IN_BYTES ) ; + + return RsGxsTunnelId( RsDirUtil::sha1sum(mem, 2*RsGxsId::SIZE_IN_BYTES).toByteArray() ) ; +} + +bool p3GxsTunnelService::locked_sendDHPublicKey(const DH *dh,const RsGxsId& own_gxs_id,const RsPeerId& virtual_peer_id) +{ + if(dh == NULL) + { + std::cerr << " (EE) DH struct is not initialised! Error." << std::endl; + return false ; + } + + RsGxsTunnelDHPublicKeyItem *dhitem = new RsGxsTunnelDHPublicKeyItem ; + dhitem->public_key = BN_dup(dh->pub_key) ; + + // we should also sign the data and check the signature on the other end. + // + RsTlvKeySignature signature ; + RsTlvSecurityKey signature_key ; + RsTlvSecurityKey signature_key_public ; + + uint32_t error_status ; + + uint32_t size = BN_num_bytes(dhitem->public_key) ; + unsigned char *data = (unsigned char *)malloc(size) ; + BN_bn2bin(dhitem->public_key, data) ; + + if(!mGixs->signData((unsigned char*)data,size,own_gxs_id,signature,error_status)) + { + switch(error_status) + { + case RsGixs::RS_GIXS_ERROR_KEY_NOT_AVAILABLE: std::cerr << "(EE) Key is not available. Cannot sign." << std::endl; + break ; + default: std::cerr << "(EE) Unknown error when signing" << std::endl; + break ; + } + free(data) ; + delete(dhitem); + return false; + } + free(data) ; + + if(!mGixs->getKey(own_gxs_id,signature_key_public)) + { + std::cerr << " (EE) Could not retrieve own public key for ID = " << own_gxs_id << ". Giging up sending DH session params." << std::endl; + return false ; + } + + + assert(!(signature_key_public.keyFlags & RSTLV_KEY_TYPE_FULL)) ; + + dhitem->signature = signature ; + dhitem->gxs_key = signature_key_public ; + dhitem->PeerId(virtual_peer_id) ; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Pushing DH session key item to pending distant messages..." << std::endl; + dhitem->print(std::cerr, 2) ; + std::cerr << std::endl; +#endif + pendingDHItems.push_back(dhitem) ; // sent off-mutex to avoid deadlocking. + + return true ; +} + +bool p3GxsTunnelService::locked_initDHSessionKey(DH *& dh) +{ + // We use our own DH group prime. This has been generated with command-line openssl and checked. + + static const std::string dh_prime_2048_hex = "B3B86A844550486C7EA459FA468D3A8EFD71139593FE1C658BBEFA9B2FC0AD2628242C2CDC2F91F5B220ED29AAC271192A7374DFA28CDDCA70252F342D0821273940344A7A6A3CB70C7897A39864309F6CAC5C7EA18020EF882693CA2C12BB211B7BA8367D5A7C7252A5B5E840C9E8F081469EBA0B98BCC3F593A4D9C4D5DF539362084F1B9581316C1F80FDAD452FD56DBC6B8ED0775F596F7BB22A3FE2B4753764221528D33DB4140DE58083DB660E3E105123FC963BFF108AC3A268B7380FFA72005A1515C371287C5706FFA6062C9AC73A9B1A6AC842C2764CDACFC85556607E86611FDF486C222E4896CDF6908F239E177ACC641FCBFF72A758D1C10CBB" ; + + if(dh != NULL) + { + DH_free(dh) ; + dh = NULL ; + } + + dh = DH_new() ; + + if(!dh) + { + std::cerr << " (EE) DH_new() failed." << std::endl; + return false ; + } + + BN_hex2bn(&dh->p,dh_prime_2048_hex.c_str()) ; + BN_hex2bn(&dh->g,"5") ; + + int codes = 0 ; + + if(!DH_check(dh, &codes) || codes != 0) + { + std::cerr << " (EE) DH check failed!" << std::endl; + return false ; + } + + if(!DH_generate_key(dh)) + { + std::cerr << " (EE) DH generate_key() failed! Error code = " << ERR_get_error() << std::endl; + return false ; + } +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " (II) DH Session key inited." << std::endl; +#endif + return true ; +} + +// Sends the item in clear. This is only used for DH key exchange. +// in this case only, the item's PeerId is equal to the virtual peer Id for the tunnel, +// since we ight not now the tunnel id yet. + +bool p3GxsTunnelService::locked_sendClearTunnelData(RsGxsTunnelDHPublicKeyItem *item) +{ +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService::sendClearTunnelData(): try sending item " << (void*)item << " to peer " << item->PeerId() << std::endl; +#endif + + // make a TurtleGenericData item out of it, and send it in clear. + // + RsTurtleGenericDataItem *gitem = new RsTurtleGenericDataItem ; + + uint32_t rssize = item->serial_size() ; + + gitem->data_size = rssize + 8 ; + gitem->data_bytes = malloc(rssize+8) ; + + // by convention, we use a IV of 0 for unencrypted data. + memset(gitem->data_bytes,0,8) ; + + if(!item->serialise(&((uint8_t*)gitem->data_bytes)[8],rssize)) + { + std::cerr << "(EE) Could not serialise item!!!" << std::endl; + delete gitem ; + return false; + } + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " GxsTunnelService::sendClearTunnelData(): Sending clear data to virtual peer: " << item->PeerId() << std::endl; + std::cerr << " gitem->data_size = " << gitem->data_size << std::endl; + std::cerr << " data = " << RsUtil::BinToHex((char*)gitem->data_bytes,gitem->data_size) ; + std::cerr << std::endl; +#endif + mTurtle->sendTurtleData(item->PeerId(),gitem) ; + + return true ; +} + +// Sends this item using secured/authenticated method, thx to the establshed cryptographic channel. + +bool p3GxsTunnelService::locked_sendEncryptedTunnelData(RsGxsTunnelItem *item) +{ + uint32_t rssize = item->serial_size(); + RsTemporaryMemory buff(rssize) ; + + if(!item->serialise(buff,rssize)) + { + std::cerr << "(EE) GxsTunnelService::sendEncryptedTunnelData(): Could not serialise item!" << std::endl; + return false; + } + + uint8_t aes_key[GXS_TUNNEL_AES_KEY_SIZE] ; + uint64_t IV = 0; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "Sending encrypted data to tunnel with vpid " << item->PeerId() << std::endl; +#endif + + RsGxsTunnelId tunnel_id ( item->PeerId() ); + + std::map::iterator it = _gxs_tunnel_contacts.find(tunnel_id) ; + + if(it == _gxs_tunnel_contacts.end()) + { + std::cerr << "(EE) Cannot find contact key info for tunnel id " << tunnel_id << ". Cannot send message!" << std::endl; + return false; + } + if(it->second.status != RS_GXS_TUNNEL_STATUS_CAN_TALK) + { + std::cerr << "(EE) Cannot talk to tunnel id " << tunnel_id << ". Tunnel status is: " << it->second.status << std::endl; + return false; + } + + it->second.total_sent += rssize ; // counts the size of clear data that is sent + + memcpy(aes_key,it->second.aes_key,GXS_TUNNEL_AES_KEY_SIZE) ; + RsPeerId virtual_peer_id = it->second.virtual_peer_id ; + + while(IV == 0) IV = RSRandom::random_u64() ; // make a random 8 bytes IV, that is not 0 + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService::sendEncryptedTunnelData(): tunnel found. Encrypting data." << std::endl; +#endif + + // Now encrypt this data using AES. + // + uint32_t encrypted_size = RsAES::get_buffer_size(rssize); + RsTemporaryMemory encrypted_data(encrypted_size) ; + + if(!RsAES::aes_crypt_8_16(buff,rssize,aes_key,(uint8_t*)&IV,encrypted_data,encrypted_size)) + { + std::cerr << "(EE) packet encryption failed." << std::endl; + delete[] encrypted_data ; + return false; + } + + // make a TurtleGenericData item out of it: + // + RsTurtleGenericDataItem *gitem = new RsTurtleGenericDataItem ; + + gitem->data_size = encrypted_size + GXS_TUNNEL_ENCRYPTION_IV_SIZE + GXS_TUNNEL_ENCRYPTION_HMAC_SIZE ; + gitem->data_bytes = malloc(gitem->data_size) ; + + memcpy(& ((uint8_t*)gitem->data_bytes)[0] ,&IV,8) ; + + unsigned int md_len = GXS_TUNNEL_ENCRYPTION_HMAC_SIZE ; + HMAC(EVP_sha1(),aes_key,GXS_TUNNEL_AES_KEY_SIZE,encrypted_data,encrypted_size,&(((uint8_t*)gitem->data_bytes)[GXS_TUNNEL_ENCRYPTION_IV_SIZE]),&md_len) ; + + memcpy(& (((uint8_t*)gitem->data_bytes)[GXS_TUNNEL_ENCRYPTION_HMAC_SIZE+GXS_TUNNEL_ENCRYPTION_IV_SIZE]),encrypted_data,encrypted_size) ; + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Using IV: " << std::hex << IV << std::dec << std::endl; + std::cerr << " Using Key: " << RsUtil::BinToHex((char*)aes_key,GXS_TUNNEL_AES_KEY_SIZE) ; std::cerr << std::endl; + std::cerr << " hmac: " << RsUtil::BinToHex((char*)gitem->data_bytes,GXS_TUNNEL_ENCRYPTION_HMAC_SIZE) << std::endl; +#endif +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService::sendEncryptedTunnelData(): Sending encrypted data to virtual peer: " << virtual_peer_id << std::endl; + std::cerr << " gitem->data_size = " << gitem->data_size << std::endl; + std::cerr << " serialised data = " << RsUtil::BinToHex((char*)gitem->data_bytes,gitem->data_size) ; + std::cerr << std::endl; +#endif + + mTurtle->sendTurtleData(virtual_peer_id,gitem) ; + + return true ; +} + +bool p3GxsTunnelService::requestSecuredTunnel(const RsGxsId& to_gxs_id, const RsGxsId& from_gxs_id, RsGxsTunnelId &tunnel_id, uint32_t service_id, uint32_t& error_code) +{ + // should be a parameter. + + std::list lst ; + mGixs->getOwnIds(lst) ; + + bool found = false ; + for(std::list::const_iterator it = lst.begin();it!=lst.end();++it) + if(*it == from_gxs_id) + { + found=true; + break ; + } + + if(!found) + { + std::cerr << " (EE) Cannot start distant chat, since GXS id " << from_gxs_id << " is not available." << std::endl; + error_code = RS_GXS_TUNNEL_ERROR_UNKNOWN_GXS_ID ; + return false ; + } + RsGxsId own_gxs_id = from_gxs_id ; + + startClientGxsTunnelConnection(to_gxs_id,own_gxs_id,service_id,tunnel_id) ; + + error_code = RS_GXS_TUNNEL_ERROR_NO_ERROR ; + + return true ; +} + +bool p3GxsTunnelService::sendData(const RsGxsTunnelId &tunnel_id, uint32_t service_id, const uint8_t *data, uint32_t size) +{ + // make sure that the tunnel ID is registered. + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "p3GxsTunnelService::sendData()" << std::endl; + std::cerr << " tunnel id : " << tunnel_id << std::endl; + std::cerr << " data size : " << size << std::endl; + std::cerr << " service id: " << std::hex << service_id << std::dec << std::endl; +#endif + + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + std::map::const_iterator it = _gxs_tunnel_contacts.find(tunnel_id) ; + + if(it == _gxs_tunnel_contacts.end()) + { + std::cerr << " (EE) no tunnel known with this ID. Sorry!" << std::endl; + return false ; + } + + // make sure the service is registered. + + if(mRegisteredServices.find(service_id) == mRegisteredServices.end()) + { + std::cerr << " (EE) no service registered with this ID. Please call rsGxsTunnel->registerClientService() at some point." << std::endl; + return false ; + } + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " verifications fine! Storing in out queue with:" << std::endl; +#endif + + RsGxsTunnelDataItem *item = new RsGxsTunnelDataItem ; + + item->unique_item_counter = global_item_counter++; // this allows to make the item unique + item->flags = 0; // not used yet. + item->service_id = service_id; + item->data_size = size; // encrypted data size + item->data = (uint8_t*)malloc(size); // encrypted data + item->PeerId(RsPeerId(tunnel_id)) ; + memcpy(item->data,data,size) ; + + GxsTunnelData& tdata( pendingGxsTunnelDataItems[item->unique_item_counter] ) ; + + tdata.data_item = item ; + tdata.last_sending_attempt = 0 ; // never sent until now + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " counter id : " << std::hex << item->unique_item_counter << std::dec << std::endl; +#endif + + return true ; +} + + +void p3GxsTunnelService::startClientGxsTunnelConnection(const RsGxsId& to_gxs_id,const RsGxsId& from_gxs_id,uint32_t service_id,RsGxsTunnelId& tunnel_id) +{ + // compute a random hash for that pair, and init the DH session for it so that we can recognise it when we get the virtual peer for it. + + RsFileHash hash = randomHashFromDestinationGxsId(to_gxs_id) ; + + tunnel_id = makeGxsTunnelId(from_gxs_id,to_gxs_id) ; + + { + RsStackMutex stack(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + if(_gxs_tunnel_contacts.find(tunnel_id) != _gxs_tunnel_contacts.end()) + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "GxsTunnelService:: asking GXS tunnel for a configuration that already exits.Ignoring." << std::endl; +#endif + return ; + } + } + + GxsTunnelPeerInfo info ; + + time_t now = time(NULL) ; + + info.last_contact = now ; + info.last_keep_alive_sent = now ; + info.status = RS_GXS_TUNNEL_STATUS_TUNNEL_DN ; + info.own_gxs_id = from_gxs_id ; + info.to_gxs_id = to_gxs_id ; + info.hash = hash ; + info.direction = RsTurtleGenericTunnelItem::DIRECTION_SERVER ; + info.virtual_peer_id.clear(); + info.client_services.insert(service_id) ; + + memset(info.aes_key,0,GXS_TUNNEL_AES_KEY_SIZE) ; + + { + RsStackMutex stack(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + _gxs_tunnel_contacts[tunnel_id] = info ; + } + +#ifdef DEBUG_GXS_TUNNEL + std::cerr << "Starting distant chat to " << to_gxs_id << ", hash = " << hash << ", from " << from_gxs_id << std::endl; + std::cerr << "Asking turtle router to monitor tunnels for hash " << hash << std::endl; +#endif + + // Now ask the turtle router to manage a tunnel for that hash. + + mTurtle->monitorTunnels(hash,this,false) ; +} + +TurtleFileHash p3GxsTunnelService::randomHashFromDestinationGxsId(const RsGxsId& destination) +{ + // This is in prevision for the "secured GXS tunnel" service, which will need a service ID to register, + // just like GRouter does. + + assert( destination.SIZE_IN_BYTES == 16) ; + assert(Sha1CheckSum::SIZE_IN_BYTES == 20) ; + + uint8_t bytes[20] ; + memcpy(&bytes[4],destination.toByteArray(),16) ; + + RAND_bytes(&bytes[0],4) ; // fill the 4 first bytes with random crap. Very important to allow tunnels from different sources and statistically avoid collisions. + + // We could rehash this, with a secret key to get a HMAC. That would allow to publish secret distant chat + // passphrases. I'll do this later if needed. + + return Sha1CheckSum(bytes) ; // this does not compute a hash, and that is on purpose. +} + +RsGxsId p3GxsTunnelService::destinationGxsIdFromHash(const TurtleFileHash& sum) +{ + assert( RsGxsId::SIZE_IN_BYTES == 16) ; + assert(Sha1CheckSum::SIZE_IN_BYTES == 20) ; + + return RsGxsId(&sum.toByteArray()[4]);// takes the last 16 bytes +} + +bool p3GxsTunnelService::getTunnelInfo(const RsGxsTunnelId& tunnel_id,GxsTunnelInfo& info) +{ + RsStackMutex stack(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + std::map::const_iterator it = _gxs_tunnel_contacts.find(tunnel_id) ; + + if(it == _gxs_tunnel_contacts.end()) + return false ; + + info.destination_gxs_id = it->second.to_gxs_id; + info.source_gxs_id = it->second.own_gxs_id; + info.tunnel_status = it->second.status; + info.total_size_sent = it->second.total_sent; + info.total_size_received= it->second.total_received; + + // Data packets + + info.pending_data_packets = 0; + info.total_data_packets_sent=0 ; + info.total_data_packets_received=0 ; + + return true ; +} + +bool p3GxsTunnelService::closeExistingTunnel(const RsGxsTunnelId& tunnel_id, uint32_t service_id) +{ + // two cases: + // - client needs to stop asking for tunnels => remove the hash from the list of tunnelled files + // - server needs to only close the window and let the tunnel die. But the window should only open + // if a message arrives. + + TurtleFileHash hash ; + TurtleVirtualPeerId vpid ; + bool close_tunnel = false ; + int direction ; + { + RsStackMutex stack(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + std::map::iterator it = _gxs_tunnel_contacts.find(tunnel_id) ; + + if(it == _gxs_tunnel_contacts.end()) + { + std::cerr << "(EE) Cannot close distant tunnel connection. No connection openned for tunnel id " << tunnel_id << std::endl; + + // We cannot stop tunnels, since their peer id is lost. Anyway, they'll die of starving. + + return false ; + } + vpid = it->second.virtual_peer_id ; + + std::map::const_iterator it2 = _gxs_tunnel_virtual_peer_ids.find(vpid) ; + + if(it2 != _gxs_tunnel_virtual_peer_ids.end()) + hash = it2->second.hash ; + + // check how many clients are used. If empty, close the tunnel + + std::set::iterator it3 = it->second.client_services.find(service_id) ; + + if(it3 == it->second.client_services.end()) + { + std::cerr << "(EE) service id not currently using that tunnel. This is an error." << std::endl; + return false; + } + + it->second.client_services.erase(it3) ; + direction = it->second.direction ; + + if(it->second.client_services.empty()) + close_tunnel = true ; + } + + if(close_tunnel) + { + // send a status item saying that we're closing the connection +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " Sending a ACK to close the tunnel since we're managing it and it's not used by any service. tunnel id=." << tunnel_id << std::endl; +#endif + + RsGxsTunnelStatusItem *cs = new RsGxsTunnelStatusItem ; + + cs->status = RS_GXS_TUNNEL_FLAG_CLOSING_DISTANT_CONNECTION; + cs->PeerId(RsPeerId(tunnel_id)) ; + + locked_sendEncryptedTunnelData(cs) ; // that needs to be done off-mutex and before we close the tunnel also ignoring failure. + + if(direction == RsTurtleGenericTunnelItem::DIRECTION_SERVER) // nothing more to do for server side. + { +#ifdef DEBUG_GXS_TUNNEL + std::cerr << " This is client side. Stopping tunnel manageement for tunnel_id " << tunnel_id << std::endl; +#endif + mTurtle->stopMonitoringTunnels( hash ) ; // still valid if the hash is null + } + + RsStackMutex stack(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + std::map::iterator it = _gxs_tunnel_contacts.find(tunnel_id) ; + + if(it == _gxs_tunnel_contacts.end()) // server side. Nothing to do. + { + std::cerr << "(EE) Cannot close chat associated to tunnel id " << tunnel_id << ": not found." << std::endl; + return false ; + } + + _gxs_tunnel_contacts.erase(it) ; + + // GxsTunnelService::removeVirtualPeerId() will be called by the turtle service. + } + return true ; +} + +bool p3GxsTunnelService::getTunnelsInfo(std::vector &infos) +{ + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + for(std::map::const_iterator it(_gxs_tunnel_contacts.begin());it!=_gxs_tunnel_contacts.end();++it) + { + GxsTunnelInfo ti ; + + ti.tunnel_id = it->first ; + ti.destination_gxs_id = it->second.to_gxs_id ; + ti.source_gxs_id = it->second.own_gxs_id ; + ti.tunnel_status = it->second.status ; + ti.total_size_sent = it->second.total_sent ; + ti.total_size_received = it->second.total_received ; + + infos.push_back(ti) ; + } + + return true ; +} + +void p3GxsTunnelService::debug_dump() +{ + RS_STACK_MUTEX(mGxsTunnelMtx); /********** STACK LOCKED MTX ******/ + + time_t now = time(NULL) ; + + std::cerr << "p3GxsTunnelService::debug_dump()" << std::endl; + std::cerr << " Registered client services: " << std::endl; + + for(std::map::const_iterator it=mRegisteredServices.begin();it!=mRegisteredServices.end();++it) + std::cerr << std::hex << " " << it->first << " - " << (void*)it->second << std::dec << std::endl; + + std::cerr << " Active tunnels" << std::endl; + + for(std::map::const_iterator it=_gxs_tunnel_contacts.begin();it!=_gxs_tunnel_contacts.end();++it) + std::cerr << " tunnel_id=" << it->first << " vpid=" << it->second.virtual_peer_id << " status=" << it->second.status << " direction=" << it->second.direction << " last_contact=" << (now-it->second.last_contact) <<" secs ago. Last_keep_alive_sent:" << (now - it->second.last_keep_alive_sent) << " secs ago." << std::endl; + + std::cerr << " Virtual peers:" << std::endl; + + for(std::map::const_iterator it=_gxs_tunnel_virtual_peer_ids.begin();it!=_gxs_tunnel_virtual_peer_ids.end();++it) + std::cerr << " vpid=" << it->first << " to=" << it->second.gxs_id << " from=" << it->second.own_gxs_id << " tunnel_id=" << it->second.tunnel_id << " status=" << it->second.status << " direction=" << it->second.direction << " hash=" << it->second.hash << std::endl; + + std::cerr << " Pending items: " << std::endl; + std::cerr << " DH : " << pendingDHItems.size() << std::endl; + std::cerr << " Tunnel Management: " << pendingGxsTunnelItems.size() << std::endl; + std::cerr << " Data (client) : " << pendingGxsTunnelDataItems.size() << std::endl; +} + + + + + + + + + + + + + + + + diff --git a/libretroshare/src/gxstunnel/p3gxstunnel.h b/libretroshare/src/gxstunnel/p3gxstunnel.h new file mode 100644 index 000000000..67589f8e5 --- /dev/null +++ b/libretroshare/src/gxstunnel/p3gxstunnel.h @@ -0,0 +1,261 @@ +/* + * libretroshare/src/chat: distantchat.h + * + * Services for RetroShare. + * + * Copyright 2015 by Cyril Soler + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License Version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + * + * Please report all bugs and problems to "csoler@users.sourceforge.net". + * + */ + +#pragma once + +// Generic tunnel service +// +// Preconditions: +// * the secured tunnel service takes care of: +// - tunnel health: tunnels are kept alive using special items, re-openned when necessary, etc. +// - transport: items are ACK-ed and re-sent if never received +// - encryption: items are all encrypted and authenticated using PFS(DH)+HMAC(sha1)+AES(128) +// * each tunnel is associated to a specific GXS id on both sides. Consequently, services that request tunnels from different IDs to a +// server for the same GXS id need to be handled correctly. +// * client services must register to the secured tunnel service if they want to use it. +// * multiple services can use the same tunnel. Items contain a service Id that is obtained when registering to the secured tunnel service. +// +// GUI +// * the GUI should show for each tunnel: +// - starting and ending GXS ids +// - tunnel status (DH ok, closed from distant peer, locally closed, etc) +// - amount of data that is transferred in the tunnel +// - number of pending items (and total size) +// - number ACKed items both ways. +// +// We can use an additional tab "Authenticated tunnels" in the statistics->turtle window for that purpose. +// +// Interaction with services: +// +// Services request tunnels from a given GXS id and to a given GXS id. When ready, they get a handle (type = RsGxsTunnelId) +// +// Services send data in the tunnel using the virtual peer id +// +// Data is send to a service ID (could be any existing service ID). The endpoint of the tunnel must register each service, in order to +// allow the data to be transmitted/sent from/to that service. Otherwise an error is issued. +// +// Encryption +// * the whole tunnel traffic is encrypted using AES-128 with random IV +// * a random key is established using DH key exchange for each connection (establishment of a new virtual peer) +// * encrypted items are authenticated with HMAC(sha1). +// * DH public keys are the only chunks of data that travel un-encrypted along the tunnel. They are +// signed to avoid any MITM interactions. No time-stamp is used in DH exchange since a replay attack would not work. +// +// Algorithms +// +// * we need two layers: the turtle layer, and the GXS id layer. +// - for each pair of GXS ids talking, a single turtle tunnel is used +// - that tunnel can be shared by multiple services using it. +// - services are responsoble for asking tunnels and also droppping them when unused. +// - at the turtle layer, the tunnel will be effectively closed only when no service uses it. +// * IDs +// TurtleVirtualPeerId: +// - Used by tunnel service for each turtle tunnel +// - one virtual peer ID per GXS tunnel +// +// GxsTunnelId: +// - one GxsTunnelId per couple of GXS ids. But we also need to allow multiple services to use the tunnel. +// +// * at the turtle layer: +// - accept virtual peers from turtle tunnel service. The hash for that VP only depends on the server GXS id at server side, which is our +// own ID at server side, and destination ID at client side. What happens if two different clients request to talk to the same GXS id? (same hash) +// They should use different virtual peers, so it should be ok. +// +// Turtle hash: [ 0 ---------------15 16---19 ] +// Destination Random +// +// We Use 16 bytes to target the exact destination of the hash. The source part is just 4 arbitrary bytes that need to be different for all source +// IDs that come from the same peer, which is quite likely to be sufficient. The real source of the tunnel will make itself known when sending the +// DH key. +// +// * at the GXS layer +// - we should be able to have as many tunnels as they are different couples of GXS ids to interact. That means the tunnel should be determined +// by a mix between our own GXS id and the GXS id we're talking to. That is what the TunnelVirtualPeer is. +// +// +// RequestTunnel(source_own_id,destination_id) - +// | | +// +---------------------------> p3Turtle::monitorTunnels( hash(destination_id) ) | +// | | +// [Turtle async work] -------------------+ | Turtle layer: one virtual peer id +// | | | +// handleTunnelRequest() <-----------------------------------------------+ | | +// | | | +// +---------------- keep record in _gxs_tunnel_virtual_peer_id, initiate DH exchange | - +// | | +// handleDHPublicKey() <-----------------------------------------------------------------------------+ | +// | | +// +---------------- update _gxs_tunnel_contacts[ tunnel_hash = hash(own_id, destination_id) ] | GxsTunnelId level +// | | +// +---------------- notify client service that Peer(destination_id, tunnel_hash) is ready to talk to | +// - + +#include +#include +#include +#include + +class RsGixs ; + +static const uint32_t GXS_TUNNEL_AES_KEY_SIZE = 16 ; + +class p3GxsTunnelService: public RsGxsTunnelService, public RsTurtleClientService, public p3Service +{ +public: + p3GxsTunnelService(RsGixs *pids) ; + virtual void connectToTurtleRouter(p3turtle *) ; + + // Creates the invite if the public key of the distant peer is available. + // Om success, stores the invite in the map above, so that we can respond to tunnel requests. + // + virtual bool requestSecuredTunnel(const RsGxsId& to_id,const RsGxsId& from_id,RsGxsTunnelId& tunnel_id,uint32_t service_id,uint32_t& error_code) ; + + virtual bool closeExistingTunnel(const RsGxsTunnelId &tunnel_id,uint32_t service_id) ; + virtual bool getTunnelsInfo(std::vector& infos); + virtual bool getTunnelInfo(const RsGxsTunnelId& tunnel_id,GxsTunnelInfo& info); + virtual bool sendData(const RsGxsTunnelId& tunnel_id,uint32_t service_id,const uint8_t *data,uint32_t size) ; + + virtual bool registerClientService(uint32_t service_id,RsGxsTunnelClientService *service) ; + + // derived from p3service + + virtual int tick(); + virtual RsServiceInfo getServiceInfo(); + +private: + void flush() ; + virtual void handleIncomingItem(const RsGxsTunnelId &tunnel_id, RsGxsTunnelItem *) ; + + class GxsTunnelPeerInfo + { + public: + GxsTunnelPeerInfo() : last_contact(0), last_keep_alive_sent(0), status(0), direction(0) + { + memset(aes_key, 0, GXS_TUNNEL_AES_KEY_SIZE); + + total_sent = 0 ; + total_received = 0 ; + } + + time_t last_contact ; // used to keep track of working connexion + time_t last_keep_alive_sent ; // last time we sent a keep alive packet. + + unsigned char aes_key[GXS_TUNNEL_AES_KEY_SIZE] ; + + uint32_t status ; // info: do we have a tunnel ? + RsPeerId virtual_peer_id; // given by the turtle router. Identifies the tunnel. + RsGxsId to_gxs_id; // gxs id we're talking to + RsGxsId own_gxs_id ; // gxs id we're using to talk. + RsTurtleGenericTunnelItem::Direction direction ; // specifiec wether we are client(managing the tunnel) or server. + TurtleFileHash hash ; // hash that is last used. This is necessary for handling tunnel establishment + std::set client_services ;// services that used this tunnel + uint32_t total_sent ; + uint32_t total_received ; + }; + + class GxsTunnelDHInfo + { + public: + GxsTunnelDHInfo() : dh(0), direction(0), status(0) {} + + DH *dh ; + RsGxsId gxs_id ; + RsGxsId own_gxs_id ; + RsGxsTunnelId tunnel_id ; // this is a proxy, since we cna always recompute that from the two previous values. + RsTurtleGenericTunnelItem::Direction direction ; + uint32_t status ; + TurtleFileHash hash ; + }; + + struct GxsTunnelData + { + RsGxsTunnelDataItem *data_item ; + time_t last_sending_attempt ; + }; + + // This maps contains the current peers to talk to with distant chat. + // + std::map _gxs_tunnel_contacts ; // current peers we can talk to + std::map _gxs_tunnel_virtual_peer_ids ; // current virtual peers. Used to figure out tunnels, etc. + + // List of items to be sent asap. Used to store items that we cannot pass directly to + // sendTurtleData(), because of Mutex protection. + + std::map pendingGxsTunnelDataItems ; // items that need provable transport and encryption + std::list pendingGxsTunnelItems ; // items that do not need provable transport, yet need encryption + std::list pendingDHItems ; + + // Overloaded from RsTurtleClientService + + virtual bool handleTunnelRequest(const RsFileHash &hash,const RsPeerId& peer_id) ; + virtual void receiveTurtleData(RsTurtleGenericTunnelItem *item,const RsFileHash& hash,const RsPeerId& virtual_peer_id,RsTurtleGenericTunnelItem::Direction direction) ; + void addVirtualPeer(const TurtleFileHash&, const TurtleVirtualPeerId&,RsTurtleGenericTunnelItem::Direction dir) ; + void removeVirtualPeer(const TurtleFileHash&, const TurtleVirtualPeerId&) ; + + // session handling handles + + void startClientGxsTunnelConnection(const RsGxsId &to_gxs_id, const RsGxsId& from_gxs_id, uint32_t service_id, RsGxsTunnelId &tunnel_id) ; + void locked_restartDHSession(const RsPeerId &virtual_peer_id, const RsGxsId &own_gxs_id) ; + + // utility functions + + static TurtleFileHash randomHashFromDestinationGxsId(const RsGxsId& destination) ; + static RsGxsId destinationGxsIdFromHash(const TurtleFileHash& sum) ; + + // Cryptography management + + void handleRecvDHPublicKey(RsGxsTunnelDHPublicKeyItem *item) ; + bool locked_sendDHPublicKey(const DH *dh, const RsGxsId& own_gxs_id, const RsPeerId& virtual_peer_id) ; + bool locked_initDHSessionKey(DH *&dh); + + TurtleVirtualPeerId virtualPeerIdFromHash(const TurtleFileHash& hash) ; // ... and to a hash for p3turtle + RsGxsTunnelId makeGxsTunnelId(const RsGxsId &own_id, const RsGxsId &distant_id) const; // creates a unique ID from two GXS ids. + + // item handling + + void handleRecvStatusItem(const RsGxsTunnelId& id,RsGxsTunnelStatusItem *item) ; + void handleRecvTunnelDataItem(const RsGxsTunnelId& id,RsGxsTunnelDataItem *item) ; + void handleRecvTunnelDataAckItem(const RsGxsTunnelId &id, RsGxsTunnelDataAckItem *item); + + // Comunication with Turtle service + + bool locked_sendEncryptedTunnelData(RsGxsTunnelItem *item) ; + bool locked_sendClearTunnelData(RsGxsTunnelDHPublicKeyItem *item); // this limits the usage to DH items. Others should be encrypted! + + bool handleEncryptedData(const uint8_t *data_bytes,uint32_t data_size,const TurtleFileHash& hash,const RsPeerId& virtual_peer_id) ; + + // local data + + p3turtle *mTurtle ; + RsGixs *mGixs ; + RsMutex mGxsTunnelMtx ; + + uint64_t global_item_counter ; + + std::map mRegisteredServices ; + + void debug_dump(); +}; + diff --git a/libretroshare/src/gxstunnel/rsgxstunnelitems.cc b/libretroshare/src/gxstunnel/rsgxstunnelitems.cc new file mode 100644 index 000000000..ebc818228 --- /dev/null +++ b/libretroshare/src/gxstunnel/rsgxstunnelitems.cc @@ -0,0 +1,486 @@ + +/* + * libretroshare/src/serialiser: rsbaseitems.cc + * + * RetroShare Serialiser. + * + * Copyright 2007-2008 by Robert Fernie. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License Version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + * + * Please report all bugs and problems to "retroshare@lunamutt.com". + * + */ + +#include +#include +#include "serialiser/rsbaseserial.h" +#include "serialiser/rstlvbase.h" +#include "util/rsprint.h" + +#include "gxstunnel/rsgxstunnelitems.h" + +#define GXS_TUNNEL_ITEM_DEBUG 1 + +std::ostream& RsGxsTunnelDHPublicKeyItem::print(std::ostream &out, uint16_t indent) +{ + printRsItemBase(out, "RsGxsTunnelDHPublicKeyItem", indent); + uint16_t int_Indent = indent + 2; + + printIndent(out, int_Indent); + out << " Signature Key ID: " << signature.keyId << std::endl ; + out << " Public Key ID: " << gxs_key.keyId << std::endl ; + + printRsItemEnd(out, "RsGxsTunnelMsgItem", indent); + return out; +} + +std::ostream& RsGxsTunnelDataItem::print(std::ostream &out, uint16_t indent) +{ + printRsItemBase(out, "RsGxsTunnelDataItem", indent); + uint16_t int_Indent = indent + 2; + + printIndent(out, int_Indent); + out << " message id : " << std::hex << unique_item_counter << std::dec << std::endl ; + out << " service id : " << std::hex << service_id << std::dec << std::endl ; + out << " flags : " << std::hex << flags << std::dec << std::endl ; + out << " size : " << data_size << std::endl ; + out << " data : " << RsUtil::BinToHex(data,std::min(50u,data_size)) << ((data_size>50u)?"...":"") << std::endl ; + + printRsItemEnd(out, "RsGxsTunnelDataItem", indent); + return out; +} +std::ostream& RsGxsTunnelDataAckItem::print(std::ostream &out, uint16_t indent) +{ + printRsItemBase(out, "RsGxsTunnelDataItem", indent); + uint16_t int_Indent = indent + 2; + + printIndent(out, int_Indent); + out << " message id : " << std::hex << unique_item_counter << std::dec << std::endl ; + + printRsItemEnd(out, "RsGxsTunnelDataAckItem", indent); + return out; +} +std::ostream& RsGxsTunnelStatusItem::print(std::ostream &out, uint16_t indent) +{ + printRsItemBase(out, "RsGxsTunnelDataItem", indent); + uint16_t int_Indent = indent + 2; + + printIndent(out, int_Indent); + out << " flags : " << std::hex << status << std::dec << std::endl ; + + printRsItemEnd(out, "RsGxsTunnelStatusItem", indent); + return out; +} + +/*************************************************************************/ + +RsGxsTunnelDHPublicKeyItem::~RsGxsTunnelDHPublicKeyItem() +{ + BN_free(public_key) ; +} + +/*************************************************************************/ + +RsItem *RsGxsTunnelSerialiser::deserialise(void *data, uint32_t *pktsize) +{ + uint32_t rstype = getRsItemId(data); + uint32_t rssize = getRsItemSize(data); + +#ifdef GXS_TUNNEL_ITEM_DEBUG + std::cerr << "deserializing packet..."<< std::endl ; +#endif + // look what we have... + if (*pktsize < rssize) /* check size */ + { + std::cerr << "GxsTunnel deserialisation: not enough size: pktsize=" << *pktsize << ", rssize=" << rssize << std::endl ; + return NULL; /* not enough data */ + } + + /* set the packet length */ + *pktsize = rssize; + + /* ready to load */ + + if ((RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) || (RS_SERVICE_TYPE_GXS_TUNNEL != getRsItemService(rstype))) + { +#ifdef GXS_TUNNEL_ITEM_DEBUG + std::cerr << "GxsTunnel deserialisation: wrong type !" << std::endl ; +#endif + return NULL; /* wrong type */ + } + + switch(getRsItemSubType(rstype)) + { + case RS_PKT_SUBTYPE_GXS_TUNNEL_DH_PUBLIC_KEY: return deserialise_RsGxsTunnelDHPublicKeyItem(data,*pktsize) ; + case RS_PKT_SUBTYPE_GXS_TUNNEL_DATA: return deserialise_RsGxsTunnelDataItem (data,*pktsize) ; + case RS_PKT_SUBTYPE_GXS_TUNNEL_DATA_ACK: return deserialise_RsGxsTunnelDataAckItem (data,*pktsize) ; + case RS_PKT_SUBTYPE_GXS_TUNNEL_STATUS: return deserialise_RsGxsTunnelStatusItem (data,*pktsize) ; + default: + std::cerr << "Unknown packet type in chat!" << std::endl ; + return NULL ; + } +} + +/*************************************************************************/ + +uint32_t RsGxsTunnelDHPublicKeyItem::serial_size() +{ + uint32_t s = 8 ; // header + s += 4 ; // BN size + s += BN_num_bytes(public_key) ; // public_key + s += signature.TlvSize() ; // signature + s += gxs_key.TlvSize() ; // gxs_key + + return s ; +} + +uint32_t RsGxsTunnelDataItem::serial_size() +{ + uint32_t s = 8 ; // header + s += 8 ; // counter + s += 4 ; // flags + s += 4 ; // service id + s += 4 ; // data_size + s += data_size; // data + + return s ; +} + +uint32_t RsGxsTunnelDataAckItem::serial_size() +{ + uint32_t s = 8 ; // header + s += 8 ; // counter + + return s ; +} + +uint32_t RsGxsTunnelStatusItem::serial_size() +{ + uint32_t s = 8 ; // header + s += 4 ; // flags + + return s ; +} +/*************************************************************************/ + +bool RsGxsTunnelDHPublicKeyItem::serialise(void *data,uint32_t& pktsize) +{ + uint32_t tlvsize = serial_size() ; + uint32_t offset = 0; + + if (pktsize < tlvsize) + return false; /* not enough space */ + + pktsize = tlvsize; + + bool ok = true; + + ok &= setRsItemHeader(data, tlvsize, PacketId(), tlvsize); + + /* skip the header */ + offset += 8; + + uint32_t s = BN_num_bytes(public_key) ; + + ok &= setRawUInt32(data, tlvsize, &offset, s); + + BN_bn2bin(public_key,&((unsigned char *)data)[offset]) ; + offset += s ; + + ok &= signature.SetTlv(data, tlvsize, &offset); + ok &= gxs_key.SetTlv(data, tlvsize, &offset); + + if (offset != tlvsize) + { + ok = false; + std::cerr << "RsGxsTunnelDHPublicKeyItem::serialiseItem() Size Error! offset=" << offset << ", tlvsize=" << tlvsize << std::endl; + } + return ok ; +} + +bool RsGxsTunnelStatusItem::serialise(void *data, uint32_t& pktsize) +{ + uint32_t tlvsize = serial_size() ; + uint32_t offset = 0; + + if (pktsize < tlvsize) + return false; /* not enough space */ + + pktsize = tlvsize; + + bool ok = true; + + ok &= setRsItemHeader(data, tlvsize, PacketId(), tlvsize); + +#ifdef GXS_TUNNEL_ITEM_DEBUG + std::cerr << "RsGxsTunnelSerialiser serialising chat status item." << std::endl; + std::cerr << "RsGxsTunnelSerialiser::serialiseItem() Header: " << ok << std::endl; + std::cerr << "RsGxsTunnelSerialiser::serialiseItem() Size: " << tlvsize << std::endl; +#endif + + /* skip the header */ + offset += 8; + + /* add mandatory parts first */ + ok &= setRawUInt32(data, tlvsize, &offset, status); + + if (offset != tlvsize) + { + ok = false; + std::cerr << "RsGxsTunnelSerialiser::serialiseItem() Size Error! " << std::endl; + } +#ifdef GXS_TUNNEL_ITEM_DEBUG + std::cerr << "computed size: " << 256*((unsigned char*)data)[6]+((unsigned char*)data)[7] << std::endl ; +#endif + + return ok; +} + +bool RsGxsTunnelDataItem::serialise(void *dt, uint32_t& pktsize) +{ + uint32_t tlvsize = serial_size() ; + uint32_t offset = 0; + + if (pktsize < tlvsize) + return false; /* not enough space */ + + pktsize = tlvsize; + + bool ok = true; + + ok &= setRsItemHeader(dt, tlvsize, PacketId(), tlvsize); + +#ifdef GXS_TUNNEL_ITEM_DEBUG + std::cerr << "RsGxsTunnelSerialiser serialising chat status item." << std::endl; + std::cerr << "RsGxsTunnelSerialiser::serialiseItem() Header: " << ok << std::endl; + std::cerr << "RsGxsTunnelSerialiser::serialiseItem() Size: " << tlvsize << std::endl; +#endif + + /* skip the header */ + offset += 8; + + /* add mandatory parts first */ + ok &= setRawUInt64(dt, tlvsize, &offset, unique_item_counter); + ok &= setRawUInt32(dt, tlvsize, &offset, flags); + ok &= setRawUInt32(dt, tlvsize, &offset, service_id); + ok &= setRawUInt32(dt, tlvsize, &offset, data_size); + + if(offset + data_size <= tlvsize) + { + memcpy(&((uint8_t*)dt)[offset],data,data_size) ; + offset += data_size ; + } + else + ok = false ; + + if (offset != tlvsize) + { + ok = false; + std::cerr << "RsGxsTunnelSerialiser::serialiseItem() Size Error! " << std::endl; + } + + return ok; +} +bool RsGxsTunnelDataAckItem::serialise(void *data, uint32_t& pktsize) +{ + uint32_t tlvsize = serial_size() ; + uint32_t offset = 0; + + if (pktsize < tlvsize) + return false; /* not enough space */ + + pktsize = tlvsize; + + bool ok = true; + + ok &= setRsItemHeader(data, tlvsize, PacketId(), tlvsize); + +#ifdef GXS_TUNNEL_ITEM_DEBUG + std::cerr << "RsGxsTunnelSerialiser serialising chat status item." << std::endl; + std::cerr << "RsGxsTunnelSerialiser::serialiseItem() Header: " << ok << std::endl; + std::cerr << "RsGxsTunnelSerialiser::serialiseItem() Size: " << tlvsize << std::endl; +#endif + + /* skip the header */ + offset += 8; + + /* add mandatory parts first */ + ok &= setRawUInt64(data, tlvsize, &offset, unique_item_counter); + + if (offset != tlvsize) + { + ok = false; + std::cerr << "RsGxsTunnelSerialiser::serialiseItem() Size Error! " << std::endl; + } + + return ok; +} + +/*************************************************************************/ + +RsGxsTunnelDHPublicKeyItem *RsGxsTunnelSerialiser::deserialise_RsGxsTunnelDHPublicKeyItem(void *data,uint32_t /*size*/) +{ + uint32_t offset = 8; // skip the header + uint32_t rssize = getRsItemSize(data); + bool ok = true ; + + RsGxsTunnelDHPublicKeyItem *item = new RsGxsTunnelDHPublicKeyItem() ; + + uint32_t s=0 ; + /* get mandatory parts first */ + ok &= getRawUInt32(data, rssize, &offset, &s); + + item->public_key = BN_bin2bn(&((unsigned char *)data)[offset],s,NULL) ; + offset += s ; + + ok &= item->signature.GetTlv(data, rssize, &offset) ; + ok &= item->gxs_key.GetTlv(data, rssize, &offset) ; + + if (offset != rssize) + { + std::cerr << "RsGxsTunnelDHPublicKeyItem::() Size error while deserializing." << std::endl ; + delete item ; + return NULL ; + } + if (!ok) + { + std::cerr << "RsGxsTunnelDHPublicKeyItem::() Unknown error while deserializing." << std::endl ; + delete item ; + return NULL ; + } + + return item ; +} + +RsGxsTunnelDataItem *RsGxsTunnelSerialiser::deserialise_RsGxsTunnelDataItem(void *dat,uint32_t size) +{ + uint32_t offset = 8; // skip the header + uint32_t rssize = getRsItemSize(dat); + bool ok = true ; + + RsGxsTunnelDataItem *item = new RsGxsTunnelDataItem(); + + /* get mandatory parts first */ + + ok &= getRawUInt64(dat, rssize, &offset, &item->unique_item_counter); + ok &= getRawUInt32(dat, rssize, &offset, &item->flags); + ok &= getRawUInt32(dat, rssize, &offset, &item->service_id); + ok &= getRawUInt32(dat, rssize, &offset, &item->data_size); + + if(offset + item->data_size <= size) + { + item->data = (unsigned char*)malloc(item->data_size) ; + + if(dat == NULL) + { + delete item ; + return NULL ; + } + + memcpy(item->data,&((uint8_t*)dat)[offset],item->data_size) ; + offset += item->data_size ; + } + else + ok = false ; + + + if (offset != rssize) + { + std::cerr << "RsGxsTunnelDHPublicKeyItem::() Size error while deserializing." << std::endl ; + delete item ; + return NULL ; + } + if (!ok) + { + std::cerr << "RsGxsTunnelDHPublicKeyItem::() Unknown error while deserializing." << std::endl ; + delete item ; + return NULL ; + } + + return item ; +} + +RsGxsTunnelDataAckItem *RsGxsTunnelSerialiser::deserialise_RsGxsTunnelDataAckItem(void *dat,uint32_t /* size */) +{ + uint32_t offset = 8; // skip the header + uint32_t rssize = getRsItemSize(dat); + bool ok = true ; + + RsGxsTunnelDataAckItem *item = new RsGxsTunnelDataAckItem(); + + /* get mandatory parts first */ + + ok &= getRawUInt64(dat, rssize, &offset, &item->unique_item_counter); + + if (offset != rssize) + { + std::cerr << "RsGxsTunnelDHPublicKeyItem::() Size error while deserializing." << std::endl ; + delete item ; + return NULL ; + } + if (!ok) + { + std::cerr << "RsGxsTunnelDHPublicKeyItem::() Unknown error while deserializing." << std::endl ; + delete item ; + return NULL ; + } + + return item ; +} + +RsGxsTunnelStatusItem *RsGxsTunnelSerialiser::deserialise_RsGxsTunnelStatusItem(void *dat,uint32_t size) +{ + uint32_t offset = 8; // skip the header + uint32_t rssize = getRsItemSize(dat); + bool ok = true ; + + RsGxsTunnelStatusItem *item = new RsGxsTunnelStatusItem(); + + /* get mandatory parts first */ + + ok &= getRawUInt32(dat, rssize, &offset, &item->status); + + if (offset != rssize) + { + std::cerr << "RsGxsTunnelDHPublicKeyItem::() Size error while deserializing." << std::endl ; + delete item ; + return NULL ; + } + if (!ok) + { + std::cerr << "RsGxsTunnelDHPublicKeyItem::() Unknown error while deserializing." << std::endl ; + delete item ; + return NULL ; + } + + return item ; +} + + + + + + + + + + + + + + + + diff --git a/libretroshare/src/gxstunnel/rsgxstunnelitems.h b/libretroshare/src/gxstunnel/rsgxstunnelitems.h new file mode 100644 index 000000000..d1abf8c20 --- /dev/null +++ b/libretroshare/src/gxstunnel/rsgxstunnelitems.h @@ -0,0 +1,177 @@ +/* + * libretroshare/src/serialiser: rschatitems.h + * + * RetroShare Serialiser. + * + * Copyright 2007-2008 by Robert Fernie. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License Version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + * + * Please report all bugs and problems to "retroshare@lunamutt.com". + * + */ + +#pragma once + +#include + +#include "retroshare/rstypes.h" +#include "serialiser/rstlvkeys.h" +#include "serialiser/rsserviceids.h" +#include "serialiser/rsserial.h" + +#include "serialiser/rstlvidset.h" +#include "serialiser/rstlvfileitem.h" + +/* chat Flags */ +const uint32_t RS_GXS_TUNNEL_FLAG_CLOSING_DISTANT_CONNECTION = 0x0400; +const uint32_t RS_GXS_TUNNEL_FLAG_ACK_DISTANT_CONNECTION = 0x0800; +const uint32_t RS_GXS_TUNNEL_FLAG_KEEP_ALIVE = 0x1000; + +const uint8_t RS_PKT_SUBTYPE_GXS_TUNNEL_DATA = 0x01 ; +const uint8_t RS_PKT_SUBTYPE_GXS_TUNNEL_DH_PUBLIC_KEY = 0x02 ; +const uint8_t RS_PKT_SUBTYPE_GXS_TUNNEL_STATUS = 0x03 ; +const uint8_t RS_PKT_SUBTYPE_GXS_TUNNEL_DATA_ACK = 0x04 ; + +typedef uint64_t GxsTunnelDHSessionId ; + +class RsGxsTunnelItem: public RsItem +{ + public: + RsGxsTunnelItem(uint8_t item_subtype) : RsItem(RS_PKT_VERSION_SERVICE,RS_SERVICE_TYPE_GXS_TUNNEL,item_subtype) + { + setPriorityLevel(QOS_PRIORITY_RS_CHAT_ITEM) ; + } + + virtual ~RsGxsTunnelItem() {} + virtual void clear() {} + virtual std::ostream& print(std::ostream &out, uint16_t indent = 0) = 0 ; + + virtual bool serialise(void *data,uint32_t& size) = 0 ; // Isn't it better that items can serialize themselves ? + virtual uint32_t serial_size() = 0 ; // deserialise is handled using a constructor +}; + +/*! + * For sending distant communication data. The item is not encrypted after being serialised, but the data it. + * The MAC is computed over encrypted data using the PFS key. All other items (except DH keys) are serialised, encrypted, and + * sent as data in a RsGxsTunnelDataItem. + * + * @see p3GxsTunnelService + */ +class RsGxsTunnelDataItem: public RsGxsTunnelItem +{ +public: + RsGxsTunnelDataItem() :RsGxsTunnelItem(RS_PKT_SUBTYPE_GXS_TUNNEL_DATA) { data=NULL ;data_size=0; } + RsGxsTunnelDataItem(uint8_t subtype) :RsGxsTunnelItem(subtype) { data=NULL ;data_size=0; } + + virtual ~RsGxsTunnelDataItem() {} + virtual void clear() {} + virtual std::ostream& print(std::ostream &out, uint16_t indent = 0); + + virtual bool serialise(void *data,uint32_t& size) ; // Isn't it better that items can serialize themselves ? + virtual uint32_t serial_size() ; // deserialise is handled using a constructor + + uint64_t unique_item_counter; // this allows to make the item unique + uint32_t flags; // mainly NEEDS_HACK? + uint32_t service_id ; + uint32_t data_size ; // encrypted data size + unsigned char *data ; // encrypted data +}; + +// Used to send status of connection. This can be closing orders, flushing orders, etc. +// These items are always sent encrypted. + +class RsGxsTunnelStatusItem: public RsGxsTunnelItem +{ + public: + RsGxsTunnelStatusItem() :RsGxsTunnelItem(RS_PKT_SUBTYPE_GXS_TUNNEL_STATUS) {} + RsGxsTunnelStatusItem(void *data,uint32_t size) ; // deserialization + + virtual ~RsGxsTunnelStatusItem() {} + virtual std::ostream& print(std::ostream &out, uint16_t indent = 0); + + virtual bool serialise(void *data,uint32_t& size) ; // Isn't it better that items can serialize themselves ? + virtual uint32_t serial_size() ; // deserialise is handled using a constructor + + uint32_t status ; +}; + +// Used to confirm reception of an encrypted item. + +class RsGxsTunnelDataAckItem: public RsGxsTunnelItem +{ + public: + RsGxsTunnelDataAckItem() :RsGxsTunnelItem(RS_PKT_SUBTYPE_GXS_TUNNEL_DATA_ACK) {} + RsGxsTunnelDataAckItem(void *data,uint32_t size) ; // deserialization + + virtual ~RsGxsTunnelDataAckItem() {} + virtual std::ostream& print(std::ostream &out, uint16_t indent = 0); + + virtual bool serialise(void *data,uint32_t& size) ; // Isn't it better that items can serialize themselves ? + virtual uint32_t serial_size() ; // deserialise is handled using a constructor + + uint64_t unique_item_counter ; // unique identifier for that item +}; + + +// This class contains the public Diffie-Hellman parameters to be sent +// when performing a DH agreement over a distant chat tunnel. +// +class RsGxsTunnelDHPublicKeyItem: public RsGxsTunnelItem +{ + public: + RsGxsTunnelDHPublicKeyItem() :RsGxsTunnelItem(RS_PKT_SUBTYPE_GXS_TUNNEL_DH_PUBLIC_KEY) {} + RsGxsTunnelDHPublicKeyItem(void *data,uint32_t size) ; // deserialization + + virtual ~RsGxsTunnelDHPublicKeyItem() ; + virtual std::ostream& print(std::ostream &out, uint16_t indent = 0); + + virtual bool serialise(void *data,uint32_t& size) ; // Isn't it better that items can serialize themselves ? + virtual uint32_t serial_size() ; // deserialise is handled using a constructor + + // Private data to DH public key item + // + BIGNUM *public_key ; + + RsTlvKeySignature signature ; // signs the public key in a row. + RsTlvSecurityKey gxs_key ; // public key of the signer + + private: + // make the object non copy-able + RsGxsTunnelDHPublicKeyItem(const RsGxsTunnelDHPublicKeyItem&) : RsGxsTunnelItem(RS_PKT_SUBTYPE_GXS_TUNNEL_DH_PUBLIC_KEY) {} + const RsGxsTunnelDHPublicKeyItem& operator=(const RsGxsTunnelDHPublicKeyItem&) { return *this ;} +}; + +class RsGxsTunnelSerialiser: public RsSerialType +{ +public: + RsGxsTunnelSerialiser() :RsSerialType(RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_GXS_TUNNEL) {} + + virtual uint32_t size (RsItem *item) + { + return static_cast(item)->serial_size() ; + } + virtual bool serialise(RsItem *item, void *data, uint32_t *size) + { + return static_cast(item)->serialise(data,*size) ; + } + RsItem *deserialise(void *data, uint32_t *pktsize); +private: + static RsGxsTunnelDataAckItem *deserialise_RsGxsTunnelDataAckItem (void *data, uint32_t size) ; + static RsGxsTunnelDataItem *deserialise_RsGxsTunnelDataItem (void *data, uint32_t size) ; + static RsGxsTunnelStatusItem *deserialise_RsGxsTunnelStatusItem (void *data, uint32_t size) ; + static RsGxsTunnelDHPublicKeyItem *deserialise_RsGxsTunnelDHPublicKeyItem(void *data, uint32_t size) ; +}; + diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index e64420b43..e9ac68bf3 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -731,6 +731,13 @@ SOURCES += serialiser/rsnxsitems.cc \ gxs/rsgxsutil.cc \ gxs/rsgxsrequesttypes.cc +# gxs tunnels +HEADERS += gxstunnel/p3gxstunnel.h \ + gxstunnel/rsgxstunnelitems.h \ + retroshare/rsgxstunnel.h + +SOURCES += gxstunnel/p3gxstunnel.cc \ + gxstunnel/rsgxstunnelitems.cc # Identity Service HEADERS += retroshare/rsidentity.h \ @@ -851,3 +858,4 @@ test_bitdht { # ENABLED UDP NOW. } + diff --git a/libretroshare/src/pqi/p3cfgmgr.cc b/libretroshare/src/pqi/p3cfgmgr.cc index 7c2d976b3..b920cffa8 100644 --- a/libretroshare/src/pqi/p3cfgmgr.cc +++ b/libretroshare/src/pqi/p3cfgmgr.cc @@ -208,7 +208,7 @@ p3Config::p3Config() } -bool p3Config::loadConfiguration(RsFileHash &loadHash) +bool p3Config::loadConfiguration(RsFileHash& /* loadHash */) { return loadConfig(); } diff --git a/libretroshare/src/pqi/p3historymgr.cc b/libretroshare/src/pqi/p3historymgr.cc index c2b173c6a..04e97f08a 100644 --- a/libretroshare/src/pqi/p3historymgr.cc +++ b/libretroshare/src/pqi/p3historymgr.cc @@ -103,17 +103,14 @@ void p3HistoryMgr::addMessage(const ChatMessage& cm) enabled = true; } - if (cm.chat_id.isGxsId() && mPrivateEnable == true) { - if (cm.incoming) { - peerName = cm.chat_id.toGxsId().toStdString(); - } else { - uint32_t status; - RsGxsId from_gxs_id; - if (rsMsgs->getDistantChatStatus(cm.chat_id.toGxsId(), status, &from_gxs_id)) - peerName = from_gxs_id.toStdString(); - } - enabled = true; - } + if(cm.chat_id.isDistantChatId()) + { + uint32_t status; + DistantChatPeerInfo dcpinfo; + if (rsMsgs->getDistantChatStatus(cm.chat_id.toDistantChatId(), dcpinfo)) + peerName = cm.chat_id.toPeerId().toStdString(); + enabled = true; + } if(enabled == false) return; @@ -411,8 +408,8 @@ bool p3HistoryMgr::chatIdToVirtualPeerId(ChatId chat_id, RsPeerId &peer_id) return true; } - if (chat_id.isGxsId()) { - peer_id = RsPeerId(chat_id.toGxsId()); + if (chat_id.isDistantChatId()) { + peer_id = RsPeerId(chat_id.toDistantChatId()); return true; } @@ -450,7 +447,7 @@ bool p3HistoryMgr::getMessages(const ChatId &chatId, std::list &msgs if (chatId.isLobbyId() && mLobbyEnable == true) { enabled = true; } - if (chatId.isGxsId() && mPrivateEnable == true) { + if (chatId.isDistantChatId() && mPrivateEnable == true) { enabled = true; } diff --git a/libretroshare/src/retroshare/rsgxstunnel.h b/libretroshare/src/retroshare/rsgxstunnel.h new file mode 100644 index 000000000..671441920 --- /dev/null +++ b/libretroshare/src/retroshare/rsgxstunnel.h @@ -0,0 +1,132 @@ +/* + * libretroshare/src/services: rsgrouter.h + * + * Services for RetroShare. + * + * Copyright 2013 by Cyril Soler + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License Version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + * + * Please report all bugs and problems to "csoler@users.sourceforge.net". + * + */ + +#pragma once + +#include "util/rsdir.h" +#include "retroshare/rsids.h" +#include "retroshare/rsturtle.h" +#include "retroshare/rsgxsifacetypes.h" + +class RsGxsTunnelService +{ +public: + typedef GXSTunnelId RsGxsTunnelId ; + + enum { + RS_GXS_TUNNEL_ERROR_NO_ERROR = 0x0000, + RS_GXS_TUNNEL_ERROR_UNKNOWN_GXS_ID = 0x0001 + }; + + enum { + RS_GXS_TUNNEL_STATUS_UNKNOWN = 0x00, + RS_GXS_TUNNEL_STATUS_TUNNEL_DN = 0x01, + RS_GXS_TUNNEL_STATUS_CAN_TALK = 0x02, + RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED = 0x03 + }; + + class RsGxsTunnelClientService + { + public: + // The client should derive this in order to handle notifications from the tunnel service. + // This cannot be ignored because the client needs to know when the tunnel is active. + + virtual void notifyTunnelStatus(const RsGxsTunnelId& tunnel_id,uint32_t tunnel_status) =0; + + // Data obtained from the corresponding GXS id. The memory ownership is transferred to the client, which + // is responsible to free it using free() once used. + + virtual void receiveData(const RsGxsTunnelId& id,unsigned char *data,uint32_t data_size) =0; + + // Used by the creator of the service to supply a pointer to the GXS tunnel service for it to be able to send data etc. + + virtual void connectToGxsTunnelService(RsGxsTunnelService *tunnel_service) =0; + }; + + class GxsTunnelInfo + { + public: + // Tunnel information + + RsGxsTunnelId tunnel_id ; + RsGxsId destination_gxs_id ; // GXS Id we're talking to + RsGxsId source_gxs_id ; // GXS Id we're using to talk + uint32_t tunnel_status ; // active, requested, DH pending, etc. + uint32_t total_size_sent ; // total bytes sent through that tunnel since openned (including management). + uint32_t total_size_received ; // total bytes received through that tunnel since openned (including management). + + // Data packets + + uint32_t pending_data_packets; // number of packets not acknowledged by other side, still on their way. Should be 0 unless something bad happens. + uint32_t total_data_packets_sent ; // total number of data packets sent (does not include tunnel management) + uint32_t total_data_packets_received ; // total number of data packets received (does not include tunnel management) + }; + + // This is the interface file for the secured tunnel service + // + //===================================================// + // Debugging info // + //===================================================// + + virtual bool getTunnelsInfo(std::vector& infos) =0; + virtual bool getTunnelInfo(const RsGxsTunnelId& tunnel_id,GxsTunnelInfo& info) =0; + + // retrieve the routing probabilities + + //===================================================// + // Communication to other services. // + //===================================================// + + // Register a new client service. The service ID needs to be unique, and it's the coder's resonsibility to use an ID that is not used elsewhere + // for the same purpose. + + virtual bool registerClientService(uint32_t service_id,RsGxsTunnelClientService *service) =0; + + // Asks for a tunnel. The service will request it to turtle router, and exchange a AES key using DH. + // When the tunnel is secured, the client---here supplied as argument---will be notified. He can + // then send data into the tunnel. The same tunnel may be used by different clients. + // The service id is passed on so that the client is notified when the tunnel is up. + + virtual bool requestSecuredTunnel(const RsGxsId& to_id,const RsGxsId& from_id,RsGxsTunnelId& tunnel_id,uint32_t service_id,uint32_t& error_code) =0 ; + + // Data is sent through the established tunnel, possibly multiple times, until reception is acknowledged. If the tunnel does not exist, the item is rejected and + // an error is issued. In any case, the memory ownership of the data is *not* transferred to the tunnel service, so the client should delete it afterwards, if needed. + + virtual bool sendData(const RsGxsTunnelId& tunnel_id, uint32_t client_service_id, const uint8_t *data, uint32_t data_size) =0; + + // Removes any established tunnel to this GXS id. This makes the tunnel refuse further data, but the tunnel will be however kept alive + // until all pending data is flushed. All clients attached to the tunnel will be notified that the tunnel gets closed. + + virtual bool closeExistingTunnel(const RsGxsTunnelId& to_id,uint32_t service_id) =0; + + //===================================================// + // Routage feedback from other services // + //===================================================// + +}; + +// To access the GRouter from anywhere +// +extern RsGxsTunnelService *rsGxsTunnel ; diff --git a/libretroshare/src/retroshare/rsids.h b/libretroshare/src/retroshare/rsids.h index 5b1a60bfa..6cd97f5ad 100644 --- a/libretroshare/src/retroshare/rsids.h +++ b/libretroshare/src/retroshare/rsids.h @@ -209,15 +209,17 @@ static const int SHA1_SIZE = 20 ; // These constants are random, but should be different, in order to make the various IDs incompatible with each other. // -static const uint32_t RS_GENERIC_ID_SSL_ID_TYPE = 0x0001 ; -static const uint32_t RS_GENERIC_ID_PGP_ID_TYPE = 0x0002 ; -static const uint32_t RS_GENERIC_ID_SHA1_ID_TYPE = 0x0003 ; -static const uint32_t RS_GENERIC_ID_PGP_FINGERPRINT_TYPE = 0x0004 ; -static const uint32_t RS_GENERIC_ID_GXS_GROUP_ID_TYPE = 0x0005 ; -static const uint32_t RS_GENERIC_ID_GXS_ID_TYPE = 0x0006 ; -static const uint32_t RS_GENERIC_ID_GXS_MSG_ID_TYPE = 0x0007 ; -static const uint32_t RS_GENERIC_ID_GXS_CIRCLE_ID_TYPE = 0x0008 ; -static const uint32_t RS_GENERIC_ID_GROUTER_ID_TYPE = 0x0009 ; +static const uint32_t RS_GENERIC_ID_SSL_ID_TYPE = 0x0001 ; +static const uint32_t RS_GENERIC_ID_PGP_ID_TYPE = 0x0002 ; +static const uint32_t RS_GENERIC_ID_SHA1_ID_TYPE = 0x0003 ; +static const uint32_t RS_GENERIC_ID_PGP_FINGERPRINT_TYPE = 0x0004 ; +static const uint32_t RS_GENERIC_ID_GXS_GROUP_ID_TYPE = 0x0005 ; +static const uint32_t RS_GENERIC_ID_GXS_ID_TYPE = 0x0006 ; +static const uint32_t RS_GENERIC_ID_GXS_MSG_ID_TYPE = 0x0007 ; +static const uint32_t RS_GENERIC_ID_GXS_CIRCLE_ID_TYPE = 0x0008 ; +static const uint32_t RS_GENERIC_ID_GROUTER_ID_TYPE = 0x0009 ; +static const uint32_t RS_GENERIC_ID_GXS_TUNNEL_ID_TYPE = 0x0010 ; +static const uint32_t RS_GENERIC_ID_GXS_DISTANT_CHAT_ID_TYPE = 0x0011 ; typedef t_RsGenericIdType< SSL_ID_SIZE , false, RS_GENERIC_ID_SSL_ID_TYPE> SSLIdType ; typedef t_RsGenericIdType< PGP_KEY_ID_SIZE , true, RS_GENERIC_ID_PGP_ID_TYPE> PGPIdType ; @@ -227,4 +229,6 @@ typedef t_RsGenericIdType< PGP_KEY_FINGERPRINT_SIZE, true, RS_GENERIC_ID_PGP_F typedef t_RsGenericIdType< CERT_SIGN_LEN , false, RS_GENERIC_ID_GXS_GROUP_ID_TYPE > GXSGroupId ; typedef t_RsGenericIdType< CERT_SIGN_LEN , false, RS_GENERIC_ID_GXS_ID_TYPE > GXSId ; typedef t_RsGenericIdType< CERT_SIGN_LEN , false, RS_GENERIC_ID_GXS_CIRCLE_ID_TYPE > GXSCircleId ; +typedef t_RsGenericIdType< SSL_ID_SIZE , false, RS_GENERIC_ID_GXS_TUNNEL_ID_TYPE > GXSTunnelId ; +typedef t_RsGenericIdType< SSL_ID_SIZE , false, RS_GENERIC_ID_GXS_DISTANT_CHAT_ID_TYPE > DistantChatPeerId ; diff --git a/libretroshare/src/retroshare/rsmsgs.h b/libretroshare/src/retroshare/rsmsgs.h index d71544d15..edeb0a66e 100644 --- a/libretroshare/src/retroshare/rsmsgs.h +++ b/libretroshare/src/retroshare/rsmsgs.h @@ -99,9 +99,6 @@ typedef uint64_t ChatLobbyId ; typedef uint64_t ChatLobbyMsgId ; typedef std::string ChatLobbyNickName ; -typedef RsPeerId DistantChatPeerId ; -//typedef GRouterKeyId DistantMsgPeerId ; - typedef uint64_t MessageId ; @@ -253,12 +250,10 @@ public: #define RS_CHAT_PRIVATE 0x0002 #define RS_CHAT_AVATAR_AVAILABLE 0x0004 -#define RS_DISTANT_CHAT_STATUS_UNKNOWN 0x0000 -#define RS_DISTANT_CHAT_STATUS_TUNNEL_DN 0x0001 -#define RS_DISTANT_CHAT_STATUS_TUNNEL_OK 0x0002 -#define RS_DISTANT_CHAT_STATUS_CAN_TALK 0x0003 -#define RS_DISTANT_CHAT_STATUS_REMOTELY_CLOSED 0x0004 -#define RS_DISTANT_CHAT_STATUS_WAITING_DH 0x0005 +#define RS_DISTANT_CHAT_STATUS_UNKNOWN 0x0000 +#define RS_DISTANT_CHAT_STATUS_TUNNEL_DN 0x0001 +#define RS_DISTANT_CHAT_STATUS_CAN_TALK 0x0002 +#define RS_DISTANT_CHAT_STATUS_REMOTELY_CLOSED 0x0003 #define RS_DISTANT_CHAT_ERROR_NO_ERROR 0x0000 #define RS_DISTANT_CHAT_ERROR_DECRYPTION_FAILED 0x0001 @@ -269,6 +264,14 @@ public: #define RS_DISTANT_CHAT_FLAG_SIGNED 0x0001 #define RS_DISTANT_CHAT_FLAG_SIGNATURE_OK 0x0002 +struct DistantChatPeerInfo +{ + RsGxsId to_id ; + RsGxsId own_id ; + DistantChatPeerId peer_id ; // this is the tunnel id actually + uint32_t status ; // see the values in rsmsgs.h +}; + // Identifier for an chat endpoint like // neighbour peer, distant peer, chatlobby, broadcast class ChatId @@ -276,8 +279,8 @@ class ChatId public: ChatId(); explicit ChatId(RsPeerId id); - explicit ChatId(RsGxsId id); explicit ChatId(ChatLobbyId id); + explicit ChatId(DistantChatPeerId id); explicit ChatId(std::string str); static ChatId makeBroadcastId(); @@ -289,13 +292,14 @@ public: bool isNotSet() const; bool isPeerId() const; - bool isGxsId() const; + bool isDistantChatId() const; bool isLobbyId() const; bool isBroadcast() const; RsPeerId toPeerId() const; RsGxsId toGxsId() const; ChatLobbyId toLobbyId() const; + DistantChatPeerId toDistantChatId() const; // for the very specific case of transfering a status string // from the chatservice to the gui, @@ -311,7 +315,7 @@ private: Type type; RsPeerId peer_id; - RsGxsId gxs_id; + DistantChatPeerId distant_chat_id; ChatLobbyId lobby_id; }; @@ -372,15 +376,6 @@ class ChatLobbyInfo time_t last_activity ; // last recorded activity. Useful for removing dead lobbies. }; -struct DistantChatInviteInfo -{ - DistantChatPeerId pid ; // pid to contact the invite and refer to it. - std::string encrypted_radix64_string ; // encrypted radix string used to for the chat link - RsPgpId destination_pgp_id ; // pgp is of the destination of the chat link - time_t time_of_validity ; // time when te invite becomes unusable - uint32_t invite_flags ; // used to keep track of wether signature was ok or not. -}; - std::ostream &operator<<(std::ostream &out, const Rs::Msgs::MessageInfo &info); class RsMsgs; @@ -481,9 +476,9 @@ virtual ChatLobbyId createChatLobby(const std::string& lobby_name,const RsGxsId& /* Distant chat */ /****************************************/ -virtual bool initiateDistantChatConnexion(const RsGxsId& to_pid,const RsGxsId& from_pid,uint32_t& error_code) = 0; -virtual bool getDistantChatStatus(const RsGxsId& gxs_id,uint32_t& status, RsGxsId *from_gxs_id = NULL) = 0; -virtual bool closeDistantChatConnexion(const RsGxsId& pid) = 0; +virtual bool initiateDistantChatConnexion(const RsGxsId& to_pid,const RsGxsId& from_pid,DistantChatPeerId& pid,uint32_t& error_code) = 0; +virtual bool getDistantChatStatus(const DistantChatPeerId& pid,DistantChatPeerInfo& info)=0; +virtual bool closeDistantChatConnexion(const DistantChatPeerId& pid)=0; }; diff --git a/libretroshare/src/retroshare/rsplugin.h b/libretroshare/src/retroshare/rsplugin.h index 6dc6dc05b..83936fe8e 100644 --- a/libretroshare/src/retroshare/rsplugin.h +++ b/libretroshare/src/retroshare/rsplugin.h @@ -42,6 +42,7 @@ class p3Service ; class RsServiceControl ; class RsReputations ; class RsTurtle ; +class RsGxsTunnelService ; class RsDht ; class RsDisc ; class RsMsgs ; @@ -117,6 +118,7 @@ public: RsUtil::inited_ptr mPgpAuxUtils; RsUtil::inited_ptr mGxsForums; RsUtil::inited_ptr mGxsChannels; + RsUtil::inited_ptr mGxsTunnels; RsUtil::inited_ptr mReputations; }; diff --git a/libretroshare/src/rsserver/p3face.h b/libretroshare/src/rsserver/p3face.h index 910434309..23efa5866 100644 --- a/libretroshare/src/rsserver/p3face.h +++ b/libretroshare/src/rsserver/p3face.h @@ -39,6 +39,7 @@ #include "util/rsthreads.h" #include "chat/p3chatservice.h" +#include "gxstunnel/p3gxstunnel.h" #include "services/p3msgservice.h" #include "services/p3statusservice.h" @@ -160,6 +161,7 @@ class RsServer: public RsControl, public RsTickingThread p3MsgService *msgSrv; p3ChatService *chatSrv; p3StatusService *mStatusSrv; + p3GxsTunnelService *mGxsTunnels; // This list contains all threaded services. It will be used to shut them down properly. diff --git a/libretroshare/src/rsserver/p3msgs.cc b/libretroshare/src/rsserver/p3msgs.cc index 6ce2d4830..e4986467e 100644 --- a/libretroshare/src/rsserver/p3msgs.cc +++ b/libretroshare/src/rsserver/p3msgs.cc @@ -65,11 +65,11 @@ ChatId::ChatId(RsPeerId id): peer_id = id; } -ChatId::ChatId(RsGxsId id): +ChatId::ChatId(DistantChatPeerId id): lobby_id(0) { type = TYPE_PRIVATE_DISTANT; - gxs_id = id; + distant_chat_id = id; } ChatId::ChatId(ChatLobbyId id): @@ -93,7 +93,7 @@ ChatId::ChatId(std::string str): else if(str[0] == 'D') { type = TYPE_PRIVATE_DISTANT; - gxs_id == GXSId(str.substr(1)); + distant_chat_id == DistantChatPeerId(str.substr(1)); } else if(str[0] == 'L') { @@ -143,7 +143,7 @@ std::string ChatId::toStdString() const else if(type == TYPE_PRIVATE_DISTANT) { str += "D"; - str += gxs_id.toStdString(); + str += distant_chat_id.toStdString(); } else if(type == TYPE_LOBBY) { @@ -186,7 +186,7 @@ bool ChatId::operator <(const ChatId& other) const case TYPE_PRIVATE: return peer_id < other.peer_id; case TYPE_PRIVATE_DISTANT: - return gxs_id < other.gxs_id; + return distant_chat_id < other.distant_chat_id; case TYPE_LOBBY: return lobby_id < other.lobby_id; case TYPE_BROADCAST: @@ -210,7 +210,7 @@ bool ChatId::isSameEndpoint(const ChatId &other) const case TYPE_PRIVATE: return peer_id == other.peer_id; case TYPE_PRIVATE_DISTANT: - return gxs_id == other.gxs_id; + return distant_chat_id == other.distant_chat_id; case TYPE_LOBBY: return lobby_id == other.lobby_id; case TYPE_BROADCAST: @@ -229,7 +229,7 @@ bool ChatId::isPeerId() const { return type == TYPE_PRIVATE; } -bool ChatId::isGxsId() const +bool ChatId::isDistantChatId() const { return type == TYPE_PRIVATE_DISTANT; } @@ -251,14 +251,15 @@ RsPeerId ChatId::toPeerId() const return RsPeerId(); } } -RsGxsId ChatId::toGxsId() const + +DistantChatPeerId ChatId::toDistantChatId() const { if(type == TYPE_PRIVATE_DISTANT) - return gxs_id; + return distant_chat_id; else { - std::cerr << "ChatId Warning: conversation to RsGxsId requested, but type is different. Current value=\"" << toStdString() << "\"" << std::endl; - return RsGxsId(); + std::cerr << "ChatId Warning: conversation to DistantChatPeerId requested, but type is different. Current value=\"" << toStdString() << "\"" << std::endl; + return DistantChatPeerId(); } } ChatLobbyId ChatId::toLobbyId() const @@ -523,15 +524,15 @@ void p3Msgs::getPendingChatLobbyInvites(std::list& invites) { mChatSrv->getPendingChatLobbyInvites(invites) ; } -bool p3Msgs::initiateDistantChatConnexion(const RsGxsId& to_gxs_id,const RsGxsId& from_gxs_id,uint32_t& error_code) +bool p3Msgs::initiateDistantChatConnexion(const RsGxsId& to_gxs_id,const RsGxsId& from_gxs_id,DistantChatPeerId& pid,uint32_t& error_code) { - return mChatSrv->initiateDistantChatConnexion(to_gxs_id,from_gxs_id,error_code) ; + return mChatSrv->initiateDistantChatConnexion(to_gxs_id,from_gxs_id,pid,error_code) ; } -bool p3Msgs::getDistantChatStatus(const RsGxsId &gxs_id,uint32_t &status,RsGxsId *from_gxs_id) +bool p3Msgs::getDistantChatStatus(const DistantChatPeerId& pid,DistantChatPeerInfo& info) { - return mChatSrv->getDistantChatStatus(gxs_id,status,from_gxs_id) ; + return mChatSrv->getDistantChatStatus(pid,info) ; } -bool p3Msgs::closeDistantChatConnexion(const RsGxsId& pid) +bool p3Msgs::closeDistantChatConnexion(const DistantChatPeerId &pid) { return mChatSrv->closeDistantChatConnexion(pid) ; } diff --git a/libretroshare/src/rsserver/p3msgs.h b/libretroshare/src/rsserver/p3msgs.h index 5599a8ee1..98e9da7de 100644 --- a/libretroshare/src/rsserver/p3msgs.h +++ b/libretroshare/src/rsserver/p3msgs.h @@ -155,9 +155,9 @@ class p3Msgs: public RsMsgs virtual bool getLobbyAutoSubscribe(const ChatLobbyId& lobby_id); virtual ChatLobbyId createChatLobby(const std::string& lobby_name,const RsGxsId& lobby_identity,const std::string& lobby_topic,const std::set& invited_friends,ChatLobbyFlags privacy_type) ; - virtual bool initiateDistantChatConnexion(const RsGxsId& to_gxs_id,const RsGxsId& from_gxs_id,uint32_t& error_code) ; - virtual bool getDistantChatStatus(const RsGxsId& gxs_id,uint32_t& status, RsGxsId *from_gxs_id=NULL) ; - virtual bool closeDistantChatConnexion(const RsGxsId &pid) ; + virtual bool initiateDistantChatConnexion(const RsGxsId& to_gxs_id, const RsGxsId& from_gxs_id, DistantChatPeerId &pid, uint32_t& error_code) ; + virtual bool getDistantChatStatus(const DistantChatPeerId& gxs_id,DistantChatPeerInfo& info); + virtual bool closeDistantChatConnexion(const DistantChatPeerId &pid) ; private: diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 80d950767..129054fea 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -56,6 +56,8 @@ #include #include +#include + #define ENABLE_GROUTER #if (defined(__unix__) || defined(unix)) && !defined(USG) @@ -264,7 +266,7 @@ bool doPortRestrictions = false; /******************************** WINDOWS/UNIX SPECIFIC PART ******************/ #ifndef WINDOWS_SYS -int RsInit::InitRetroShare(int argc, char **argv, bool strictCheck) +int RsInit::InitRetroShare(int argc, char **argv, bool /* strictCheck */) { /******************************** WINDOWS/UNIX SPECIFIC PART ******************/ #else @@ -1486,13 +1488,17 @@ int RsServer::StartupRetroShare() pqih -> addService(tr,true); pqih -> addService(ftserver,true); + mGxsTunnels = new p3GxsTunnelService(mGxsIdService) ; + mGxsTunnels->connectToTurtleRouter(tr) ; + rsGxsTunnel = mGxsTunnels; + rsDisc = mDisc; rsMsgs = new p3Msgs(msgSrv, chatSrv); // connect components to turtle router. ftserver->connectToTurtleRouter(tr) ; - chatSrv->connectToTurtleRouter(tr) ; + chatSrv->connectToGxsTunnelService(mGxsTunnels) ; gr->connectToTurtleRouter(tr) ; #ifdef ENABLE_GROUTER msgSrv->connectToGlobalRouter(gr) ; @@ -1504,6 +1510,7 @@ int RsServer::StartupRetroShare() pqih -> addService(msgSrv,true); pqih -> addService(chatSrv,true); pqih -> addService(mStatusSrv,true); + pqih -> addService(mGxsTunnels,true); pqih -> addService(mReputations,true); // set interfaces for plugins @@ -1525,6 +1532,7 @@ int RsServer::StartupRetroShare() interfaces.mPgpAuxUtils = pgpAuxUtils; interfaces.mGxsForums = mGxsForums; interfaces.mGxsChannels = mGxsChannels; + interfaces.mGxsTunnels = mGxsTunnels; interfaces.mReputations = mReputations; mPluginsManager->setInterfaces(interfaces); diff --git a/libretroshare/src/serialiser/rsserviceids.h b/libretroshare/src/serialiser/rsserviceids.h index 3460008a8..b7d7b2fa1 100644 --- a/libretroshare/src/serialiser/rsserviceids.h +++ b/libretroshare/src/serialiser/rsserviceids.h @@ -38,32 +38,33 @@ */ /* These are Cache Only */ -const uint16_t RS_SERVICE_TYPE_FILE_INDEX = 0x0001; +const uint16_t RS_SERVICE_TYPE_FILE_INDEX = 0x0001; /* These are Services only */ -const uint16_t RS_SERVICE_TYPE_DISC = 0x0011; -const uint16_t RS_SERVICE_TYPE_CHAT = 0x0012; -const uint16_t RS_SERVICE_TYPE_MSG = 0x0013; -const uint16_t RS_SERVICE_TYPE_TURTLE = 0x0014; -const uint16_t RS_SERVICE_TYPE_TUNNEL = 0x0015; -const uint16_t RS_SERVICE_TYPE_HEARTBEAT = 0x0016; -const uint16_t RS_SERVICE_TYPE_FILE_TRANSFER = 0x0017; -const uint16_t RS_SERVICE_TYPE_GROUTER = 0x0018; +const uint16_t RS_SERVICE_TYPE_DISC = 0x0011; +const uint16_t RS_SERVICE_TYPE_CHAT = 0x0012; +const uint16_t RS_SERVICE_TYPE_MSG = 0x0013; +const uint16_t RS_SERVICE_TYPE_TURTLE = 0x0014; +const uint16_t RS_SERVICE_TYPE_TUNNEL = 0x0015; +const uint16_t RS_SERVICE_TYPE_HEARTBEAT = 0x0016; +const uint16_t RS_SERVICE_TYPE_FILE_TRANSFER = 0x0017; +const uint16_t RS_SERVICE_TYPE_GROUTER = 0x0018; -const uint16_t RS_SERVICE_TYPE_SERVICEINFO = 0x0020; +const uint16_t RS_SERVICE_TYPE_SERVICEINFO = 0x0020; /* Bandwidth Control */ -const uint16_t RS_SERVICE_TYPE_BWCTRL = 0x0021; -// New Mail Service (replace old Msg Service) -const uint16_t RS_SERVICE_TYPE_MAIL = 0x0022; -const uint16_t RS_SERVICE_TYPE_DIRECT_MAIL = 0x0023; -const uint16_t RS_SERVICE_TYPE_DISTANT_MAIL = 0x0024; -const uint16_t RS_SERVICE_TYPE_GWEMAIL_MAIL = 0x0025; +const uint16_t RS_SERVICE_TYPE_BWCTRL = 0x0021; +// New Mail Service (replace old Msg Service) +const uint16_t RS_SERVICE_TYPE_MAIL = 0x0022; +const uint16_t RS_SERVICE_TYPE_DIRECT_MAIL = 0x0023; +const uint16_t RS_SERVICE_TYPE_DISTANT_MAIL = 0x0024; +const uint16_t RS_SERVICE_TYPE_GWEMAIL_MAIL = 0x0025; const uint16_t RS_SERVICE_TYPE_SERVICE_CONTROL= 0x0026; -const uint16_t RS_SERVICE_TYPE_DISTANT_CHAT = 0x0027; +const uint16_t RS_SERVICE_TYPE_DISTANT_CHAT = 0x0027; +const uint16_t RS_SERVICE_TYPE_GXS_TUNNEL = 0x0028; // Non essential services. -const uint16_t RS_SERVICE_TYPE_BANLIST = 0x0101; -const uint16_t RS_SERVICE_TYPE_STATUS = 0x0102; +const uint16_t RS_SERVICE_TYPE_BANLIST = 0x0101; +const uint16_t RS_SERVICE_TYPE_STATUS = 0x0102; /* New Cache Services */ /* Rs Network Exchange Service */ diff --git a/libretroshare/src/services/p3gxschannels.cc b/libretroshare/src/services/p3gxschannels.cc index bac137bc8..d25220662 100644 --- a/libretroshare/src/services/p3gxschannels.cc +++ b/libretroshare/src/services/p3gxschannels.cc @@ -122,7 +122,7 @@ uint32_t p3GxsChannels::channelsAuthenPolicy() /** Overloaded to cache new groups **/ -RsGenExchange::ServiceCreate_Return p3GxsChannels::service_CreateGroup(RsGxsGrpItem* grpItem, RsTlvSecurityKeySet& keySet) +RsGenExchange::ServiceCreate_Return p3GxsChannels::service_CreateGroup(RsGxsGrpItem* grpItem, RsTlvSecurityKeySet& /* keySet */) { updateSubscribedGroup(grpItem->meta); return SERVICE_CREATE_SUCCESS; diff --git a/libretroshare/src/upnp/upnphandler_linux.h b/libretroshare/src/upnp/upnphandler_linux.h index b0300dfc9..b22a9a9e2 100644 --- a/libretroshare/src/upnp/upnphandler_linux.h +++ b/libretroshare/src/upnp/upnphandler_linux.h @@ -40,8 +40,8 @@ class upnphandler: public pqiNetAssistFirewall virtual bool getExternalAddress(struct sockaddr_storage &addr); /* TO IMPLEMENT: New Port Forward interface to support as many ports as necessary */ - virtual bool requestPortForward(const PortForwardParams ¶ms) { return false; } - virtual bool statusPortForward(const uint32_t fwdId, PortForwardParams ¶ms) { return false; } + virtual bool requestPortForward(const PortForwardParams & /* params */) { return false; } + virtual bool statusPortForward(const uint32_t /* fwdId */, PortForwardParams & /*params*/) { return false; } /* Public functions - for background thread operation, * but effectively private from rest of RS, as in derived class diff --git a/libretroshare/src/util/rsprint.cc b/libretroshare/src/util/rsprint.cc index 97848b3c0..0d0ec5a04 100644 --- a/libretroshare/src/util/rsprint.cc +++ b/libretroshare/src/util/rsprint.cc @@ -40,6 +40,10 @@ std::string RsUtil::BinToHex(const std::string &bin) return BinToHex(bin.c_str(), bin.length()); } +std::string RsUtil::BinToHex(const unsigned char *arr, const uint32_t len) +{ + return BinToHex((char*)arr,len) ; +} std::string RsUtil::BinToHex(const char *arr, const uint32_t len) { std::string out; diff --git a/libretroshare/src/util/rsprint.h b/libretroshare/src/util/rsprint.h index b5a370a49..9a1dbe7c3 100644 --- a/libretroshare/src/util/rsprint.h +++ b/libretroshare/src/util/rsprint.h @@ -35,6 +35,7 @@ namespace RsUtil { std::string BinToHex(const std::string &bin); std::string BinToHex(const char *arr, const uint32_t len); +std::string BinToHex(const unsigned char *arr, const uint32_t len); std::string HashId(const std::string &id, bool reverse = false); //std::string AccurateTimeString(); diff --git a/retroshare-gui/src/changelog.txt b/retroshare-gui/src/changelog.txt index c535d3a42..3da31f03c 100644 --- a/retroshare-gui/src/changelog.txt +++ b/retroshare-gui/src/changelog.txt @@ -364,7 +364,7 @@ Changes for 0.5.5a * Bug fixes - Fixed proper display of crypto params for UDP connections - - Added missing location from cert when addign new friend + - Added missing location from cert when adding new friend - Added missing IndicateConfigChanged to p3PeerMgrIMPL::setDynDNS - Fixed crash when closing the main window without the setting "Minimize to Tray Icon" - Renamed the setting "Do not Minimize to Tray Icon" to "Minimize to Tray @@ -1189,7 +1189,7 @@ Changes for v0.5.3b - Added BSD specific changes - data directory and #including - improved plugin management to allow services to be used, and config pages to be added - Improvement to plugin system: - - made config page system more automatic, to allow addign config pages from plugins + - made config page system more automatic, to allow adding config pages from plugins - added (disabled) checkbox and function to allow all plugins for development - added config page methods to RsPlugin class - Mark local existing files in SearchDialog with red text color. @@ -1202,7 +1202,7 @@ Changes for v0.5.3b - Added Cache system for GPG Certificates. - This should reduce gpg calls by 90+%. - Added translation for "[ ... Missing Message ... ]". - - removed cache adding strategy to DL queue that was O(n^2). Now addign cache at the end of the queue + - removed cache adding strategy to DL queue that was O(n^2). Now adding cache at the end of the queue - The channel message (in channels) is set to read when the user clicks on the show more button. - The forum/channel news feed is removed when the user reads the message in forums/channels. - The standard font is now used for new chat lobbies. diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index 443620f78..ec9eaf1da 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -1004,8 +1004,9 @@ void IdDialog::chatIdentity() RsGxsId from_gxs_id(action->data().toString().toStdString()); uint32_t error_code ; + DistantChatPeerId did ; - if(!rsMsgs->initiateDistantChatConnexion(RsGxsId(keyId), from_gxs_id, error_code)) + if(!rsMsgs->initiateDistantChatConnexion(RsGxsId(keyId), from_gxs_id, did, error_code)) QMessageBox::information(NULL, tr("Distant chat cannot work"), QString("%1 %2: %3").arg(tr("Distant chat refused with this person.")).arg(tr("Error code")).arg(error_code)) ; } diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp index e3b975843..3efd0c23d 100644 --- a/retroshare-gui/src/gui/MainWindow.cpp +++ b/retroshare-gui/src/gui/MainWindow.cpp @@ -401,7 +401,7 @@ void MainWindow::initStackedPage() else icon = QIcon(":images/extension_48.png") ; - std::cerr << " Addign widget page for plugin " << rsPlugins->plugin(i)->getPluginName() << std::endl; + std::cerr << " Adding widget page for plugin " << rsPlugins->plugin(i)->getPluginName() << std::endl; pluginPage->setIconPixmap(icon); pluginPage->setPageName(QString::fromUtf8(rsPlugins->plugin(i)->getPluginName().c_str())); addPage(pluginPage, grp, ¬ify); diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 6c64a7b89..fddb96ee3 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -1341,7 +1341,7 @@ void FlatStyle_RDM::updateRefs() if(details->type == DIR_TYPE_FILE) // only push files, not directories nor persons. _ref_entries.push_back(std::pair(ref,computeDirectoryPath(*details))); #ifdef RDM_DEBUG - std::cerr << "FlatStyle_RDM::postMods(): addign ref " << ref << std::endl; + std::cerr << "FlatStyle_RDM::postMods(): adding ref " << ref << std::endl; #endif for(std::list::const_iterator it = details->children.begin(); it != details->children.end(); ++it) _ref_stack.push_back(it->ref) ; diff --git a/retroshare-gui/src/gui/chat/ChatDialog.cpp b/retroshare-gui/src/gui/chat/ChatDialog.cpp index d1fb0bd47..2ab8b8e90 100644 --- a/retroshare-gui/src/gui/chat/ChatDialog.cpp +++ b/retroshare-gui/src/gui/chat/ChatDialog.cpp @@ -96,7 +96,7 @@ void ChatDialog::init(ChatId id, const QString &title) if (cd == NULL) { - if(id.isGxsId()) + if(id.isDistantChatId()) chatflags = RS_CHAT_OPEN | RS_CHAT_FOCUS; // force open for distant chat if (chatflags & RS_CHAT_OPEN) { @@ -104,12 +104,16 @@ void ChatDialog::init(ChatId id, const QString &title) ChatLobbyDialog* cld = new ChatLobbyDialog(id.toLobbyId()); cld->init(); cd = cld; - } else if(id.isGxsId()) { - PopupDistantChatDialog* pdcd = new PopupDistantChatDialog(); - QString peer_name = pdcd->getPeerName(id) ; - pdcd->init(id.toGxsId(), tr("Talking to")+" "+peer_name) ; + } + else if(id.isDistantChatId()) + { + PopupDistantChatDialog* pdcd = new PopupDistantChatDialog(id.toDistantChatId()); + + pdcd->init(id.toDistantChatId()); cd = pdcd; - } else { + } + else + { RsPeerDetails sslDetails; if (rsPeers->getPeerDetails(id.toPeerId(), sslDetails)) { PopupChatDialog* pcd = new PopupChatDialog(); @@ -168,7 +172,7 @@ void ChatDialog::init(ChatId id, const QString &title) if(msg.chat_id.isBroadcast()) return; // broadcast is not handled by a chat dialog - if(msg.incoming && (msg.chat_id.isPeerId() || msg.chat_id.isGxsId())) + if(msg.incoming && (msg.chat_id.isPeerId() || msg.chat_id.isDistantChatId())) // play sound when recv a message SoundManager::play(SOUND_NEW_CHAT_MESSAGE); @@ -334,8 +338,8 @@ void ChatDialog::setPeerStatus(uint32_t status) RsPeerId vpid; if(mChatId.isPeerId()) vpid = mChatId.toPeerId(); - if(mChatId.isGxsId()) - vpid = RsPeerId(mChatId.toGxsId()); + if(mChatId.isDistantChatId()) + vpid = RsPeerId(mChatId.toDistantChatId()); cw->updateStatus(QString::fromStdString(vpid.toStdString()), status); } } diff --git a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp index 202c8c2cb..e87120c4a 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp +++ b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp @@ -562,8 +562,9 @@ void ChatLobbyDialog::distantChatParticipant() rsMsgs->getIdentityForChatLobby(lobbyId, own_id); uint32_t error_code ; + DistantChatPeerId tunnel_id; - if(! rsMsgs->initiateDistantChatConnexion(gxs_id,own_id,error_code)) + if(! rsMsgs->initiateDistantChatConnexion(gxs_id,own_id,tunnel_id,error_code)) { QString error_str ; switch(error_code) diff --git a/retroshare-gui/src/gui/chat/ChatUserNotify.cpp b/retroshare-gui/src/gui/chat/ChatUserNotify.cpp index 9aed09807..1bc4a1df9 100644 --- a/retroshare-gui/src/gui/chat/ChatUserNotify.cpp +++ b/retroshare-gui/src/gui/chat/ChatUserNotify.cpp @@ -109,7 +109,7 @@ void ChatUserNotify::chatMessageReceived(ChatMessage msg) if(!msg.chat_id.isBroadcast() &&( ChatDialog::getExistingChat(msg.chat_id) || (Settings->getChatFlags() & RS_CHAT_OPEN) - || msg.chat_id.isGxsId())) + || msg.chat_id.isDistantChatId())) { ChatDialog::chatMessageReceived(msg); } diff --git a/retroshare-gui/src/gui/chat/ChatWidget.cpp b/retroshare-gui/src/gui/chat/ChatWidget.cpp index 564c0860c..5bf42f035 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.cpp +++ b/retroshare-gui/src/gui/chat/ChatWidget.cpp @@ -255,7 +255,7 @@ void ChatWidget::init(const ChatId &chat_id, const QString &title) RsPeerId ownId = rsPeers->getOwnId(); setName(QString::fromUtf8(rsPeers->getPeerName(ownId).c_str())); - if(chatId.isPeerId() || chatId.isGxsId()) + if(chatId.isPeerId() || chatId.isDistantChatId()) chatStyle.setStyleFromSettings(ChatStyle::TYPE_PRIVATE); if(chatId.isBroadcast() || chatId.isLobbyId()) chatStyle.setStyleFromSettings(ChatStyle::TYPE_PUBLIC); @@ -334,7 +334,8 @@ void ChatWidget::init(const ChatId &chat_id, const QString &title) continue; QString name; - if (chatId.isLobbyId() || chatId.isGxsId()) { + if (chatId.isLobbyId() || chatId.isDistantChatId()) + { RsIdentityDetails details; if (rsIdentity->getIdDetails(RsGxsId(historyIt->peerName), details)) name = QString::fromUtf8(details.mNickname.c_str()); @@ -366,7 +367,7 @@ ChatWidget::ChatType ChatWidget::chatType() // but maybe it is good to have separate types in libretroshare and gui if(chatId.isPeerId()) return CHATTYPE_PRIVATE; - if(chatId.isGxsId()) + if(chatId.isDistantChatId()) return CHATTYPE_DISTANT; if(chatId.isLobbyId()) return CHATTYPE_LOBBY; @@ -1522,77 +1523,83 @@ void ChatWidget::updateStatus(const QString &peer_id, int status) // make virtual peer id from gxs id in case of distant chat RsPeerId vpid; - if(chatId.isGxsId()) - vpid = RsPeerId(chatId.toGxsId()); + if(chatId.isDistantChatId()) + vpid = RsPeerId(chatId.toDistantChatId()); else vpid = chatId.toPeerId(); /* set font size for status */ - if (peer_id.toStdString() == vpid.toStdString()) { - // the peers status has changed + if (peer_id.toStdString() == vpid.toStdString()) + { + // the peers status has changed - QString peerName ; - if(chatId.isGxsId()) - { - RsIdentityDetails details ; - if(rsIdentity->getIdDetails(chatId.toGxsId(),details)) - peerName = QString::fromUtf8( details.mNickname.c_str() ) ; - else - peerName = QString::fromStdString(chatId.toGxsId().toStdString()) ; - } - else - peerName = QString::fromUtf8(rsPeers->getPeerName(chatId.toPeerId()).c_str()); + QString peerName ; + if(chatId.isDistantChatId()) + { + DistantChatPeerInfo dcpinfo ; + RsIdentityDetails details ; - // is scrollbar at the end? - QScrollBar *scrollbar = ui->textBrowser->verticalScrollBar(); - bool atEnd = (scrollbar->value() == scrollbar->maximum()); + if(rsMsgs->getDistantChatStatus(chatId.toDistantChatId(),dcpinfo)) + if(rsIdentity->getIdDetails(dcpinfo.to_id,details)) + peerName = QString::fromUtf8( details.mNickname.c_str() ) ; + else + peerName = QString::fromStdString(dcpinfo.to_id.toStdString()) ; + else + peerName = QString::fromStdString(chatId.toDistantChatId().toStdString()) ; + } + else + peerName = QString::fromUtf8(rsPeers->getPeerName(chatId.toPeerId()).c_str()); - switch (status) { - case RS_STATUS_OFFLINE: - ui->infoFrame->setVisible(true); - ui->infoLabel->setText(peerName + " " + tr("appears to be Offline.") +"\n" + tr("Messages you send will be delivered after Friend is again Online")); - break; + // is scrollbar at the end? + QScrollBar *scrollbar = ui->textBrowser->verticalScrollBar(); + bool atEnd = (scrollbar->value() == scrollbar->maximum()); - case RS_STATUS_INACTIVE: - ui->infoFrame->setVisible(true); - ui->infoLabel->setText(peerName + " " + tr("is Idle and may not reply")); - break; + switch (status) { + case RS_STATUS_OFFLINE: + ui->infoFrame->setVisible(true); + ui->infoLabel->setText(peerName + " " + tr("appears to be Offline.") +"\n" + tr("Messages you send will be delivered after Friend is again Online")); + break; - case RS_STATUS_ONLINE: - ui->infoFrame->setVisible(false); - break; + case RS_STATUS_INACTIVE: + ui->infoFrame->setVisible(true); + ui->infoLabel->setText(peerName + " " + tr("is Idle and may not reply")); + break; - case RS_STATUS_AWAY: - ui->infoLabel->setText(peerName + " " + tr("is Away and may not reply")); - ui->infoFrame->setVisible(true); - break; + case RS_STATUS_ONLINE: + ui->infoFrame->setVisible(false); + break; - case RS_STATUS_BUSY: - ui->infoLabel->setText(peerName + " " + tr("is Busy and may not reply")); - ui->infoFrame->setVisible(true); - break; - } + case RS_STATUS_AWAY: + ui->infoLabel->setText(peerName + " " + tr("is Away and may not reply")); + ui->infoFrame->setVisible(true); + break; - ui->titleLabel->setText(peerName); - ui->statusLabel->setText(QString("(%1)").arg(StatusDefs::name(status))); + case RS_STATUS_BUSY: + ui->infoLabel->setText(peerName + " " + tr("is Busy and may not reply")); + ui->infoFrame->setVisible(true); + break; + } - peerStatus = status; + ui->titleLabel->setText(peerName); + ui->statusLabel->setText(QString("(%1)").arg(StatusDefs::name(status))); - if (atEnd) { - // scroll to the end - scrollbar->setValue(scrollbar->maximum()); - } + peerStatus = status; - emit infoChanged(this); - emit statusChanged(status); + if (atEnd) { + // scroll to the end + scrollbar->setValue(scrollbar->maximum()); + } - // Notify all ChatWidgetHolder - foreach (ChatWidgetHolder *chatWidgetHolder, mChatWidgetHolder) { - chatWidgetHolder->updateStatus(status); - } + emit infoChanged(this); + emit statusChanged(status); - return; - } + // Notify all ChatWidgetHolder + foreach (ChatWidgetHolder *chatWidgetHolder, mChatWidgetHolder) { + chatWidgetHolder->updateStatus(status); + } + + return; + } // ignore status change } diff --git a/retroshare-gui/src/gui/chat/PopupDistantChatDialog.cpp b/retroshare-gui/src/gui/chat/PopupDistantChatDialog.cpp index 890fd0e40..12911efc9 100644 --- a/retroshare-gui/src/gui/chat/PopupDistantChatDialog.cpp +++ b/retroshare-gui/src/gui/chat/PopupDistantChatDialog.cpp @@ -43,9 +43,11 @@ PopupDistantChatDialog::~PopupDistantChatDialog() delete _update_timer ; } -PopupDistantChatDialog::PopupDistantChatDialog(QWidget *parent, Qt::WindowFlags flags) +PopupDistantChatDialog::PopupDistantChatDialog(const DistantChatPeerId& tunnel_id,QWidget *parent, Qt::WindowFlags flags) : PopupChatDialog(parent,flags) { + _tunnel_id = tunnel_id ; + _status_label = new QToolButton ; _update_timer = new QTimer ; @@ -61,97 +63,98 @@ PopupDistantChatDialog::PopupDistantChatDialog(QWidget *parent, Qt::WindowFlags updateDisplay() ; } -void PopupDistantChatDialog::init(const RsGxsId &gxs_id,const QString & title) +void PopupDistantChatDialog::init(const DistantChatPeerId &peer_id) { - _pid = gxs_id ; - PopupChatDialog::init(ChatId(gxs_id), title) ; + _tunnel_id = peer_id; + DistantChatPeerInfo tinfo; + + if(!rsMsgs->getDistantChatStatus(_tunnel_id,tinfo)) + return ; + + RsIdentityDetails iddetails ; + + if(rsIdentity->getIdDetails(tinfo.to_id,iddetails)) + PopupChatDialog::init(ChatId(peer_id), QString::fromUtf8(iddetails.mNickname.c_str())) ; + else + PopupChatDialog::init(ChatId(peer_id), QString::fromStdString(tinfo.to_id.toStdString())) ; - RsGxsId own_gxs_id ; - uint32_t status ; - - // do not use setOwnId, because we don't want the user to change the GXS avatar from the chat window + // Do not use setOwnId, because we don't want the user to change the GXS avatar from the chat window // it will not be transmitted. - if(rsMsgs->getDistantChatStatus(gxs_id,status,&own_gxs_id)) - ui.ownAvatarWidget->setId(ChatId(own_gxs_id)); + ui.ownAvatarWidget->setOwnId() ; // sets the flag + ui.ownAvatarWidget->setId(ChatId(peer_id)) ; // sets the actual Id } void PopupDistantChatDialog::updateDisplay() { - if(RsAutoUpdatePage::eventsLocked()) // we need to do that by end, because it's not possible to derive from both PopupChatDialog and RsAutoUpdatePage - return ; // which both derive from QObject. Signals-slot connexions won't work anymore. + if(RsAutoUpdatePage::eventsLocked()) // we need to do that by end, because it's not possible to derive from both PopupChatDialog and RsAutoUpdatePage + return ; // which both derive from QObject. Signals-slot connexions won't work anymore. - if(!isVisible()) - return ; + if(!isVisible()) + return ; - //std::cerr << "Checking tunnel..." ; - // make sure about the tunnel status - // - - uint32_t status= RS_DISTANT_CHAT_STATUS_UNKNOWN; - rsMsgs->getDistantChatStatus(_pid,status) ; + //std::cerr << "Checking tunnel..." ; + // make sure about the tunnel status + // - ui.avatarWidget->setId(ChatId(_pid)); + DistantChatPeerInfo tinfo; + rsMsgs->getDistantChatStatus(_tunnel_id,tinfo) ; + + ui.avatarWidget->setId(ChatId(_tunnel_id)); QString msg; - switch(status) - { - case RS_DISTANT_CHAT_STATUS_UNKNOWN: //std::cerr << "Unknown hash. Error!" << std::endl; - _status_label->setIcon(QIcon(IMAGE_GRY_LED)) ; - msg = tr("Hash Error. No tunnel."); - _status_label->setToolTip(msg) ; - getChatWidget()->updateStatusString("%1", msg, true); - getChatWidget()->blockSending(tr("Can't send message, because there is no tunnel.")); - setPeerStatus(RS_STATUS_OFFLINE) ; - break ; - case RS_DISTANT_CHAT_STATUS_REMOTELY_CLOSED: std::cerr << "Chat remotely closed. " << std::endl; - _status_label->setIcon(QIcon(IMAGE_RED_LED)) ; - _status_label->setToolTip(QObject::tr("Distant peer has closed the chat")) ; - getChatWidget()->updateStatusString("%1", tr("The person you're talking to has deleted the secured chat tunnel. You may remove the chat window now."), true); - getChatWidget()->blockSending(tr("Can't send message, because the chat partner deleted the secure tunnel.")); - setPeerStatus(RS_STATUS_OFFLINE) ; + switch(tinfo.status) + { + case RS_DISTANT_CHAT_STATUS_UNKNOWN: //std::cerr << "Unknown hash. Error!" << std::endl; + _status_label->setIcon(QIcon(IMAGE_GRY_LED)) ; + msg = tr("Chat remotely closed. Please close this window."); + _status_label->setToolTip(msg) ; + getChatWidget()->updateStatusString("%1", msg, true); + getChatWidget()->blockSending(tr("Can't send message, because there is no tunnel.")); + setPeerStatus(RS_STATUS_OFFLINE) ; + break ; + case RS_DISTANT_CHAT_STATUS_REMOTELY_CLOSED: std::cerr << "Chat remotely closed. " << std::endl; + _status_label->setIcon(QIcon(IMAGE_RED_LED)) ; + _status_label->setToolTip(QObject::tr("Distant peer has closed the chat")) ; - break ; - case RS_DISTANT_CHAT_STATUS_TUNNEL_DN: //std::cerr << "Tunnel asked. Waiting for reponse. " << std::endl; - _status_label->setIcon(QIcon(IMAGE_RED_LED)) ; - msg = QObject::tr("Tunnel is pending..."); - _status_label->setToolTip(msg) ; - getChatWidget()->updateStatusString("%1", msg, true); - getChatWidget()->blockSending(msg); - setPeerStatus(RS_STATUS_OFFLINE) ; - break ; - case RS_DISTANT_CHAT_STATUS_TUNNEL_OK: //std::cerr << "Tunnel is ok. " << std::endl; - _status_label->setIcon(QIcon(IMAGE_YEL_LED)) ; - msg = QObject::tr("Secured tunnel established. Waiting for ACK..."); - _status_label->setToolTip(msg) ; - getChatWidget()->updateStatusString("%1", msg, true); - getChatWidget()->blockSending(msg); - setPeerStatus(RS_STATUS_ONLINE) ; - break ; - case RS_DISTANT_CHAT_STATUS_CAN_TALK: //std::cerr << "Tunnel is ok and data is transmitted." << std::endl; - _status_label->setIcon(QIcon(IMAGE_GRN_LED)) ; - msg = QObject::tr("Secured tunnel is working. You can talk!"); - _status_label->setToolTip(msg) ; - getChatWidget()->unblockSending(); - setPeerStatus(RS_STATUS_ONLINE) ; - break ; - } + getChatWidget()->updateStatusString("%1", tr("The person you're talking to has deleted the secured chat tunnel. You may remove the chat window now."), true); + getChatWidget()->blockSending(tr("Can't send message, because the chat partner deleted the secure tunnel.")); + setPeerStatus(RS_STATUS_OFFLINE) ; + + break ; + case RS_DISTANT_CHAT_STATUS_TUNNEL_DN: //std::cerr << "Tunnel asked. Waiting for reponse. " << std::endl; + _status_label->setIcon(QIcon(IMAGE_RED_LED)) ; + msg = QObject::tr("Tunnel is pending..."); + _status_label->setToolTip(msg) ; + getChatWidget()->updateStatusString("%1", msg, true); + getChatWidget()->blockSending(msg); + setPeerStatus(RS_STATUS_OFFLINE) ; + break ; + case RS_DISTANT_CHAT_STATUS_CAN_TALK: //std::cerr << "Tunnel is ok and data is transmitted." << std::endl; + _status_label->setIcon(QIcon(IMAGE_GRN_LED)) ; + msg = QObject::tr("Secured tunnel is working. You can talk!"); + _status_label->setToolTip(msg) ; + getChatWidget()->unblockSending(); + setPeerStatus(RS_STATUS_ONLINE) ; + break ; + } } void PopupDistantChatDialog::closeEvent(QCloseEvent *e) { - //std::cerr << "Closing window => closing distant chat for hash " << _pid << std::endl; + //std::cerr << "Closing window => closing distant chat for hash " << _pid << std::endl; - uint32_t status= RS_DISTANT_CHAT_STATUS_UNKNOWN; - rsMsgs->getDistantChatStatus(_pid,status) ; + DistantChatPeerInfo tinfo ; + + rsMsgs->getDistantChatStatus(_tunnel_id,tinfo) ; - if(status != RS_DISTANT_CHAT_STATUS_REMOTELY_CLOSED) + if(tinfo.status != RS_DISTANT_CHAT_STATUS_REMOTELY_CLOSED) { QString msg = tr("Closing this window will end the conversation, notify the peer and remove the encrypted tunnel.") ; if(QMessageBox::Ok == QMessageBox::critical(NULL,tr("Kill the tunnel?"),msg, QMessageBox::Ok | QMessageBox::Cancel)) - rsMsgs->closeDistantChatConnexion(_pid) ; + rsMsgs->closeDistantChatConnexion(_tunnel_id) ; else { e->ignore() ; @@ -166,23 +169,26 @@ void PopupDistantChatDialog::closeEvent(QCloseEvent *e) QString PopupDistantChatDialog::getPeerName(const ChatId &id) const { + DistantChatPeerInfo tinfo; + + rsMsgs->getDistantChatStatus(_tunnel_id,tinfo) ; + RsIdentityDetails details ; - if(rsIdentity->getIdDetails(id.toGxsId(),details)) + if(rsIdentity->getIdDetails(tinfo.to_id,details)) return QString::fromUtf8( details.mNickname.c_str() ) ; else - return QString::fromStdString(id.toGxsId().toStdString()) ; + return QString::fromStdString(tinfo.to_id.toStdString()) ; } QString PopupDistantChatDialog::getOwnName() const { - uint32_t status= RS_DISTANT_CHAT_STATUS_UNKNOWN; - RsGxsId from_gxs_id ; + DistantChatPeerInfo tinfo; - rsMsgs->getDistantChatStatus(_pid,status,&from_gxs_id) ; + rsMsgs->getDistantChatStatus(_tunnel_id,tinfo) ; RsIdentityDetails details ; - if(rsIdentity->getIdDetails(from_gxs_id,details)) + if(rsIdentity->getIdDetails(tinfo.own_id,details)) return QString::fromUtf8( details.mNickname.c_str() ) ; else - return QString::fromStdString(from_gxs_id.toStdString()) ; + return QString::fromStdString(tinfo.own_id.toStdString()) ; } diff --git a/retroshare-gui/src/gui/chat/PopupDistantChatDialog.h b/retroshare-gui/src/gui/chat/PopupDistantChatDialog.h index 2747ef09a..dfe75b9de 100644 --- a/retroshare-gui/src/gui/chat/PopupDistantChatDialog.h +++ b/retroshare-gui/src/gui/chat/PopupDistantChatDialog.h @@ -22,6 +22,7 @@ #pragma once +#include #include "PopupChatDialog.h" class QTimer ; @@ -33,11 +34,11 @@ class PopupDistantChatDialog: public PopupChatDialog protected: /** Default constructor */ - PopupDistantChatDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0); + PopupDistantChatDialog(const DistantChatPeerId &tunnel_id, QWidget *parent = 0, Qt::WindowFlags flags = 0); /** Default destructor */ virtual ~PopupDistantChatDialog(); - virtual void init(const RsGxsId &gxs_id, const QString &title); + virtual void init(const DistantChatPeerId& peer_id); virtual void closeEvent(QCloseEvent *e) ; virtual QString getPeerName(const ChatId &id) const ; @@ -48,7 +49,7 @@ class PopupDistantChatDialog: public PopupChatDialog private: QTimer *_update_timer ; - RsGxsId _pid ; + DistantChatPeerId _tunnel_id ; QToolButton *_status_label ; friend class ChatDialog; diff --git a/retroshare-gui/src/gui/common/AvatarWidget.cpp b/retroshare-gui/src/gui/common/AvatarWidget.cpp index c8c5fe0d7..71147d7b6 100644 --- a/retroshare-gui/src/gui/common/AvatarWidget.cpp +++ b/retroshare-gui/src/gui/common/AvatarWidget.cpp @@ -124,24 +124,33 @@ void AvatarWidget::setFrameType(FrameType type) void AvatarWidget::setId(const ChatId &id) { mId = id; -// mPgpId = rsPeers->getGPGId(id) ; -// mFlag.isGpg = false ; + mGxsId.clear(); setPixmap(QPixmap()); if (id.isNotSet()) { setEnabled(false); } + + refreshAvatarImage(); + refreshStatus(); +} + +void AvatarWidget::setGxsId(const RsGxsId &id) +{ + mId = ChatId(); + mGxsId = id; + + setPixmap(QPixmap()); + + if (id.isNull()) { + setEnabled(false); + } refreshAvatarImage(); refreshStatus(); } -void AvatarWidget::setOwnId(const RsGxsId& own_gxs_id) -{ - mFlag.isOwnId = true; - setId(ChatId(own_gxs_id)); -} void AvatarWidget::setOwnId() { mFlag.isOwnId = true; @@ -181,7 +190,7 @@ void AvatarWidget::refreshStatus() rsStatus->getOwnStatus(statusInfo); status = statusInfo.status ; } - else if(mId.isGxsId()) + else if(mId.isDistantChatId()) status = RS_STATUS_ONLINE ; else { @@ -198,11 +207,15 @@ void AvatarWidget::refreshStatus() rsStatus->getStatus(mId.toPeerId(), statusInfo); status = statusInfo.status ; } - else if(mId.isGxsId()) - { - if(!rsMsgs->getDistantChatStatus(mId.toGxsId(),status)) - status = RS_STATUS_OFFLINE ; - } + else if(mId.isDistantChatId()) + { + DistantChatPeerInfo dcpinfo ; + + if(rsMsgs->getDistantChatStatus(mId.toDistantChatId(),dcpinfo)) + status = dcpinfo.status ; + else + std::cerr << "(EE) cannot get distant chat status for ID=" << mId.toDistantChatId() << std::endl; + } else { std::cerr << "Unhandled chat id type in AvatarWidget::refreshStatus()" << std::endl; @@ -235,13 +248,22 @@ void AvatarWidget::updateStatus(int status) void AvatarWidget::updateAvatar(const QString &peerId) { if(mId.isPeerId() && mId.toPeerId() == RsPeerId(peerId.toStdString())) - refreshAvatarImage() ; - - if(mId.isGxsId() && mId.toGxsId() == RsGxsId(peerId.toStdString())) - refreshAvatarImage() ; + refreshAvatarImage() ; + else if(mId.isDistantChatId() && mId.toDistantChatId() == DistantChatPeerId(peerId.toStdString())) + refreshAvatarImage() ; + else + std::cerr << "(EE) cannot update avatar. mId has unhandled type." << std::endl; } void AvatarWidget::refreshAvatarImage() { + if (mGxsId.isNull()==false) + { + QPixmap avatar; + + AvatarDefs::getAvatarFromGxsId(mGxsId, avatar, defaultAvatar); + setPixmap(avatar); + return; + } if (mId.isNotSet()) { QPixmap avatar(defaultAvatar); @@ -262,12 +284,21 @@ void AvatarWidget::refreshAvatarImage() setPixmap(avatar); return; } - else if (mId.isGxsId()) + else if (mId.isDistantChatId()) { - QPixmap avatar; - AvatarDefs::getAvatarFromGxsId(mId.toGxsId(), avatar, defaultAvatar); - setPixmap(avatar); - return; + QPixmap avatar; + + DistantChatPeerInfo dcpinfo ; + + if(rsMsgs->getDistantChatStatus(mId.toDistantChatId(),dcpinfo)) + { + if(mFlag.isOwnId) + AvatarDefs::getAvatarFromGxsId(dcpinfo.own_id, avatar, defaultAvatar); + else + AvatarDefs::getAvatarFromGxsId(dcpinfo.to_id, avatar, defaultAvatar); + setPixmap(avatar); + return; + } } else std::cerr << "WARNING: unhandled situation in AvatarWidget::refreshAvatarImage()" << std::endl; diff --git a/retroshare-gui/src/gui/common/AvatarWidget.h b/retroshare-gui/src/gui/common/AvatarWidget.h index 61db1bada..de15ef066 100644 --- a/retroshare-gui/src/gui/common/AvatarWidget.h +++ b/retroshare-gui/src/gui/common/AvatarWidget.h @@ -50,8 +50,8 @@ public: QString frameState(); void setFrameType(FrameType type); void setId(const ChatId& id) ; + void setGxsId(const RsGxsId& id) ; void setOwnId(); - void setOwnId(const RsGxsId&); void setDefaultAvatar(const QString &avatar_file_name); protected: @@ -71,6 +71,7 @@ private: Ui::AvatarWidget *ui; ChatId mId; + RsGxsId mGxsId; struct { bool isOwnId : 1; diff --git a/retroshare-gui/src/gui/common/GroupSelectionBox.cpp b/retroshare-gui/src/gui/common/GroupSelectionBox.cpp index 55317115d..613459019 100644 --- a/retroshare-gui/src/gui/common/GroupSelectionBox.cpp +++ b/retroshare-gui/src/gui/common/GroupSelectionBox.cpp @@ -46,7 +46,7 @@ void GroupSelectionBox::selectedGroupIds(std::list &groupIds) const QListWidgetItem *listItem = item(i); if (listItem->checkState() == Qt::Checked) { groupIds.push_back(item(i)->data(ROLE_ID).toString().toStdString()); - std::cerr << "Addign selected item " << groupIds.back() << std::endl; + std::cerr << "Adding selected item " << groupIds.back() << std::endl; } } } @@ -74,7 +74,7 @@ void GroupSelectionBox::selectedGroupNames(QList &groupNames) const QListWidgetItem *listItem = item(i); if (listItem->checkState() == Qt::Checked) { groupNames.push_back(item(i)->text()); - std::cerr << "Addign selected item " << groupNames.back().toUtf8().constData() << std::endl; + std::cerr << "Adding selected item " << groupNames.back().toUtf8().constData() << std::endl; } } } diff --git a/retroshare-gui/src/gui/feeds/MsgItem.cpp b/retroshare-gui/src/gui/feeds/MsgItem.cpp index cb44de952..93b9d3e8c 100644 --- a/retroshare-gui/src/gui/feeds/MsgItem.cpp +++ b/retroshare-gui/src/gui/feeds/MsgItem.cpp @@ -88,11 +88,9 @@ void MsgItem::updateItemStatic() /* get peer Id */ if(mi.msgflags & RS_MSG_SIGNED) - mPeerId = ChatId(mi.rsgxsid_srcId); + avatar->setGxsId(mi.rsgxsid_srcId); else - mPeerId = ChatId(mi.rspeerid_srcId); - - avatar->setId(mPeerId); + avatar->setId(ChatId(mi.rspeerid_srcId)); QString title; QString srcName; diff --git a/retroshare-gui/src/gui/feeds/MsgItem.h b/retroshare-gui/src/gui/feeds/MsgItem.h index dd82dfb50..9b036260e 100644 --- a/retroshare-gui/src/gui/feeds/MsgItem.h +++ b/retroshare-gui/src/gui/feeds/MsgItem.h @@ -64,7 +64,6 @@ private: FeedHolder *mParent; uint32_t mFeedId; - ChatId mPeerId; std::string mMsgId; QString mMsg; diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp index cd873b6d0..5785d68fb 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp @@ -259,6 +259,13 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) QMenu contextMnu(this); QAction *action; + + if (mMessageWidget) { + action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab())); + if (mGroupId.isNull() || messageWidget(mGroupId, true)) { + action->setEnabled(false); + } + } if (isSubscribed) { action = contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup())); @@ -268,13 +275,6 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) action->setDisabled (mGroupId.isNull() || IS_GROUP_SUBSCRIBED(subscribeFlags)); } - if (mMessageWidget) { - action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab())); - if (mGroupId.isNull() || messageWidget(mGroupId, true)) { - action->setEnabled(false); - } - } - contextMnu.addSeparator(); contextMnu.addAction(QIcon(icon(ICON_NEW)), text(TEXT_NEW), this, SLOT(newGroup())); diff --git a/retroshare-gui/src/gui/im_history/ImHistoryBrowser.cpp b/retroshare-gui/src/gui/im_history/ImHistoryBrowser.cpp index f2d15a8c6..a107f82da 100644 --- a/retroshare-gui/src/gui/im_history/ImHistoryBrowser.cpp +++ b/retroshare-gui/src/gui/im_history/ImHistoryBrowser.cpp @@ -116,7 +116,7 @@ ImHistoryBrowser::ImHistoryBrowser(const ChatId &chatId, QTextEdit *edit, QWidge ui.filterLineEdit->showFilterIcon(); // embed smileys ? - if (m_chatId.isPeerId() || m_chatId.isGxsId()) { + if (m_chatId.isPeerId() || m_chatId.isDistantChatId()) { embedSmileys = Settings->valueFromGroup("Chat", "Emoteicons_PrivatChat", true).toBool(); } else { embedSmileys = Settings->valueFromGroup("Chat", "Emoteicons_GroupChat", true).toBool(); @@ -275,7 +275,7 @@ void ImHistoryBrowser::fillItem(QListWidgetItem *itemWidget, HistoryMsg& msg) QString messageText = RsHtml().formatText(NULL, QString::fromUtf8(msg.message.c_str()), formatTextFlag); QString name; - if (m_chatId.isLobbyId() || m_chatId.isGxsId()) { + if (m_chatId.isLobbyId() || m_chatId.isDistantChatId()) { RsIdentityDetails details; if (rsIdentity->getIdDetails(RsGxsId(msg.peerName), details)) name = QString::fromUtf8(details.mNickname.c_str()); diff --git a/retroshare-gui/src/gui/settings/RsharePeerSettings.cpp b/retroshare-gui/src/gui/settings/RsharePeerSettings.cpp index d01d494a8..05dae3e17 100644 --- a/retroshare-gui/src/gui/settings/RsharePeerSettings.cpp +++ b/retroshare-gui/src/gui/settings/RsharePeerSettings.cpp @@ -112,7 +112,7 @@ bool RsharePeerSettings::getSettingsIdOfPeerId(const ChatId& chatId, std::string m_SslToGpg[peerId] = settingsId ; return true; } - if(chatId.isGxsId() || chatId.isLobbyId() || chatId.isBroadcast()) + if(chatId.isDistantChatId() || chatId.isLobbyId() || chatId.isBroadcast()) { settingsId = chatId.toStdString(); return true; diff --git a/retroshare-gui/src/gui/statistics/TurtleRouterDialog.cpp b/retroshare-gui/src/gui/statistics/TurtleRouterDialog.cpp index 682a20e7c..a216e2102 100644 --- a/retroshare-gui/src/gui/statistics/TurtleRouterDialog.cpp +++ b/retroshare-gui/src/gui/statistics/TurtleRouterDialog.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "TurtleRouterDialog.h" #include #include @@ -216,3 +217,156 @@ QTreeWidgetItem *TurtleRouterDialog::findParentHashItem(const std::string& hash) else return items.front() ; } +//=======================================================================================================================// + + +GxsTunnelsDialog::GxsTunnelsDialog(QWidget *parent) + : RsAutoUpdatePage(2000,parent) +{ +// setupUi(this) ; + + m_bProcessSettings = false; + + float fontHeight = QFontMetricsF(font()).height(); + float fact = fontHeight/14.0; + + maxWidth = 200 ; + maxHeight = 200 ; + + // load settings + processSettings(true); +} + +GxsTunnelsDialog::~GxsTunnelsDialog() +{ + + // save settings + processSettings(false); +} + +void GxsTunnelsDialog::processSettings(bool bLoad) +{ + m_bProcessSettings = true; + + Settings->beginGroup(QString("TurtleRouterStatistics")); + + if (bLoad) { + // load settings + } else { + // save settings + } + + Settings->endGroup(); + + m_bProcessSettings = false; +} + +void GxsTunnelsDialog::updateDisplay() +{ + // Request info about ongoing tunnels + + std::vector tunnel_infos ; + + rsGxsTunnel->getTunnelsInfo(tunnel_infos) ; + + // // Tunnel information + // + // GxsTunnelId tunnel_id ; // GXS Id we're talking to + // RsGxsId destination_gxs_id ; // GXS Id we're talking to + // RsGxsId source_gxs_id ; // GXS Id we're using to talk + // uint32_t tunnel_status ; // active, requested, DH pending, etc. + // uint32_t total_size_sent ; // total bytes sent through that tunnel since openned (including management). + // uint32_t total_size_received ; // total bytes received through that tunnel since openned (including management). + + // // Data packets + + // uint32_t pending_data_packets; // number of packets not acknowledged by other side, still on their way. Should be 0 unless something bad happens. + // uint32_t total_data_packets_sent ; // total number of data packets sent (does not include tunnel management) + // uint32_t total_data_packets_received ; // total number of data packets received (does not include tunnel management) + + // now draw the shit + QPixmap tmppixmap(maxWidth, maxHeight); + tmppixmap.fill(Qt::transparent); + //setFixedHeight(maxHeight); + + QPainter painter(&tmppixmap); + painter.initFrom(this); + + // extracts the height of the fonts in pixels. This is used to calibrate the size of the objects to draw. + + float fontHeight = QFontMetricsF(font()).height(); + float fact = fontHeight/14.0; + //maxHeight = 500*fact ; + + int cellx = 6*fact ; + int celly = (10+4)*fact ; + int ox=5*fact,oy=5*fact ; + + painter.setPen(QColor::fromRgb(0,0,0)) ; + painter.drawText(ox+2*cellx,oy+celly,tr("Authenticated tunnels:")) ; oy += celly ; + + for(uint32_t i=0;i names ; + + std::map::const_iterator it = names.find(peer_id) ; + + if( it != names.end()) + return it->second ; + else + { + RsPeerDetails detail ; + if(!rsPeers->getPeerDetails(peer_id,detail)) + return tr("Unknown Peer"); + + return (names[peer_id] = QString::fromUtf8(detail.name.c_str())) ; + } +} + +QString GxsTunnelsDialog::speedString(float f) +{ + if(f < 1.0f) + return QString("0 B/s") ; + if(f < 1024.0f) + return QString::number((int)f)+" B/s" ; + + return QString::number(f/1024.0,'f',2) + " KB/s"; +} + +void GxsTunnelsDialog::paintEvent(QPaintEvent */*event*/) +{ + QStylePainter(this).drawPixmap(0, 0, pixmap); +} + +void GxsTunnelsDialog::resizeEvent(QResizeEvent *event) +{ + QRect TaskGraphRect = geometry(); + + maxWidth = TaskGraphRect.width(); + maxHeight = TaskGraphRect.height() ; + + QWidget::resizeEvent(event); + update(); +} diff --git a/retroshare-gui/src/gui/statistics/TurtleRouterDialog.h b/retroshare-gui/src/gui/statistics/TurtleRouterDialog.h index 77bb4359e..3beab4738 100644 --- a/retroshare-gui/src/gui/statistics/TurtleRouterDialog.h +++ b/retroshare-gui/src/gui/statistics/TurtleRouterDialog.h @@ -3,6 +3,7 @@ #include #include #include "ui_TurtleRouterDialog.h" +#include "ui_TurtleRouterStatistics.h" #include "RsAutoUpdatePage.h" @@ -35,3 +36,30 @@ class TurtleRouterDialog: public RsAutoUpdatePage, public Ui::TurtleRouterDialog QTreeWidgetItem *top_level_t_requests ; } ; + +class GxsTunnelsDialog: public RsAutoUpdatePage +{ + Q_OBJECT + +public: + GxsTunnelsDialog(QWidget *parent = NULL) ; + ~GxsTunnelsDialog(); + + // Cache for peer names. + static QString getPeerName(const RsPeerId &peer_id) ; + +protected: + virtual void paintEvent(QPaintEvent *); + virtual void resizeEvent(QResizeEvent *event); +private: + void processSettings(bool bLoad); + bool m_bProcessSettings; + static QString speedString(float f); + + virtual void updateDisplay() ; + + int maxWidth ; + int maxHeight ; + + QPixmap pixmap; +} ; diff --git a/retroshare-gui/src/gui/statistics/TurtleRouterStatistics.cpp b/retroshare-gui/src/gui/statistics/TurtleRouterStatistics.cpp index 7b432e597..a9713f0fa 100644 --- a/retroshare-gui/src/gui/statistics/TurtleRouterStatistics.cpp +++ b/retroshare-gui/src/gui/statistics/TurtleRouterStatistics.cpp @@ -195,8 +195,8 @@ TurtleRouterStatistics::TurtleRouterStatistics(QWidget *parent) _tunnel_statistics_F->setFrameStyle(QFrame::NoFrame); _tunnel_statistics_F->setFocusPolicy(Qt::NoFocus); - routertabWidget->addTab(new TurtleRouterDialog(),QString(tr("Tunnel Requests"))); - + routertabWidget->addTab(new TurtleRouterDialog(),QString(tr("Anonymous tunnels"))); + routertabWidget->addTab(new GxsTunnelsDialog(),QString(tr("Authenticated tunnels"))); float fontHeight = QFontMetricsF(font()).height(); float fact = fontHeight/14.0; diff --git a/retroshare-gui/src/gui/toaster/ChatLobbyToaster.cpp b/retroshare-gui/src/gui/toaster/ChatLobbyToaster.cpp index 882004975..25d5c2d2a 100644 --- a/retroshare-gui/src/gui/toaster/ChatLobbyToaster.cpp +++ b/retroshare-gui/src/gui/toaster/ChatLobbyToaster.cpp @@ -45,7 +45,7 @@ ChatLobbyToaster::ChatLobbyToaster(const ChatLobbyId &lobby_id, const RsGxsId &s if(!rsIdentity->getIdDetails(sender_id, idd)) return; - ui.avatarWidget->setId(ChatId(sender_id)); + ui.avatarWidget->setGxsId(sender_id); QString lobbyName = RsHtml::plainText(idd.mNickname);