2014-11-08 09:55:29 -05:00
/*
* 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 " .
*
*/
2014-11-08 12:57:21 -05:00
# include <unistd.h>
2015-12-25 22:37:06 -05:00
# include <sstream>
2014-11-08 12:57:21 -05:00
2014-11-08 09:55:29 -05:00
# include "openssl/rand.h"
# include "openssl/dh.h"
# include "openssl/err.h"
# include "util/rsaes.h"
2015-11-26 20:40:06 -05:00
# include "util/rsmemory.h"
2015-11-30 00:02:44 -05:00
# include "util/rsprint.h"
2014-11-08 09:55:29 -05:00
# include <serialiser/rsmsgitems.h>
# include <retroshare/rsmsgs.h>
# include <retroshare/rsidentity.h>
# include <retroshare/rsiface.h>
# include <rsserver/p3face.h>
# include <services/p3idservice.h>
# include <gxs/gxssecurity.h>
# include <turtle/p3turtle.h>
# include <retroshare/rsids.h>
# include "distantchat.h"
2015-02-14 16:10:05 -05:00
//#define DEBUG_DISTANT_CHAT
2014-12-07 16:51:30 -05:00
2015-03-15 09:23:03 -04:00
static const uint32_t DISTANT_CHAT_KEEP_ALIVE_TIMEOUT = 6 ; // send keep alive packet so as to avoid tunnel breaks.
2014-11-08 09:55:29 -05:00
2015-02-14 15:55:05 -05:00
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 ;
2015-11-26 20:40:06 -05:00
static const uint32_t DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID = 0xa0001 ;
2015-03-15 09:23:03 -04:00
2015-11-26 20:40:06 -05:00
typedef RsGxsTunnelService : : RsGxsTunnelId RsGxsTunnelId ;
2015-03-15 09:23:03 -04:00
2015-12-27 21:10:20 -05:00
DistantChatService : : DistantChatService ( ) : mDistantChatMtx ( " distant chat " )
{
mGxsTunnels = NULL ;
mDistantChatPermissions = RS_DISTANT_CHAT_CONTACT_PERMISSION_FLAG_FILTER_NONE ; // default: accept everyone
}
2015-11-26 20:40:06 -05:00
void DistantChatService : : connectToGxsTunnelService ( RsGxsTunnelService * tr )
2014-11-08 09:55:29 -05:00
{
2015-11-26 20:40:06 -05:00
mGxsTunnels = tr ;
tr - > registerClientService ( DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID , this ) ;
2014-11-08 09:55:29 -05:00
}
2015-11-26 20:40:06 -05:00
2014-11-08 09:55:29 -05:00
bool DistantChatService : : handleOutgoingItem ( RsChatItem * item )
{
2015-11-26 20:40:06 -05:00
RsGxsTunnelId tunnel_id ;
2014-12-07 16:51:30 -05:00
{
RS_STACK_MUTEX ( mDistantChatMtx ) ;
2014-11-20 18:18:14 -05:00
2015-11-26 20:40:06 -05:00
std : : map < DistantChatPeerId , DistantChatContact > : : const_iterator it = mDistantChatContacts . find ( DistantChatPeerId ( item - > PeerId ( ) ) ) ;
2014-12-07 16:51:30 -05:00
2015-11-26 20:40:06 -05:00
if ( it = = mDistantChatContacts . end ( ) )
2014-12-07 16:51:30 -05:00
return false ;
}
2014-11-22 07:47:21 -05:00
2014-11-08 09:55:29 -05:00
# ifdef CHAT_DEBUG
2014-11-22 07:47:21 -05:00
std : : cerr < < " p3ChatService::handleOutgoingItem(): sending to " < < item - > PeerId ( ) < < " : interpreted as a distant chat virtual peer id. " < < std : : endl ;
2014-11-08 09:55:29 -05:00
# endif
2015-11-26 20:40:06 -05:00
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 ;
}
2015-12-27 22:19:30 -05:00
# ifdef DEBUG_DISTANT_CHAT
2015-11-30 00:02:44 -05:00
std : : cerr < < " sending: " < < RsUtil : : BinToHex ( mem , size ) < < std : : endl ;
2015-12-27 22:19:30 -05:00
# endif
2015-11-30 00:02:44 -05:00
2015-11-26 20:40:06 -05:00
mGxsTunnels - > sendData ( RsGxsTunnelId ( item - > PeerId ( ) ) , DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID , mem , size ) ;
2014-11-22 07:47:21 -05:00
return true ;
2014-11-08 09:55:29 -05:00
}
void DistantChatService : : handleRecvChatStatusItem ( RsChatStatusItem * cs )
{
2015-12-27 22:16:04 -05:00
if ( cs - > flags & RS_CHAT_FLAG_CONNEXION_REFUSED )
{
2015-12-27 22:19:30 -05:00
# ifdef DEBUG_DISTANT_CHAT
2015-12-27 22:16:04 -05:00
std : : cerr < < " (II) Distant chat: received notification that peer refuses conversation. " < < std : : endl ;
2015-12-27 22:19:30 -05:00
# endif
2015-12-27 22:16:04 -05:00
RsServer : : notify ( ) - > notifyChatStatus ( ChatId ( DistantChatPeerId ( cs - > PeerId ( ) ) ) , " Connexion refused by distant peer! " ) ;
}
2014-12-07 16:51:30 -05:00
if ( cs - > flags & RS_CHAT_FLAG_CLOSING_DISTANT_CONNECTION )
2015-11-28 14:55:56 -05:00
markDistantChatAsClosed ( DistantChatPeerId ( cs - > PeerId ( ) ) ) ;
2014-12-07 16:51:30 -05:00
// nothing more to do, because the decryption routing will update the last_contact time when decrypting.
if ( cs - > flags & RS_CHAT_FLAG_KEEP_ALIVE )
std : : cerr < < " DistantChatService::handleRecvChatStatusItem(): received keep alive packet for inactive chat! peerId= " < < cs - > PeerId ( ) < < std : : endl ;
2014-11-08 09:55:29 -05:00
}
2016-04-10 21:09:47 -04:00
bool DistantChatService : : acceptDataFromPeer ( const RsGxsId & gxs_id , const RsGxsTunnelId & tunnel_id , bool is_client_side )
2015-12-25 22:37:06 -05:00
{
2015-12-27 22:16:04 -05:00
bool res = true ;
2016-04-10 21:09:47 -04:00
if ( is_client_side ) // always accept distant chat when we're the client side.
return true ;
2015-12-27 21:10:20 -05:00
if ( mDistantChatPermissions & RS_DISTANT_CHAT_CONTACT_PERMISSION_FLAG_FILTER_NON_CONTACTS )
2015-12-27 22:16:04 -05:00
res = ( rsIdentity ! = NULL ) & & rsIdentity - > isARegularContact ( gxs_id ) ;
2015-12-25 22:37:06 -05:00
2015-12-27 21:10:20 -05:00
if ( mDistantChatPermissions & RS_DISTANT_CHAT_CONTACT_PERMISSION_FLAG_FILTER_EVERYBODY )
2015-12-27 22:16:04 -05:00
res = false ;
if ( ! res )
{
2015-12-27 22:19:30 -05:00
# ifdef DEBUG_DISTANT_CHAT
2015-12-27 22:16:04 -05:00
std : : cerr < < " (II) refusing distant chat from peer " < < gxs_id < < " . Sending a notification back to tunnel " < < tunnel_id < < std : : endl ;
2015-12-27 22:19:30 -05:00
# endif
2015-12-27 22:16:04 -05:00
RsChatStatusItem * item = new RsChatStatusItem ;
item - > flags = RS_CHAT_FLAG_CONNEXION_REFUSED ;
item - > status_string . clear ( ) ; // is not used yet! But could be set in GUI to some message (??).
item - > PeerId ( RsPeerId ( tunnel_id ) ) ;
// we do not use handleOutGoingItem() because there's no distant chat contact, as the chat is refused.
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 ) ;
}
2015-12-25 22:37:06 -05:00
2015-12-27 22:16:04 -05:00
return res ;
2015-12-25 22:37:06 -05:00
}
2015-11-28 14:55:56 -05:00
void DistantChatService : : notifyTunnelStatus ( const RsGxsTunnelService : : RsGxsTunnelId & tunnel_id , uint32_t tunnel_status )
{
2015-12-27 22:19:30 -05:00
# ifdef DEBUG_DISTANT_CHAT
2015-11-28 14:55:56 -05:00
std : : cerr < < " DistantChatService::notifyTunnelStatus(): got notification " < < std : : hex < < tunnel_status < < std : : dec < < " for tunnel " < < tunnel_id < < std : : endl ;
2015-12-27 22:19:30 -05:00
# endif
2015-11-30 00:02:44 -05:00
switch ( tunnel_status )
{
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 ;
}
2015-11-28 14:55:56 -05:00
}
void DistantChatService : : receiveData ( const RsGxsTunnelService : : RsGxsTunnelId & tunnel_id , unsigned char * data , uint32_t data_size )
{
2015-12-27 22:19:30 -05:00
# ifdef DEBUG_DISTANT_CHAT
2015-11-28 14:55:56 -05:00
std : : cerr < < " DistantChatService::receiveData(): got data of size " < < data_size < < " for tunnel " < < tunnel_id < < std : : endl ;
2015-11-30 00:02:44 -05:00
std : : cerr < < " received: " < < RsUtil : : BinToHex ( data , data_size ) < < std : : endl ;
std : : cerr < < " deserialising... " < < std : : endl ;
2015-12-27 22:19:30 -05:00
# endif
2015-12-25 22:37:06 -05:00
2015-11-30 00:02:44 -05:00
// 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.
{
RS_STACK_MUTEX ( mDistantChatMtx ) ;
RsGxsTunnelService : : GxsTunnelInfo tinfo ;
if ( ! mGxsTunnels - > getTunnelInfo ( tunnel_id , tinfo ) )
return ;
2015-12-25 22:37:06 -05:00
// Check if the data is accepted. We cannot prevent the creation of tunnels at the level of p3GxsTunnels, since tunnels are shared between services.
// however,
2015-11-30 00:02:44 -05:00
DistantChatContact & contact ( mDistantChatContacts [ DistantChatPeerId ( tunnel_id ) ] ) ;
2015-12-25 22:37:06 -05:00
2015-11-30 00:02:44 -05:00
contact . to_id = tinfo . destination_gxs_id ;
contact . from_id = tinfo . source_gxs_id ;
}
2015-12-25 22:37:06 -05:00
RsItem * item = RsChatSerialiser ( ) . deserialise ( data , & data_size ) ;
2015-11-30 00:02:44 -05:00
if ( item ! = NULL )
{
2015-12-25 22:37:06 -05:00
item - > PeerId ( RsPeerId ( tunnel_id ) ) ; // just in case, but normally this is already done.
2015-11-30 00:02:44 -05:00
handleIncomingItem ( item ) ;
RsServer : : notify ( ) - > notifyListChange ( NOTIFY_LIST_PRIVATE_INCOMING_CHAT , NOTIFY_TYPE_ADD ) ;
}
else
2015-12-25 22:37:06 -05:00
std : : cerr < < " (EE) item could not be deserialised! " < < std : : endl ;
2015-11-28 14:55:56 -05:00
}
void DistantChatService : : markDistantChatAsClosed ( const DistantChatPeerId & dcpid )
{
2015-11-30 00:02:44 -05:00
mGxsTunnels - > closeExistingTunnel ( RsGxsTunnelService : : RsGxsTunnelId ( dcpid ) , DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID ) ;
2015-11-28 14:55:56 -05:00
RS_STACK_MUTEX ( mDistantChatMtx ) ;
std : : map < DistantChatPeerId , DistantChatContact > : : iterator it = mDistantChatContacts . find ( dcpid ) ;
if ( it ! = mDistantChatContacts . end ( ) )
mDistantChatContacts . erase ( it ) ;
}
2017-03-01 17:07:53 -05:00
bool DistantChatService : : initiateDistantChatConnexion (
const RsGxsId & to_gxs_id , const RsGxsId & from_gxs_id ,
DistantChatPeerId & dcpid , uint32_t & error_code , bool notify )
2015-02-14 15:55:05 -05:00
{
2015-11-26 20:40:06 -05:00
RsGxsTunnelId tunnel_id ;
2015-11-30 00:02:44 -05:00
if ( ! mGxsTunnels - > requestSecuredTunnel ( to_gxs_id , from_gxs_id , tunnel_id , DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID , error_code ) )
2015-11-26 20:40:06 -05:00
return false ;
2015-11-30 00:02:44 -05:00
2015-11-26 20:40:06 -05:00
dcpid = DistantChatPeerId ( tunnel_id ) ;
2015-02-14 15:55:05 -05:00
2015-11-26 20:40:06 -05:00
DistantChatContact & dc_contact ( mDistantChatContacts [ dcpid ] ) ;
2015-02-14 15:55:05 -05:00
2015-11-26 20:40:06 -05:00
dc_contact . from_id = from_gxs_id ;
dc_contact . to_id = to_gxs_id ;
2014-11-20 18:18:14 -05:00
2014-11-17 16:56:41 -05:00
error_code = RS_DISTANT_CHAT_ERROR_NO_ERROR ;
2017-03-01 17:07:53 -05:00
if ( notify )
{
// Make a self message to raise the chat window
RsChatMsgItem * item = new RsChatMsgItem ;
item - > message = " [Starting distant chat. Please wait for secure tunnel " ;
item - > message + = " to be established] " ;
item - > chatFlags = RS_CHAT_FLAG_PRIVATE ;
item - > sendTime = time ( NULL ) ;
item - > PeerId ( RsPeerId ( tunnel_id ) ) ;
handleRecvChatMsgItem ( item ) ;
delete item ;
}
2014-11-20 18:18:14 -05:00
return true ;
2014-11-08 09:55:29 -05:00
}
2015-11-26 20:40:06 -05:00
bool DistantChatService : : getDistantChatStatus ( const DistantChatPeerId & tunnel_id , DistantChatPeerInfo & cinfo )
2014-11-08 09:55:29 -05:00
{
2016-12-07 14:09:14 -05:00
RS_STACK_MUTEX ( mDistantChatMtx ) ;
RsGxsTunnelService : : GxsTunnelInfo tinfo ;
if ( ! mGxsTunnels - > getTunnelInfo ( RsGxsTunnelId ( tunnel_id ) , tinfo ) ) return false ;
cinfo . to_id = tinfo . destination_gxs_id ;
cinfo . own_id = tinfo . source_gxs_id ;
cinfo . peer_id = tunnel_id ;
switch ( tinfo . tunnel_status )
{
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 ;
case RsGxsTunnelService : : RS_GXS_TUNNEL_STATUS_UNKNOWN :
default :
cinfo . status = RS_DISTANT_CHAT_STATUS_UNKNOWN ; break ;
}
return true ;
2014-11-08 09:55:29 -05:00
}
2015-11-26 20:40:06 -05:00
bool DistantChatService : : closeDistantChatConnexion ( const DistantChatPeerId & tunnel_id )
2014-11-08 09:55:29 -05:00
{
2015-11-30 00:02:44 -05:00
mGxsTunnels - > closeExistingTunnel ( RsGxsTunnelId ( tunnel_id ) , DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID ) ;
2015-11-26 20:40:06 -05:00
// also remove contact. Or do we wait for the notification?
2014-11-20 18:18:14 -05:00
return true ;
2014-11-08 09:55:29 -05:00
}
2015-12-25 22:37:06 -05:00
uint32_t DistantChatService : : getDistantChatPermissionFlags ( )
{
return mDistantChatPermissions ;
}
bool DistantChatService : : setDistantChatPermissionFlags ( uint32_t flags )
{
if ( mDistantChatPermissions ! = flags )
{
mDistantChatPermissions = flags ;
2015-12-27 22:19:30 -05:00
# ifdef DEBUG_DISTANT_CHAT
2015-12-25 22:37:06 -05:00
std : : cerr < < " (II) Changing distant chat permissions to " < < flags < < " . Existing openned chats will however remain active until closed " < < std : : endl ;
2015-12-27 22:19:30 -05:00
# endif
2015-12-25 22:37:06 -05:00
triggerConfigSave ( ) ;
}
return true ;
}
void DistantChatService : : addToSaveList ( std : : list < RsItem * > & list ) const
{
/* Save permission flags */
RsConfigKeyValueSet * vitem = new RsConfigKeyValueSet ;
RsTlvKeyValue kv ;
kv . key = " DISTANT_CHAT_PERMISSION_FLAGS " ;
kv . value = RsUtil : : NumberToString ( mDistantChatPermissions ) ;
vitem - > tlvkvs . pairs . push_back ( kv ) ;
list . push_back ( vitem ) ;
}
bool DistantChatService : : processLoadListItem ( const RsItem * item )
{
const RsConfigKeyValueSet * vitem = NULL ;
if ( NULL ! = ( vitem = dynamic_cast < const RsConfigKeyValueSet * > ( item ) ) )
for ( std : : list < RsTlvKeyValue > : : const_iterator kit = vitem - > tlvkvs . pairs . begin ( ) ; kit ! = vitem - > tlvkvs . pairs . end ( ) ; + + kit )
if ( kit - > key = = " DISTANT_CHAT_PERMISSION_FLAGS " )
{
# ifdef DEBUG_DISTANT_CHAT
std : : cerr < < " Loaded distant chat permission flags: " < < kit - > value < < std : : endl ;
# endif
if ( ! kit - > value . empty ( ) )
{
std : : istringstream is ( kit - > value ) ;
uint32_t tmp ;
is > > tmp ;
if ( tmp < 3 )
mDistantChatPermissions = tmp ;
else
std : : cerr < < " (EE) Invalid value read for DistantChatPermission flags in config: " < < tmp < < std : : endl ;
}
return true ;
}
return false ;
}
2014-11-08 09:55:29 -05:00