RetroShare/libretroshare/src/ft/ftserver.cc
Phenom f12cd5774d Fix Partial Dir Check
Add WrongValue StyleSheet property, when bad directory selected.
Update QLineEdit with current setting so it's possible to see if
something is modified. No need to restart.
2018-05-12 14:31:28 +02:00

1774 lines
53 KiB
C++

/*
* libretroshare/src/ft: ftserver.cc
*
* File Transfer for RetroShare.
*
* Copyright 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 "crypto/chacha20.h"
//const int ftserverzone = 29539;
#include "file_sharing/p3filelists.h"
#include "ft/ftcontroller.h"
#include "ft/ftdatamultiplex.h"
//#include "ft/ftdwlqueue.h"
#include "ft/ftextralist.h"
#include "ft/ftfileprovider.h"
#include "ft/ftfilesearch.h"
#include "ft/ftserver.h"
#include "ft/ftturtlefiletransferitem.h"
#include "pqi/p3linkmgr.h"
#include "pqi/p3notify.h"
#include "pqi/pqi.h"
#include "retroshare/rstypes.h"
#include "retroshare/rspeers.h"
#include "rsitems/rsfiletransferitems.h"
#include "rsitems/rsserviceids.h"
#include "rsserver/p3face.h"
#include "rsserver/rsaccounts.h"
#include "turtle/p3turtle.h"
#include "util/rsdebug.h"
#include "util/rsdir.h"
#include "util/rsprint.h"
#include <iostream>
#include <time.h>
/***
* #define SERVER_DEBUG 1
* #define SERVER_DEBUG_CACHE 1
***/
#define FTSERVER_DEBUG() std::cerr << time(NULL) << " : FILE_SERVER : " << __FUNCTION__ << " : "
#define FTSERVER_ERROR() std::cerr << "(EE) FILE_SERVER ERROR : "
static const time_t FILE_TRANSFER_LOW_PRIORITY_TASKS_PERIOD = 5 ; // low priority tasks handling every 5 seconds
static const time_t FILE_TRANSFER_MAX_DELAY_BEFORE_DROP_USAGE_RECORD = 10 ; // keep usage records for 10 secs at most.
/* Setup */
ftServer::ftServer(p3PeerMgr *pm, p3ServiceControl *sc)
: p3Service(),RsServiceSerializer(RS_SERVICE_TYPE_TURTLE), // should be FT, but this is for backward compatibility
mPeerMgr(pm), mServiceCtrl(sc),
mFileDatabase(NULL),
mFtController(NULL), mFtExtra(NULL),
mFtDataplex(NULL), mFtSearch(NULL), srvMutex("ftServer")
{
addSerialType(new RsFileTransferSerialiser()) ;
}
const std::string FILE_TRANSFER_APP_NAME = "ft";
const uint16_t FILE_TRANSFER_APP_MAJOR_VERSION = 1;
const uint16_t FILE_TRANSFER_APP_MINOR_VERSION = 0;
const uint16_t FILE_TRANSFER_MIN_MAJOR_VERSION = 1;
const uint16_t FILE_TRANSFER_MIN_MINOR_VERSION = 0;
RsServiceInfo ftServer::getServiceInfo()
{
return RsServiceInfo(RS_SERVICE_TYPE_FILE_TRANSFER,
FILE_TRANSFER_APP_NAME,
FILE_TRANSFER_APP_MAJOR_VERSION,
FILE_TRANSFER_APP_MINOR_VERSION,
FILE_TRANSFER_MIN_MAJOR_VERSION,
FILE_TRANSFER_MIN_MINOR_VERSION);
}
void ftServer::setConfigDirectory(std::string path)
{
mConfigPath = path;
/* Must update the sub classes ... if they exist
* TODO.
*/
std::string basecachedir = mConfigPath + "/cache";
std::string localcachedir = mConfigPath + "/cache/local";
std::string remotecachedir = mConfigPath + "/cache/remote";
RsDirUtil::checkCreateDirectory(basecachedir) ;
RsDirUtil::checkCreateDirectory(localcachedir) ;
RsDirUtil::checkCreateDirectory(remotecachedir) ;
}
/* Control Interface */
/* add Config Items (Extra, Controller) */
void ftServer::addConfigComponents(p3ConfigMgr */*mgr*/)
{
/* NOT SURE ABOUT THIS ONE */
}
const RsPeerId& ftServer::OwnId()
{
static RsPeerId null_id ;
if (mServiceCtrl)
return mServiceCtrl->getOwnId();
else
return null_id ;
}
/* Final Setup (once everything is assigned) */
void ftServer::SetupFtServer()
{
/* setup FiStore/Monitor */
//std::string localcachedir = mConfigPath + "/cache/local";
//std::string remotecachedir = mConfigPath + "/cache/remote";
RsPeerId ownId = mServiceCtrl->getOwnId();
/* search/extras List */
mFtExtra = new ftExtraList();
mFtSearch = new ftFileSearch();
/* Transport */
mFtDataplex = new ftDataMultiplex(ownId, this, mFtSearch);
/* make Controller */
mFtController = new ftController(mFtDataplex, mServiceCtrl, getServiceInfo().mServiceType);
mFtController -> setFtSearchNExtra(mFtSearch, mFtExtra);
std::string emergencySaveDir = rsAccounts->PathAccountDirectory();
std::string emergencyPartialsDir = rsAccounts->PathAccountDirectory();
if (emergencySaveDir != "")
{
emergencySaveDir += "/";
emergencyPartialsDir += "/";
}
emergencySaveDir += "Downloads";
emergencyPartialsDir += "Partials";
mFtController->setDownloadDirectory(emergencySaveDir);
mFtController->setPartialsDirectory(emergencyPartialsDir);
/* complete search setup */
mFtSearch->addSearchMode(mFtExtra, RS_FILE_HINTS_EXTRA);
mServiceCtrl->registerServiceMonitor(mFtController, getServiceInfo().mServiceType);
return;
}
void ftServer::connectToFileDatabase(p3FileDatabase *fdb)
{
mFileDatabase = fdb ;
mFtSearch->addSearchMode(fdb, RS_FILE_HINTS_LOCAL); // due to a bug in addSearchModule, modules can only be added one by one. Using | between flags wont work.
mFtSearch->addSearchMode(fdb, RS_FILE_HINTS_REMOTE);
}
void ftServer::connectToTurtleRouter(p3turtle *fts)
{
mTurtleRouter = fts ;
mFtController->setTurtleRouter(fts) ;
mFtController->setFtServer(this) ;
mTurtleRouter->registerTunnelService(this) ;
}
void ftServer::StartupThreads()
{
/* start up order - important for dependencies */
/* self contained threads */
/* startup ExtraList Thread */
mFtExtra->start("ft extra lst");
/* startup Monitor Thread */
/* startup the FileMonitor (after cache load) */
/* start it up */
mFileDatabase->startThreads();
/* Controller thread */
mFtController->start("ft ctrl");
/* Dataplex */
mFtDataplex->start("ft dataplex");
}
void ftServer::StopThreads()
{
/* stop Dataplex */
mFtDataplex->join();
/* stop Controller thread */
mFtController->join();
/* self contained threads */
/* stop ExtraList Thread */
mFtExtra->join();
delete (mFtDataplex);
mFtDataplex = NULL;
delete (mFtController);
mFtController = NULL;
delete (mFtExtra);
mFtExtra = NULL;
/* stop Monitor Thread */
mFileDatabase->stopThreads();
delete mFileDatabase;
mFileDatabase = NULL ;
}
/***************************************************************/
/********************** RsFiles Interface **********************/
/***************************************************************/
/***************************************************************/
/********************** Controller Access **********************/
/***************************************************************/
bool ftServer::ResumeTransfers()
{
mFtController->activate();
return true;
}
bool ftServer::getFileData(const RsFileHash& hash, uint64_t offset, uint32_t& requested_size,uint8_t *data)
{
return mFtDataplex->getFileData(hash, offset, requested_size,data);
}
bool ftServer::alreadyHaveFile(const RsFileHash& hash, FileInfo &info)
{
return mFileDatabase->search(hash, RS_FILE_HINTS_LOCAL, info);
}
bool ftServer::FileRequest(const std::string& fname, const RsFileHash& hash, uint64_t size, const std::string& dest, TransferRequestFlags flags, const std::list<RsPeerId>& srcIds)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "Requesting " << fname << std::endl ;
#endif
if(!mFtController->FileRequest(fname, hash, size, dest, flags, srcIds))
return false ;
return true ;
}
bool ftServer::activateTunnels(const RsFileHash& hash,uint32_t encryption_policy,TransferRequestFlags flags,bool onoff)
{
RsFileHash hash_of_hash ;
encryptHash(hash,hash_of_hash) ;
mEncryptedHashes.insert(std::make_pair(hash_of_hash,hash)) ;
if(onoff)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "Activating tunnels for hash " << hash << std::endl;
#endif
if(flags & RS_FILE_REQ_ENCRYPTED)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << " flags require end-to-end encryption. Requesting hash of hash " << hash_of_hash << std::endl;
#endif
mTurtleRouter->monitorTunnels(hash_of_hash,this,true) ;
}
if((flags & RS_FILE_REQ_UNENCRYPTED) && (encryption_policy != RS_FILE_CTRL_ENCRYPTION_POLICY_STRICT))
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << " flags require no end-to-end encryption. Requesting hash " << hash << std::endl;
#endif
mTurtleRouter->monitorTunnels(hash,this,true) ;
}
}
else
{
mTurtleRouter->stopMonitoringTunnels(hash_of_hash);
mTurtleRouter->stopMonitoringTunnels(hash);
}
return true ;
}
bool ftServer::setDestinationDirectory(const RsFileHash& hash,const std::string& directory)
{
return mFtController->setDestinationDirectory(hash,directory);
}
bool ftServer::setDestinationName(const RsFileHash& hash,const std::string& name)
{
return mFtController->setDestinationName(hash,name);
}
bool ftServer::setChunkStrategy(const RsFileHash& hash,FileChunksInfo::ChunkStrategy s)
{
return mFtController->setChunkStrategy(hash,s);
}
void ftServer::setDefaultChunkStrategy(FileChunksInfo::ChunkStrategy s)
{
mFtController->setDefaultChunkStrategy(s) ;
}
FileChunksInfo::ChunkStrategy ftServer::defaultChunkStrategy()
{
return mFtController->defaultChunkStrategy() ;
}
uint32_t ftServer::freeDiskSpaceLimit()const
{
return mFtController->freeDiskSpaceLimit() ;
}
void ftServer::setFreeDiskSpaceLimit(uint32_t s)
{
mFtController->setFreeDiskSpaceLimit(s) ;
}
void ftServer::setDefaultEncryptionPolicy(uint32_t s)
{
mFtController->setDefaultEncryptionPolicy(s) ;
}
uint32_t ftServer::defaultEncryptionPolicy()
{
return mFtController->defaultEncryptionPolicy() ;
}
void ftServer::setMaxUploadSlotsPerFriend(uint32_t n)
{
mFtController->setMaxUploadsPerFriend(n) ;
}
uint32_t ftServer::getMaxUploadSlotsPerFriend()
{
return mFtController->getMaxUploadsPerFriend() ;
}
void ftServer::setFilePermDirectDL(uint32_t perm)
{
mFtController->setFilePermDirectDL(perm);
}
uint32_t ftServer::filePermDirectDL()
{
return mFtController->filePermDirectDL() ;
}
bool ftServer::FileCancel(const RsFileHash& hash)
{
// Remove from both queue and ftController, by default.
//
mFtController->FileCancel(hash);
return true ;
}
bool ftServer::FileControl(const RsFileHash& hash, uint32_t flags)
{
return mFtController->FileControl(hash, flags);
}
bool ftServer::FileClearCompleted()
{
return mFtController->FileClearCompleted();
}
void ftServer::setQueueSize(uint32_t s)
{
mFtController->setQueueSize(s) ;
}
uint32_t ftServer::getQueueSize()
{
return mFtController->getQueueSize() ;
}
/* Control of Downloads Priority. */
bool ftServer::changeQueuePosition(const RsFileHash& hash, QueueMove mv)
{
mFtController->moveInQueue(hash,mv) ;
return true ;
}
bool ftServer::changeDownloadSpeed(const RsFileHash& hash, int speed)
{
mFtController->setPriority(hash, (DwlSpeed)speed);
return true ;
}
bool ftServer::getDownloadSpeed(const RsFileHash& hash, int & speed)
{
DwlSpeed _speed;
int ret = mFtController->getPriority(hash, _speed);
if (ret)
speed = _speed;
return ret;
}
bool ftServer::clearDownload(const RsFileHash& /*hash*/)
{
return true ;
}
bool ftServer::FileDownloadChunksDetails(const RsFileHash& hash,FileChunksInfo& info)
{
return mFtController->getFileDownloadChunksDetails(hash,info);
}
void ftServer::requestDirUpdate(void *ref)
{
mFileDatabase->requestDirUpdate(ref) ;
}
/* Directory Handling */
bool ftServer::setDownloadDirectory(std::string path)
{
return mFtController->setDownloadDirectory(path);
}
std::string ftServer::getDownloadDirectory()
{
return mFtController->getDownloadDirectory();
}
bool ftServer::setPartialsDirectory(std::string path)
{
return mFtController->setPartialsDirectory(path);
}
std::string ftServer::getPartialsDirectory()
{
return mFtController->getPartialsDirectory();
}
/***************************************************************/
/************************* Other Access ************************/
/***************************************************************/
bool ftServer::copyFile(const std::string& source, const std::string& dest)
{
return RsDirUtil::copyFile(source, dest);
}
void ftServer::FileDownloads(std::list<RsFileHash> &hashs)
{
mFtController->FileDownloads(hashs);
}
bool ftServer::FileUploadChunksDetails(const RsFileHash& hash,const RsPeerId& peer_id,CompressedChunkMap& cmap)
{
return mFtDataplex->getClientChunkMap(hash,peer_id,cmap);
}
bool ftServer::FileUploads(std::list<RsFileHash> &hashs)
{
return mFtDataplex->FileUploads(hashs);
}
bool ftServer::FileDetails(const RsFileHash &hash, FileSearchFlags hintflags, FileInfo &info)
{
if (hintflags & RS_FILE_HINTS_DOWNLOAD)
if(mFtController->FileDetails(hash, info))
return true ;
if(hintflags & RS_FILE_HINTS_UPLOAD)
if(mFtDataplex->FileDetails(hash, hintflags, info))
{
// We also check if the file is a DL as well. In such a case we use
// the DL as the file name, to replace the hash. If the file is a cache
// file, we skip the call to fileDetails() for efficiency reasons.
//
FileInfo info2 ;
if(mFtController->FileDetails(hash, info2))
info.fname = info2.fname ;
return true ;
}
if(hintflags & ~(RS_FILE_HINTS_UPLOAD | RS_FILE_HINTS_DOWNLOAD))
if(mFtSearch->search(hash, hintflags, info))
return true ;
return false;
}
RsItem *ftServer::create_item(uint16_t service,uint8_t item_type) const
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "p3turtle: deserialising packet: " << std::endl ;
#endif
if (RS_SERVICE_TYPE_TURTLE != service)
{
FTSERVER_ERROR() << " Wrong type !!" << std::endl ;
return NULL; /* wrong type */
}
try
{
switch(item_type)
{
case RS_TURTLE_SUBTYPE_FILE_REQUEST : return new RsTurtleFileRequestItem();
case RS_TURTLE_SUBTYPE_FILE_DATA : return new RsTurtleFileDataItem();
case RS_TURTLE_SUBTYPE_FILE_MAP_REQUEST : return new RsTurtleFileMapRequestItem();
case RS_TURTLE_SUBTYPE_FILE_MAP : return new RsTurtleFileMapItem();
case RS_TURTLE_SUBTYPE_CHUNK_CRC_REQUEST : return new RsTurtleChunkCrcRequestItem();
case RS_TURTLE_SUBTYPE_CHUNK_CRC : return new RsTurtleChunkCrcItem();
default:
return NULL ;
}
}
catch(std::exception& e)
{
FTSERVER_ERROR() << "(EE) deserialisation error in " << __PRETTY_FUNCTION__ << ": " << e.what() << std::endl;
return NULL ;
}
}
bool ftServer::isEncryptedSource(const RsPeerId& virtual_peer_id)
{
RS_STACK_MUTEX(srvMutex) ;
return mEncryptedPeerIds.find(virtual_peer_id) != mEncryptedPeerIds.end();
}
void ftServer::addVirtualPeer(const TurtleFileHash& hash,const TurtleVirtualPeerId& virtual_peer_id,RsTurtleGenericTunnelItem::Direction dir)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "adding virtual peer. Direction=" << dir << ", hash=" << hash << ", vpid=" << virtual_peer_id << std::endl;
#endif
RsFileHash real_hash ;
{
if(findRealHash(hash,real_hash))
{
RS_STACK_MUTEX(srvMutex) ;
mEncryptedPeerIds[virtual_peer_id] = hash ;
}
else
real_hash = hash;
}
if(dir == RsTurtleGenericTunnelItem::DIRECTION_SERVER)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << " direction is SERVER. Adding file source for end-to-end encrypted tunnel for real hash " << real_hash << ", virtual peer id = " << virtual_peer_id << std::endl;
#endif
mFtController->addFileSource(real_hash,virtual_peer_id) ;
}
}
void ftServer::removeVirtualPeer(const TurtleFileHash& hash,const TurtleVirtualPeerId& virtual_peer_id)
{
RsFileHash real_hash ;
if(findRealHash(hash,real_hash))
mFtController->removeFileSource(real_hash,virtual_peer_id) ;
else
mFtController->removeFileSource(hash,virtual_peer_id) ;
RS_STACK_MUTEX(srvMutex) ;
mEncryptedPeerIds.erase(virtual_peer_id) ;
}
bool ftServer::handleTunnelRequest(const RsFileHash& hash,const RsPeerId& peer_id)
{
FileInfo info ;
RsFileHash real_hash ;
bool found = false ;
if(FileDetails(hash, RS_FILE_HINTS_NETWORK_WIDE | RS_FILE_HINTS_LOCAL | RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_SPEC_ONLY, info))
{
if(info.transfer_info_flags & RS_FILE_REQ_ENCRYPTED)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "handleTunnelRequest: openning encrypted FT tunnel for H(H(F))=" << hash << " and H(F)=" << info.hash << std::endl;
#endif
RS_STACK_MUTEX(srvMutex) ;
mEncryptedHashes[hash] = info.hash;
real_hash = info.hash ;
}
else
real_hash = hash ;
found = true ;
}
else // try to see if we're already swarming the file
{
{
RS_STACK_MUTEX(srvMutex) ;
std::map<RsFileHash,RsFileHash>::const_iterator it = mEncryptedHashes.find(hash) ;
if(it != mEncryptedHashes.end())
real_hash = it->second ;
else
real_hash = hash ;
}
if(FileDetails(real_hash,RS_FILE_HINTS_DOWNLOAD,info))
{
// This file is currently being downloaded. Let's look if we already have a chunk or not. If not, no need to
// share the file!
FileChunksInfo info2 ;
if(rsFiles->FileDownloadChunksDetails(hash, info2))
for(uint32_t i=0;i<info2.chunks.size();++i)
if(info2.chunks[i] == FileChunksInfo::CHUNK_DONE)
{
found = true ;
if(info.transfer_info_flags & RS_FILE_REQ_ANONYMOUS_ROUTING)
info.storage_permission_flags = DIR_FLAGS_ANONYMOUS_DOWNLOAD ; // this is to allow swarming
break ;
}
}
}
if(found && mFtController->defaultEncryptionPolicy() == RS_FILE_CTRL_ENCRYPTION_POLICY_STRICT && hash == real_hash)
{
#ifdef SERVER_DEBUG
std::cerr << "(WW) rejecting file transfer for hash " << hash << " because the hash is not encrypted and encryption policy requires it." << std::endl;
#endif
return false ;
}
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer: performing local hash search for hash " << hash << std::endl;
if(found)
{
FTSERVER_DEBUG() << "Found hash: " << std::endl;
FTSERVER_DEBUG() << " hash = " << real_hash << std::endl;
FTSERVER_DEBUG() << " peer = " << peer_id << std::endl;
FTSERVER_DEBUG() << " flags = " << info.storage_permission_flags << std::endl;
FTSERVER_DEBUG() << " groups= " ;
for(std::list<RsNodeGroupId>::const_iterator it(info.parent_groups.begin());it!=info.parent_groups.end();++it)
FTSERVER_DEBUG() << (*it) << ", " ;
FTSERVER_DEBUG() << std::endl;
FTSERVER_DEBUG() << " clear = " << rsPeers->computePeerPermissionFlags(peer_id,info.storage_permission_flags,info.parent_groups) << std::endl;
}
#endif
// The call to computeHashPeerClearance() return a combination of RS_FILE_HINTS_NETWORK_WIDE and RS_FILE_HINTS_BROWSABLE
// This is an additional computation cost, but the way it's written here, it's only called when res is true.
//
found = found && (RS_FILE_HINTS_NETWORK_WIDE & rsPeers->computePeerPermissionFlags(peer_id,info.storage_permission_flags,info.parent_groups)) ;
return found ;
}
/***************************************************************/
/******************* ExtraFileList Access **********************/
/***************************************************************/
bool ftServer::ExtraFileAdd(std::string fname, const RsFileHash& hash, uint64_t size, uint32_t period, TransferRequestFlags flags)
{
return mFtExtra->addExtraFile(fname, hash, size, period, flags);
}
bool ftServer::ExtraFileRemove(const RsFileHash& hash, TransferRequestFlags flags)
{
return mFtExtra->removeExtraFile(hash, flags);
}
bool ftServer::ExtraFileHash(std::string localpath, uint32_t period, TransferRequestFlags flags)
{
return mFtExtra->hashExtraFile(localpath, period, flags);
}
bool ftServer::ExtraFileStatus(std::string localpath, FileInfo &info)
{
return mFtExtra->hashExtraFileDone(localpath, info);
}
bool ftServer::ExtraFileMove(std::string fname, const RsFileHash& hash, uint64_t size, std::string destpath)
{
return mFtExtra->moveExtraFile(fname, hash, size, destpath);
}
/***************************************************************/
/******************** Directory Listing ************************/
/***************************************************************/
int ftServer::RequestDirDetails(const RsPeerId& uid, const std::string& path, DirDetails &details)
{
return mFileDatabase->RequestDirDetails(uid, path, details);
}
bool ftServer::findChildPointer(void *ref, int row, void *& result, FileSearchFlags flags)
{
return mFileDatabase->findChildPointer(ref,row,result,flags) ;
}
int ftServer::RequestDirDetails(void *ref, DirDetails &details, FileSearchFlags flags)
{
return mFileDatabase->RequestDirDetails(ref,details,flags) ;
}
uint32_t ftServer::getType(void *ref, FileSearchFlags /* flags */)
{
return mFileDatabase->getType(ref) ;
}
/***************************************************************/
/******************** Search Interface *************************/
/***************************************************************/
int ftServer::SearchKeywords(std::list<std::string> keywords, std::list<DirDetails> &results,FileSearchFlags flags)
{
return mFileDatabase->SearchKeywords(keywords, results,flags,RsPeerId());
}
int ftServer::SearchKeywords(std::list<std::string> keywords, std::list<DirDetails> &results,FileSearchFlags flags,const RsPeerId& peer_id)
{
return mFileDatabase->SearchKeywords(keywords, results,flags,peer_id);
}
int ftServer::SearchBoolExp(RsRegularExpression::Expression * exp, std::list<DirDetails> &results,FileSearchFlags flags)
{
return mFileDatabase->SearchBoolExp(exp, results,flags,RsPeerId());
}
int ftServer::SearchBoolExp(RsRegularExpression::Expression * exp, std::list<DirDetails> &results,FileSearchFlags flags,const RsPeerId& peer_id)
{
return mFileDatabase->SearchBoolExp(exp,results,flags,peer_id) ;
}
int ftServer::getSharedDirStatistics(const RsPeerId& pid, SharedDirStats& stats)
{
return mFileDatabase->getSharedDirStatistics(pid,stats) ;
}
/***************************************************************/
/*************** Local Shared Dir Interface ********************/
/***************************************************************/
bool ftServer::ConvertSharedFilePath(std::string path, std::string &fullpath)
{
return mFileDatabase->convertSharedFilePath(path, fullpath);
}
void ftServer::updateSinceGroupPermissionsChanged()
{
mFileDatabase->forceSyncWithPeers();
}
void ftServer::ForceDirectoryCheck()
{
mFileDatabase->forceDirectoryCheck();
return;
}
bool ftServer::InDirectoryCheck()
{
return mFileDatabase->inDirectoryCheck();
}
bool ftServer::getSharedDirectories(std::list<SharedDirInfo> &dirs)
{
mFileDatabase->getSharedDirectories(dirs);
return true;
}
bool ftServer::setSharedDirectories(const std::list<SharedDirInfo>& dirs)
{
mFileDatabase->setSharedDirectories(dirs);
return true;
}
bool ftServer::addSharedDirectory(const SharedDirInfo& dir)
{
SharedDirInfo _dir = dir;
_dir.filename = RsDirUtil::convertPathToUnix(_dir.filename);
std::list<SharedDirInfo> dirList;
mFileDatabase->getSharedDirectories(dirList);
// check that the directory is not already in the list.
for(std::list<SharedDirInfo>::const_iterator it(dirList.begin());it!=dirList.end();++it)
if((*it).filename == _dir.filename)
return false ;
// ok then, add the shared directory.
dirList.push_back(_dir);
mFileDatabase->setSharedDirectories(dirList);
return true;
}
bool ftServer::updateShareFlags(const SharedDirInfo& info)
{
mFileDatabase->updateShareFlags(info);
return true ;
}
bool ftServer::removeSharedDirectory(std::string dir)
{
dir = RsDirUtil::convertPathToUnix(dir);
std::list<SharedDirInfo> dirList;
std::list<SharedDirInfo>::iterator it;
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::removeSharedDirectory(" << dir << ")" << std::endl;
#endif
mFileDatabase->getSharedDirectories(dirList);
#ifdef SERVER_DEBUG
for(it = dirList.begin(); it != dirList.end(); ++it)
FTSERVER_DEBUG() << " existing: " << (*it).filename << std::endl;
#endif
for(it = dirList.begin();it!=dirList.end() && (*it).filename != dir;++it) ;
if(it == dirList.end())
{
FTSERVER_ERROR() << "(EE) ftServer::removeSharedDirectory(): Cannot Find Directory... Fail" << std::endl;
return false;
}
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << " Updating Directories" << std::endl;
#endif
dirList.erase(it);
mFileDatabase->setSharedDirectories(dirList);
return true;
}
bool ftServer::getIgnoreLists(std::list<std::string>& ignored_prefixes, std::list<std::string>& ignored_suffixes,uint32_t& ignore_flags)
{
return mFileDatabase->getIgnoreLists(ignored_prefixes,ignored_suffixes,ignore_flags) ;
}
void ftServer::setIgnoreLists(const std::list<std::string>& ignored_prefixes, const std::list<std::string>& ignored_suffixes, uint32_t ignore_flags)
{
mFileDatabase->setIgnoreLists(ignored_prefixes,ignored_suffixes,ignore_flags) ;
}
bool ftServer::watchEnabled() { return mFileDatabase->watchEnabled() ; }
int ftServer::watchPeriod() const { return mFileDatabase->watchPeriod()/60 ; }
bool ftServer::followSymLinks() const { return mFileDatabase->followSymLinks() ; }
bool ftServer::ignoreDuplicates() { return mFileDatabase->ignoreDuplicates() ; }
int ftServer::maxShareDepth() const { return mFileDatabase->maxShareDepth() ; }
void ftServer::setWatchEnabled(bool b) { mFileDatabase->setWatchEnabled(b) ; }
void ftServer::setWatchPeriod(int minutes) { mFileDatabase->setWatchPeriod(minutes*60) ; }
void ftServer::setFollowSymLinks(bool b) { mFileDatabase->setFollowSymLinks(b) ; }
void ftServer::setIgnoreDuplicates(bool ignore) { mFileDatabase->setIgnoreDuplicates(ignore); }
void ftServer::setMaxShareDepth(int depth) { mFileDatabase->setMaxShareDepth(depth) ; }
void ftServer::togglePauseHashingProcess() { mFileDatabase->togglePauseHashingProcess() ; }
bool ftServer::hashingProcessPaused() { return mFileDatabase->hashingProcessPaused() ; }
bool ftServer::getShareDownloadDirectory()
{
std::list<SharedDirInfo> dirList;
mFileDatabase->getSharedDirectories(dirList);
std::string dir = mFtController->getDownloadDirectory();
// check if the download directory is in the list.
for (std::list<SharedDirInfo>::const_iterator it(dirList.begin()); it != dirList.end(); ++it)
if ((*it).filename == dir)
return true;
return false;
}
bool ftServer::shareDownloadDirectory(bool share)
{
if (share)
{
/* Share */
SharedDirInfo inf ;
inf.filename = mFtController->getDownloadDirectory();
inf.shareflags = DIR_FLAGS_ANONYMOUS_DOWNLOAD ;
return addSharedDirectory(inf);
}
else
{
/* Unshare */
std::string dir = mFtController->getDownloadDirectory();
return removeSharedDirectory(dir);
}
}
/***************************************************************/
/****************** End of RsFiles Interface *******************/
/***************************************************************/
//bool ftServer::loadConfigMap(std::map<std::string, std::string> &/*configMap*/)
//{
// return true;
//}
/***************************************************************/
/********************** Data Flow **********************/
/***************************************************************/
bool ftServer::sendTurtleItem(const RsPeerId& peerId,const RsFileHash& hash,RsTurtleGenericTunnelItem *item)
{
// we cannot look in the encrypted hash map, since the same hash--on this side of the FT--can be used with both
// encrypted and unencrypted peers ids. So the information comes from the virtual peer Id.
RsFileHash encrypted_hash;
if(findEncryptedHash(peerId,encrypted_hash))
{
// we encrypt the item
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "Sending turtle item to peer ID " << peerId << " using encrypted tunnel." << std::endl;
#endif
RsTurtleGenericDataItem *encrypted_item ;
if(!encryptItem(item, hash, encrypted_item))
return false ;
delete item ;
mTurtleRouter->sendTurtleData(peerId,encrypted_item) ;
}
else
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "Sending turtle item to peer ID " << peerId << " using non uncrypted tunnel." << std::endl;
#endif
mTurtleRouter->sendTurtleData(peerId,item) ;
}
return true ;
}
/* Client Send */
bool ftServer::sendDataRequest(const RsPeerId& peerId, const RsFileHash& hash, uint64_t size, uint64_t offset, uint32_t chunksize)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::sendDataRequest() to peer " << peerId << " for hash " << hash << ", offset=" << offset << ", chunk size="<< chunksize << std::endl;
#endif
if(mTurtleRouter->isTurtlePeer(peerId))
{
RsTurtleFileRequestItem *item = new RsTurtleFileRequestItem ;
item->chunk_offset = offset ;
item->chunk_size = chunksize ;
sendTurtleItem(peerId,hash,item) ;
}
else
{
/* create a packet */
/* push to networking part */
RsFileTransferDataRequestItem *rfi = new RsFileTransferDataRequestItem();
/* id */
rfi->PeerId(peerId);
/* file info */
rfi->file.filesize = size;
rfi->file.hash = hash; /* ftr->hash; */
/* offsets */
rfi->fileoffset = offset; /* ftr->offset; */
rfi->chunksize = chunksize; /* ftr->chunk; */
sendItem(rfi);
}
return true;
}
bool ftServer::sendChunkMapRequest(const RsPeerId& peerId,const RsFileHash& hash,bool is_client)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::sendChunkMapRequest() to peer " << peerId << " for hash " << hash << std::endl;
#endif
if(mTurtleRouter->isTurtlePeer(peerId))
{
RsTurtleFileMapRequestItem *item = new RsTurtleFileMapRequestItem ;
sendTurtleItem(peerId,hash,item) ;
}
else
{
/* create a packet */
/* push to networking part */
RsFileTransferChunkMapRequestItem *rfi = new RsFileTransferChunkMapRequestItem();
/* id */
rfi->PeerId(peerId);
/* file info */
rfi->hash = hash; /* ftr->hash; */
rfi->is_client = is_client ;
sendItem(rfi);
}
return true ;
}
bool ftServer::sendChunkMap(const RsPeerId& peerId,const RsFileHash& hash,const CompressedChunkMap& map,bool is_client)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::sendChunkMap() to peer " << peerId << " for hash " << hash << std::endl;
#endif
if(mTurtleRouter->isTurtlePeer(peerId))
{
RsTurtleFileMapItem *item = new RsTurtleFileMapItem ;
item->compressed_map = map ;
sendTurtleItem(peerId,hash,item) ;
}
else
{
/* create a packet */
/* push to networking part */
RsFileTransferChunkMapItem *rfi = new RsFileTransferChunkMapItem();
/* id */
rfi->PeerId(peerId);
/* file info */
rfi->hash = hash; /* ftr->hash; */
rfi->is_client = is_client; /* ftr->hash; */
rfi->compressed_map = map; /* ftr->hash; */
sendItem(rfi);
}
return true ;
}
bool ftServer::sendSingleChunkCRCRequest(const RsPeerId& peerId,const RsFileHash& hash,uint32_t chunk_number)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::sendSingleCRCRequest() to peer " << peerId << " for hash " << hash << ", chunk number=" << chunk_number << std::endl;
#endif
if(mTurtleRouter->isTurtlePeer(peerId))
{
RsTurtleChunkCrcRequestItem *item = new RsTurtleChunkCrcRequestItem;
item->chunk_number = chunk_number ;
sendTurtleItem(peerId,hash,item) ;
}
else
{
/* create a packet */
/* push to networking part */
RsFileTransferSingleChunkCrcRequestItem *rfi = new RsFileTransferSingleChunkCrcRequestItem();
/* id */
rfi->PeerId(peerId);
/* file info */
rfi->hash = hash; /* ftr->hash; */
rfi->chunk_number = chunk_number ;
sendItem(rfi);
}
return true ;
}
bool ftServer::sendSingleChunkCRC(const RsPeerId& peerId,const RsFileHash& hash,uint32_t chunk_number,const Sha1CheckSum& crc)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::sendSingleCRC() to peer " << peerId << " for hash " << hash << ", chunk number=" << chunk_number << std::endl;
#endif
if(mTurtleRouter->isTurtlePeer(peerId))
{
RsTurtleChunkCrcItem *item = new RsTurtleChunkCrcItem;
item->chunk_number = chunk_number ;
item->check_sum = crc ;
sendTurtleItem(peerId,hash,item) ;
}
else
{
/* create a packet */
/* push to networking part */
RsFileTransferSingleChunkCrcItem *rfi = new RsFileTransferSingleChunkCrcItem();
/* id */
rfi->PeerId(peerId);
/* file info */
rfi->hash = hash; /* ftr->hash; */
rfi->check_sum = crc;
rfi->chunk_number = chunk_number;
sendItem(rfi);
}
return true ;
}
/* Server Send */
bool ftServer::sendData(const RsPeerId& peerId, const RsFileHash& hash, uint64_t size, uint64_t baseoffset, uint32_t chunksize, void *data)
{
/* create a packet */
/* push to networking part */
uint32_t tosend = chunksize;
uint64_t offset = 0;
uint32_t chunk;
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::sendData() to " << peerId << ", hash: " << hash << " offset: " << baseoffset << " chunk: " << chunksize << " data: " << data << std::endl;
#endif
while(tosend > 0)
{
//static const uint32_t MAX_FT_CHUNK = 32 * 1024; /* 32K */
//static const uint32_t MAX_FT_CHUNK = 16 * 1024; /* 16K */
//
static const uint32_t MAX_FT_CHUNK = 8 * 1024; /* 16K */
/* workout size */
chunk = MAX_FT_CHUNK;
if (chunk > tosend)
{
chunk = tosend;
}
/******** New Serialiser Type *******/
if(mTurtleRouter->isTurtlePeer(peerId))
{
RsTurtleFileDataItem *item = new RsTurtleFileDataItem ;
item->chunk_offset = offset+baseoffset ;
item->chunk_size = chunk;
item->chunk_data = rs_malloc(chunk) ;
if(item->chunk_data == NULL)
{
delete item;
return false;
}
memcpy(item->chunk_data,&(((uint8_t *) data)[offset]),chunk) ;
sendTurtleItem(peerId,hash,item) ;
}
else
{
RsFileTransferDataItem *rfd = new RsFileTransferDataItem();
/* set id */
rfd->PeerId(peerId);
/* file info */
rfd->fd.file.filesize = size;
rfd->fd.file.hash = hash;
rfd->fd.file.name = ""; /* blank other data */
rfd->fd.file.path = "";
rfd->fd.file.pop = 0;
rfd->fd.file.age = 0;
rfd->fd.file_offset = baseoffset + offset;
/* file data */
rfd->fd.binData.setBinData( &(((uint8_t *) data)[offset]), chunk);
sendItem(rfd);
/* print the data pointer */
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::sendData() Packet: " << " offset: " << rfd->fd.file_offset << " chunk: " << chunk << " len: " << rfd->fd.binData.bin_len << " data: " << rfd->fd.binData.bin_data << std::endl;
#endif
}
offset += chunk;
tosend -= chunk;
}
/* clean up data */
free(data);
return true;
}
// Encrypts the given item using aead-chacha20-poly1305
//
// The format is the following
//
// [encryption format] [random initialization vector] [encrypted data size] [encrypted data] [authentication tag]
// 4 bytes 12 bytes 4 bytes variable 16 bytes
//
// +-------------------- authenticated data part ----------------------+
//
//
// Encryption format:
// ae ad 01 01 : encryption using AEAD, format 01 (authed with Poly1305 ), version 01
// ae ad 02 01 : encryption using AEAD, format 02 (authed with HMAC Sha256), version 01
//
//
void ftServer::deriveEncryptionKey(const RsFileHash& hash, uint8_t *key)
{
// The encryption key is simply the 256 hash of the
SHA256_CTX sha_ctx ;
if(SHA256_DIGEST_LENGTH != 32)
throw std::runtime_error("Warning: can't compute Sha1Sum with sum size != 32") ;
SHA256_Init(&sha_ctx);
SHA256_Update(&sha_ctx, hash.toByteArray(), hash.SIZE_IN_BYTES);
SHA256_Final (key, &sha_ctx);
}
static const uint32_t ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE = 12 ;
static const uint32_t ENCRYPTED_FT_AUTHENTICATION_TAG_SIZE = 16 ;
static const uint32_t ENCRYPTED_FT_HEADER_SIZE = 4 ;
static const uint32_t ENCRYPTED_FT_EDATA_SIZE = 4 ;
static const uint8_t ENCRYPTED_FT_FORMAT_AEAD_CHACHA20_POLY1305 = 0x01 ;
static const uint8_t ENCRYPTED_FT_FORMAT_AEAD_CHACHA20_SHA256 = 0x02 ;
bool ftServer::encryptItem(RsTurtleGenericTunnelItem *clear_item,const RsFileHash& hash,RsTurtleGenericDataItem *& encrypted_item)
{
uint8_t initialization_vector[ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE] ;
RSRandom::random_bytes(initialization_vector,ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE) ;
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::Encrypting ft item." << std::endl;
FTSERVER_DEBUG() << " random nonce : " << RsUtil::BinToHex(initialization_vector,ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE) << std::endl;
#endif
uint32_t item_serialized_size = size(clear_item) ;
uint32_t total_data_size = ENCRYPTED_FT_HEADER_SIZE + ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE + ENCRYPTED_FT_EDATA_SIZE + item_serialized_size + ENCRYPTED_FT_AUTHENTICATION_TAG_SIZE ;
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << " clear part size : " << size(clear_item) << std::endl;
FTSERVER_DEBUG() << " total item size : " << total_data_size << std::endl;
#endif
encrypted_item = new RsTurtleGenericDataItem ;
encrypted_item->data_bytes = rs_malloc( total_data_size ) ;
encrypted_item->data_size = total_data_size ;
if(encrypted_item->data_bytes == NULL)
return false ;
uint8_t *edata = (uint8_t*)encrypted_item->data_bytes ;
uint32_t edata_size = item_serialized_size;
uint32_t offset = 0;
edata[0] = 0xae ;
edata[1] = 0xad ;
edata[2] = ENCRYPTED_FT_FORMAT_AEAD_CHACHA20_SHA256 ; // means AEAD_chacha20_sha256
edata[3] = 0x01 ;
offset += ENCRYPTED_FT_HEADER_SIZE;
uint32_t aad_offset = offset ;
uint32_t aad_size = ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE + ENCRYPTED_FT_EDATA_SIZE ;
memcpy(&edata[offset], initialization_vector, ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE) ;
offset += ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE ;
edata[offset+0] = (edata_size >> 0) & 0xff ;
edata[offset+1] = (edata_size >> 8) & 0xff ;
edata[offset+2] = (edata_size >> 16) & 0xff ;
edata[offset+3] = (edata_size >> 24) & 0xff ;
offset += ENCRYPTED_FT_EDATA_SIZE ;
uint32_t ser_size = (uint32_t)((int)total_data_size - (int)offset);
serialise(clear_item,&edata[offset], &ser_size);
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << " clear item : " << RsUtil::BinToHex(&edata[offset],std::min(50,(int)total_data_size-(int)offset)) << "(...)" << std::endl;
#endif
uint32_t clear_item_offset = offset ;
offset += edata_size ;
uint32_t authentication_tag_offset = offset ;
assert(ENCRYPTED_FT_AUTHENTICATION_TAG_SIZE + offset == total_data_size) ;
uint8_t encryption_key[32] ;
deriveEncryptionKey(hash,encryption_key) ;
if(edata[2] == ENCRYPTED_FT_FORMAT_AEAD_CHACHA20_POLY1305)
librs::crypto::AEAD_chacha20_poly1305(encryption_key,initialization_vector,&edata[clear_item_offset],edata_size, &edata[aad_offset],aad_size, &edata[authentication_tag_offset],true) ;
else if(edata[2] == ENCRYPTED_FT_FORMAT_AEAD_CHACHA20_SHA256)
librs::crypto::AEAD_chacha20_sha256 (encryption_key,initialization_vector,&edata[clear_item_offset],edata_size, &edata[aad_offset],aad_size, &edata[authentication_tag_offset],true) ;
else
return false ;
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << " encryption key : " << RsUtil::BinToHex(encryption_key,32) << std::endl;
FTSERVER_DEBUG() << " authen. tag : " << RsUtil::BinToHex(&edata[authentication_tag_offset],ENCRYPTED_FT_AUTHENTICATION_TAG_SIZE) << std::endl;
FTSERVER_DEBUG() << " final item : " << RsUtil::BinToHex(&edata[0],std::min(50u,total_data_size)) << "(...)" << std::endl;
#endif
return true ;
}
// Decrypts the given item using aead-chacha20-poly1305
bool ftServer::decryptItem(RsTurtleGenericDataItem *encrypted_item,const RsFileHash& hash,RsTurtleGenericTunnelItem *& decrypted_item)
{
uint8_t encryption_key[32] ;
deriveEncryptionKey(hash,encryption_key) ;
uint8_t *edata = (uint8_t*)encrypted_item->data_bytes ;
uint32_t offset = 0;
if(encrypted_item->data_size < ENCRYPTED_FT_HEADER_SIZE + ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE + ENCRYPTED_FT_EDATA_SIZE) return false ;
if(edata[0] != 0xae) return false ;
if(edata[1] != 0xad) return false ;
if(edata[2] != ENCRYPTED_FT_FORMAT_AEAD_CHACHA20_POLY1305 && edata[2] != ENCRYPTED_FT_FORMAT_AEAD_CHACHA20_SHA256) return false ;
if(edata[3] != 0x01) return false ;
offset += ENCRYPTED_FT_HEADER_SIZE ;
uint32_t aad_offset = offset ;
uint32_t aad_size = ENCRYPTED_FT_EDATA_SIZE + ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE ;
uint8_t *initialization_vector = &edata[offset] ;
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::decrypting ft item." << std::endl;
FTSERVER_DEBUG() << " item data : " << RsUtil::BinToHex(edata,std::min(50u,encrypted_item->data_size)) << "(...)" << std::endl;
FTSERVER_DEBUG() << " hash : " << hash << std::endl;
FTSERVER_DEBUG() << " encryption key : " << RsUtil::BinToHex(encryption_key,32) << std::endl;
FTSERVER_DEBUG() << " random nonce : " << RsUtil::BinToHex(initialization_vector,ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE) << std::endl;
#endif
offset += ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE ;
uint32_t edata_size = 0 ;
edata_size += ((uint32_t)edata[offset+0]) << 0 ;
edata_size += ((uint32_t)edata[offset+1]) << 8 ;
edata_size += ((uint32_t)edata[offset+2]) << 16 ;
edata_size += ((uint32_t)edata[offset+3]) << 24 ;
if(edata_size + ENCRYPTED_FT_EDATA_SIZE + ENCRYPTED_FT_AUTHENTICATION_TAG_SIZE + ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE + ENCRYPTED_FT_HEADER_SIZE != encrypted_item->data_size)
{
FTSERVER_ERROR() << " ERROR: encrypted data size is " << edata_size << ", should be " << encrypted_item->data_size - (ENCRYPTED_FT_EDATA_SIZE + ENCRYPTED_FT_AUTHENTICATION_TAG_SIZE + ENCRYPTED_FT_INITIALIZATION_VECTOR_SIZE + ENCRYPTED_FT_HEADER_SIZE ) << std::endl;
return false ;
}
offset += ENCRYPTED_FT_EDATA_SIZE ;
uint32_t clear_item_offset = offset ;
uint32_t authentication_tag_offset = offset + edata_size ;
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << " authen. tag : " << RsUtil::BinToHex(&edata[authentication_tag_offset],ENCRYPTED_FT_AUTHENTICATION_TAG_SIZE) << std::endl;
#endif
bool result ;
if(edata[2] == ENCRYPTED_FT_FORMAT_AEAD_CHACHA20_POLY1305)
result = librs::crypto::AEAD_chacha20_poly1305(encryption_key,initialization_vector,&edata[clear_item_offset],edata_size, &edata[aad_offset],aad_size, &edata[authentication_tag_offset],false) ;
else if(edata[2] == ENCRYPTED_FT_FORMAT_AEAD_CHACHA20_SHA256)
result = librs::crypto::AEAD_chacha20_sha256 (encryption_key,initialization_vector,&edata[clear_item_offset],edata_size, &edata[aad_offset],aad_size, &edata[authentication_tag_offset],false) ;
else
return false ;
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << " authen. result : " << result << std::endl;
FTSERVER_DEBUG() << " decrypted daya : " << RsUtil::BinToHex(&edata[clear_item_offset],std::min(50u,edata_size)) << "(...)" << std::endl;
#endif
if(!result)
{
FTSERVER_ERROR() << "(EE) decryption/authentication went wrong." << std::endl;
return false ;
}
decrypted_item = dynamic_cast<RsTurtleGenericTunnelItem*>(deserialise(&edata[clear_item_offset],&edata_size)) ;
if(decrypted_item == NULL)
return false ;
return true ;
}
bool ftServer::encryptHash(const RsFileHash& hash, RsFileHash& hash_of_hash)
{
hash_of_hash = RsDirUtil::sha1sum(hash.toByteArray(),hash.SIZE_IN_BYTES);
return true ;
}
bool ftServer::findEncryptedHash(const RsPeerId& virtual_peer_id, RsFileHash& encrypted_hash)
{
RS_STACK_MUTEX(srvMutex);
std::map<RsPeerId,RsFileHash>::const_iterator it = mEncryptedPeerIds.find(virtual_peer_id) ;
if(it != mEncryptedPeerIds.end())
{
encrypted_hash = it->second ;
return true ;
}
else
return false ;
}
bool ftServer::findRealHash(const RsFileHash& hash, RsFileHash& real_hash)
{
RS_STACK_MUTEX(srvMutex);
std::map<RsFileHash,RsFileHash>::const_iterator it = mEncryptedHashes.find(hash) ;
if(it != mEncryptedHashes.end())
{
real_hash = it->second ;
return true ;
}
else
return false ;
}
// Dont delete the item. The client (p3turtle) is doing it after calling this.
//
void ftServer::receiveTurtleData(RsTurtleGenericTunnelItem *i,
const RsFileHash& hash,
const RsPeerId& virtual_peer_id,
RsTurtleGenericTunnelItem::Direction direction)
{
if(i->PacketSubType() == RS_TURTLE_SUBTYPE_GENERIC_DATA)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "Received encrypted data item. Trying to decrypt" << std::endl;
#endif
RsFileHash real_hash ;
if(!findRealHash(hash,real_hash))
{
FTSERVER_ERROR() << "(EE) Cannot find real hash for encrypted data item with H(H(F))=" << hash << ". This is unexpected." << std::endl;
return ;
}
RsTurtleGenericTunnelItem *decrypted_item ;
if(!decryptItem(dynamic_cast<RsTurtleGenericDataItem *>(i),real_hash,decrypted_item))
{
FTSERVER_ERROR() << "(EE) decryption error." << std::endl;
return ;
}
receiveTurtleData(decrypted_item, real_hash, virtual_peer_id,direction) ;
delete decrypted_item ;
return ;
}
switch(i->PacketSubType())
{
case RS_TURTLE_SUBTYPE_FILE_REQUEST:
{
RsTurtleFileRequestItem *item = dynamic_cast<RsTurtleFileRequestItem *>(i) ;
if (item)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::receiveTurtleData(): received file data request for " << hash << " from peer " << virtual_peer_id << std::endl;
#endif
getMultiplexer()->recvDataRequest(virtual_peer_id,hash,0,item->chunk_offset,item->chunk_size) ;
}
}
break ;
case RS_TURTLE_SUBTYPE_FILE_DATA :
{
RsTurtleFileDataItem *item = dynamic_cast<RsTurtleFileDataItem *>(i) ;
if (item)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::receiveTurtleData(): received file data for " << hash << " from peer " << virtual_peer_id << std::endl;
#endif
getMultiplexer()->recvData(virtual_peer_id,hash,0,item->chunk_offset,item->chunk_size,item->chunk_data) ;
item->chunk_data = NULL ; // this prevents deletion in the destructor of RsFileDataItem, because data will be deleted
// down _ft_server->getMultiplexer()->recvData()...in ftTransferModule::recvFileData
}
}
break ;
case RS_TURTLE_SUBTYPE_FILE_MAP :
{
RsTurtleFileMapItem *item = dynamic_cast<RsTurtleFileMapItem *>(i) ;
if (item)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::receiveTurtleData(): received chunk map for hash " << hash << " from peer " << virtual_peer_id << std::endl;
#endif
getMultiplexer()->recvChunkMap(virtual_peer_id,hash,item->compressed_map,direction == RsTurtleGenericTunnelItem::DIRECTION_CLIENT) ;
}
}
break ;
case RS_TURTLE_SUBTYPE_FILE_MAP_REQUEST:
{
//RsTurtleFileMapRequestItem *item = dynamic_cast<RsTurtleFileMapRequestItem *>(i) ;
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::receiveTurtleData(): received chunkmap request for hash " << hash << " from peer " << virtual_peer_id << std::endl;
#endif
getMultiplexer()->recvChunkMapRequest(virtual_peer_id,hash,direction == RsTurtleGenericTunnelItem::DIRECTION_CLIENT) ;
}
break ;
case RS_TURTLE_SUBTYPE_CHUNK_CRC :
{
RsTurtleChunkCrcItem *item = dynamic_cast<RsTurtleChunkCrcItem *>(i) ;
if (item)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::receiveTurtleData(): received single chunk CRC for hash " << hash << " from peer " << virtual_peer_id << std::endl;
#endif
getMultiplexer()->recvSingleChunkCRC(virtual_peer_id,hash,item->chunk_number,item->check_sum) ;
}
}
break ;
case RS_TURTLE_SUBTYPE_CHUNK_CRC_REQUEST:
{
RsTurtleChunkCrcRequestItem *item = dynamic_cast<RsTurtleChunkCrcRequestItem *>(i) ;
if (item)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::receiveTurtleData(): received single chunk CRC request for hash " << hash << " from peer " << virtual_peer_id << std::endl;
#endif
getMultiplexer()->recvSingleChunkCRCRequest(virtual_peer_id,hash,item->chunk_number) ;
}
}
break ;
default:
FTSERVER_ERROR() << "WARNING: Unknown packet type received: sub_id=" << reinterpret_cast<void*>(i->PacketSubType()) << ". Is somebody trying to poison you ?" << std::endl ;
}
}
/* NB: The rsCore lock must be activated before calling this.
* This Lock should be moved lower into the system...
* most likely destination is in ftServer.
*/
int ftServer::tick()
{
bool moreToTick = false ;
if(handleIncoming())
moreToTick = true;
static time_t last_law_priority_tasks_handling_time = 0 ;
time_t now = time(NULL) ;
if(last_law_priority_tasks_handling_time + FILE_TRANSFER_LOW_PRIORITY_TASKS_PERIOD < now)
{
last_law_priority_tasks_handling_time = now ;
mFtDataplex->deleteUnusedServers() ;
mFtDataplex->handlePendingCrcRequests() ;
mFtDataplex->dispatchReceivedChunkCheckSum() ;
}
return moreToTick;
}
bool ftServer::checkUploadLimit(const RsPeerId& pid,const RsFileHash& hash)
{
// No need for this extra cost if the value means "unlimited"
#ifdef SERVER_DEBUG
std::cerr << "Checking upload limit for friend " << pid << " and hash " << hash << ": " ;
#endif
uint32_t max_ups = mFtController->getMaxUploadsPerFriend() ;
RS_STACK_MUTEX(srvMutex) ;
if(max_ups == 0)
{
#ifdef SERVER_DEBUG
std::cerr << " no limit! returning true." << std::endl;
#endif
return true ;
}
#ifdef SERVER_DEBUG
std::cerr << " max=" << max_ups ;
#endif
// Find the latest records for this pid.
std::map<RsFileHash,time_t>& tmap(mUploadLimitMap[pid]) ;
std::map<RsFileHash,time_t>::iterator it ;
time_t now = time(NULL) ;
// If the limit has been decresed, we arbitrarily drop some ongoing slots.
while(tmap.size() > max_ups)
tmap.erase(tmap.begin()) ;
// Look in the upload record map. If it's not full, directly allocate a slot. If full, re-use an existing slot if a file is already cited.
if(tmap.size() < max_ups || (tmap.size()==max_ups && tmap.end() != (it = tmap.find(hash))))
{
#ifdef SERVER_DEBUG
std::cerr << " allocated slot for this hash => true" << std::endl;
#endif
tmap[hash] = now ;
return true ;
}
// There's no room in the used slots, but maybe some of them are not used anymore, in which case we remove them, which freeze a slot.
uint32_t cleaned = 0 ;
for(it = tmap.begin();it!=tmap.end() && cleaned<2;)
if(it->second + FILE_TRANSFER_MAX_DELAY_BEFORE_DROP_USAGE_RECORD < now)
{
std::map<RsFileHash,time_t>::iterator tmp(it) ;
++tmp;
tmap.erase(it) ;
it = tmp;
++cleaned ;
}
else
++it ;
if(cleaned > 0)
{
#ifdef SERVER_DEBUG
std::cerr << " cleaned up " << cleaned << " old hashes => true" << std::endl;
#endif
tmap[hash] = now ;
return true ;
}
#ifdef SERVER_DEBUG
std::cerr << " no slot for this hash => false" << std::endl;
#endif
return false ;
}
int ftServer::handleIncoming()
{
// now File Input.
int nhandled = 0 ;
RsItem *item = NULL ;
while(NULL != (item = recvItem()))
{
nhandled++ ;
switch(item->PacketSubType())
{
case RS_PKT_SUBTYPE_FT_DATA_REQUEST:
{
RsFileTransferDataRequestItem *f = dynamic_cast<RsFileTransferDataRequestItem*>(item) ;
if (f && checkUploadLimit(f->PeerId(),f->file.hash))
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::handleIncoming: received data request for hash " << f->file.hash << ", offset=" << f->fileoffset << ", chunk size=" << f->chunksize << std::endl;
#endif
mFtDataplex->recvDataRequest(f->PeerId(), f->file.hash, f->file.filesize, f->fileoffset, f->chunksize);
}
}
break ;
case RS_PKT_SUBTYPE_FT_DATA:
{
RsFileTransferDataItem *f = dynamic_cast<RsFileTransferDataItem*>(item) ;
if (f)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::handleIncoming: received data for hash " << f->fd.file.hash << ", offset=" << f->fd.file_offset << ", chunk size=" << f->fd.binData.bin_len << std::endl;
#endif
mFtDataplex->recvData(f->PeerId(), f->fd.file.hash, f->fd.file.filesize, f->fd.file_offset, f->fd.binData.bin_len, f->fd.binData.bin_data);
/* we've stolen the data part -> so blank before delete
*/
f->fd.binData.TlvShallowClear();
}
}
break ;
case RS_PKT_SUBTYPE_FT_CHUNK_MAP_REQUEST:
{
RsFileTransferChunkMapRequestItem *f = dynamic_cast<RsFileTransferChunkMapRequestItem*>(item) ;
if (f)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::handleIncoming: received chunkmap request for hash " << f->hash << ", client=" << f->is_client << std::endl;
#endif
mFtDataplex->recvChunkMapRequest(f->PeerId(), f->hash,f->is_client) ;
}
}
break ;
case RS_PKT_SUBTYPE_FT_CHUNK_MAP:
{
RsFileTransferChunkMapItem *f = dynamic_cast<RsFileTransferChunkMapItem*>(item) ;
if (f)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::handleIncoming: received chunkmap for hash " << f->hash << ", client=" << f->is_client << /*", map=" << f->compressed_map <<*/ std::endl;
#endif
mFtDataplex->recvChunkMap(f->PeerId(), f->hash,f->compressed_map,f->is_client) ;
}
}
break ;
case RS_PKT_SUBTYPE_FT_CHUNK_CRC_REQUEST:
{
RsFileTransferSingleChunkCrcRequestItem *f = dynamic_cast<RsFileTransferSingleChunkCrcRequestItem*>(item) ;
if (f)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::handleIncoming: received single chunk crc req for hash " << f->hash << ", chunk number=" << f->chunk_number << std::endl;
#endif
mFtDataplex->recvSingleChunkCRCRequest(f->PeerId(), f->hash,f->chunk_number) ;
}
}
break ;
case RS_PKT_SUBTYPE_FT_CHUNK_CRC:
{
RsFileTransferSingleChunkCrcItem *f = dynamic_cast<RsFileTransferSingleChunkCrcItem *>(item) ;
if (f)
{
#ifdef SERVER_DEBUG
FTSERVER_DEBUG() << "ftServer::handleIncoming: received single chunk crc req for hash " << f->hash << ", chunk number=" << f->chunk_number << ", checksum = " << f->check_sum << std::endl;
#endif
mFtDataplex->recvSingleChunkCRC(f->PeerId(), f->hash,f->chunk_number,f->check_sum);
}
}
break ;
}
delete item ;
}
return nhandled;
}
/**********************************
**********************************
**********************************
*********************************/
/***************************** CONFIG ****************************/
bool ftServer::addConfiguration(p3ConfigMgr *cfgmgr)
{
/* add all the subbits to config mgr */
cfgmgr->addConfiguration("ft_database.cfg", mFileDatabase);
cfgmgr->addConfiguration("ft_extra.cfg", mFtExtra);
cfgmgr->addConfiguration("ft_transfers.cfg", mFtController);
return true;
}