diff --git a/libretroshare/src/_dbase/cachestrapper.cc b/libretroshare/src/_dbase/cachestrapper.cc new file mode 100644 index 000000000..2dcd7fe83 --- /dev/null +++ b/libretroshare/src/_dbase/cachestrapper.cc @@ -0,0 +1,1125 @@ +/* + * RetroShare FileCache Module: cachestrapper.cc + * + * Copyright 2004-2007 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 "_dbase/cachestrapper.h" +#include "_serialiser/rsconfigitems.h" +#include "_pqi/p3connmgr.h" +#include "_util/rsdir.h" + +#include +#include +#include + +/**** + * #define CS_DEBUG 1 + ***/ + +bool operator<(const CacheId &a, const CacheId &b) +{ + if (a.type == b.type) + return (a.subid < b.subid); + return (a.type < b.type); +} + +bool operator<(const CachePair &a, const CachePair &b) +{ + return (a.id < b.id); +} + + +std::ostream &operator<<(std::ostream &out, const CacheData &d) +{ + out << "[ p: " << d.pid << " id: <" << d.cid.type << "," << d.cid.subid; + out << "> #" << d.hash << " size: " << d.size; + out << " \"" << d.name << "\"@\"" << d.path; + out << "\" ]"; + return out; +} + +/********************************* Cache Store / Source ************************** + * This is a generic interface which interacts with the FileTransfer Unit + * to collect Data to be Cached. + * + ********************************* Cache Store / Source *************************/ + +CacheSource::CacheSource(uint16_t t, bool m, CacheStrapper *cs, std::string cachedir) + :cacheType(t), multiCache(m), mStrapper(cs), cacheDir(cachedir) + { + return; + } + + /* Mutex Stuff -> to be done... */ +void CacheSource::lockData() const +{ +#ifdef CS_DEBUG + std::cerr << "CacheSource::lockData()" << std::endl; +#endif + cMutex.lock(); +} + +void CacheSource::unlockData() const +{ +#ifdef CS_DEBUG + std::cerr << "CacheSource::unlockData()" << std::endl; +#endif + cMutex.unlock(); +} + + /* to be overloaded for inherited Classes */ +bool CacheSource::loadLocalCache(const CacheData &data) +{ + return refreshCache(data); +} + + /* control Caches available */ +bool CacheSource::refreshCache(const CacheData &data) +{ + lockData(); /* LOCK MUTEX */ + + bool ret = false; + if (data.cid.type == getCacheType()) + { + if (isMultiCache()) + { + caches[data.cid.subid] = data; + } + else + { + caches[0] = data; + } + ret = true; + } + + unlockData(); /* UNLOCK MUTEX */ + + if (mStrapper) /* allow testing without full feedback */ + mStrapper->refreshCache(data); + return ret; +} + +bool CacheSource::clearCache(CacheId id) +{ + lockData(); /* LOCK MUTEX */ + + bool ret = false; + if (id.type == getCacheType()) + { + CacheSet::iterator it; + if (caches.end() != (it = caches.find(id.subid))) + { + caches.erase(it); + ret = true; + } + } + + unlockData(); /* UNLOCK MUTEX */ + return ret; +} + +bool CacheSource::cachesAvailable(RsPeerId pid, std::map &ids) +{ + lockData(); /* LOCK MUTEX */ + + /* can overwrite for more control! */ + CacheSet::iterator it; + for(it = caches.begin(); it != caches.end(); it++) + { + ids[(it->second).cid] = it->second; + } + bool ret = (caches.size() > 0); + + unlockData(); /* UNLOCK MUTEX */ + return ret; + +} + + +bool CacheSource::findCache(std::string hash, CacheData &data) const +{ + lockData(); /* LOCK MUTEX */ + + bool found = false; + CacheSet::const_iterator it; + for(it = caches.begin(); it != caches.end(); it++) + { + if (hash == (it->second).hash) + { + data = it->second; + found = true; + break; + } + } + + unlockData(); /* UNLOCK MUTEX */ + + return found; +} + + +void CacheSource::listCaches(std::ostream &out) +{ + lockData(); /* LOCK MUTEX */ + + /* can overwrite for more control! */ + CacheSet::iterator it; + out << "CacheSource::listCaches() [" << getCacheType(); + out << "] Total: " << caches.size() << std::endl; + int i; + for(i = 0, it = caches.begin(); it != caches.end(); it++, i++) + { + out << "\tC[" << i << "] : " << it->second << std::endl; + } + + unlockData(); /* UNLOCK MUTEX */ + return; +} + + +CacheStore::CacheStore(uint16_t t, bool m, + CacheStrapper *cs, CacheTransfer *cft, std::string cachedir) + :cacheType(t), multiCache(m), mStrapper(cs), + cacheTransfer(cft), cacheDir(cachedir) + { + /* not much */ + return; + } + + /* Mutex Stuff -> to be done... */ +void CacheStore::lockData() const +{ +#ifdef CS_DEBUG +// std::cerr << "CacheStore::lockData()" << std::endl; +#endif + cMutex.lock(); +} + +void CacheStore::unlockData() const +{ +#ifdef CS_DEBUG +// std::cerr << "CacheStore::unlockData()" << std::endl; +#endif + cMutex.unlock(); +} + +void CacheStore::listCaches(std::ostream &out) +{ + lockData(); /* LOCK MUTEX */ + + /* can overwrite for more control! */ + std::map::iterator pit; + out << "CacheStore::listCaches() [" << getCacheType(); + out << "] Total People: " << caches.size(); + out << std::endl; + for(pit = caches.begin(); pit != caches.end(); pit++) + { + CacheSet::iterator it; + out << "\tTotal for [" << pit->first << "] : " << (pit->second).size(); + out << std::endl; + for(it = (pit->second).begin(); it != (pit->second).end(); it++) + { + out << "\t\t" << it->second; + out << std::endl; + } + } + + unlockData(); /* UNLOCK MUTEX */ + return; +} + + + /* look for stored data. using pic/cid in CacheData + */ +bool CacheStore::getStoredCache(CacheData &data) +{ + lockData(); /* LOCK MUTEX */ + + bool ok = locked_getStoredCache(data); + + unlockData(); /* UNLOCK MUTEX */ + return ok; +} + + +bool CacheStore::locked_getStoredCache(CacheData &data) +{ + if (data.cid.type != getCacheType()) + { + return false; + } + + std::map::iterator pit; + if (caches.end() == (pit = caches.find(data.pid))) + { + return false; + } + + CacheSet::iterator cit; + if (isMultiCache()) + { + /* look for subid */ + if ((pit->second).end() == + (cit = (pit->second).find(data.cid.subid))) + { + return false; + } + } + else + { + if ((pit->second).end() == + (cit = (pit->second).find(0))) + { + return false; + } + } + + /* we found it! (cit) */ + data = cit->second; + + return true; +} + + + +bool CacheStore::getAllStoredCaches(std::list &data) +{ + lockData(); /* LOCK MUTEX */ + + std::map::iterator pit; + for(pit = caches.begin(); pit != caches.end(); pit++) + { + CacheSet::iterator cit; + /* look for subid */ + for(cit = (pit->second).begin(); + cit != (pit->second).end(); cit++) + { + data.push_back(cit->second); + } + } + + unlockData(); /* UNLOCK MUTEX */ + + return true; +} + + + /* input from CacheStrapper. + * check if we want to download it... + * determine the new name/path + * then request it. + */ +void CacheStore::availableCache(const CacheData &data) +{ +#ifdef CS_DEBUG + std::cerr << "CacheStore::availableCache() :" << data << std::endl; +#endif + + /* basic checks */ + lockData(); /* LOCK MUTEX */ + + bool rightCache = (data.cid.type == getCacheType()); + + unlockData(); /* UNLOCK MUTEX */ + + if (!rightCache) + { + return; /* bad id */ + } + + /* These Functions lock the Mutex themselves + */ + + if (!fetchCache(data)) + { + return; /* ignore it */ + } + + CacheData rData = data; + + /* get new name */ + if (!nameCache(rData)) + { + return; /* error naming */ + } + + /* request it */ + cacheTransfer -> RequestCache(rData, this); + + /* will get callback when it is complete */ + return; +} + + + /* called when the download is completed ... updates internal data */ +void CacheStore::downloadedCache(const CacheData &data) +{ +#ifdef CS_DEBUG + std::cerr << "CacheStore::downloadedCache() :" << data << std::endl; +#endif + + /* updates data */ + if (!loadCache(data)) + { + return; + } +} + /* called when the download is completed ... updates internal data */ +void CacheStore::failedCache(const CacheData &data) +{ +#ifdef CS_DEBUG + std::cerr << "CacheStore::failedCache() :" << data << std::endl; +#endif + return; +} + + + /* virtual function overloaded by cache implementor */ +bool CacheStore::fetchCache(const CacheData &data) +{ + /* should we fetch it? */ +#ifdef CS_DEBUG + std::cerr << "CacheStore::fetchCache() tofetch?:" << data << std::endl; +#endif + + CacheData incache = data; + + lockData(); /* LOCK MUTEX */ + + bool haveCache = ((locked_getStoredCache(incache)) && (data.hash == incache.hash)); + + unlockData(); /* UNLOCK MUTEX */ + + + if (haveCache) + { +#ifdef CS_DEBUG + std::cerr << "CacheStore::fetchCache() Already have it: false" << std::endl; +#endif + return false; + } + +#ifdef CS_DEBUG + std::cerr << "CacheStore::fetchCache() Missing this cache: true" << std::endl; +#endif + return true; +} + + +int CacheStore::nameCache(CacheData &data) +{ + /* name it... */ + lockData(); /* LOCK MUTEX */ + +#ifdef CS_DEBUG + std::cerr << "CacheStore::nameCache() for:" << data << std::endl; +#endif + + data.name = data.hash; + data.path = getCacheDir(); + +#ifdef CS_DEBUG + std::cerr << "CacheStore::nameCache() done:" << data << std::endl; +#endif + + unlockData(); /* UNLOCK MUTEX */ + + return 1; +} + + +int CacheStore::loadCache(const CacheData &data) +{ + /* attempt to load -> dummy function */ +#ifdef CS_DEBUG + std::cerr << "CacheStore::loadCache() Dummy Load for:" << data << std::endl; +#endif + + lockData(); /* LOCK MUTEX */ + + locked_storeCacheEntry(data); + + unlockData(); /* UNLOCK MUTEX */ + + return 1; +} + +/* This function is called to store Cache Entry in the CacheStore Table. + * it must be called from within a Mutex Lock.... + * + * It doesn't lock itself -> to avoid race conditions + */ + +void CacheStore::locked_storeCacheEntry(const CacheData &data) +{ + /* store what we loaded - overwriting if necessary */ + std::map::iterator pit; + if (caches.end() == (pit = caches.find(data.pid))) + { + /* add in a new CacheSet */ + CacheSet emptySet; + caches[data.pid] = emptySet; + + pit = caches.find(data.pid); + } + + if (isMultiCache()) + { + (pit->second)[data.cid.subid] = data; + } + else + { + (pit->second)[0] = data; + } + + /* tell the strapper we've loaded one */ + if (mStrapper) + { + mStrapper->refreshCacheStore(data); + } + return; +} + + +/********************************* CacheStrapper ********************************* + * This is the bit which handles queries + * + ********************************* CacheStrapper ********************************/ + +CacheStrapper::CacheStrapper(p3AuthMgr *am, p3ConnectMgr *cm) + :p3Config(CONFIG_TYPE_CACHE), mAuthMgr(am), mConnMgr(cm) +{ + return; +} + + +void CacheStrapper::addCachePair(CachePair set) +{ + caches[set.id.type] = set; +} + + + /**************** from pqimonclient ********************/ + +void CacheStrapper::statusChange(const std::list &plist) +{ + std::list::const_iterator it; + for(it = plist.begin(); it != plist.end(); it++) + { + if (it->actions & RS_PEER_CONNECTED) + { + /* grab all the cache ids and add */ + + std::map hashs; + std::map::iterator cit; + + handleCacheQuery(it->id, hashs); + + RsStackMutex stack(csMtx); /******* LOCK STACK MUTEX *********/ + for(cit = hashs.begin(); cit != hashs.end(); cit++) + { + mCacheUpdates.push_back(std::make_pair(it->id, cit->second)); + } + } + } +} + + /**************** from pqimonclient ********************/ + + +void CacheStrapper::refreshCache(const CacheData &data) +{ + /* we've received an update + * send to all online peers + self + */ +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::refreshCache() : " << data << std::endl; +#endif + + std::list ids; + std::list::iterator it; + + mConnMgr->getOnlineList(ids); + + RsStackMutex stack(csMtx); /******* LOCK STACK MUTEX *********/ + for(it = ids.begin(); it != ids.end(); it++) + { +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::refreshCache() Send To: " << *it << std::endl; +#endif + + mCacheUpdates.push_back(std::make_pair(*it, data)); + } + + mCacheUpdates.push_back(std::make_pair(mConnMgr->getOwnId(), data)); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ +} + + +void CacheStrapper::refreshCacheStore(const CacheData &data) +{ + + /* indicate to save data */ + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + +} + +bool CacheStrapper::getCacheUpdates(std::list > &updates) +{ + RsStackMutex stack(csMtx); /******* LOCK STACK MUTEX *********/ + updates = mCacheUpdates; + mCacheUpdates.clear(); + + return true; +} + + + + /* pass to correct CacheSet */ +void CacheStrapper::recvCacheResponse(CacheData &data, time_t ts) +{ + /* find cache store */ + std::map::iterator it2; + if (caches.end() == (it2 = caches.find(data.cid.type))) + { + /* error - don't have this type of cache */ + return; + } + + /* notify the CacheStore */ + (it2 -> second).store -> availableCache(data); + +} + + + /* generate periodically or at a change */ +#if 0 +bool CacheStrapper::sendCacheQuery(std::list &id, time_t ts) +{ + /* iterate through peers, and see who we haven't got an answer from recently */ + std::map::iterator it; + for(it = status.begin(); it != status.end(); it++) + { + if ((ts - (it->second).query) > queryPeriod) + { + /* query this one */ + id.push_back(it->first); + (it->second).query = ts; + } + } + return (id.size() > 0); +} +#endif + + +void CacheStrapper::handleCacheQuery(RsPeerId id, std::map &hashs) +{ + /* basic version just iterates through .... + * more complex could decide who gets what! + * + * or that can be handled on a cache by cache basis. + */ + + std::map::iterator it; + for(it = caches.begin(); it != caches.end(); it++) + { + (it->second).source -> cachesAvailable(id, hashs); + } + return; +} + +void CacheStrapper::listCaches(std::ostream &out) +{ + /* can overwrite for more control! */ + std::map::iterator it; + out << "CacheStrapper::listCaches() [" << mConnMgr->getOwnId(); + out << "] " << " Total Caches: " << caches.size(); + out << std::endl; + for(it = caches.begin(); it != caches.end(); it++) + { + out << "CacheType: " << it->first; + out << std::endl; + + (it->second).source->listCaches(out); + (it->second).store->listCaches(out); + out << std::endl; + } + return; +} + +void CacheStrapper::listPeerStatus(std::ostream &out) +{ +#if 0 + std::map::iterator it; + out << "CacheStrapper::listPeerStatus() [" << ownId; + out << "] Total Peers: " << status.size() << " Total Caches: " << caches.size(); + out << std::endl; + for(it = status.begin(); it != status.end(); it++) + { + out << "Peer: " << it->first; + out << " Query: " << (it->second).query; + out << " Answer: " << (it->second).answer; + out << std::endl; + } + return; +#endif +} + + +bool CacheStrapper::findCache(std::string hash, CacheData &data) const +{ + /* can overwrite for more control! */ + std::map::const_iterator it; + for(it = caches.begin(); it != caches.end(); it++) + { + if ((it->second).source->findCache(hash, data)) + { + return true; + } + } + return false; +} + + + +/***************************************************************************/ +/****************************** CONFIGURATION HANDLING *********************/ +/***************************************************************************/ + +/**** OVERLOADED FROM p3Config ****/ + +RsSerialiser *CacheStrapper::setupSerialiser() +{ + RsSerialiser *rss = new RsSerialiser(); + + /* add in the types we need! */ + rss->addSerialType(new RsCacheConfigSerialiser()); + + return rss; +} + + +std::list CacheStrapper::saveList(bool &cleanup) +{ + std::list saveData; + + /* it can delete them! */ + cleanup = true; + +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::saveList()" << std::endl; +#endif + + /* iterate through the Caches (local first) */ + + std::list::iterator cit; + std::list ownCaches; + std::list remoteCaches; + std::string ownId = mConnMgr->getOwnId(); + + std::map::iterator it; + for(it = caches.begin(); it != caches.end(); it++) + { + std::map::iterator tit; + std::map ownTmp; + (it->second).source -> cachesAvailable(ownId, ownTmp); + (it->second).store -> getAllStoredCaches(remoteCaches); + + for(tit = ownTmp.begin(); tit != ownTmp.end(); tit++) + { + ownCaches.push_back(tit->second); + } + } + + for(cit = ownCaches.begin(); cit != ownCaches.end(); cit++) + { + RsCacheConfig *rscc = new RsCacheConfig(); + + // Fixup lazy behaviour in clients... + // This ensures correct loading later. + // (used to be: rscc->pid = cit->pid;) + rscc->pid = ownId; + + //rscc->pname = cit->pname; + rscc->cachetypeid = cit->cid.type; + rscc->cachesubid = cit->cid.subid; + rscc->path = cit->path; + rscc->name = cit->name; + rscc->hash = cit->hash; + rscc->size = cit->size; + rscc->recvd = cit->recvd; + + saveData.push_back(rscc); + } + + for(cit = remoteCaches.begin(); cit != remoteCaches.end(); cit++) + { + if (cit->pid == ownId) + { +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() discarding Own Remote Cache"; + std::cerr << std::endl; +#endif + continue; /* skip own caches -> will get transferred anyway */ + } + + RsCacheConfig *rscc = new RsCacheConfig(); + + rscc->pid = cit->pid; + //rscc->pname = cit->pname; + rscc->cachetypeid = cit->cid.type; + rscc->cachesubid = cit->cid.subid; + rscc->path = cit->path; + rscc->name = cit->name; + rscc->hash = cit->hash; + rscc->size = cit->size; + rscc->recvd = cit->recvd; + + saveData.push_back(rscc); + } + + /* list completed! */ + return saveData; +} + + +bool CacheStrapper::loadList(std::list load) +{ + std::list::iterator it; + RsCacheConfig *rscc; + +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() Item Count: " << load.size(); + std::cerr << std::endl; +#endif + std::list ownCaches; + std::list remoteCaches; + std::string ownId = mConnMgr->getOwnId(); + + std::map > saveFiles; + std::map >::iterator sit; + + for(it = load.begin(); it != load.end(); it++) + { + /* switch on type */ + if (NULL != (rscc = dynamic_cast(*it))) + { +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() Item: "; + std::cerr << std::endl; + rscc->print(std::cerr, 10); + std::cerr << std::endl; +#endif + CacheData cd; + + cd.pid = rscc->pid; + cd.pname = mAuthMgr->getName(cd.pid); + cd.cid.type = rscc->cachetypeid; + cd.cid.subid = rscc->cachesubid; + cd.path = rscc->path; + cd.name = rscc->name; + cd.hash = rscc->hash; + cd.size = rscc->size; + cd.recvd = rscc->recvd; + + /* store files that we want to keep */ + (saveFiles[cd.path]).push_back(cd.name); + + std::map::iterator it2; + if (caches.end() == (it2 = caches.find(cd.cid.type))) + { + /* error - don't have this type of cache */ +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() Can't Find Cache discarding"; + std::cerr << std::endl; +#endif + } + else + { + if (cd.pid == ownId) + { + /* load local */ + (it2 -> second).source -> loadLocalCache(cd); +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() loaded Local"; + std::cerr << std::endl; +#endif + } + else + { + /* load remote */ + (it2 -> second).store -> loadCache(cd); +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() loaded Remote"; + std::cerr << std::endl; +#endif + } + } + + /* cleanup */ + delete (*it); + + } + else + { + /* cleanup */ + delete (*it); + } + } + + /* assemble a list of dirs to clean (union of cache dirs) */ + std::list cacheDirs; + std::list::iterator dit, fit; + std::map::iterator cit; + for(cit = caches.begin(); cit != caches.end(); cit++) + { + std::string lcdir = (cit->second).source->getCacheDir(); + std::string rcdir = (cit->second).store->getCacheDir(); + + if (cacheDirs.end() == std::find(cacheDirs.begin(), cacheDirs.end(), lcdir)) + { + cacheDirs.push_back(lcdir); + } + + if (cacheDirs.end() == std::find(cacheDirs.begin(), cacheDirs.end(), rcdir)) + { + cacheDirs.push_back(rcdir); + } + } + +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() Files To Save:" << std::endl; +#endif + + for(sit = saveFiles.begin(); sit != saveFiles.end(); sit++) + { +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() Files To Save in dir: <" << sit->first << ">" << std::endl; +#endif + for(fit = (sit->second).begin(); fit != (sit->second).end(); fit++) + { +#ifdef CS_DEBUG + std::cerr << "\tFile: " << *fit << std::endl; +#endif + } + } + + std::list emptyList; + for(dit = cacheDirs.begin(); dit != cacheDirs.end(); dit++) + { +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() Cleaning cache dir: <" << *dit << ">" << std::endl; +#endif + sit = saveFiles.find(*dit); + if (sit != saveFiles.end()) + { + for(fit = (sit->second).begin(); fit != (sit->second).end(); fit++) + { +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() Keeping File: " << *fit << std::endl; +#endif + } + RsDirUtil::cleanupDirectory(*dit, sit->second); + } + else + { +#ifdef CS_DEBUG + std::cerr << "CacheStrapper::loadList() No Files to save here!" << std::endl; +#endif + RsDirUtil::cleanupDirectory(*dit, emptyList); + } + } + + return true; + +} + + +/********************************* CacheStrapper ********************************* + * This is the bit which handles queries + * + ********************************* CacheStrapper ********************************/ + + +/* request from CacheStore */ +bool CacheTransfer::RequestCache(CacheData &data, CacheStore *cbStore) +{ + /* check for a previous request -> and cancel + * + * - if duplicate pid, cid -> cancel old transfer + * - if duplicate hash -> Fail Transfer + */ + + std::map::iterator dit; + std::map::iterator sit; + + for(dit = cbData.begin(); dit != cbData.end(); dit++) + { + if (((dit->second).pid == data.pid) && + ((dit->second).cid.type == data.cid.type) && + ((dit->second).cid.subid == data.cid.subid)) + { + /* cancel old transfer */ + CancelCacheFile(dit->second.pid, dit->second.path, + dit->second.hash, dit->second.size); + + sit = cbStores.find(dit->second.hash); + cbData.erase(dit); + cbStores.erase(sit); + + break; + } + } + + /* find in store.... */ + sit = cbStores.find(data.hash); + if (sit != cbStores.end()) + { + /* Duplicate Current Request */ + cbStore -> failedCache(data); + return false; + } + + + /* store request */ + cbData[data.hash] = data; + cbStores[data.hash] = cbStore; + + /* request data */ + RequestCacheFile(data.pid, data.path, data.hash, data.size); + + /* wait for answer */ + return true; +} + + +/* to be overloaded */ +bool CacheTransfer::RequestCacheFile(RsPeerId id, std::string path, std::string hash, uint64_t size) +{ +#ifdef CS_DEBUG + std::cerr << "CacheTransfer::RequestCacheFile() : from:" << id << " #"; + std::cerr << hash << " size: " << size; + std::cerr << " savepath: " << path << std::endl; + std::cerr << "CacheTransfer::RequestCacheFile() Dummy... saying completed"; + std::cerr << std::endl; +#endif + + /* just tell them we've completed! */ + CompletedCache(hash); + return true; +} + +/* to be overloaded */ +bool CacheTransfer::CancelCacheFile(RsPeerId id, std::string path, std::string hash, uint64_t size) +{ +#ifdef CS_DEBUG + std::cerr << "CacheTransfer::CancelCacheFile() : from:" << id << " #"; + std::cerr << hash << " size: " << size; + std::cerr << " savepath: " << path << std::endl; + std::cerr << "CacheTransfer::CancelCacheFile() Dummy fn"; + std::cerr << std::endl; +#endif + + return true; +} + + +/* internal completion -> does cb */ +bool CacheTransfer::CompletedCache(std::string hash) +{ + std::map::iterator dit; + std::map::iterator sit; + +#ifdef CS_DEBUG + std::cerr << "CacheTransfer::CompletedCache(" << hash << ")"; + std::cerr << std::endl; +#endif + + /* find in store.... */ + sit = cbStores.find(hash); + dit = cbData.find(hash); + + if ((sit == cbStores.end()) || (dit == cbData.end())) + { +#ifdef CS_DEBUG + std::cerr << "CacheTransfer::CompletedCache() Failed to find it"; + std::cerr << std::endl; +#endif + + return false; + } + +#ifdef CS_DEBUG + std::cerr << "CacheTransfer::CompletedCache() callback to store"; + std::cerr << std::endl; +#endif + /* callback */ + (sit -> second) -> downloadedCache(dit->second); + + /* clean up store */ + cbStores.erase(sit); + cbData.erase(dit); + + return true; +} + +/* internal completion -> does cb */ +bool CacheTransfer::FailedCache(std::string hash) +{ + std::map::iterator dit; + std::map::iterator sit; + + /* find in store.... */ + sit = cbStores.find(hash); + dit = cbData.find(hash); + + if ((sit == cbStores.end()) || (dit == cbData.end())) + { + return false; + } + + /* callback */ + (sit -> second) -> failedCache(dit->second); + + /* clean up store */ + cbStores.erase(sit); + cbData.erase(dit); + + return true; +} + + +bool CacheTransfer::FindCacheFile(std::string hash, std::string &path, uint64_t &size) +{ + CacheData data; + if (strapper->findCache(hash, data)) + { + path = data.path + "/" + data.name; + size = data.size; + return true; + } + + return false; +} + + + diff --git a/libretroshare/src/_dbase/cachestrapper.h b/libretroshare/src/_dbase/cachestrapper.h new file mode 100644 index 000000000..5413b68c1 --- /dev/null +++ b/libretroshare/src/_dbase/cachestrapper.h @@ -0,0 +1,329 @@ +/* + * RetroShare FileCache Module: cachestrapper.h + * + * Copyright 2004-2007 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". + * + */ + +#ifndef CACHESTRAPPER_H +#define CACHESTRAPPER_H + +#include "_pqi/p3cfgmgr.h" +#include "_pqi/pqimonitor.h" +#include "_util/rsthreads.h" + +#include +#include +#include +#include + +/******************* CacheStrapper and Related Classes ******************* + * A generic Cache Update system. + * + * CacheStrapper: maintains a set of CacheSources, and CacheStores, + * queries and updates as new information arrives. + * + * CacheTransfer: Interface for FileTransfer Class to support. + * + * CacheSource: Base Class for cache data provider. eg. FileIndexMonitor. + * CacheStore: Base Class for data cache. eg. FileCache/Store. + * + * Still TODO: + * (1) Design and Implement the Upload side of CacheTransfer/CacheStrapper. + * (2) CacheStrapper:: Save / Load Cache lists.... + * (3) Clean up lists, maps on shutdown etc. + * (4) Consider Mutexes for multithreaded operations. + * (5) Test the MultiSource/Store capabilities. + * + ******************* CacheStrapper and Related Classes *******************/ + + +class CacheTransfer; /* Interface for File Transfer */ +class CacheSource; /* Interface for local File Index/Monitor */ +class CacheStore; /* Interface for the actual Cache */ +class CacheStrapper; /* Controlling Class */ + +/**** +typedef uint32_t RsPeerId; +*****/ +typedef std::string RsPeerId; + +/******************************** CacheId ********************************/ +class CacheId +{ +public: + CacheId() :type(0), subid(0) { return; } + CacheId(uint16_t a, uint16_t b) :type(a), subid(b) { return; } + uint16_t type; + uint16_t subid; +}; + + +bool operator<(const CacheId &a, const CacheId &b); + +class CacheData +{ + public: + + RsPeerId pid; + std::string pname; /* peer name (can be used by cachestore) */ + CacheId cid; + std::string path; + std::string name; + std::string hash; + uint64_t size; + time_t recvd; +}; + +std::ostream &operator<<(std::ostream &out, const CacheData &d); + +/***************************** CacheTransfer *****************************/ + +class CacheTransfer +{ +public: + CacheTransfer(CacheStrapper *cs) :strapper(cs) { return; } + virtual ~CacheTransfer() {} + + /* upload side of things .... searches through CacheStrapper. */ + bool FindCacheFile(std::string hash, std::string &path, uint64_t &size); + + + /* At the download side RequestCache() => overloaded RequestCacheFile() + * the class should then call CompletedCache() or FailedCache() + */ + + bool RequestCache(CacheData &data, CacheStore *cbStore); /* request from CacheStore */ + +protected: + /* to be overloaded */ + virtual bool RequestCacheFile(RsPeerId id, std::string path, std::string hash, uint64_t size); + virtual bool CancelCacheFile(RsPeerId id, std::string path, std::string hash, uint64_t size); + + bool CompletedCache(std::string hash); /* internal completion -> does cb */ + bool FailedCache(std::string hash); /* internal completion -> does cb */ + +private: + + CacheStrapper *strapper; + + std::map cbData; + std::map cbStores; +}; + + + +/************************ CacheSource/CacheStore *************************/ + +typedef std::map CacheSet; + +class CacheSource +{ +public: + CacheSource(uint16_t t, bool m, CacheStrapper *cs, std::string cachedir); + virtual ~CacheSource() {} + + /* called to determine available cache for peer - + * default acceptable (returns all) + */ + virtual bool cachesAvailable(RsPeerId pid, std::map &ids); + + /* function called at startup to load from + * configuration file.... + * to be overloaded by inherited class + */ + virtual bool loadLocalCache(const CacheData &data); + + /* control Caches available */ + bool refreshCache(const CacheData &data); + bool clearCache(CacheId id); + + /* get private data */ + std::string getCacheDir() { return cacheDir; } + bool isMultiCache() { return multiCache; } + uint16_t getCacheType() { return cacheType; } + + /* display */ + void listCaches(std::ostream &out); + + /* search */ + bool findCache(std::string hash, CacheData &data) const; + +protected: + + uint16_t cacheType; /* for checking */ + bool multiCache; /* do we care about subid's */ + CacheStrapper *mStrapper; + + /*** MUTEX LOCKING */ + void lockData() const; + void unlockData() const; + + CacheSet caches; + + private: + + std::string cacheDir; + mutable RsMutex cMutex; +}; + + +class CacheStore +{ +public: + + CacheStore(uint16_t t, bool m, CacheStrapper *cs, CacheTransfer *cft, std::string cachedir); + virtual ~CacheStore() {} + + /* current stored data */ + bool getStoredCache(CacheData &data); /* use pid/cid in data */ + bool getAllStoredCaches(std::list &data); /* use pid/cid in data */ + + /* input from CacheStrapper -> store can then download new data */ + void availableCache(const CacheData &data); + + /* called when the download is completed ... updates internal data */ + void downloadedCache(const CacheData &data); + + /* called if the download fails */ + void failedCache(const CacheData &data); + + /* virtual functions overloaded by cache implementor */ + virtual bool fetchCache(const CacheData &data); /* a question? */ + virtual int nameCache(CacheData &data); /* fill in the name/path */ + virtual int loadCache(const CacheData &data); /* actual load, once data available */ + + /* get private data */ + std::string getCacheDir() { return cacheDir; } + bool isMultiCache() { return multiCache; } + uint16_t getCacheType() { return cacheType; } + + /* display */ + void listCaches(std::ostream &out); + +protected: + /*** MUTEX LOCKING */ + void lockData() const; + void unlockData() const; + + /* This function is called to store Cache Entry in the CacheStore Table. + * it must be called from within a Mutex Lock.... + * + * It doesn't lock itself -> to avoid race conditions + */ + void locked_storeCacheEntry(const CacheData &data); + bool locked_getStoredCache(CacheData &data); + +private: + + uint16_t cacheType; /* for checking */ + bool multiCache; /* do we care about subid's */ + + CacheStrapper *mStrapper; + CacheTransfer *cacheTransfer; + + std::string cacheDir; + + mutable RsMutex cMutex; + + std::map caches; + +}; + + + +/***************************** CacheStrapper *****************************/ + + +/* Make Sure you get the Ids right! */ +class CachePair +{ +public: + CachePair() + :source(NULL), store(NULL), id(0, 0) { return; } + + CachePair(CacheSource *a, CacheStore *b, CacheId c) + :source(a), store(b), id(c) { return; } + + CacheSource *source; + CacheStore *store; + CacheId id; +}; + + +bool operator<(const CachePair &a, const CachePair &b); + + +class p3AuthMgr; + +class CacheStrapper: public pqiMonitor, public p3Config +{ +public: + CacheStrapper(p3AuthMgr *am, p3ConnectMgr *cm); + virtual ~CacheStrapper() { return; } + + /************* from pqiMonitor *******************/ + virtual void statusChange(const std::list &plist); + /************* from pqiMonitor *******************/ + + /* Feedback from CacheSources */ + void refreshCache(const CacheData &data); + void refreshCacheStore(const CacheData &data); + + /* list of Caches to send out */ + bool getCacheUpdates(std::list > &updates); + + void addCachePair(CachePair pair); + + /*** I/O (2) ***/ + void recvCacheResponse(CacheData &data, time_t ts); + void handleCacheQuery(RsPeerId id, std::map &data); + + + /* search through CacheSources. */ + bool findCache(std::string hash, CacheData &data) const; + + /* display */ + void listCaches(std::ostream &out); + void listPeerStatus(std::ostream &out); + + + /* Config */ + protected: + + /* Key Functions to be overloaded for Full Configuration */ + virtual RsSerialiser *setupSerialiser(); + virtual std::list saveList(bool &cleanup); + virtual bool loadList(std::list load); + +private: + + /* these are static - so shouldn't need mutex */ + p3AuthMgr *mAuthMgr; + p3ConnectMgr *mConnMgr; + + std::map caches; + + RsMutex csMtx; /* protect below */ + + std::list > mCacheUpdates; +}; + + +#endif // CACHESTRAPPER_H diff --git a/libretroshare/src/_dbase/dbase.pri b/libretroshare/src/_dbase/dbase.pri new file mode 100644 index 000000000..644b27732 --- /dev/null +++ b/libretroshare/src/_dbase/dbase.pri @@ -0,0 +1,8 @@ +INCLUDEPATH += $$PWD \ + ../$$PWP +DEPENDPATH += $$PWD + +SOURCES = cachestrapper.cc + +HEADERS = cachestrapper.h + diff --git a/libretroshare/src/_pqi/authgpg.cc b/libretroshare/src/_pqi/authgpg.cc index 1b3865d54..f8cc488b4 100644 --- a/libretroshare/src/_pqi/authgpg.cc +++ b/libretroshare/src/_pqi/authgpg.cc @@ -54,23 +54,8 @@ */ #include "authgpg.h" -#include -#include -/* Turn a set of parameters into a string */ -static std::string setKeyPairParams(bool useRsa, unsigned int blen, - std::string name, std::string comment, std::string email, - std::string passphrase); -static gpgme_key_t getKey(gpgme_ctx_t, std::string, std::string, std::string); - -static gpg_error_t keySignCallback(void *, gpgme_status_code_t, \ - const char *, int); - -static gpg_error_t trustCallback(void *, gpgme_status_code_t, \ - const char *, int); - -static void ProcessPGPmeError(gpgme_error_t ERR); /* Function to sign X509_REQ via GPGme. */ diff --git a/libretroshare/src/_pqi/authgpg.h b/libretroshare/src/_pqi/authgpg.h index c7d24ece4..778ee61de 100644 --- a/libretroshare/src/_pqi/authgpg.h +++ b/libretroshare/src/_pqi/authgpg.h @@ -27,12 +27,32 @@ * */ -#ifndef RS_GPG_AUTH_HEADER -#define RS_GPG_AUTH_HEADER +#ifndef AUTHGPG_H +#define AUTHGPG_H //#include "p3authmgr.h" #include "authssl.h" #include +#include +#include + + +/* Turn a set of parameters into a string */ +static std::string setKeyPairParams(bool useRsa, unsigned int blen, + std::string name, std::string comment, std::string email, + std::string passphrase); + +static gpgme_key_t getKey(gpgme_ctx_t, std::string, std::string, std::string); + +static gpg_error_t keySignCallback(void *, gpgme_status_code_t, + const char *, int); + +static gpg_error_t trustCallback(void *, gpgme_status_code_t, + const char *, int); +static void ProcessPGPmeError(gpgme_error_t ERR); + + + /* gpgcert is the identifier for a person. * It is a wrapper class for a GPGme OpenPGP certificate. @@ -382,4 +402,4 @@ public: } }; -#endif +#endif // AUTHGPG_H diff --git a/libretroshare/src/_pqi/authssl.cc b/libretroshare/src/_pqi/authssl.cc new file mode 100644 index 000000000..a7f7c5d39 --- /dev/null +++ b/libretroshare/src/_pqi/authssl.cc @@ -0,0 +1,2690 @@ +/* + * libretroshare/src/pqi: authssl.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + * + * This class is designed to provide authentication using ssl certificates + * only. It is intended to be wrapped by an gpgauthmgr to provide + * pgp + ssl web-of-trust authentication. + * + */ + +#include "authssl.h" +#include "cleanupxpgp.h" + +#include "pqinetwork.h" + +/******************** notify of new Cert **************************/ +#include "pqinotify.h" + +#include +#include +#include +#include + +#include +#include + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ + +static int verify_x509_callback(int preverify_ok, X509_STORE_CTX *ctx); + +/*********** + ** #define AUTHSSL_DEBUG 1 + **********/ +#define AUTHSSL_DEBUG 1 + +#ifdef PQI_USE_SSLONLY + +// the single instance of this, but only when SSL Only +static AuthSSL instance_sslroot; + +p3AuthMgr *getAuthMgr() +{ + return &instance_sslroot; +} + +#endif + + +sslcert::sslcert(X509 *x509, std::string pid) +{ + certificate = x509; + id = pid; + name = getX509CNString(x509->cert_info->subject); + org = getX509OrgString(x509->cert_info->subject); + location = getX509LocString(x509->cert_info->subject); + email = ""; + + issuer = getX509CNString(x509->cert_info->issuer); + + authed = false; +} + +X509_REQ *GenerateX509Req( + std::string pkey_file, std::string passwd, + std::string name, std::string email, std::string org, + std::string loc, std::string state, std::string country, + int nbits_in, std::string &errString) +{ + /* generate request */ + X509_REQ *req=X509_REQ_new(); + + // setup output. + BIO *bio_out = NULL; + bio_out = BIO_new(BIO_s_file()); + BIO_set_fp(bio_out,stdout,BIO_NOCLOSE); + + EVP_PKEY *pkey = NULL; + + // first generate a key.... + if ((pkey=EVP_PKEY_new()) == NULL) + { + fprintf(stderr,"GenerateX509Req: Couldn't Create Key\n"); + return 0; + } + + int nbits = 2048; + unsigned long e = 0x10001; + + if ((nbits_in >= 512) && (nbits_in <= 4096)) + { + nbits = nbits_in; + } + else + { + fprintf(stderr,"GenerateX509Req: strange num of nbits: %d\n", nbits_in); + fprintf(stderr,"GenerateX509Req: reverting to %d\n", nbits); + } + + + RSA *rsa = RSA_generate_key(nbits, e, NULL, NULL); + if ((rsa == NULL) || !EVP_PKEY_assign_RSA(pkey, rsa)) + { + if(rsa) RSA_free(rsa); + fprintf(stderr,"GenerateX509Req: Couldn't Generate RSA Key!\n"); + return 0; + } + + + // open the file. + FILE *out; + if (NULL == (out = fopen(pkey_file.c_str(), "w"))) + { + fprintf(stderr,"GenerateX509Req: Couldn't Create Key File!"); + fprintf(stderr," : %s\n", pkey_file.c_str()); + return 0; + } + + const EVP_CIPHER *cipher = EVP_des_ede3_cbc(); + + if (!PEM_write_PrivateKey(out,pkey,cipher, + NULL,0,NULL,(void *) passwd.c_str())) + { + fprintf(stderr,"GenerateX509Req() Couldn't Save Private Key"); + fprintf(stderr," : %s\n", pkey_file.c_str()); + return 0; + } + fclose(out); + + // We have now created a private key.... + fprintf(stderr,"GenerateX509Req() Saved Private Key"); + fprintf(stderr," : %s\n", pkey_file.c_str()); + + /********** Test Loading the private Key.... ************/ + FILE *tst_in = NULL; + EVP_PKEY *tst_pkey = NULL; + if (NULL == (tst_in = fopen(pkey_file.c_str(), "rb"))) + { + fprintf(stderr,"GenerateX509Req() Couldn't Open Private Key"); + fprintf(stderr," : %s\n", pkey_file.c_str()); + return 0; + } + + if (NULL == (tst_pkey = + PEM_read_PrivateKey(tst_in,NULL,NULL,(void *) passwd.c_str()))) + { + fprintf(stderr,"GenerateX509Req() Couldn't Read Private Key"); + fprintf(stderr," : %s\n", pkey_file.c_str()); + return 0; + } + fclose(tst_in); + EVP_PKEY_free(tst_pkey); + /********** Test Loading the private Key.... ************/ + + /* Fill in details: fields. + req->req_info; + req->req_info->enc; + req->req_info->version; + req->req_info->subject; + req->req_info->pubkey; + ****************************/ + + long version = 0x00; + unsigned long chtype = MBSTRING_ASC; + X509_NAME *x509_name = X509_NAME_new(); + + // fill in the request. + + /**** X509_REQ -> Version ********************************/ + if (!X509_REQ_set_version(req,version)) /* version 1 */ + { + fprintf(stderr,"GenerateX509Req(): Couldn't Set Version!\n"); + return 0; + } + /**** X509_REQ -> Version ********************************/ + /**** X509_REQ -> Key ********************************/ + + if (!X509_REQ_set_pubkey(req,pkey)) + { + fprintf(stderr,"GenerateX509Req() Couldn't Set PUBKEY Version!\n"); + return 0; + } + + /**** SUBJECT ********************************/ + // create the name. + + // fields to add. + // commonName CN + // emailAddress (none) + // organizationName O + // localityName L + // stateOrProvinceName ST + // countryName C + + if (0 < strlen(name.c_str())) + { + X509_NAME_add_entry_by_txt(x509_name, "CN", chtype, + (unsigned char *) name.c_str(), -1, -1, 0); + } + else + { + fprintf(stderr,"GenerateX509Req(): No Name -> Not creating X509 Cert Req\n"); + return 0; + } + + if (0 < strlen(email.c_str())) + { + //X509_NAME_add_entry_by_txt(x509_name, "Email", 0, + // (unsigned char *) ui -> gen_email -> value(), -1, -1, 0); + X509_NAME_add_entry_by_NID(x509_name, 48, 0, + (unsigned char *) email.c_str(), -1, -1, 0); + } + + if (0 < strlen(org.c_str())) + { + X509_NAME_add_entry_by_txt(x509_name, "O", chtype, + (unsigned char *) org.c_str(), -1, -1, 0); + } + + if (0 < strlen(loc.c_str())) + { + X509_NAME_add_entry_by_txt(x509_name, "L", chtype, + (unsigned char *) loc.c_str(), -1, -1, 0); + } + + if (0 < strlen(state.c_str())) + { + X509_NAME_add_entry_by_txt(x509_name, "ST", chtype, + (unsigned char *) state.c_str(), -1, -1, 0); + } + + if (0 < strlen(country.c_str())) + { + X509_NAME_add_entry_by_txt(x509_name, "C", chtype, + (unsigned char *) country.c_str(), -1, -1, 0); + } + + if (!X509_REQ_set_subject_name(req,x509_name)) + { + fprintf(stderr,"GenerateX509Req() Couldn't Set Name to Request!\n"); + X509_NAME_free(x509_name); + return 0; + } + + X509_NAME_free(x509_name); + /**** SUBJECT ********************************/ + + if (!X509_REQ_sign(req,pkey,EVP_sha1())) + { + fprintf(stderr,"GenerateX509Req() Failed to Sign REQ\n"); + return 0; + } + + return req; +} + +#define SERIAL_RAND_BITS 64 + +X509 *SignX509Certificate(X509_NAME *issuer, EVP_PKEY *privkey, X509_REQ *req, long days) +{ + const EVP_MD *digest = EVP_sha1(); + ASN1_INTEGER *serial = ASN1_INTEGER_new(); + EVP_PKEY *tmppkey; + X509 *x509 = X509_new(); + if (x509 == NULL) + return NULL; + + BIGNUM *btmp = BN_new(); + if (!BN_pseudo_rand(btmp, SERIAL_RAND_BITS, 0, 0)) + { + fprintf(stderr,"SignX509Certificate() Failed: "); + fprintf(stderr," pseudo_rand\n"); + + return NULL; + } + if (!BN_to_ASN1_INTEGER(btmp, serial)) + { + fprintf(stderr,"SignX509Certificate() Failed: "); + fprintf(stderr," int\n"); + + return NULL; + } + BN_free(btmp); + + if (!X509_set_serialNumber(x509, serial)) + { + fprintf(stderr,"SignX509Certificate() Failed: "); + fprintf(stderr," serialNumber\n"); + + return NULL; + } + ASN1_INTEGER_free(serial); + + if (!X509_set_issuer_name(x509, issuer)) + { + fprintf(stderr,"SignX509Certificate() Failed: "); + fprintf(stderr," issuer\n"); + + return NULL; + } + + if (!X509_gmtime_adj(x509->cert_info->validity->notBefore, 0)) + { + fprintf(stderr,"SignX509Certificate() Failed: "); + fprintf(stderr," notBefore\n"); + + return NULL; + } + + //x509->cert_info->validity->notAfter + //if (!X509_gmtime_adj(X509_get_notAfter(x509), (long)60*60*24*days)) + if (!X509_gmtime_adj(x509->cert_info->validity->notAfter, (long)60*60*24*days)) + { + fprintf(stderr,"SignX509Certificate() Failed: "); + fprintf(stderr," notAfter\n"); + + return NULL; + } + + if (!X509_set_subject_name(x509, X509_REQ_get_subject_name(req))) + { + fprintf(stderr,"SignX509Certificate() Failed: "); + fprintf(stderr," subject_name\n"); + + return NULL; + } + + + tmppkey = X509_REQ_get_pubkey(req); + if (!tmppkey || !X509_set_pubkey(x509,tmppkey)) + { + fprintf(stderr,"SignX509Certificate() Failed: "); + fprintf(stderr," pubkey\n"); + + return NULL; + } + + + /* Cleanup Algorithm part */ + + X509_ALGOR *algor1 = x509->cert_info->signature; + X509_ALGOR *algor2 = x509->sig_alg; + + X509_ALGOR *a; + + a = algor1; + ASN1_TYPE_free(a->parameter); + a->parameter=ASN1_TYPE_new(); + a->parameter->type=V_ASN1_NULL; + + ASN1_OBJECT_free(a->algorithm); + a->algorithm=OBJ_nid2obj(digest->pkey_type); + + a = algor2; + ASN1_TYPE_free(a->parameter); + a->parameter=ASN1_TYPE_new(); + a->parameter->type=V_ASN1_NULL; + + ASN1_OBJECT_free(a->algorithm); + a->algorithm=OBJ_nid2obj(digest->pkey_type); + + + if (!X509_sign(x509,privkey,digest)) + { + long e = ERR_get_error(); + + fprintf(stderr,"SignX509Certificate() Failed: "); + fprintf(stderr," signing Error: %ld\n", e); + + fprintf(stderr,"ERR: %s, %s, %s\n", + ERR_lib_error_string(e), + ERR_func_error_string(e), + ERR_reason_error_string(e)); + + int inl=i2d_X509(x509,NULL); + int outl=EVP_PKEY_size(privkey); + fprintf(stderr,"Size Check: inl: %d, outl: %d\n", inl, outl); + + return NULL; + } + + fprintf(stderr,"SignX509Certificate() Success\n"); + + return x509; +} + + + +AuthSSL::AuthSSL() + :init(0), sslctx(NULL), pkey(NULL), mToSaveCerts(false), mConfigSaveActive(true) +{ +} + +bool AuthSSL::active() +{ + return init; +} + +// args: server cert, server private key, trusted certificates. + +int AuthSSL::InitAuth(const char *cert_file, const char *priv_key_file, + const char *passwd) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::InitAuth()"; + std::cerr << std::endl; +#endif + +static int initLib = 0; + if (!initLib) + { + initLib = 1; + SSL_load_error_strings(); + SSL_library_init(); + } + + + if (init == 1) + { + return 1; + } + + if ((cert_file == NULL) || + (priv_key_file == NULL) || + (passwd == NULL)) + { + fprintf(stderr, "sslroot::initssl() missing parameters!\n"); + return 0; + } + + + // actions_to_seed_PRNG(); + RAND_seed(passwd, strlen(passwd)); + + std::cerr << "SSL Library Init!" << std::endl; + + // setup connection method + sslctx = SSL_CTX_new(TLSv1_method()); + + // setup cipher lists. + SSL_CTX_set_cipher_list(sslctx, "DEFAULT"); + + // certificates (Set Local Server Certificate). + FILE *ownfp = fopen(cert_file, "r"); + if (ownfp == NULL) + { + std::cerr << "Couldn't open Own Certificate!" << std::endl; + return -1; + } + + + + // get xPGP certificate. + X509 *x509 = PEM_read_X509(ownfp, NULL, NULL, NULL); + fclose(ownfp); + + if (x509 == NULL) + { + std::cerr << "AuthSSL::InitAuth() PEM_read_X509() Failed"; + std::cerr << std::endl; +#ifdef AUTHSSL_DEBUG +#endif + + return -1; + } + SSL_CTX_use_certificate(sslctx, x509); + + // get private key + FILE *pkfp = fopen(priv_key_file, "rb"); + if (pkfp == NULL) + { + std::cerr << "Couldn't Open PrivKey File!" << std::endl; + CloseAuth(); + return -1; + } + + pkey = PEM_read_PrivateKey(pkfp, NULL, NULL, (void *) passwd); + fclose(pkfp); + + if (pkey == NULL) + { + std::cerr << "AuthSSL::InitAuth() PEM_read_PrivateKey() Failed"; + std::cerr << std::endl; +#ifdef AUTHSSL_DEBUG +#endif + return -1; + } + SSL_CTX_use_PrivateKey(sslctx, pkey); + + if (1 != SSL_CTX_check_private_key(sslctx)) + { + std::cerr << "Issues With Private Key! - Doesn't match your Cert" << std::endl; + std::cerr << "Check your input key/certificate:" << std::endl; + std::cerr << priv_key_file << " & " << cert_file; + std::cerr << std::endl; + CloseAuth(); + return -1; + } + + if (!getX509id(x509, mOwnId)) + { + std::cerr << "AuthSSL::InitAuth() getX509id() Failed"; + std::cerr << std::endl; + + /* bad certificate */ + CloseAuth(); + return -1; + } + + /* Check that Certificate is Ok ( virtual function ) + * for gpg/pgp or CA verification + */ + + if (!validateOwnCertificate(x509, pkey)) + { + std::cerr << "AuthSSL::InitAuth() validateOwnCertificate() Failed"; + std::cerr << std::endl; + + /* bad certificate */ + CloseAuth(); + return -1; + } + + + // enable verification of certificates (PEER) + // and install verify callback. + SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_x509_callback); + + std::cerr << "SSL Verification Set" << std::endl; + + mOwnCert = new sslcert(x509, mOwnId); + + init = 1; + return 1; +} + +/* Dummy function to be overloaded by real implementation */ +bool AuthSSL::validateOwnCertificate(X509 *x509, EVP_PKEY *pkey) +{ + return true; + //return false; +} + +bool AuthSSL::CloseAuth() +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::CloseAuth()"; + std::cerr << std::endl; +#endif + SSL_CTX_free(sslctx); + + // clean up private key.... + // remove certificates etc -> opposite of initssl. + init = 0; + return 1; +} + +/* Context handling */ +SSL_CTX *AuthSSL::getCTX() +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getCTX()"; + std::cerr << std::endl; +#endif + return sslctx; +} + +int AuthSSL::setConfigDirectories(std::string configfile, std::string neighdir) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::setConfigDirectories() "; + std::cerr << " configfile: " << configfile; + std::cerr << " neighdir: " << neighdir; + std::cerr << std::endl; +#endif + sslMtx.lock(); /***** LOCK *****/ + + mCertConfigFile = configfile; + mNeighDir = neighdir; + + sslMtx.unlock(); /**** UNLOCK ****/ + return 1; +} + +/* no trust in SSL certs */ +bool AuthSSL::isTrustingMe(std::string id) +{ + return false; +} +void AuthSSL::addTrustingPeer(std::string id) +{ + return; +} + +std::string AuthSSL::OwnId() +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::OwnId()"; + std::cerr << std::endl; +#endif + sslMtx.lock(); /***** LOCK *****/ + + std::string id = mOwnId; + + sslMtx.unlock(); /**** UNLOCK ****/ + return id; +} + +bool AuthSSL::getAllList(std::list &ids) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getAllList()"; + std::cerr << std::endl; +#endif + sslMtx.lock(); /***** LOCK *****/ + + /* iterate through both lists */ + std::map::iterator it; + + for(it = mCerts.begin(); it != mCerts.end(); it++) + { + ids.push_back(it->first); + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return true; +} + +bool AuthSSL::getAuthenticatedList(std::list &ids) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getAuthenticatedList()"; + std::cerr << std::endl; +#endif + sslMtx.lock(); /***** LOCK *****/ + + /* iterate through both lists */ + std::map::iterator it; + + for(it = mCerts.begin(); it != mCerts.end(); it++) + { + if (it->second->authed) + { + ids.push_back(it->first); + } + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return true; +} + +bool AuthSSL::getUnknownList(std::list &ids) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getUnknownList()"; + std::cerr << std::endl; +#endif + sslMtx.lock(); /***** LOCK *****/ + + /* iterate through both lists */ + std::map::iterator it; + + for(it = mCerts.begin(); it != mCerts.end(); it++) + { + if (!it->second->authed) + { + ids.push_back(it->first); + } + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return true; +} + + /* silly question really - only valid certs get saved to map + * so if in map its okay + */ +bool AuthSSL::isValid(std::string id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::isValid() " << id; + std::cerr << std::endl; +#endif + sslMtx.lock(); /***** LOCK *****/ + bool valid = false; + + if (id == mOwnId) + { + valid = true; + } + else + { + valid = (mCerts.end() != mCerts.find(id)); + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return valid; +} + +bool AuthSSL::isAuthenticated(std::string id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::isAuthenticated() " << id; + std::cerr << std::endl; +#endif + sslMtx.lock(); /***** LOCK *****/ + + sslcert *cert = NULL; + bool auth = false; + + if (locked_FindCert(id, &cert)) + { + auth = cert->authed; + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return auth; +} + +std::string AuthSSL::getName(std::string id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getName() " << id; + std::cerr << std::endl; +#endif + std::string name; + + sslMtx.lock(); /***** LOCK *****/ + + sslcert *cert = NULL; + if (id == mOwnId) + { + name = mOwnCert->name; + } + else if (locked_FindCert(id, &cert)) + { + name = cert->name; + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return name; +} + +std::string AuthSSL::getIssuerName(std::string id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getIssuerName() " << id; + std::cerr << std::endl; +#endif + std::string issuer; + + sslMtx.lock(); /***** LOCK *****/ + + sslcert *cert = NULL; + if (id == mOwnId) + { + issuer = mOwnCert->issuer; + } + else if (locked_FindCert(id, &cert)) + { + issuer = cert->issuer; + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return issuer; +} + +bool AuthSSL::getDetails(std::string id, pqiAuthDetails &details) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getDetails() " << id; + std::cerr << std::endl; +#endif + sslMtx.lock(); /***** LOCK *****/ + + bool valid = false; + sslcert *cert = NULL; + if (id == mOwnId) + { + cert = mOwnCert; + valid = true; + } + else if (locked_FindCert(id, &cert)) + { + valid = true; + } + + if (valid) + { + /* fill details */ + details.id = cert->id; + details.name = cert->name; + details.email = cert->email; + details.location= cert->location; + details.org = cert->org; + details.issuer = cert->issuer; + + details.fpr = cert->fpr; + details.signers = cert->signers; + + //details.trustLvl= cert->trustLvl; + //details.ownsign = cert->ownsign; + //details.trusted = cert->trusted; + details.trusted = cert->authed; + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return valid; +} + + + /* Load/Save certificates */ + +bool AuthSSL::LoadCertificateFromString(std::string pem, std::string &id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::LoadCertificateFromString() " << id; + std::cerr << std::endl; +#endif + +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::LoadCertificateFromString() Cleaning up Certificate First!"; + std::cerr << std::endl; +#endif + + std::string cleancert = cleanUpCertificate(pem); + + X509 *x509 = loadX509FromPEM(cleancert); + if (!x509) + return false; + + return ProcessX509(x509, id); +} + +std::string AuthSSL::SaveCertificateToString(std::string id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::SaveCertificateToString() " << id; + std::cerr << std::endl; +#endif + + + sslMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + std::string certstr; + sslcert *cert = NULL; + bool valid = false; + + if (id == mOwnId) + { + cert = mOwnCert; + valid = true; + } + else if (locked_FindCert(id, &cert)) + { + valid = true; + } + + if (valid) + { + BIO *bp = BIO_new(BIO_s_mem()); + + PEM_write_bio_X509(bp, cert->certificate); + + /* translate the bp data to a string */ + char *data; + int len = BIO_get_mem_data(bp, &data); + for(int i = 0; i < len; i++) + { + certstr += data[i]; + } + + BIO_free(bp); + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return certstr; +} + + + +bool AuthSSL::LoadCertificateFromFile(std::string filename, std::string &id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::LoadCertificateFromFile() " << id; + std::cerr << std::endl; +#endif + + std::string nullhash; + + X509 *x509 = loadX509FromFile(filename.c_str(), nullhash); + if (!x509) + return false; + + return ProcessX509(x509, id); +} + +//============================================================================ + +//! Saves something to filename + +//! \returns true on success, false on failure +bool AuthSSL::SaveCertificateToFile(std::string id, std::string filename) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::SaveCertificateToFile() " << id; + std::cerr << std::endl; +#endif + + sslMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + sslcert *cert = NULL; + bool valid = false; + std::string hash; + + if (id == mOwnId) + { + cert = mOwnCert; + valid = true; + } + else if (locked_FindCert(id, &cert)) + { + valid = true; + } + if (valid) + { + valid = saveX509ToFile(cert->certificate, filename, hash); + } + + sslMtx.unlock(); /**** UNLOCK ****/ + return valid; +} + + /**** To/From DER format ***/ + +bool AuthSSL::LoadCertificateFromBinary(const uint8_t *ptr, uint32_t len, std::string &id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::LoadCertificateFromFile() " << id; + std::cerr << std::endl; +#endif + + X509 *x509 = loadX509FromDER(ptr, len); + if (!x509) + return false; + + return ProcessX509(x509, id); + +} + +bool AuthSSL::SaveCertificateToBinary(std::string id, uint8_t **ptr, uint32_t *len) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::SaveCertificateToBinary() " << id; + std::cerr << std::endl; +#endif + + sslMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + sslcert *cert = NULL; + bool valid = false; + std::string hash; + + if (id == mOwnId) + { + cert = mOwnCert; + valid = true; + } + else if (locked_FindCert(id, &cert)) + { + valid = true; + } + if (valid) + { + valid = saveX509ToDER(cert->certificate, ptr, len); + } + + sslMtx.unlock(); /**** UNLOCK ****/ + return valid; +} + + + /* Signatures */ + /* NO Signatures in SSL Certificates */ + +bool AuthSSL::SignCertificate(std::string id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::SignCertificate() NULL " << id; + std::cerr << std::endl; +#endif + bool valid = false; + return valid; +} + +bool AuthSSL::TrustCertificate(std::string id, bool totrust) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::TrustCertificate() NULL " << id; + std::cerr << std::endl; +#endif + bool valid = false; + return valid; +} + +bool AuthSSL::RevokeCertificate(std::string id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::RevokeCertificate() NULL " << id; + std::cerr << std::endl; +#endif + + sslMtx.lock(); /***** LOCK *****/ + sslMtx.unlock(); /**** UNLOCK ****/ + + return false; +} + + +bool AuthSSL::AuthCertificate(std::string id) +{ + +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::AuthCertificate() " << id; + std::cerr << std::endl; +#endif + + sslMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + sslcert *cert = NULL; + sslcert *own = mOwnCert; + bool valid = false; + + if (locked_FindCert(id, &cert)) + { + /* ensuring this function can do nothing in PGP mode */ +#ifdef PQI_USE_SSLONLY + cert->authed=true; + mToSaveCerts = true; +#endif + } + + sslMtx.unlock(); /**** UNLOCK ****/ + return valid; +} + + + /* Sign / Encrypt / Verify Data (TODO) */ + +bool AuthSSL::SignData(std::string input, std::string &sign) +{ + return SignData(input.c_str(), input.length(), sign); +} + +bool AuthSSL::SignData(const void *data, const uint32_t len, std::string &sign) +{ + + RsStackMutex stack(sslMtx); /***** STACK LOCK MUTEX *****/ + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + if (0 == EVP_SignInit(mdctx, EVP_sha1())) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_SignUpdate(mdctx, data, len)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + EVP_MD_CTX_destroy(mdctx); + + sign.clear(); + std::ostringstream out; + out << std::hex; + for(uint32_t i = 0; i < signlen; i++) + { + out << std::setw(2) << std::setfill('0'); + out << (uint32_t) (signature[i]); + } + + sign = out.str(); + + return true; +} + + +bool AuthSSL::SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen) +{ + return SignDataBin(input.c_str(), input.length(), sign, signlen); +} + +bool AuthSSL::SignDataBin(const void *data, const uint32_t len, + unsigned char *sign, unsigned int *signlen) +{ + + RsStackMutex stack(sslMtx); /***** STACK LOCK MUTEX *****/ + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + unsigned int req_signlen = EVP_PKEY_size(pkey); + if (req_signlen > *signlen) + { + /* not enough space */ + std::cerr << "SignDataBin() Not Enough Sign SpacegnInit Failure!" << std::endl; + return false; + } + + + + + if (0 == EVP_SignInit(mdctx, EVP_sha1())) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_SignUpdate(mdctx, data, len)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_SignFinal(mdctx, sign, signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + EVP_MD_CTX_destroy(mdctx); + return true; +} + + +bool AuthSSL::VerifySignBin(std::string pid, + const void *data, const uint32_t len, + unsigned char *sign, unsigned int signlen) +{ + RsStackMutex stack(sslMtx); /***** STACK LOCK MUTEX *****/ + + /* find the peer */ + + sslcert *peer; + if (pid == mOwnId) + { + peer = mOwnCert; + } + else if (!locked_FindCert(pid, &peer)) + { + std::cerr << "VerifySignBin() no peer" << std::endl; + return false; + } + + EVP_PKEY *peerkey = peer->certificate->cert_info->key->pkey; + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_VerifyInit(mdctx, EVP_sha1())) + { + std::cerr << "EVP_VerifyInit Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_VerifyUpdate(mdctx, data, len)) + { + std::cerr << "EVP_VerifyUpdate Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_VerifyFinal(mdctx, sign, signlen, peerkey)) + { + std::cerr << "EVP_VerifyFinal Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + EVP_MD_CTX_destroy(mdctx); + return true; +} + + + + + + + /**** NEW functions we've added ****/ + + + /**** AUX Functions ****/ +bool AuthSSL::locked_FindCert(std::string id, sslcert **cert) +{ + std::map::iterator it; + + if (mCerts.end() != (it = mCerts.find(id))) + { + *cert = it->second; + return true; + } + return false; +} + + +X509 *AuthSSL::loadX509FromFile(std::string fname, std::string hash) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::LoadX509FromFile()"; + std::cerr << std::endl; +#endif + + // if there is a hash - check that the file matches it before loading. + X509 *pc = NULL; + FILE *pcertfp = fopen(fname.c_str(), "rb"); + + // load certificates from file. + if (pcertfp == NULL) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "sslroot::loadcertificate() Bad File: " << fname; + std::cerr << " Cannot be Hashed!" << std::endl; +#endif + return NULL; + } + + /* We only check a signature's hash if + * we are loading from a configuration file. + * Therefore we saved the file and it should be identical. + * and a direct load + verify will work. + * + * If however it has been transported by email.... + * Then we might have to correct the data (strip out crap) + * from the configuration at the end. (X509 load should work!) + */ + + if (hash.length() > 1) + { + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + int maxsize = 20480; /* should be enough for about 50 signatures */ + int rbytes; + char inall[maxsize]; + if (0 == (rbytes = fread(inall, 1, maxsize, pcertfp))) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Error Reading Peer Record!" << std::endl; +#endif + return NULL; + } + //std::cerr << "Read " << rbytes << std::endl; + + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + } + + EVP_MD_CTX_destroy(mdctx); + + bool same = true; + if (signlen != hash.length()) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Different Length Signatures... "; + std::cerr << "Cannot Load Certificate!" << std::endl; +#endif + fclose(pcertfp); + return NULL; + } + + for(int i = 0; i < (signed) signlen; i++) + { + if (signature[i] != (unsigned char) hash[i]) + { + same = false; +#ifdef AUTHSSL_DEBUG + std::cerr << "Invalid Signature... "; + std::cerr << "Cannot Load Certificate!" << std::endl; +#endif + fclose(pcertfp); + return NULL; + } + } +#ifdef AUTHSSL_DEBUG + std::cerr << "Verified Signature for: " << fname; + std::cerr << std::endl; +#endif + } + else + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Not checking cert signature" << std::endl; +#endif + } + + fseek(pcertfp, 0, SEEK_SET); /* rewind */ + pc = PEM_read_X509(pcertfp, NULL, NULL, NULL); + fclose(pcertfp); + + if (pc != NULL) + { + // read a certificate. +#ifdef AUTHSSL_DEBUG + std::cerr << "Loaded Certificate: " << pc -> name << std::endl; +#endif + } + else // (pc == NULL) + { + unsigned long err = ERR_get_error(); + std::cerr << "Read Failed .... CODE(" << err << ")" << std::endl; + std::cerr << ERR_error_string(err, NULL) << std::endl; + + return NULL; + } + return pc; +} + +bool AuthSSL::saveX509ToFile(X509 *x509, std::string fname, std::string &hash) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::saveX509ToFile()"; + std::cerr << std::endl; +#endif + + // load certificates from file. + FILE *setfp = fopen(fname.c_str(), "wb"); + if (setfp == NULL) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "sslroot::savecertificate() Bad File: " << fname; + std::cerr << " Cannot be Written!" << std::endl; +#endif + return false; + } + +#ifdef AUTHSSL_DEBUG + std::cerr << "Writing out Cert...:" << x509->name << std::endl; +#endif + PEM_write_X509(setfp, x509); + + fclose(setfp); + + // then reopen to generate hash. + setfp = fopen(fname.c_str(), "rb"); + if (setfp == NULL) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "sslroot::savecertificate() Bad File: " << fname; + std::cerr << " Opened for ReHash!" << std::endl; +#endif + return false; + } + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + int maxsize = 20480; + int rbytes; + char inall[maxsize]; + if (0 == (rbytes = fread(inall, 1, maxsize, setfp))) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Error Writing Peer Record!" << std::endl; +#endif + return -1; + } +#ifdef AUTHSSL_DEBUG + std::cerr << "Read " << rbytes << std::endl; +#endif + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + } + +#ifdef AUTHSSL_DEBUG + std::cerr << "Saved Cert: " << x509->name; + std::cerr << std::endl; +#endif + +#ifdef AUTHSSL_DEBUG + std::cerr << "Cert + Setting Signature is(" << signlen << "): "; +#endif + std::string signstr; + for(uint32_t i = 0; i < signlen; i++) + { +#ifdef AUTHSSL_DEBUG + fprintf(stderr, "%02x", signature[i]); +#endif + signstr += signature[i]; + } +#ifdef AUTHSSL_DEBUG + std::cerr << std::endl; +#endif + + hash = signstr; + fclose(setfp); + + EVP_MD_CTX_destroy(mdctx); + + return true; +} + + +X509 *AuthSSL::loadX509FromPEM(std::string pem) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::LoadX509FromPEM()"; + std::cerr << std::endl; +#endif + + /* Put the data into a mem BIO */ + char *certstr = strdup(pem.c_str()); + + BIO *bp = BIO_new_mem_buf(certstr, -1); + + X509 *pc = PEM_read_bio_X509(bp, NULL, NULL, NULL); + + BIO_free(bp); + free(certstr); + + return pc; +} + +X509 *AuthSSL::loadX509FromDER(const uint8_t *ptr, uint32_t len) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::LoadX509FromDER()"; + std::cerr << std::endl; +#endif + + X509 *tmp = NULL; + const unsigned char **certptr = (const unsigned char **) &ptr; + X509 *x509 = d2i_X509(&tmp, certptr, len); + + return x509; +} + +bool AuthSSL::saveX509ToDER(X509 *x509, uint8_t **ptr, uint32_t *len) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::saveX509ToDER()"; + std::cerr << std::endl; +#endif + + int certlen = i2d_X509(x509, (unsigned char **) ptr); + if (certlen > 0) + { + *len = certlen; + return true; + } + else + { + *len = 0; + return false; + } + return false; +} + + + + +bool AuthSSL::ProcessX509(X509 *x509, std::string &id) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509()"; + std::cerr << std::endl; +#endif + + /* extract id */ + std::string xid; + + bool valid = ValidateCertificate(x509, xid); + + if (!valid) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509() ValidateCertificate FAILED"; + std::cerr << std::endl; +#endif + +#ifdef PQI_USE_SSLONLY + /* bad ( or unknown pgp issuer ) certificate */ + X509_free(x509); + return false; +#endif + } + + sslcert *cert = NULL; + bool duplicate = false; + + sslMtx.lock(); /***** LOCK *****/ + + if (xid == mOwnId) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509() Cert is own id (dup)"; + std::cerr << std::endl; +#endif + + cert = mOwnCert; + duplicate = true; + } + else if (locked_FindCert(xid, &cert)) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509() Found Duplicate"; + std::cerr << std::endl; +#endif + + duplicate = true; + } + + if (duplicate) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509() Processing as dup"; + std::cerr << std::endl; +#endif + + /* have a duplicate */ + /* check that they are exact */ + if (0 != X509_cmp(cert->certificate, x509)) + { + +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509() Not the same: MAJOR ERROR"; + std::cerr << std::endl; +#endif + + /* MAJOR ERROR */ + X509_free(x509); + sslMtx.unlock(); /**** UNLOCK ****/ + return false; + } + + X509_free(x509); + + /* we accepted it! */ + id = xid; + + if (!cert->authed) + { + cert->authed = valid; +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509() "; + std::cerr << "Updating Unauthed duplicate: "; + std::cerr << (valid ? "true" : "false"); + std::cerr << std::endl; +#endif + } + else + { +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509() "; + std::cerr << "Original already Valid"; + std::cerr << std::endl; +#endif + } + +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509() Accepted Dup"; + std::cerr << std::endl; +#endif + + + sslMtx.unlock(); /**** UNLOCK ****/ + return true; + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + /* if we get here -> its a new certificate */ + cert = new sslcert(x509, xid); + cert->authed = valid; + + sslMtx.lock(); /***** LOCK *****/ + + mCerts[xid] = cert; + + /* resave if new certificate */ + mToSaveCerts = true; + sslMtx.unlock(); /**** UNLOCK ****/ + +#if 0 + /******************** notify of new Cert **************************/ + pqiNotify *pqinotify = getPqiNotify(); + if (pqinotify) + { + pqinotify->AddFeedItem(RS_FEED_ITEM_PEER_NEW, xid, "",""); + } + /******************** notify of new Cert **************************/ +#endif + + id = xid; + +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ProcessX509() Accepted New Cert"; + std::cerr << std::endl; +#endif + return true; +} + + +bool getX509id(X509 *x509, std::string &xid) +{ +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getX509id()"; + std::cerr << std::endl; +#endif + + xid = ""; + if (x509 == NULL) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getX509id() NULL pointer"; + std::cerr << std::endl; +#endif + return false; + } + + // get the signature from the cert, and copy to the array. + ASN1_BIT_STRING *signature = x509->signature; + int signlen = ASN1_STRING_length(signature); + if (signlen < CERTSIGNLEN) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::getX509id() ERROR: Short Signature"; + std::cerr << std::endl; +#endif + return false; + } + + // else copy in the first CERTSIGNLEN. + unsigned char *signdata = ASN1_STRING_data(signature); + + std::ostringstream id; + /* switched to the other end of the signature. for + * more randomness + */ + for(uint32_t i = signlen - CERTSIGNLEN; i < signlen; i++) + { + id << std::hex << std::setw(2) << std::setfill('0') + << (uint16_t) (((uint8_t *) (signdata))[i]); + } + xid = id.str(); + return true; +} + + + + /* validate + get id */ +bool AuthSSL::ValidateCertificate(X509 *x509, std::string &peerId) +{ + /* check self signed */ +#warning "ValidateCertificate Not Finished" + +#if 0 + if (!X509_check_valid_certificate(x509)) + { + /* bad certificate */ + return false; + } +#endif + +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::ValidateCertificate() Not Finished!"; + std::cerr << std::endl; +#endif + + return getX509id(x509, peerId); +} + +/* store for discovery */ +bool AuthSSL::FailedCertificate(X509 *x509, bool incoming) +{ + std::string id; + return ProcessX509(x509, id); +} + +/* check that they are exact match */ +bool AuthSSL::CheckCertificate(std::string x509Id, X509 *x509) +{ + sslMtx.lock(); /***** LOCK *****/ + + sslcert *cert = NULL; + if (!locked_FindCert(x509Id, &cert)) + { + /* not there -> error */ + X509_free(x509); + + sslMtx.unlock(); /**** UNLOCK ****/ + return false; + } + else + { + /* have a duplicate */ + /* check that they are exact */ + if (0 != X509_cmp(cert->certificate, x509)) + { + /* MAJOR ERROR */ + X509_free(x509); + sslMtx.unlock(); /**** UNLOCK ****/ + return false; + } + + /* transfer new signatures */ + //X509_copy_known_signatures(pgp_keyring, cert->certificate, x509); + X509_free(x509); + + /* update signers */ + //cert->signers = getX509signers(cert->certificate); + + sslMtx.unlock(); /**** UNLOCK ****/ + return true; + } +} + + + + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ + +int pem_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + strncpy(buf, (char *)(password), size); + buf[size - 1] = '\0'; + return(strlen(buf)); +} + + +static int verify_x509_callback(int preverify_ok, X509_STORE_CTX *ctx) +{ + AuthSSL *authssl = (AuthSSL *) getAuthMgr(); + return authssl->VerifyX509Callback(preverify_ok, ctx); + +} + + +int AuthSSL::VerifyX509Callback(int preverify_ok, X509_STORE_CTX *ctx) +{ + char buf[256]; + X509 *err_cert; + int err, depth; + //SSL *ssl; + //mydata_t *mydata; + + err_cert = X509_STORE_CTX_get_current_cert(ctx); + err = X509_STORE_CTX_get_error(ctx); + depth = X509_STORE_CTX_get_error_depth(ctx); + +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::VerifyX509Callback(preverify_ok: " << preverify_ok + << " Err: " << err << " Depth: " << depth; + std::cerr << std::endl; +#endif + + /* + * Retrieve the pointer to the SSL of the connection currently treated + * and the application specific data stored into the SSL object. + */ + //ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + //mydata = SSL_get_ex_data(ssl, mydata_index); + + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); + + std::cerr << "AuthSSL::VerifyX509Callback: depth: " << depth << ":" << buf; + std::cerr << std::endl; + + +// X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT +// X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN +// +// We accept self signed certificates. + if (!preverify_ok && (depth == 0) && (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)) + { + std::cerr << "AuthSSL::VerifyX509Callback() Accepting SELF_SIGNED_CERT"; + std::cerr << std::endl; + preverify_ok = 1; + } + + if (!preverify_ok) { + fprintf(stderr, "Verify error:num=%d:%s:depth=%d:%s\n", err, + X509_verify_cert_error_string(err), depth, buf); + } + +#if 0 + else if (mydata->verbose_mode) + { + printf("depth=%d:%s\n", depth, buf); + } +#endif + + /* + * At this point, err contains the last verification error. We can use + * it for something special + */ + + if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) + { + X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256); + printf("issuer= %s\n", buf); + } + + +#if 0 + if (mydata->always_continue) + return 1; + else + return preverify_ok; +#endif + return preverify_ok; + +} + + + + + +// Not dependent on sslroot. load, and detroys the X509 memory. + +int LoadCheckX509andGetName(const char *cert_file, std::string &userName, std::string &userId) +{ + /* This function loads the X509 certificate from the file, + * and checks the certificate + */ + + FILE *tmpfp = fopen(cert_file, "r"); + if (tmpfp == NULL) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "sslroot::LoadCheckAndGetX509Name()"; + std::cerr << " Failed to open Certificate File:" << cert_file; + std::cerr << std::endl; +#endif + return 0; + } + + // get xPGP certificate. + X509 *x509 = PEM_read_X509(tmpfp, NULL, NULL, NULL); + fclose(tmpfp); + + // check the certificate. + bool valid = false; + if (x509) + { + valid = ((AuthSSL *) getAuthMgr())->ValidateCertificate(x509, userId); + } + + if (valid) + { + // extract the name. + userName = getX509CNString(x509->cert_info->subject); + } + + std::cout << getX509Info(x509) << std::endl ; + // clean up. + X509_free(x509); + + if (valid) + { + // happy! + return 1; + } + else + { + // something went wrong! + return 0; + } +} + +std::string getX509NameString(X509_NAME *name) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + namestr += "\t"; + namestr += OBJ_nid2ln(OBJ_obj2nid(entry_obj)); + namestr += " : "; + + //namestr += entry_obj -> flags; + //namestr += entry_data -> length; + //namestr += entry_data -> type; + + //namestr += entry_data -> flags; + //entry -> set; + + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "NULL"; + } + + if (i + 1 < X509_NAME_entry_count(name)) + { + namestr += "\n"; + } + + } + return namestr; +} + + +std::string getX509CNString(X509_NAME *name) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + if (0 == strncmp("CN", OBJ_nid2sn(OBJ_obj2nid(entry_obj)), 2)) + { + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "Unknown"; + } + return namestr; + } + } + return namestr; +} + + +std::string getX509TypeString(X509_NAME *name, const char *type, int len) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + if (0 == strncmp(type, OBJ_nid2sn(OBJ_obj2nid(entry_obj)), len)) + { + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "Unknown"; + } + return namestr; + } + } + return namestr; +} + + +std::string getX509LocString(X509_NAME *name) +{ + return getX509TypeString(name, "L", 2); +} + +std::string getX509OrgString(X509_NAME *name) +{ + return getX509TypeString(name, "O", 2); +} + + +std::string getX509CountryString(X509_NAME *name) +{ + return getX509TypeString(name, "C", 2); +} + + +std::string getX509Info(X509 *cert) +{ + std::stringstream out; + long l; + int i,j; + + out << "X509 Certificate:" << std::endl; + l=X509_get_version(cert); + out << " Version: " << l+1 << "(0x" << l << ")" << std::endl; + out << " Subject: " << std::endl; + out << " " << getX509NameString(cert->cert_info->subject); + out << std::endl; + out << std::endl; + out << " Signatures:" << std::endl; + return out.str(); +} + + + +std::string getX509AuthCode(X509 *x509) +{ + /* get the self signature -> the first signature */ + + std::stringstream out; + + ASN1_BIT_STRING *signature = x509->signature; + int signlen = ASN1_STRING_length(signature); + unsigned char *signdata = ASN1_STRING_data(signature); + + /* extract the authcode from the signature */ + /* convert it to a string, inverse of 2 bytes of signdata */ + if (signlen > 2) + signlen = 2; + int j; + for(j=0;jcert_info->subject); + // strip out bad chars. + for(int i = 0; i < (signed) name.length(); i++) + { + if ((name[i] == '/') || (name[i] == ' ') || (name[i] == '=') || + (name[i] == '\\') || (name[i] == '\t') || (name[i] == '\n')) + { + name[i] = '_'; + } + } + return name; +} + +#if 0 +#endif + + +/********** SSL ERROR STUFF ******************************************/ + +int printSSLError(SSL *ssl, int retval, int err, unsigned long err2, + std::ostream &out) +{ + std::string reason; + + std::string mainreason = std::string("UNKNOWN ERROR CODE"); + if (err == SSL_ERROR_NONE) + { + mainreason = std::string("SSL_ERROR_NONE"); + } + else if (err == SSL_ERROR_ZERO_RETURN) + { + mainreason = std::string("SSL_ERROR_ZERO_RETURN"); + } + else if (err == SSL_ERROR_WANT_READ) + { + mainreason = std::string("SSL_ERROR_WANT_READ"); + } + else if (err == SSL_ERROR_WANT_WRITE) + { + mainreason = std::string("SSL_ERROR_WANT_WRITE"); + } + else if (err == SSL_ERROR_WANT_CONNECT) + { + mainreason = std::string("SSL_ERROR_WANT_CONNECT"); + } + else if (err == SSL_ERROR_WANT_ACCEPT) + { + mainreason = std::string("SSL_ERROR_WANT_ACCEPT"); + } + else if (err == SSL_ERROR_WANT_X509_LOOKUP) + { + mainreason = std::string("SSL_ERROR_WANT_X509_LOOKUP"); + } + else if (err == SSL_ERROR_SYSCALL) + { + mainreason = std::string("SSL_ERROR_SYSCALL"); + } + else if (err == SSL_ERROR_SSL) + { + mainreason = std::string("SSL_ERROR_SSL"); + } + out << "RetVal(" << retval; + out << ") -> SSL Error: " << mainreason << std::endl; + out << "\t + ERR Error: " << ERR_error_string(err2, NULL) << std::endl; + return 1; +} + + +/***************************** OLD STORAGE of CERTS ************************* + * We will retain the existing CERT storage format for the moment.... + * This will enable the existing certs to be loaded in. + * + * BUT Save will change the format - removing the options from + * the configuration file. This will mean that we can catch NEW/OLD formats. + * + * We only want to load old format ONCE. as we'll use it to get + * the list of existing friends... + * + * + * + */ + +bool AuthSSL::FinalSaveCertificates() +{ + CheckSaveCertificates(); + + RsStackMutex stack(sslMtx); /***** LOCK *****/ + mConfigSaveActive = false; + return true; +} + +bool AuthSSL::CheckSaveCertificates() +{ + sslMtx.lock(); /***** LOCK *****/ + + if ((mConfigSaveActive) && (mToSaveCerts)) + { + mToSaveCerts = false; + sslMtx.unlock(); /**** UNLOCK ****/ + + saveCertificates(); + return true; + } + + sslMtx.unlock(); /**** UNLOCK ****/ + + return false; +} + +bool AuthSSL::saveCertificates() +{ + // construct file name. + // create the file in memory - hash + sign. + // write out data to a file. + + sslMtx.lock(); /***** LOCK *****/ + + std::string configfile = mCertConfigFile; + std::string neighdir = mNeighDir; + + sslMtx.unlock(); /**** UNLOCK ****/ + + /* add on the slash */ + if (neighdir != "") + { + neighdir += "/"; + } + + std::map::iterator mit; + + std::string conftxt; + std::string empty(""); + unsigned int i; + +#ifdef AUTHSSL_DEBUG + std::cerr << "AuthSSL::saveCertificates()"; + std::cerr << std::endl; +#endif + sslMtx.lock(); /***** LOCK *****/ + + /* iterate through both lists */ + std::map::iterator it; + + for(it = mCerts.begin(); it != mCerts.end(); it++) + { +// SAVE ALL CERTS +#if PQI_USE_PQISSL +#endif +// Save only Authed Certs; + if (it->second->authed) + { + X509 *x509 = it->second->certificate; + std::string hash; + std::string neighfile = neighdir + getCertName(x509) + ".pqi"; + + if (saveX509ToFile(x509, neighfile, hash)) + { + conftxt += "CERT "; + conftxt += getCertName(x509); + conftxt += "\n"; + conftxt += hash; + conftxt += "\n"; + } + } + } + + + // now work out signature of it all. This relies on the + // EVP library of openSSL..... We are going to use signing + // for the moment. + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + //OpenSSL_add_all_digests(); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "EVP_SignInit Failure!" << std::endl; +#endif + } + + if (0 == EVP_SignUpdate(mdctx, conftxt.c_str(), conftxt.length())) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "EVP_SignUpdate Failure!" << std::endl; +#endif + } + + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "EVP_SignFinal Failure!" << std::endl; +#endif + } + +#ifdef AUTHSSL_DEBUG + std::cerr << "Conf Signature is(" << signlen << "): "; +#endif + for(i = 0; i < signlen; i++) + { +#ifdef AUTHSSL_DEBUG + fprintf(stderr, "%02x", signature[i]); +#endif + conftxt += signature[i]; + } +#ifdef AUTHSSL_DEBUG + std::cerr << std::endl; +#endif + EVP_MD_CTX_destroy(mdctx); + + FILE *cfd = fopen(configfile.c_str(), "wb"); + if (cfd == NULL) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Failed to open: " << configfile << std::endl; +#endif + sslMtx.unlock(); /**** UNLOCK ****/ + + return false; + } + + int wrec; + if (1 != (wrec = fwrite(conftxt.c_str(), conftxt.length(), 1, cfd))) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Error writing: " << configfile << std::endl; + std::cerr << "Wrote: " << wrec << "/" << 1 << " Records" << std::endl; +#endif + } + + fclose(cfd); + + sslMtx.unlock(); /**** UNLOCK ****/ + + return true; +} + + +/****** + * Special version for backwards compatibility + * + * has two extra parameters. + * bool oldFormat & std::map keyvaluemap + * + * We'll leave these in for the next couple of months... + * so that old versions will automatically be converted to the + * new format! + * + */ + +bool AuthSSL::loadCertificates() +{ + bool oldFormat; + std::map keyValueMap; + + return loadCertificates(oldFormat, keyValueMap); +} + +/********************* + * NOTE no need to Lock here. locking handled in ProcessX509() + */ +static const uint32_t OPT_LEN = 16; +static const uint32_t VAL_LEN = 1000; + +bool AuthSSL::loadCertificates(bool &oldFormat, std::map &keyValueMap) +{ + + /******************************************* + * open the configuration file. + * read in CERT + Hash. + * + * construct file name. + * create the file in memory - hash + sign. + * write out data to a file. + *****************************************/ + + sslMtx.lock(); /***** LOCK *****/ + + std::string configfile = mCertConfigFile; + std::string neighdir = mNeighDir; + + sslMtx.unlock(); /**** UNLOCK ****/ + + /* add on the slash */ + if (neighdir != "") + { + neighdir += "/"; + } + + oldFormat = false; + + std::string conftxt; + + unsigned int maxnamesize = 1024; + char name[maxnamesize]; + + int c; + unsigned int i; + + FILE *cfd = fopen(configfile.c_str(), "rb"); + if (cfd == NULL) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Unable to Load Configuration File!" << std::endl; + std::cerr << "File: " << configfile << std::endl; +#endif + return false; + } + + std::list fnames; + std::list hashes; + std::map::iterator mit; + std::map tmpsettings; + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char conf_signature[signlen]; + char *ret = NULL; + + for(ret = fgets(name, maxnamesize, cfd); + ((ret != NULL) && (!strncmp(name, "CERT ", 5))); + ret = fgets(name, maxnamesize, cfd)) + { + for(i = 5; (name[i] != '\n') && (i < (unsigned) maxnamesize); i++); + + if (name[i] == '\n') + { + name[i] = '\0'; + } + + // so the name is first.... + std::string fname = &(name[5]); + + // now read the + std::string hash; + std::string signature; + + for(i = 0; i < signlen; i++) + { + if (EOF == (c = fgetc(cfd))) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Error Reading Signature of: "; + std::cerr << fname; + std::cerr << std::endl; + std::cerr << "ABorting Load!"; + std::cerr << std::endl; +#endif + return -1; + } + unsigned char uc = (unsigned char) c; + signature += (unsigned char) uc; + } + if ('\n' != (c = fgetc(cfd))) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Warning Mising seperator" << std::endl; +#endif + } + +#ifdef AUTHSSL_DEBUG + std::cerr << "Read fname:" << fname << std::endl; + std::cerr << "Signature:" << std::endl; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", (unsigned char) signature[i]); + } + std::cerr << std::endl; + std::cerr << std::endl; +#endif + + // push back..... + fnames.push_back(fname); + hashes.push_back(signature); + + conftxt += "CERT "; + conftxt += fname; + conftxt += "\n"; + conftxt += signature; + conftxt += "\n"; + + // be sure to write over a bit... + name[0] = 'N'; + name[1] = 'O'; + } + + // string already waiting! + for(; ((ret != NULL) && (!strncmp(name, "OPT ", 4))); + ret = fgets(name, maxnamesize, cfd)) + { + for(i = 4; (name[i] != '\n') && (i < OPT_LEN); i++); + // terminate the string. + name[i] = '\0'; + + // so the name is first.... + std::string opt = &(name[4]); + + // now read the + std::string val; // cleaned up value. + std::string valsign; // value in the file. + for(i = 0; i < VAL_LEN; i++) + { + if (EOF == (c = fgetc(cfd))) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Error Reading Value of: "; + std::cerr << opt; + std::cerr << std::endl; + std::cerr << "ABorting Load!"; + std::cerr << std::endl; +#endif + return -1; + } + // remove zeros on strings... + if (c != '\0') + { + val += (unsigned char) c; + } + valsign += (unsigned char) c; + } + if ('\n' != (c = fgetc(cfd))) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Warning Mising seperator" << std::endl; +#endif + } + +#ifdef AUTHSSL_DEBUG + std::cerr << "Read OPT:" << opt; + std::cerr << " Val:" << val << std::endl; +#endif + + // push back..... + tmpsettings[opt] = val; + + conftxt += "OPT "; + conftxt += opt; + conftxt += "\n"; + conftxt += valsign; + conftxt += "\n"; + + // be sure to write over a bit... + name[0] = 'N'; + name[1] = 'O'; + } + + // only read up to the first newline symbol.... + // continue... + for(i = 0; (name[i] != '\n') && (i < signlen); i++); + + if (i != signlen) + { + for(i++; i < signlen; i++) + { + c = fgetc(cfd); + if (c == EOF) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Error Reading Conf Signature:"; + std::cerr << std::endl; +#endif + return 1; + } + unsigned char uc = (unsigned char) c; + name[i] = uc; + } + } + +#ifdef AUTHSSL_DEBUG + std::cerr << "Configuration File Signature: " << std::endl; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", (unsigned char) name[i]); + } + std::cerr << std::endl; +#endif + + + // when we get here - should have the final signature in the buffer. + // check. + // + // compare signatures. + // instead of verifying with the public key.... + // we'll sign it again - and compare .... FIX LATER... + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit(mdctx, EVP_sha1())) + { +#ifdef AUTHSSL_DEBUG +#endif + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, conftxt.c_str(), conftxt.length())) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "EVP_SignUpdate Failure!" << std::endl; +#endif + } + + if (0 == EVP_SignFinal(mdctx, conf_signature, &signlen, pkey)) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "EVP_SignFinal Failure!" << std::endl; +#endif + } + + EVP_MD_CTX_destroy(mdctx); + fclose(cfd); + +#ifdef AUTHSSL_DEBUG + std::cerr << "Recalced File Signature: " << std::endl; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", conf_signature[i]); + } + std::cerr << std::endl; +#endif + + bool same = true; + for(i = 0; i < signlen; i++) + { + if ((unsigned char) name[i] != conf_signature[i]) + { + same = false; + } + } + + if (same == false) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "ERROR VALIDATING CONFIGURATION!" << std::endl; + std::cerr << "PLEASE FIX!" << std::endl; +#endif + return false; + } + + std::list::iterator it; + std::list::iterator it2; + for(it = fnames.begin(), it2 = hashes.begin(); it != fnames.end(); it++, it2++) + { + std::string neighfile = neighdir + (*it) + ".pqi"; + X509 *x509 = loadX509FromFile(neighfile, (*it2)); + if (x509 != NULL) + { + std::string id; + if (ProcessX509(x509, id)) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "Loaded Certificate: " << id; + std::cerr << std::endl; +#endif + } + } + } + for(mit = tmpsettings.begin(); mit != tmpsettings.end(); mit++) + { + keyValueMap[mit -> first] = mit -> second; + } + + mToSaveCerts = false; + + if (keyValueMap.size() > 0) + { + oldFormat = true; + mToSaveCerts = true; + } + + return true; +} + diff --git a/libretroshare/src/_pqi/authssl.h b/libretroshare/src/_pqi/authssl.h new file mode 100644 index 000000000..d5a6fb4e1 --- /dev/null +++ b/libretroshare/src/_pqi/authssl.h @@ -0,0 +1,241 @@ +/* + * libretroshare/src/pqi: authssl.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + +#ifndef AUTHSSL_H +#define AUTHSSL_H + +/* + * This is an implementation of SSL certificate authentication, which can be + * overloaded with pgp style signatures, and web-of-trust authentication. + * + * There are several virtual functions with can be overloaded to acheive this. + * SignCertificate() + * AuthCertificate() + * + * To use as an SSL authentication system, you must use a common CA certificate. + * and compilation should be done with PQI_USE_XPGP off, and PQI_USE_SSLONLY on + * * The pqissl stuff doesn't need to differentiate between SSL, SSL + PGP, + * as its X509 certs. + * * The rsserver stuff has to distinguish between all three types ;( + * + */ + +#include +#include + +#include +#include + +#include "util/rsthreads.h" + +#include "pqi/pqi_base.h" +#include "pqi/pqinetwork.h" +#include "pqi/p3authmgr.h" + +class AuthSSL; + +class sslcert +{ + public: + sslcert(X509 *x509, std::string id); + + /* certificate parameters */ + std::string id; + std::string name; + std::string location; + std::string org; + std::string email; + + std::string issuer; + + std::string fpr; + std::list signers; + + /* Auth settings */ + bool authed; + + /* INTERNAL Parameters */ + X509 *certificate; +}; + + +class AuthSSL: public p3AuthMgr +{ +public: + + /* Initialisation Functions (Unique) */ + AuthSSL(); + bool validateOwnCertificate(X509 *x509, EVP_PKEY *pkey); + + virtual bool active(); + virtual int InitAuth(const char *srvr_cert, const char *priv_key, + const char *passwd); + virtual bool CloseAuth(); + virtual int setConfigDirectories(std::string confFile, std::string neighDir); + + + /*********** Overloaded Functions from p3AuthMgr **********/ + + /* get Certificate Ids */ + + virtual std::string OwnId(); + virtual bool getAllList(std::list &ids); + virtual bool getAuthenticatedList(std::list &ids); + virtual bool getUnknownList(std::list &ids); + + /* get Details from the Certificates */ + + virtual bool isValid(std::string id); + virtual bool isAuthenticated(std::string id); + virtual std::string getName(std::string id); + virtual std::string getIssuerName(std::string id); + virtual bool getDetails(std::string id, pqiAuthDetails &details); + + /* first party trust info (dummy) */ + virtual bool isTrustingMe(std::string id) ; + virtual void addTrustingPeer(std::string id) ; + + + /* High Level Load/Save Configuration */ + virtual bool FinalSaveCertificates(); + virtual bool CheckSaveCertificates(); + virtual bool saveCertificates(); + virtual bool loadCertificates(); + + /* Load/Save certificates */ + virtual bool LoadCertificateFromString(std::string pem, std::string &id); + virtual std::string SaveCertificateToString(std::string id); + virtual bool LoadCertificateFromFile(std::string filename, std::string &id); + virtual bool SaveCertificateToFile(std::string id, std::string filename); + + virtual bool LoadCertificateFromBinary(const uint8_t *ptr, uint32_t len, std::string &id); + virtual bool SaveCertificateToBinary(std::string id, uint8_t **ptr, uint32_t *len); + + /* Signatures */ + + virtual bool AuthCertificate(std::string uid); + + /* These are dummy functions */ + virtual bool SignCertificate(std::string id); + virtual bool RevokeCertificate(std::string id); + virtual bool TrustCertificate(std::string id, bool trust); + + /* Sign / Encrypt / Verify Data (TODO) */ + virtual bool SignData(std::string input, std::string &sign); + virtual bool SignData(const void *data, const uint32_t len, std::string &sign); + virtual bool SignDataBin(std::string, unsigned char*, unsigned int*); + virtual bool SignDataBin(const void*, uint32_t, unsigned char*, unsigned int*); + virtual bool VerifySignBin(std::string, const void*, uint32_t, unsigned char*, unsigned int); + + + /*********** Overloaded Functions from p3AuthMgr **********/ + + /************* Virtual Functions from AuthSSL *************/ + + virtual int VerifyX509Callback(int preverify_ok, X509_STORE_CTX *ctx); + virtual bool ValidateCertificate(X509 *x509, std::string &peerId); /* validate + get id */ + + /************* Virtual Functions from AuthSSL *************/ + + +public: /* SSL specific functions used in pqissl/pqissllistener */ + SSL_CTX *getCTX(); + + + bool FailedCertificate(X509 *x509, bool incoming); /* store for discovery */ + bool CheckCertificate(std::string peerId, X509 *x509); /* check that they are exact match */ + + /* Special Config Loading (backwards compatibility) */ + bool loadCertificates(bool &oldFormat, std::map &keyValueMap); + + + +private: + + /* Helper Functions */ + + bool ProcessX509(X509 *x509, std::string &id); + + X509 * loadX509FromPEM(std::string pem); + X509 * loadX509FromFile(std::string fname, std::string hash); + bool saveX509ToFile(X509 *x509, std::string fname, std::string &hash); + + X509 * loadX509FromDER(const uint8_t *ptr, uint32_t len); + bool saveX509ToDER(X509 *x509, uint8_t **ptr, uint32_t *len); + + /*********** LOCKED Functions ******/ + bool locked_FindCert(std::string id, sslcert **cert); + + + /* Data */ + RsMutex sslMtx; /**** LOCKING */ + + int init; + std::string mCertConfigFile; + std::string mNeighDir; + + SSL_CTX *sslctx; + + std::string mOwnId; + sslcert *mOwnCert; + EVP_PKEY *pkey; + + bool mToSaveCerts; + bool mConfigSaveActive; + std::map mCerts; +}; + + +X509_REQ *GenerateX509Req( + std::string pkey_file, std::string passwd, + std::string name, std::string email, std::string org, + std::string loc, std::string state, std::string country, + int nbits_in, std::string &errString); + +X509 *SignX509Certificate(X509_NAME *issuer, EVP_PKEY *privkey, X509_REQ *req, long days); + + +/* Helper Functions */ +int printSSLError(SSL *ssl, int retval, int err, unsigned long err2, std::ostream &out); +std::string getX509NameString(X509_NAME *name); +std::string getX509CNString(X509_NAME *name); + +std::string getX509OrgString(X509_NAME *name); +std::string getX509LocString(X509_NAME *name); +std::string getX509CountryString(X509_NAME *name); + +#if 0 +std::list getXPGPsigners(XPGP *cert); +std::string getXPGPAuthCode(XPGP *xpgp); + +#endif + +std::string getX509Info(X509 *cert); +bool getX509id(X509 *x509, std::string &xid); + +int LoadCheckX509andGetName(const char *cert_file, + std::string &userName, std::string &userId); + +#endif // AUTHSSL_H diff --git a/libretroshare/src/_pqi/authxpgp.cc b/libretroshare/src/_pqi/authxpgp.cc new file mode 100644 index 000000000..85db0564e --- /dev/null +++ b/libretroshare/src/_pqi/authxpgp.cc @@ -0,0 +1,2361 @@ +/* + * libretroshare/src/pqi: authxpgp.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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 "authxpgp.h" +#include "cleanupxpgp.h" + +#include "pqinetwork.h" + +/******************** notify of new Cert **************************/ +#include "pqinotify.h" + +#include +#include +#include + +#include +#include + +/******************************** TRUST LVLS +xPGP_vfy.h:#define TRUST_SIGN_OWN 6 +xPGP_vfy.h:#define TRUST_SIGN_TRSTED 5 +xPGP_vfy.h:#define TRUST_SIGN_AUTHEN 4 +xPGP_vfy.h:#define TRUST_SIGN_BASIC 3 +xPGP_vfy.h:#define TRUST_SIGN_UNTRUSTED 2 +xPGP_vfy.h:#define TRUST_SIGN_UNKNOWN 1 +xPGP_vfy.h:#define TRUST_SIGN_NONE 0 +xPGP_vfy.h:#define TRUST_SIGN_BAD -1 +******************************************************/ + + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ + +/*********** + ** #define AUTHXPGP_DEBUG 1 + **********/ + +// the single instance of this. +static AuthXPGP instance_xpgproot; + +p3AuthMgr *getAuthMgr() +{ + return &instance_xpgproot; +} + + +xpgpcert::xpgpcert(XPGP *xpgp, std::string pid) +{ + certificate = xpgp; + id = pid; + name = getX509CNString(xpgp->subject -> subject); + org = getX509OrgString(xpgp->subject -> subject); + location = getX509LocString(xpgp->subject -> subject); + email = ""; + + /* These should be filled in afterwards */ + fpr = pid; + + trustLvl = 0; + ownsign = false; + trusted = false; +} + + +AuthXPGP::AuthXPGP() + :init(0), sslctx(NULL), pkey(NULL), mToSaveCerts(false), mConfigSaveActive(true) +{ +} + +bool AuthXPGP::active() +{ + return init; +} + +// args: server cert, server private key, trusted certificates. + +int AuthXPGP::InitAuth(const char *cert_file, const char *priv_key_file, + const char *passwd) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::InitAuth()"; + std::cerr << std::endl; +#endif + +static int initLib = 0; + if (!initLib) + { + initLib = 1; + SSL_load_error_strings(); + SSL_library_init(); + } + + + if (init == 1) + { + return 1; + } + + if ((cert_file == NULL) || + (priv_key_file == NULL) || + (passwd == NULL)) + { + fprintf(stderr, "sslroot::initssl() missing parameters!\n"); + return 0; + } + + + // XXX TODO + // actions_to_seed_PRNG(); + + std::cerr << "SSL Library Init!" << std::endl; + + // setup connection method + sslctx = SSL_CTX_new(PGPv1_method()); + + // setup cipher lists. + SSL_CTX_set_cipher_list(sslctx, "DEFAULT"); + + // certificates (Set Local Server Certificate). + FILE *ownfp = fopen(cert_file, "r"); + if (ownfp == NULL) + { + std::cerr << "Couldn't open Own Certificate!" << std::endl; + return -1; + } + + + + // get xPGP certificate. + XPGP *xpgp = PEM_read_XPGP(ownfp, NULL, NULL, NULL); + fclose(ownfp); + + if (xpgp == NULL) + { + return -1; + } + SSL_CTX_use_pgp_certificate(sslctx, xpgp); + + // get private key + FILE *pkfp = fopen(priv_key_file, "rb"); + if (pkfp == NULL) + { + std::cerr << "Couldn't Open PrivKey File!" << std::endl; + CloseAuth(); + return -1; + } + + pkey = PEM_read_PrivateKey(pkfp, NULL, NULL, (void *) passwd); + fclose(pkfp); + + if (pkey == NULL) + { + return -1; + } + SSL_CTX_use_pgp_PrivateKey(sslctx, pkey); + + if (1 != SSL_CTX_check_pgp_private_key(sslctx)) + { + std::cerr << "Issues With Private Key! - Doesn't match your Cert" << std::endl; + std::cerr << "Check your input key/certificate:" << std::endl; + std::cerr << priv_key_file << " & " << cert_file; + std::cerr << std::endl; + CloseAuth(); + return -1; + } + + // make keyring. + pgp_keyring = createPGPContext(xpgp, pkey); + SSL_CTX_set_XPGP_KEYRING(sslctx, pgp_keyring); + + + // Setup the certificate. (after keyring is made!). + if (!XPGP_check_valid_certificate(xpgp)) + { + /* bad certificate */ + CloseAuth(); + return -1; + } + + if (!getXPGPid(xpgp, mOwnId)) + { + /* bad certificate */ + CloseAuth(); + return -1; + } + + + // enable verification of certificates (PEER) + SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + + std::cerr << "SSL Verification Set" << std::endl; + + mOwnCert = new xpgpcert(xpgp, mOwnId); + + /* add to keyring */ + XPGP_add_certificate(pgp_keyring, mOwnCert->certificate); + mOwnCert->trustLvl = XPGP_auth_certificate(pgp_keyring, mOwnCert->certificate); + mOwnCert->trusted = true; + mOwnCert->ownsign = true; + + init = 1; + return 1; +} + + + +bool AuthXPGP::CloseAuth() +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::CloseAuth()"; + std::cerr << std::endl; +#endif + SSL_CTX_free(sslctx); + + // clean up private key.... + // remove certificates etc -> opposite of initssl. + init = 0; + return 1; +} + +/* Context handling */ +SSL_CTX *AuthXPGP::getCTX() +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::getCTX()"; + std::cerr << std::endl; +#endif + return sslctx; +} + +int AuthXPGP::setConfigDirectories(std::string configfile, std::string neighdir) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::setConfigDirectories()"; + std::cerr << std::endl; +#endif + xpgpMtx.lock(); /***** LOCK *****/ + + mCertConfigFile = configfile; + mNeighDir = neighdir; + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return 1; +} + +bool AuthXPGP::isTrustingMe(std::string id) +{ + xpgpMtx.lock(); /***** LOCK *****/ + + bool res = false ; + + for(std::list::const_iterator it(_trusting_peers.begin());it!=_trusting_peers.end() && !res;++it) + if( *it == id ) + res = true ; + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return res ; +} +void AuthXPGP::addTrustingPeer(std::string id) +{ + if( !isTrustingMe(id) ) + { + xpgpMtx.lock(); /***** LOCK *****/ + + _trusting_peers.push_back(id) ; + + xpgpMtx.unlock(); /**** UNLOCK ****/ + } +} + +std::string AuthXPGP::OwnId() +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::OwnId()"; + std::cerr << std::endl; +#endif + xpgpMtx.lock(); /***** LOCK *****/ + + std::string id = mOwnId; + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return id; +} + +bool AuthXPGP::getAllList(std::list &ids) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::getAllList()"; + std::cerr << std::endl; +#endif + xpgpMtx.lock(); /***** LOCK *****/ + + /* iterate through both lists */ + std::map::iterator it; + + for(it = mCerts.begin(); it != mCerts.end(); it++) + { + ids.push_back(it->first); + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return true; +} + +bool AuthXPGP::getAuthenticatedList(std::list &ids) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::getAuthenticatedList()"; + std::cerr << std::endl; +#endif + xpgpMtx.lock(); /***** LOCK *****/ + + /* iterate through both lists */ + std::map::iterator it; + + for(it = mCerts.begin(); it != mCerts.end(); it++) + { + if (it->second->trustLvl > TRUST_SIGN_BASIC) + { + ids.push_back(it->first); + } + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return true; +} + +bool AuthXPGP::getUnknownList(std::list &ids) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::getUnknownList()"; + std::cerr << std::endl; +#endif + xpgpMtx.lock(); /***** LOCK *****/ + + /* iterate through both lists */ + std::map::iterator it; + + for(it = mCerts.begin(); it != mCerts.end(); it++) + { + if (it->second->trustLvl <= TRUST_SIGN_BASIC) + { + ids.push_back(it->first); + } + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return true; +} + + /* silly question really - only valid certs get saved to map + * so if in map its okay + */ +bool AuthXPGP::isValid(std::string id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::isValid() " << id; + std::cerr << std::endl; +#endif + xpgpMtx.lock(); /***** LOCK *****/ + bool valid = false; + + if (id == mOwnId) + { + valid = true; + } + else + { + valid = (mCerts.end() != mCerts.find(id)); + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return valid; +} + +bool AuthXPGP::isAuthenticated(std::string id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::isAuthenticated() " << id; + std::cerr << std::endl; +#endif + xpgpMtx.lock(); /***** LOCK *****/ + + xpgpcert *cert = NULL; + bool auth = false; + + if (locked_FindCert(id, &cert)) + { + auth = (cert->trustLvl > TRUST_SIGN_BASIC); + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return auth; +} + +std::string AuthXPGP::getName(std::string id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::getName() " << id; + std::cerr << std::endl; +#endif + std::string name; + + xpgpMtx.lock(); /***** LOCK *****/ + + xpgpcert *cert = NULL; + if (id == mOwnId) + { + name = mOwnCert->name; + } + else if (locked_FindCert(id, &cert)) + { + name = cert->name; + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return name; +} + +bool AuthXPGP::getDetails(std::string id, pqiAuthDetails &details) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::getDetails() " << id; + std::cerr << std::endl; +#endif + xpgpMtx.lock(); /***** LOCK *****/ + + bool valid = false; + xpgpcert *cert = NULL; + if (id == mOwnId) + { + cert = mOwnCert; + valid = true; + } + else if (locked_FindCert(id, &cert)) + { + valid = true; + } + + if (valid) + { + /* fill details */ + details.id = cert->id; + details.name = cert->name; + details.email = cert->email; + details.location= cert->location; + details.org = cert->org; + + details.fpr = cert->fpr; + details.signers = cert->signers; + + details.trustLvl= cert->trustLvl; + details.ownsign = cert->ownsign; + details.trusted = cert->trusted; + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return valid; +} + + + /* Load/Save certificates */ + +bool AuthXPGP::LoadCertificateFromString(std::string pem, std::string &id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::LoadCertificateFromString() " << id; + std::cerr << std::endl; +#endif + +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::LoadCertificateFromString() Cleaning up Certificate First!"; + std::cerr << std::endl; +#endif + + std::string cleancert = cleanUpCertificate(pem); + + XPGP *xpgp = loadXPGPFromPEM(cleancert); + if (!xpgp) + return false; + + return ProcessXPGP(xpgp, id); +} + +std::string AuthXPGP::SaveCertificateToString(std::string id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::SaveCertificateToString() " << id; + std::cerr << std::endl; +#endif + + + xpgpMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + std::string certstr; + xpgpcert *cert = NULL; + bool valid = false; + + if (id == mOwnId) + { + cert = mOwnCert; + valid = true; + } + else if (locked_FindCert(id, &cert)) + { + valid = true; + } + + if (valid) + { + BIO *bp = BIO_new(BIO_s_mem()); + + PEM_write_bio_XPGP(bp, cert->certificate); + + /* translate the bp data to a string */ + char *data; + int len = BIO_get_mem_data(bp, &data); + for(int i = 0; i < len; i++) + { + certstr += data[i]; + } + + BIO_free(bp); + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return certstr; +} + + + +bool AuthXPGP::LoadCertificateFromFile(std::string filename, std::string &id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::LoadCertificateFromFile() " << id; + std::cerr << std::endl; +#endif + + std::string nullhash; + + XPGP *xpgp = loadXPGPFromFile(filename.c_str(), nullhash); + if (!xpgp) + return false; + + return ProcessXPGP(xpgp, id); +} + +//============================================================================ + +//! Saves something to filename + +//! \returns true on success, false on failure +bool AuthXPGP::SaveCertificateToFile(std::string id, std::string filename) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::SaveCertificateToFile() " << id; + std::cerr << std::endl; +#endif + + xpgpMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + xpgpcert *cert = NULL; + bool valid = false; + std::string hash; + + if (id == mOwnId) + { + cert = mOwnCert; + valid = true; + } + else if (locked_FindCert(id, &cert)) + { + valid = true; + } + if (valid) + { + valid = saveXPGPToFile(cert->certificate, filename, hash); + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return valid; +} + + /**** To/From DER format ***/ + +bool AuthXPGP::LoadCertificateFromBinary(const uint8_t *ptr, uint32_t len, std::string &id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::LoadCertificateFromFile() " << id; + std::cerr << std::endl; +#endif + + XPGP *xpgp = loadXPGPFromDER(ptr, len); + if (!xpgp) + return false; + + return ProcessXPGP(xpgp, id); + +} + +bool AuthXPGP::SaveCertificateToBinary(std::string id, uint8_t **ptr, uint32_t *len) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::SaveCertificateToBinary() " << id; + std::cerr << std::endl; +#endif + + xpgpMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + xpgpcert *cert = NULL; + bool valid = false; + std::string hash; + + if (id == mOwnId) + { + cert = mOwnCert; + valid = true; + } + else if (locked_FindCert(id, &cert)) + { + valid = true; + } + if (valid) + { + valid = saveXPGPToDER(cert->certificate, ptr, len); + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return valid; +} + + + /* Signatures */ +bool AuthXPGP::SignCertificate(std::string id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::SignCertificate() " << id; + std::cerr << std::endl; +#endif + + xpgpMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + xpgpcert *cert = NULL; + xpgpcert *own = mOwnCert; + bool valid = false; + + if (locked_FindCert(id, &cert)) + { + if (0 < validateCertificateIsSignedByKey( + cert->certificate, own->certificate)) + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::SignCertificate() Signed Already: " << id; + std::cerr << std::endl; +#endif + cert->ownsign=true; + } + else + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::SignCertificate() Signing Cert: " << id; + std::cerr << std::endl; +#endif + /* sign certificate */ + XPGP_sign_certificate(pgp_keyring, cert->certificate, own->certificate); + + /* reevaluate the auth of the xpgp */ + cert->trustLvl = XPGP_auth_certificate(pgp_keyring, cert->certificate); + cert->ownsign = true; + + mToSaveCerts = true; + } + valid = true; + } + + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return valid; +} + +bool AuthXPGP::TrustCertificate(std::string id, bool totrust) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::TrustCertificate() " << id; + std::cerr << std::endl; +#endif + + xpgpMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + xpgpcert *cert = NULL; + bool valid = false; + + if (locked_FindCert(id, &cert)) + { + + /* if trusted -> untrust */ + if (!totrust) + { + XPGP_signer_untrusted(pgp_keyring, cert->certificate); + cert->trusted = false; + } + else + { + /* if auth then we can trust them */ + if (XPGP_signer_trusted(pgp_keyring, cert->certificate)) + { + cert->trusted = true; + } + } + + /* reevaluate the auth of the xpgp */ + cert->trustLvl = XPGP_auth_certificate(pgp_keyring, cert->certificate); + valid = true; + + /* resave if changed trust setting */ + mToSaveCerts = true; + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return valid; +} + +bool AuthXPGP::RevokeCertificate(std::string id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::RevokeCertificate() " << id; + std::cerr << std::endl; +#endif + + xpgpMtx.lock(); /***** LOCK *****/ + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return false; +} + + +bool AuthXPGP::AuthCertificate(std::string id) +{ + +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::AuthCertificate() " << id; + std::cerr << std::endl; +#endif + + xpgpMtx.lock(); /***** LOCK *****/ + + /* get the cert first */ + xpgpcert *cert = NULL; + xpgpcert *own = mOwnCert; + bool valid = false; + + if (locked_FindCert(id, &cert)) + { + /* ADD IN LATER */ + //if (cert->trustLvl > TRUST_SIGN_BASIC) + //{ +#ifdef AUTHXPGP_DEBUG + // std::cerr << "AuthXPGP::AuthCertificate() Already Authed: " << id; + // std::cerr << std::endl; +#endif + //} + + if (0 < validateCertificateIsSignedByKey( + cert->certificate, own->certificate)) + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::AuthCertificate() Signed Already: " << id; + std::cerr << std::endl; +#endif + cert->ownsign=true; + } + else + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::AuthCertificate() Signing Cert: " << id; + std::cerr << std::endl; +#endif + /* sign certificate */ + XPGP_sign_certificate(pgp_keyring, cert->certificate, own->certificate); + + /* reevaluate the auth of the xpgp */ + cert->trustLvl = XPGP_auth_certificate(pgp_keyring, cert->certificate); + cert->ownsign = true; + + mToSaveCerts = true; + } + valid = true; + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return valid; +} + + + /* Sign / Encrypt / Verify Data (TODO) */ + +bool AuthXPGP::SignData(std::string input, std::string &sign) +{ + return SignData(input.c_str(), input.length(), sign); +} + +bool AuthXPGP::SignData(const void *data, const uint32_t len, std::string &sign) +{ + + RsStackMutex stack(xpgpMtx); /***** STACK LOCK MUTEX *****/ + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + if (0 == EVP_SignInit(mdctx, EVP_sha1())) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_SignUpdate(mdctx, data, len)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + EVP_MD_CTX_destroy(mdctx); + + sign.clear(); + std::ostringstream out; + out << std::hex; + for(uint32_t i = 0; i < signlen; i++) + { + out << std::setw(2) << std::setfill('0'); + out << (uint32_t) (signature[i]); + } + + sign = out.str(); + + return true; +} + + +bool AuthXPGP::SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen) +{ + return SignDataBin(input.c_str(), input.length(), sign, signlen); +} + +bool AuthXPGP::SignDataBin(const void *data, const uint32_t len, + unsigned char *sign, unsigned int *signlen) +{ + + RsStackMutex stack(xpgpMtx); /***** STACK LOCK MUTEX *****/ + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + unsigned int req_signlen = EVP_PKEY_size(pkey); + if (req_signlen > *signlen) + { + /* not enough space */ + std::cerr << "SignDataBin() Not Enough Sign SpacegnInit Failure!" << std::endl; + return false; + } + + + + + if (0 == EVP_SignInit(mdctx, EVP_sha1())) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_SignUpdate(mdctx, data, len)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_SignFinal(mdctx, sign, signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + EVP_MD_CTX_destroy(mdctx); + return true; +} + + +bool AuthXPGP::VerifySignBin(std::string pid, + const void *data, const uint32_t len, + unsigned char *sign, unsigned int signlen) +{ + RsStackMutex stack(xpgpMtx); /***** STACK LOCK MUTEX *****/ + + /* find the peer */ + + xpgpcert *peer; + if (pid == mOwnId) + { + peer = mOwnCert; + } + else if (!locked_FindCert(pid, &peer)) + { + std::cerr << "VerifySignBin() no peer" << std::endl; + return false; + } + + EVP_PKEY *peerkey = peer->certificate->key->key->pkey; + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_VerifyInit(mdctx, EVP_sha1())) + { + std::cerr << "EVP_VerifyInit Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_VerifyUpdate(mdctx, data, len)) + { + std::cerr << "EVP_VerifyUpdate Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + if (0 == EVP_VerifyFinal(mdctx, sign, signlen, peerkey)) + { + std::cerr << "EVP_VerifyFinal Failure!" << std::endl; + + EVP_MD_CTX_destroy(mdctx); + return false; + } + + EVP_MD_CTX_destroy(mdctx); + return true; +} + + + + + + + /**** NEW functions we've added ****/ + + + /**** AUX Functions ****/ +bool AuthXPGP::locked_FindCert(std::string id, xpgpcert **cert) +{ + std::map::iterator it; + + if (mCerts.end() != (it = mCerts.find(id))) + { + *cert = it->second; + return true; + } + return false; +} + + +XPGP *AuthXPGP::loadXPGPFromFile(std::string fname, std::string hash) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::LoadXPGPFromFile()"; + std::cerr << std::endl; +#endif + + // if there is a hash - check that the file matches it before loading. + XPGP *pc = NULL; + FILE *pcertfp = fopen(fname.c_str(), "rb"); + + // load certificates from file. + if (pcertfp == NULL) + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "sslroot::loadcertificate() Bad File: " << fname; + std::cerr << " Cannot be Hashed!" << std::endl; +#endif + return NULL; + } + + /* We only check a signature's hash if + * we are loading from a configuration file. + * Therefore we saved the file and it should be identical. + * and a direct load + verify will work. + * + * If however it has been transported by email.... + * Then we might have to correct the data (strip out crap) + * from the configuration at the end. (XPGP load should work!) + */ + + if (hash.length() > 1) + { + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + int maxsize = 20480; /* should be enough for about 50 signatures */ + int rbytes; + char inall[maxsize]; + if (0 == (rbytes = fread(inall, 1, maxsize, pcertfp))) + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "Error Reading Peer Record!" << std::endl; +#endif + return NULL; + } + //std::cerr << "Read " << rbytes << std::endl; + + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + } + + EVP_MD_CTX_destroy(mdctx); + + bool same = true; + if (signlen != hash.length()) + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "Different Length Signatures... "; + std::cerr << "Cannot Load Certificate!" << std::endl; +#endif + fclose(pcertfp); + return NULL; + } + + for(int i = 0; i < (signed) signlen; i++) + { + if (signature[i] != (unsigned char) hash[i]) + { + same = false; +#ifdef AUTHXPGP_DEBUG + std::cerr << "Invalid Signature... "; + std::cerr << "Cannot Load Certificate!" << std::endl; +#endif + fclose(pcertfp); + return NULL; + } + } +#ifdef AUTHXPGP_DEBUG + std::cerr << "Verified Signature for: " << fname; + std::cerr << std::endl; +#endif + } + else + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "Not checking cert signature" << std::endl; +#endif + } + + fseek(pcertfp, 0, SEEK_SET); /* rewind */ + pc = PEM_read_XPGP(pcertfp, NULL, NULL, NULL); + fclose(pcertfp); + + if (pc != NULL) + { + // read a certificate. +#ifdef AUTHXPGP_DEBUG + std::cerr << "Loaded Certificate: " << pc -> name << std::endl; +#endif + } + else // (pc == NULL) + { + unsigned long err = ERR_get_error(); + std::cerr << "Read Failed .... CODE(" << err << ")" << std::endl; + std::cerr << ERR_error_string(err, NULL) << std::endl; + + return NULL; + } + return pc; +} + +bool AuthXPGP::saveXPGPToFile(XPGP *xpgp, std::string fname, std::string &hash) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::saveXPGPToFile()"; + std::cerr << std::endl; +#endif + + // load certificates from file. + FILE *setfp = fopen(fname.c_str(), "wb"); + if (setfp == NULL) + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "sslroot::savecertificate() Bad File: " << fname; + std::cerr << " Cannot be Written!" << std::endl; +#endif + return false; + } + +#ifdef AUTHXPGP_DEBUG + std::cerr << "Writing out Cert...:" << xpgp->name << std::endl; +#endif + PEM_write_XPGP(setfp, xpgp); + + fclose(setfp); + + // then reopen to generate hash. + setfp = fopen(fname.c_str(), "rb"); + if (setfp == NULL) + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "sslroot::savecertificate() Bad File: " << fname; + std::cerr << " Opened for ReHash!" << std::endl; +#endif + return false; + } + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + int maxsize = 20480; + int rbytes; + char inall[maxsize]; + if (0 == (rbytes = fread(inall, 1, maxsize, setfp))) + { +#ifdef AUTHXPGP_DEBUG + std::cerr << "Error Writing Peer Record!" << std::endl; +#endif + return -1; + } +#ifdef AUTHXPGP_DEBUG + std::cerr << "Read " << rbytes << std::endl; +#endif + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + } + +#ifdef AUTHXPGP_DEBUG + std::cerr << "Saved Cert: " << xpgp->name; + std::cerr << std::endl; +#endif + +#ifdef AUTHXPGP_DEBUG + std::cerr << "Cert + Setting Signature is(" << signlen << "): "; +#endif + std::string signstr; + for(uint32_t i = 0; i < signlen; i++) + { +#ifdef AUTHXPGP_DEBUG + fprintf(stderr, "%02x", signature[i]); +#endif + signstr += signature[i]; + } +#ifdef AUTHXPGP_DEBUG + std::cerr << std::endl; +#endif + + hash = signstr; + fclose(setfp); + + EVP_MD_CTX_destroy(mdctx); + + return true; +} + + +XPGP *AuthXPGP::loadXPGPFromPEM(std::string pem) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::LoadXPGPFromPEM()"; + std::cerr << std::endl; +#endif + + /* Put the data into a mem BIO */ + char *certstr = strdup(pem.c_str()); + + BIO *bp = BIO_new_mem_buf(certstr, -1); + + XPGP *pc = PEM_read_bio_XPGP(bp, NULL, NULL, NULL); + + BIO_free(bp); + free(certstr); + + return pc; +} + +XPGP *AuthXPGP::loadXPGPFromDER(const uint8_t *ptr, uint32_t len) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::LoadXPGPFromDER()"; + std::cerr << std::endl; +#endif + + XPGP *tmp = NULL; + unsigned char **certptr = (unsigned char **) &ptr; + XPGP *xpgp = d2i_XPGP(&tmp, certptr, len); + + return xpgp; +} + +bool AuthXPGP::saveXPGPToDER(XPGP *xpgp, uint8_t **ptr, uint32_t *len) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::saveXPGPToDER()"; + std::cerr << std::endl; +#endif + + int certlen = i2d_XPGP(xpgp, (unsigned char **) ptr); + if (certlen > 0) + { + *len = certlen; + return true; + } + else + { + *len = 0; + return false; + } + return false; +} + + + + +bool AuthXPGP::ProcessXPGP(XPGP *xpgp, std::string &id) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::ProcessXPGP()"; + std::cerr << std::endl; +#endif + + /* extract id */ + std::string xpgpid; + + if (!XPGP_check_valid_certificate(xpgp)) + { + /* bad certificate */ + XPGP_free(xpgp); + return false; + } + + if (!getXPGPid(xpgp, xpgpid)) + { + /* bad certificate */ + XPGP_free(xpgp); + return false; + } + + xpgpcert *cert = NULL; + bool duplicate = false; + + xpgpMtx.lock(); /***** LOCK *****/ + + if (xpgpid == mOwnId) + { + cert = mOwnCert; + duplicate = true; + } + else if (locked_FindCert(xpgpid, &cert)) + { + duplicate = true; + } + + if (duplicate) + { + /* have a duplicate */ + /* check that they are exact */ + if (0 != XPGP_cmp(cert->certificate, xpgp)) + { + /* MAJOR ERROR */ + XPGP_free(xpgp); + xpgpMtx.unlock(); /**** UNLOCK ****/ + return false; + } + + /* transfer new signatures */ + XPGP_copy_known_signatures(pgp_keyring, cert->certificate, xpgp); + XPGP_free(xpgp); + + /* we accepted it! */ + id = xpgpid; + + /* update signers */ + cert->signers = getXPGPsigners(cert->certificate); + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return true; + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + /* if we get here -> its a new certificate */ + cert = new xpgpcert(xpgp, xpgpid); + + xpgpMtx.lock(); /***** LOCK *****/ + + /* add to keyring */ + XPGP_add_certificate(pgp_keyring, cert->certificate); + mCerts[xpgpid] = cert; + + cert -> trustLvl = XPGP_auth_certificate(pgp_keyring, cert->certificate); + if (cert -> trustLvl == TRUST_SIGN_TRSTED) + { + cert->trusted = true; + cert->ownsign = true; + } + else if (cert->trustLvl == TRUST_SIGN_OWN) + { + cert->ownsign = true; + } + + cert->signers = getXPGPsigners(xpgp); + + /* resave if new certificate */ + mToSaveCerts = true; + xpgpMtx.unlock(); /**** UNLOCK ****/ + +#if 0 + /******************** notify of new Cert **************************/ + pqiNotify *pqinotify = getPqiNotify(); + if (pqinotify) + { + pqinotify->AddFeedItem(RS_FEED_ITEM_PEER_NEW, xpgpid, "",""); + } + /******************** notify of new Cert **************************/ +#endif + + id = xpgpid; + + return true; +} + + +bool getXPGPid(XPGP *xpgp, std::string &xpgpid) +{ +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::getXPGPid()"; + std::cerr << std::endl; +#endif + + xpgpid = ""; + if (xpgp == NULL) + { +#ifdef XPGP_DEBUG + std::cerr << "AuthXPGP::getXPGPid() NULL pointer"; + std::cerr << std::endl; +#endif + return false; + } + + // get the first signature.... + if (sk_XPGP_SIGNATURE_num(xpgp->signs) < 1) + { +#ifdef XPGP_DEBUG + std::cerr << "AuthXPGP::getXPGPid() ERROR: No Signature"; + std::cerr << std::endl; +#endif + return false; + } + XPGP_SIGNATURE *xpgpsign = sk_XPGP_SIGNATURE_value(xpgp->signs, 0); + + // Validate that it is a self signature. + // (Already Done - but not in this function) + + // get the signature from the cert, and copy to the array. + ASN1_BIT_STRING *signature = xpgpsign->signature; + int signlen = ASN1_STRING_length(signature); + if (signlen < CERTSIGNLEN) + { +#ifdef XPGP_DEBUG + std::cerr << "AuthXPGP::getXPGPid() ERROR: Short Signature"; + std::cerr << std::endl; +#endif + return false; + } + + // else copy in the first CERTSIGNLEN. + unsigned char *signdata = ASN1_STRING_data(signature); + + std::ostringstream id; + for(uint32_t i = 0; i < CERTSIGNLEN; i++) + { + id << std::hex << std::setw(2) << std::setfill('0') + << (uint16_t) (((uint8_t *) (signdata))[i]); + } + xpgpid = id.str(); + return true; +} + + + + /* validate + get id */ +bool AuthXPGP::ValidateCertificateXPGP(XPGP *xpgp, std::string &peerId) +{ + /* check self signed */ + if (!XPGP_check_valid_certificate(xpgp)) + { + /* bad certificate */ + return false; + } + + return getXPGPid(xpgp, peerId); +} + +/* store for discovery */ +bool AuthXPGP::FailedCertificateXPGP(XPGP *xpgp, bool incoming) +{ + std::string id; + return ProcessXPGP(xpgp, id); +} + +/* check that they are exact match */ +bool AuthXPGP::CheckCertificateXPGP(std::string xpgpId, XPGP *xpgp) +{ + xpgpMtx.lock(); /***** LOCK *****/ + + xpgpcert *cert = NULL; + if (!locked_FindCert(xpgpId, &cert)) + { + /* not there -> error */ + XPGP_free(xpgp); + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return false; + } + else + { + /* have a duplicate */ + /* check that they are exact */ + if (0 != XPGP_cmp(cert->certificate, xpgp)) + { + /* MAJOR ERROR */ + XPGP_free(xpgp); + xpgpMtx.unlock(); /**** UNLOCK ****/ + return false; + } + + /* transfer new signatures */ + XPGP_copy_known_signatures(pgp_keyring, cert->certificate, xpgp); + XPGP_free(xpgp); + + /* update signers */ + cert->signers = getXPGPsigners(cert->certificate); + + xpgpMtx.unlock(); /**** UNLOCK ****/ + return true; + } +} + + + + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ + +int pem_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + strncpy(buf, (char *)(password), size); + buf[size - 1] = '\0'; + return(strlen(buf)); +} + +// Not dependent on sslroot. load, and detroys the XPGP memory. + +int LoadCheckXPGPandGetName(const char *cert_file, std::string &userName, std::string &userId) +{ + /* This function loads the XPGP certificate from the file, + * and checks the certificate + */ + + FILE *tmpfp = fopen(cert_file, "r"); + if (tmpfp == NULL) + { +#ifdef XPGP_DEBUG + std::cerr << "sslroot::LoadCheckAndGetXPGPName()"; + std::cerr << " Failed to open Certificate File:" << cert_file; + std::cerr << std::endl; +#endif + return 0; + } + + // get xPGP certificate. + XPGP *xpgp = PEM_read_XPGP(tmpfp, NULL, NULL, NULL); + fclose(tmpfp); + + // check the certificate. + bool valid = false; + if (xpgp) + { + valid = XPGP_check_valid_certificate(xpgp); + } + + if (valid) + { + // extract the name. + userName = getX509CNString(xpgp->subject->subject); + } + + if (!getXPGPid(xpgp, userId)) + { + valid = false; + } + + std::cout << getXPGPInfo(xpgp) << std::endl ; + // clean up. + XPGP_free(xpgp); + + if (valid) + { + // happy! + return 1; + } + else + { + // something went wrong! + return 0; + } +} + +std::string getX509NameString(X509_NAME *name) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + namestr += "\t"; + namestr += OBJ_nid2ln(OBJ_obj2nid(entry_obj)); + namestr += " : "; + + //namestr += entry_obj -> flags; + //namestr += entry_data -> length; + //namestr += entry_data -> type; + + //namestr += entry_data -> flags; + //entry -> set; + + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "NULL"; + } + + if (i + 1 < X509_NAME_entry_count(name)) + { + namestr += "\n"; + } + + } + return namestr; +} + + +std::string getX509CNString(X509_NAME *name) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + if (0 == strncmp("CN", OBJ_nid2sn(OBJ_obj2nid(entry_obj)), 2)) + { + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "Unknown"; + } + return namestr; + } + } + return namestr; +} + + +std::string getX509TypeString(X509_NAME *name, const char *type, int len) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + if (0 == strncmp(type, OBJ_nid2sn(OBJ_obj2nid(entry_obj)), len)) + { + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "Unknown"; + } + return namestr; + } + } + return namestr; +} + + +std::string getX509LocString(X509_NAME *name) +{ + return getX509TypeString(name, "L", 2); +} + +std::string getX509OrgString(X509_NAME *name) +{ + return getX509TypeString(name, "O", 2); +} + + +std::string getX509CountryString(X509_NAME *name) +{ + return getX509TypeString(name, "C", 2); +} + + +std::string getXPGPInfo(XPGP *cert) +{ + std::stringstream out; + long l; + int i,j; + + out << "XPGP Certificate:" << std::endl; + l=XPGP_get_version(cert); + out << " Version: " << l+1 << "(0x" << l << ")" << std::endl; + out << " Subject: " << std::endl; + out << " " << getX509NameString(cert -> subject -> subject); + out << std::endl; + out << std::endl; + out << " Signatures:" << std::endl; + + for(i = 0; i < sk_XPGP_SIGNATURE_num(cert->signs); i++) + { + out << "Sign[" << i << "] -> ["; + + XPGP_SIGNATURE *sig = sk_XPGP_SIGNATURE_value(cert->signs,i); + ASN1_BIT_STRING *signature = sig->signature; + int signlen = ASN1_STRING_length(signature); + unsigned char *signdata = ASN1_STRING_data(signature); + + /* only show the first 8 bytes */ + if (signlen > 8) + signlen = 8; + for(j=0;jissuer); + out << std::endl; + out << std::endl; + } + + return out.str(); +} + + + +std::string getXPGPAuthCode(XPGP *xpgp) +{ + /* get the self signature -> the first signature */ + + std::stringstream out; + if (1 > sk_XPGP_SIGNATURE_num(xpgp->signs)) + { + out.str(); + } + + XPGP_SIGNATURE *sig = sk_XPGP_SIGNATURE_value(xpgp->signs,0); + ASN1_BIT_STRING *signature = sig->signature; + int signlen = ASN1_STRING_length(signature); + unsigned char *signdata = ASN1_STRING_data(signature); + + /* extract the authcode from the signature */ + /* convert it to a string, inverse of 2 bytes of signdata */ + if (signlen > 2) + signlen = 2; + int j; + for(j=0;j getXPGPsigners(XPGP *cert) +{ + std::list signers; + int i; + + for(i = 0; i < sk_XPGP_SIGNATURE_num(cert->signs); i++) + { + XPGP_SIGNATURE *sig = sk_XPGP_SIGNATURE_value(cert->signs,i); + std::string str = getX509CNString(sig->issuer); + signers.push_back(str); +#ifdef XPGP_DEBUG + std::cerr << "XPGPsigners(" << i << ")" << str << std::endl; +#endif + } + return signers; +} + +// other fns +std::string getCertName(XPGP *xpgp) +{ + std::string name = xpgp->name; + // strip out bad chars. + for(int i = 0; i < (signed) name.length(); i++) + { + if ((name[i] == '/') || (name[i] == ' ') || (name[i] == '=') || + (name[i] == '\\') || (name[i] == '\t') || (name[i] == '\n')) + { + name[i] = '_'; + } + } + return name; +} + + +/********** SSL ERROR STUFF ******************************************/ + +int printSSLError(SSL *ssl, int retval, int err, unsigned long err2, + std::ostream &out) +{ + std::string reason; + + std::string mainreason = std::string("UNKNOWN ERROR CODE"); + if (err == SSL_ERROR_NONE) + { + mainreason = std::string("SSL_ERROR_NONE"); + } + else if (err == SSL_ERROR_ZERO_RETURN) + { + mainreason = std::string("SSL_ERROR_ZERO_RETURN"); + } + else if (err == SSL_ERROR_WANT_READ) + { + mainreason = std::string("SSL_ERROR_WANT_READ"); + } + else if (err == SSL_ERROR_WANT_WRITE) + { + mainreason = std::string("SSL_ERROR_WANT_WRITE"); + } + else if (err == SSL_ERROR_WANT_CONNECT) + { + mainreason = std::string("SSL_ERROR_WANT_CONNECT"); + } + else if (err == SSL_ERROR_WANT_ACCEPT) + { + mainreason = std::string("SSL_ERROR_WANT_ACCEPT"); + } + else if (err == SSL_ERROR_WANT_X509_LOOKUP) + { + mainreason = std::string("SSL_ERROR_WANT_X509_LOOKUP"); + } + else if (err == SSL_ERROR_SYSCALL) + { + mainreason = std::string("SSL_ERROR_SYSCALL"); + } + else if (err == SSL_ERROR_SSL) + { + mainreason = std::string("SSL_ERROR_SSL"); + } + out << "RetVal(" << retval; + out << ") -> SSL Error: " << mainreason << std::endl; + out << "\t + ERR Error: " << ERR_error_string(err2, NULL) << std::endl; + return 1; +} + + +/***************************** OLD STORAGE of CERTS ************************* + * We will retain the existing CERT storage format for the moment.... + * This will enable the existing certs to be loaded in. + * + * BUT Save will change the format - removing the options from + * the configuration file. This will mean that we can catch NEW/OLD formats. + * + * We only want to load old format ONCE. as we'll use it to get + * the list of existing friends... + * + * + * + */ + +bool AuthXPGP::FinalSaveCertificates() +{ + CheckSaveCertificates(); + + RsStackMutex stack(xpgpMtx); /***** LOCK *****/ + mConfigSaveActive = false; + return true; +} + +bool AuthXPGP::CheckSaveCertificates() +{ + xpgpMtx.lock(); /***** LOCK *****/ + + if ((mConfigSaveActive) && (mToSaveCerts)) + { + mToSaveCerts = false; + xpgpMtx.unlock(); /**** UNLOCK ****/ + + saveCertificates(); + return true; + } + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return false; +} + +bool AuthXPGP::saveCertificates() +{ + // construct file name. + // create the file in memory - hash + sign. + // write out data to a file. + + xpgpMtx.lock(); /***** LOCK *****/ + + std::string configfile = mCertConfigFile; + std::string neighdir = mNeighDir; + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + /* add on the slash */ + if (neighdir != "") + { + neighdir += "/"; + } + + std::map::iterator mit; + + std::string conftxt; + std::string empty(""); + unsigned int i; + +#ifdef AUTHXPGP_DEBUG + std::cerr << "AuthXPGP::saveCertificates()"; + std::cerr << std::endl; +#endif + xpgpMtx.lock(); /***** LOCK *****/ + + /* iterate through both lists */ + std::map::iterator it; + + for(it = mCerts.begin(); it != mCerts.end(); it++) + { + if (it->second->trustLvl > TRUST_SIGN_BASIC) + { + XPGP *xpgp = it->second->certificate; + std::string hash; + std::string neighfile = neighdir + getCertName(xpgp) + ".pqi"; + + if (saveXPGPToFile(xpgp, neighfile, hash)) + { + conftxt += "CERT "; + conftxt += getCertName(xpgp); + conftxt += "\n"; + conftxt += hash; + conftxt += "\n"; + } + } + } + + + // now work out signature of it all. This relies on the + // EVP library of openSSL..... We are going to use signing + // for the moment. + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + //OpenSSL_add_all_digests(); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { +#ifdef XPGP_DEBUG + std::cerr << "EVP_SignInit Failure!" << std::endl; +#endif + } + + if (0 == EVP_SignUpdate(mdctx, conftxt.c_str(), conftxt.length())) + { +#ifdef XPGP_DEBUG + std::cerr << "EVP_SignUpdate Failure!" << std::endl; +#endif + } + + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { +#ifdef XPGP_DEBUG + std::cerr << "EVP_SignFinal Failure!" << std::endl; +#endif + } + +#ifdef XPGP_DEBUG + std::cerr << "Conf Signature is(" << signlen << "): "; +#endif + for(i = 0; i < signlen; i++) + { +#ifdef XPGP_DEBUG + fprintf(stderr, "%02x", signature[i]); +#endif + conftxt += signature[i]; + } +#ifdef XPGP_DEBUG + std::cerr << std::endl; +#endif + + FILE *cfd = fopen(configfile.c_str(), "wb"); + int wrec; + if (1 != (wrec = fwrite(conftxt.c_str(), conftxt.length(), 1, cfd))) + { +#ifdef XPGP_DEBUG + std::cerr << "Error writing: " << configfile << std::endl; + std::cerr << "Wrote: " << wrec << "/" << 1 << " Records" << std::endl; +#endif + } + + EVP_MD_CTX_destroy(mdctx); + fclose(cfd); + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + return true; +} + + +/****** + * Special version for backwards compatibility + * + * has two extra parameters. + * bool oldFormat & std::map keyvaluemap + * + * We'll leave these in for the next couple of months... + * so that old versions will automatically be converted to the + * new format! + * + */ + +bool AuthXPGP::loadCertificates() +{ + bool oldFormat; + std::map keyValueMap; + + return loadCertificates(oldFormat, keyValueMap); +} + +/********************* + * NOTE no need to Lock here. locking handled in ProcessXPGP() + */ +static const uint32_t OPT_LEN = 16; +static const uint32_t VAL_LEN = 1000; + +bool AuthXPGP::loadCertificates(bool &oldFormat, std::map &keyValueMap) +{ + + /******************************************* + * open the configuration file. + * read in CERT + Hash. + * + * construct file name. + * create the file in memory - hash + sign. + * write out data to a file. + *****************************************/ + + xpgpMtx.lock(); /***** LOCK *****/ + + std::string configfile = mCertConfigFile; + std::string neighdir = mNeighDir; + + xpgpMtx.unlock(); /**** UNLOCK ****/ + + /* add on the slash */ + if (neighdir != "") + { + neighdir += "/"; + } + + oldFormat = false; + + std::string conftxt; + + unsigned int maxnamesize = 1024; + char name[maxnamesize]; + + int c; + unsigned int i; + + FILE *cfd = fopen(configfile.c_str(), "rb"); + if (cfd == NULL) + { +#ifdef XPGP_DEBUG + std::cerr << "Unable to Load Configuration File!" << std::endl; + std::cerr << "File: " << configfile << std::endl; +#endif + return false; + } + + std::list fnames; + std::list hashes; + std::map::iterator mit; + std::map tmpsettings; + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char conf_signature[signlen]; + char *ret = NULL; + + for(ret = fgets(name, maxnamesize, cfd); + ((ret != NULL) && (!strncmp(name, "CERT ", 5))); + ret = fgets(name, maxnamesize, cfd)) + { + for(i = 5; (name[i] != '\n') && (i < (unsigned) maxnamesize); i++); + + if (name[i] == '\n') + { + name[i] = '\0'; + } + + // so the name is first.... + std::string fname = &(name[5]); + + // now read the + std::string hash; + std::string signature; + + for(i = 0; i < signlen; i++) + { + if (EOF == (c = fgetc(cfd))) + { +#ifdef XPGP_DEBUG + std::cerr << "Error Reading Signature of: "; + std::cerr << fname; + std::cerr << std::endl; + std::cerr << "ABorting Load!"; + std::cerr << std::endl; +#endif + return -1; + } + unsigned char uc = (unsigned char) c; + signature += (unsigned char) uc; + } + if ('\n' != (c = fgetc(cfd))) + { +#ifdef XPGP_DEBUG + std::cerr << "Warning Mising seperator" << std::endl; +#endif + } + +#ifdef XPGP_DEBUG + std::cerr << "Read fname:" << fname << std::endl; + std::cerr << "Signature:" << std::endl; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", (unsigned char) signature[i]); + } + std::cerr << std::endl; + std::cerr << std::endl; +#endif + + // push back..... + fnames.push_back(fname); + hashes.push_back(signature); + + conftxt += "CERT "; + conftxt += fname; + conftxt += "\n"; + conftxt += signature; + conftxt += "\n"; + + // be sure to write over a bit... + name[0] = 'N'; + name[1] = 'O'; + } + + // string already waiting! + for(; ((ret != NULL) && (!strncmp(name, "OPT ", 4))); + ret = fgets(name, maxnamesize, cfd)) + { + for(i = 4; (name[i] != '\n') && (i < OPT_LEN); i++); + // terminate the string. + name[i] = '\0'; + + // so the name is first.... + std::string opt = &(name[4]); + + // now read the + std::string val; // cleaned up value. + std::string valsign; // value in the file. + for(i = 0; i < VAL_LEN; i++) + { + if (EOF == (c = fgetc(cfd))) + { +#ifdef XPGP_DEBUG + std::cerr << "Error Reading Value of: "; + std::cerr << opt; + std::cerr << std::endl; + std::cerr << "ABorting Load!"; + std::cerr << std::endl; +#endif + return -1; + } + // remove zeros on strings... + if (c != '\0') + { + val += (unsigned char) c; + } + valsign += (unsigned char) c; + } + if ('\n' != (c = fgetc(cfd))) + { +#ifdef XPGP_DEBUG + std::cerr << "Warning Mising seperator" << std::endl; +#endif + } + +#ifdef XPGP_DEBUG + std::cerr << "Read OPT:" << opt; + std::cerr << " Val:" << val << std::endl; +#endif + + // push back..... + tmpsettings[opt] = val; + + conftxt += "OPT "; + conftxt += opt; + conftxt += "\n"; + conftxt += valsign; + conftxt += "\n"; + + // be sure to write over a bit... + name[0] = 'N'; + name[1] = 'O'; + } + + // only read up to the first newline symbol.... + // continue... + for(i = 0; (name[i] != '\n') && (i < signlen); i++); + + if (i != signlen) + { + for(i++; i < signlen; i++) + { + c = fgetc(cfd); + if (c == EOF) + { +#ifdef XPGP_DEBUG + std::cerr << "Error Reading Conf Signature:"; + std::cerr << std::endl; +#endif + return 1; + } + unsigned char uc = (unsigned char) c; + name[i] = uc; + } + } + +#ifdef XPGP_DEBUG + std::cerr << "Configuration File Signature: " << std::endl; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", (unsigned char) name[i]); + } + std::cerr << std::endl; +#endif + + + // when we get here - should have the final signature in the buffer. + // check. + // + // compare signatures. + // instead of verifying with the public key.... + // we'll sign it again - and compare .... FIX LATER... + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit(mdctx, EVP_sha1())) + { +#ifdef XPGP_DEBUG +#endif + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, conftxt.c_str(), conftxt.length())) + { +#ifdef XPGP_DEBUG + std::cerr << "EVP_SignUpdate Failure!" << std::endl; +#endif + } + + if (0 == EVP_SignFinal(mdctx, conf_signature, &signlen, pkey)) + { +#ifdef XPGP_DEBUG + std::cerr << "EVP_SignFinal Failure!" << std::endl; +#endif + } + + EVP_MD_CTX_destroy(mdctx); + fclose(cfd); + +#ifdef XPGP_DEBUG + std::cerr << "Recalced File Signature: " << std::endl; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", conf_signature[i]); + } + std::cerr << std::endl; +#endif + + bool same = true; + for(i = 0; i < signlen; i++) + { + if ((unsigned char) name[i] != conf_signature[i]) + { + same = false; + } + } + + if (same == false) + { +#ifdef XPGP_DEBUG + std::cerr << "ERROR VALIDATING CONFIGURATION!" << std::endl; + std::cerr << "PLEASE FIX!" << std::endl; +#endif + return false; + } + + std::list::iterator it; + std::list::iterator it2; + for(it = fnames.begin(), it2 = hashes.begin(); it != fnames.end(); it++, it2++) + { + std::string neighfile = neighdir + (*it) + ".pqi"; + XPGP *xpgp = loadXPGPFromFile(neighfile, (*it2)); + if (xpgp != NULL) + { + std::string id; + if (ProcessXPGP(xpgp, id)) + { +#ifdef XPGP_DEBUG + std::cerr << "Loaded Certificate: " << id; + std::cerr << std::endl; +#endif + } + } + } + for(mit = tmpsettings.begin(); mit != tmpsettings.end(); mit++) + { + keyValueMap[mit -> first] = mit -> second; + } + + mToSaveCerts = false; + + if (keyValueMap.size() > 0) + { + oldFormat = true; + mToSaveCerts = true; + } + + return true; +} + diff --git a/libretroshare/src/_pqi/authxpgp.h b/libretroshare/src/_pqi/authxpgp.h new file mode 100644 index 000000000..15ba11111 --- /dev/null +++ b/libretroshare/src/_pqi/authxpgp.h @@ -0,0 +1,211 @@ +/* + * libretroshare/src/pqi: authxpgp.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + +#ifndef MRK_AUTH_SSL_XPGP_HEADER +#define MRK_AUTH_SSL_XPGP_HEADER + +/* This is the trial XPGP version + * + * It has to be compiled against XPGP ssl version. + * this is only a hacked up version, merging + * (so both can operate in parallel will happen later) + * + */ + +#include +#include + +#include +#include + +#include "util/rsthreads.h" + +#include "pqi/pqi_base.h" +#include "pqi/pqinetwork.h" +#include "pqi/p3authmgr.h" + +class AuthXPGP; + + +class xpgpcert +{ +public: + xpgpcert(XPGP *xpgp, std::string id); + + /* certificate parameters */ + std::string id; + std::string name; + std::string location; + std::string org; + std::string email; + + std::string fpr; + std::list signers; + + /* Auth settings */ + + uint32_t trustLvl; + bool ownsign; + bool trusted; + + /* INTERNAL Parameters */ + XPGP *certificate; +}; + +class AuthXPGP: public p3AuthMgr +{ + public: + + /* Initialisation Functions (Unique) */ + AuthXPGP(); +virtual bool active(); +virtual int InitAuth(const char *srvr_cert, const char *priv_key, + const char *passwd); +virtual bool CloseAuth(); +virtual int setConfigDirectories(std::string confFile, std::string neighDir); + + /*********** Overloaded Functions from p3AuthMgr **********/ + + /* get Certificate Ids */ + +virtual std::string OwnId(); +virtual bool getAllList(std::list &ids); +virtual bool getAuthenticatedList(std::list &ids); +virtual bool getUnknownList(std::list &ids); + + /* get Details from the Certificates */ + +virtual bool isValid(std::string id); +virtual bool isAuthenticated(std::string id); +virtual std::string getName(std::string id); +virtual bool getDetails(std::string id, pqiAuthDetails &details); + + /* first party trust info */ +virtual bool isTrustingMe(std::string id) ; +virtual void addTrustingPeer(std::string id) ; + + /* High Level Load/Save Configuration */ +virtual bool FinalSaveCertificates(); +virtual bool CheckSaveCertificates(); +virtual bool saveCertificates(); +virtual bool loadCertificates(); + + /* Load/Save certificates */ +virtual bool LoadCertificateFromString(std::string pem, std::string &id); +virtual std::string SaveCertificateToString(std::string id); +virtual bool LoadCertificateFromFile(std::string filename, std::string &id); +virtual bool SaveCertificateToFile(std::string id, std::string filename); + +virtual bool LoadCertificateFromBinary(const uint8_t *ptr, uint32_t len, std::string &id); +virtual bool SaveCertificateToBinary(std::string id, uint8_t **ptr, uint32_t *len); + + /* Signatures */ + +virtual bool AuthCertificate(std::string uid); +virtual bool SignCertificate(std::string id); +virtual bool RevokeCertificate(std::string id); +virtual bool TrustCertificate(std::string id, bool trust); + + /* Sign / Encrypt / Verify Data (TODO) */ +virtual bool SignData(std::string input, std::string &sign); +virtual bool SignData(const void *data, const uint32_t len, std::string &sign); + /* for proper signatures! */ +virtual bool SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen); +virtual bool SignDataBin(const void *data, const uint32_t len, + unsigned char *sign, unsigned int *signlen); + +virtual bool VerifySignBin(std::string pid, + const void *data, const uint32_t len, + unsigned char *sign, unsigned int signlen); + + /*********** Overloaded Functions from p3AuthMgr **********/ + + public: /* XPGP specific functions used in pqissl/pqissllistener */ +SSL_CTX *getCTX(); + +bool ValidateCertificateXPGP(XPGP *xpgp, std::string &peerId); /* validate + get id */ +bool FailedCertificateXPGP(XPGP *xpgp, bool incoming); /* store for discovery */ +bool CheckCertificateXPGP(std::string peerId, XPGP *xpgp); /* check that they are exact match */ + + /* Special Config Loading (backwards compatibility) */ +bool loadCertificates(bool &oldFormat, std::map &keyValueMap); + + private: + + /* Helper Functions */ + +bool ProcessXPGP(XPGP *xpgp, std::string &id); + +XPGP * loadXPGPFromPEM(std::string pem); +XPGP * loadXPGPFromFile(std::string fname, std::string hash); +bool saveXPGPToFile(XPGP *xpgp, std::string fname, std::string &hash); + +XPGP * loadXPGPFromDER(const uint8_t *ptr, uint32_t len); +bool saveXPGPToDER(XPGP *xpgp, uint8_t **ptr, uint32_t *len); + + /*********** LOCKED Functions ******/ +bool locked_FindCert(std::string id, xpgpcert **cert); + + + /* Data */ + RsMutex xpgpMtx; /**** LOCKING */ + + int init; + std::string mCertConfigFile; + std::string mNeighDir; + + SSL_CTX *sslctx; + XPGP_KEYRING *pgp_keyring; + + std::string mOwnId; + xpgpcert *mOwnCert; + EVP_PKEY *pkey; + + bool mToSaveCerts; + bool mConfigSaveActive; + std::map mCerts; + + std::list _trusting_peers ; +}; + +/* Helper Functions */ +int printSSLError(SSL *ssl, int retval, int err, unsigned long err2, std::ostream &out); +std::string getX509NameString(X509_NAME *name); +std::string getX509CNString(X509_NAME *name); + +std::string getX509OrgString(X509_NAME *name); +std::string getX509LocString(X509_NAME *name); +std::string getX509CountryString(X509_NAME *name); + +std::list getXPGPsigners(XPGP *cert); +std::string getXPGPInfo(XPGP *cert); +std::string getXPGPAuthCode(XPGP *xpgp); + +int LoadCheckXPGPandGetName(const char *cert_file, + std::string &userName, std::string &userId); +bool getXPGPid(XPGP *xpgp, std::string &xpgpid); + + +#endif // MRK_SSL_XPGP_CERT_HEADER diff --git a/libretroshare/src/_pqi/cleanupxpgp.cc b/libretroshare/src/_pqi/cleanupxpgp.cc new file mode 100644 index 000000000..22f98086b --- /dev/null +++ b/libretroshare/src/_pqi/cleanupxpgp.cc @@ -0,0 +1,371 @@ +/* + * libretroshare/src/pqi: cleanupxpgp.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2008 by Sourashis Roy + * + * 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 "cleanupxpgp.h" + + +/* +Method for cleaning up the certificate. This method removes any unnecessay white spaces and unnecessary +new line characters in the certificate. Also it makes sure that there are 64 characters per line in +the certificate. This function removes white spaces and new line characters in the entire segment +-----BEGIN XPGP CERTIFICATE----- + +-----END XPGP CERTIFICATE----- +We also take care of correcting cases like ----- BEGIN. Here extra empty spaces +have been introduced between ----- and BEGIN. Similarly for the +end tag we take care of cases like ----- END XPGP . Here extra empty spaces have been +introduced and the actual tag should have been -----END XPGP +*/ + +std::string cleanUpCertificate(std::string badCertificate) +{ + /* + Buffer for storing the cleaned certificate. In certain cases the + cleanCertificate can be larger than the badCertificate + */ + char * cleanCertificate=new char[badCertificate.length()+100]; + //The entire certificate begin tag + char * beginCertTag="-----BEGIN"; + //The entire certificate end tag + char * endCertTag="-----END"; + //Tag containing dots. The common part of both start and end tags + char * commonTag="-----"; + //Only BEGIN part of the begin tag + char * beginTag="BEGIN"; + //Only END part of the end tag + char * endTag="END"; + //The start index of the ----- part of the certificate begin tag + int beginCertStartIdx1=0; + //The start index of the BEGIN part of the certificate begin tag + int beginCertStartIdx2=0; + //The start index of the end part(-----) of the certificate begin tag. The begin tag ends with -----. Example -----BEGIN XPGP CERTIFICATE----- + int beginCertEndIdx=0; + //The start index of the ----- part of the certificate end tag + int endCertStartIdx1=0; + //The start index of the END part of the certificate end tag + int endCertStartIdx2=0; + //The start index of the end part(-----) of the certificate end tag. The begin tag ends with -----. Example -----BEGIN XPGP CERTIFICATE----- + int endCertEndIdx=0; + //The length of the bad certificate. + int lengthOfCert=badCertificate.length(); + //The current index value in the cleaned certificate. + int currCleanCertIdx=0; + //The current index value in the bad certificate + int currBadCertIdx=0; + //Temporary index value + int tmpIdx=0; + //Boolean flag showing if the begin tag or the end tag has been found + bool found=false; + /* + Calculating the value of the beginCertStartIdx1 and beginCertStartIdx2. Here we first locate the occurance of ----- and then + the location of BEGIN. Next we check if there are any non space or non new-line characters between their occureance. If there are any other + characters between the two(----- and BEGIN), other than space and new line then it means that it is the certificate begin tag. + Here we take care of the fact that we may have introduced some spaces and newlines in the begin tag by mistake. This + takes care of the spaces and newlines between ----- and BEGIN. + */ + + while(found==false && (beginCertStartIdx1=badCertificate.find(commonTag,tmpIdx))!=std::string::npos) + { + beginCertStartIdx2=badCertificate.find(beginTag,beginCertStartIdx1+strlen(commonTag)); + tmpIdx=beginCertStartIdx1+strlen(commonTag); + if(beginCertStartIdx2!=std::string::npos) + { + found=true; + for(int i=beginCertStartIdx1+strlen(commonTag);i tag"< tag"< tag"<=lengthOfCert) + { + std::cerr<<"Certificate corrupted beyond repair: No <------END > tag"< +#include +#include //strlen + +//! Converts invitation to clean XPGP certificate + +//! This function was used for extracting XPGP certificates from invitation +//!letters. Typical input was something like +//! You have been invited to join the retroshare community by: beardog-unstable-02 +//! +//! Retroshare is a Friend-2-Friend network that enables you to communicate securely and privately .... +//! ... text stuff ..... +//! +//!-----BEGIN XPGP CERTIFICATE----- +//!MIICxQIBADCCAUkCAQAwHhcNMDkwMjI4MTgzODIyWhcNMTQwMjI3MTgzODIyWjCC +//! ...more ines here... +//!mEuhG8UmDIzC1jeTu8rTMnO+DO3FH/cek1vlfFl4t9g/xktG9U4SPLg= +//!-----END XPGP CERTIFICATE----- +//! +//! In the newer gui version, users send each other almost clean certificates, +//! so this functon is used only to avoid possible bugs with line endings + +std::string cleanUpCertificate(std::string badCertificate); + +int findEndIdxOfCertStartTag(std::string badCertificate); + +#endif // CLEANUPPXPGP_H diff --git a/libretroshare/src/_pqi/p3authmgr.cc b/libretroshare/src/_pqi/p3authmgr.cc new file mode 100644 index 000000000..088415ac7 --- /dev/null +++ b/libretroshare/src/_pqi/p3authmgr.cc @@ -0,0 +1,302 @@ +/* + * libretroshare/src/pqi: p3authmgr.cc + * + * 3P/PQI network interface for RetroShare. + * + * 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 "_pqi/p3authmgr.h" + +pqiAuthDetails::pqiAuthDetails() + :trustLvl(0), validLvl(0), ownsign(false), trusted(false) +{ + return; +} + +p3DummyAuthMgr::p3DummyAuthMgr() +{ + /* for the truely dummy option */ + mOwnId = "OWNID"; + + pqiAuthDetails ownDetails; + ownDetails.id = mOwnId; + ownDetails.name = "Youself"; + ownDetails.email = "me@me.com"; + ownDetails.location = "here"; + ownDetails.org = "me.com"; + + ownDetails.trustLvl = 6; + ownDetails.ownsign = true; + ownDetails.trusted = true; + + /* ignoring fpr and signers */ + + mPeerList[mOwnId] = ownDetails; + +} + +bool p3DummyAuthMgr::isTrustingMe(std::string id) +{ + std::cerr << "isTrustingMe is not implemented in p3DummyAuthMgr. Look into authxpgp.cc." << std::endl ; + return false ; +} +void p3DummyAuthMgr::addTrustingPeer(std::string id) +{ + std::cerr << "addTrustingPeer is not implemented in p3DummyAuthMgr. Look into authxpgp.cc." << std::endl ; +} + +p3DummyAuthMgr::p3DummyAuthMgr(std::string ownId, std::list peers) +{ + mOwnId = ownId; + bool addedOwn = false; + + std::list::iterator it; + for(it = peers.begin(); it != peers.end(); it++) + { + mPeerList[it->id] = (*it); + if (it->id == ownId) + { + addedOwn = true; + } + } + if (!addedOwn) + { + pqiAuthDetails ownDetails; + ownDetails.id = mOwnId; + ownDetails.name = "Youself"; + ownDetails.email = "me@me.com"; + ownDetails.location = "here"; + ownDetails.org = "me.com"; + + ownDetails.trustLvl = 6; + ownDetails.ownsign = true; + ownDetails.trusted = true; + + /* ignoring fpr and signers */ + + mPeerList[mOwnId] = ownDetails; + } +} + +bool p3DummyAuthMgr:: active() +{ + return true; +} + +int p3DummyAuthMgr::InitAuth(const char *srvr_cert, const char *priv_key, + const char *passwd) +{ + return 1; +} + +bool p3DummyAuthMgr::CloseAuth() +{ + return true; +} + +int p3DummyAuthMgr::setConfigDirectories(std::string confFile, std::string neighDir) +{ + return 1; +} + +std::string p3DummyAuthMgr::OwnId() +{ + return mOwnId; +} + +bool p3DummyAuthMgr::getAllList(std::list &ids) +{ + std::map::iterator it; + for(it = mPeerList.begin(); it != mPeerList.end(); it++) + { + ids.push_back(it->first); + } + return true; +} + +bool p3DummyAuthMgr::getAuthenticatedList(std::list &ids) +{ + std::map::iterator it; + for(it = mPeerList.begin(); it != mPeerList.end(); it++) + { + if (it->second.trustLvl > 3) + { + ids.push_back(it->first); + } + } + return true; +} + +bool p3DummyAuthMgr::getUnknownList(std::list &ids) +{ + std::map::iterator it; + for(it = mPeerList.begin(); it != mPeerList.end(); it++) + { + if (it->second.trustLvl <= 3) + { + ids.push_back(it->first); + } + } + return true; +} + +bool p3DummyAuthMgr::isValid(std::string id) +{ + std::map::iterator it; + return (mPeerList.end() != mPeerList.find(id)); +} + + +bool p3DummyAuthMgr::isAuthenticated(std::string id) +{ + std::map::iterator it; + if (mPeerList.end() != (it = mPeerList.find(id))) + { + return (it->second.trustLvl > 3); + } + return false; +} + +std::string p3DummyAuthMgr::getName(std::string id) +{ + std::map::iterator it; + if (mPeerList.end() != (it = mPeerList.find(id))) + { + return it->second.name; + } + std::string empty(""); + return empty; +} + +bool p3DummyAuthMgr::getDetails(std::string id, pqiAuthDetails &details) +{ + std::map::iterator it; + if (mPeerList.end() != (it = mPeerList.find(id))) + { + details = it->second; + return true; + } + return false; +} + +bool p3DummyAuthMgr::FinalSaveCertificates() +{ + return false; +} + +bool p3DummyAuthMgr::CheckSaveCertificates() +{ + return false; +} + +bool p3DummyAuthMgr::saveCertificates() +{ + return false; +} + +bool p3DummyAuthMgr::loadCertificates() +{ + return false; +} + +bool p3DummyAuthMgr::LoadCertificateFromString(std::string pem, std::string &id) +{ + return false; +} + +std::string p3DummyAuthMgr::SaveCertificateToString(std::string id) +{ + std::string dummy("CERT STRING"); + return dummy; +} + +bool p3DummyAuthMgr::LoadCertificateFromFile(std::string filename, std::string &id) +{ + return false; +} + +bool p3DummyAuthMgr::SaveCertificateToFile(std::string id, std::string filename) +{ + return false; +} +bool p3DummyAuthMgr::LoadCertificateFromBinary(const uint8_t *ptr, uint32_t len, std::string &id) +{ + return false; +} + +bool p3DummyAuthMgr::SaveCertificateToBinary(std::string id, uint8_t **ptr, uint32_t *len) +{ + return false; +} + + /* Signatures */ +bool p3DummyAuthMgr::AuthCertificate(std::string id) +{ + return false; +} + +bool p3DummyAuthMgr::SignCertificate(std::string id) +{ + return false; +} + +bool p3DummyAuthMgr::RevokeCertificate(std::string id) +{ + return false; +} + +bool p3DummyAuthMgr::TrustCertificate(std::string id, bool trust) +{ + return false; +} + +bool p3DummyAuthMgr::SignData(std::string input, std::string &sign) +{ + return false; +} + +bool p3DummyAuthMgr::SignData(const void *data, const uint32_t len, std::string &sign) +{ + return false; +} + +bool p3DummyAuthMgr::SignDataBin(std::string input, + unsigned char *sign, unsigned int *signlen) +{ + return false; +} + +bool p3DummyAuthMgr::SignDataBin(const void *data, const uint32_t len, + unsigned char *sign, unsigned int *signlen) +{ + return false; +} + +bool p3DummyAuthMgr::VerifySignBin(std::string pid, + const void *data, const uint32_t len, + unsigned char *sign, unsigned int signlen) +{ + return false; +} + + diff --git a/libretroshare/src/_pqi/p3authmgr.h b/libretroshare/src/_pqi/p3authmgr.h new file mode 100644 index 000000000..b41c95ae2 --- /dev/null +++ b/libretroshare/src/_pqi/p3authmgr.h @@ -0,0 +1,222 @@ +/* + * libretroshare/src/pqi: p3authmgr.h + * + * 3P/PQI network interface for RetroShare. + * + * 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". + * + */ + +#ifndef RS_GENERIC_AUTH_HEADER +#define RS_GENERIC_AUTH_HEADER + +#include +#include +#include + +/************** GENERIC AUTHENTICATION MANAGER *********** + * Provides a common interface for certificates. + * + * Initialisation must be done in derived classes + * + * Key features: + * everything indexed by std::string id; + * has auth perspective: authed / not authed - different to friends. + * load/save certificates as strings or files. + * + */ + +class p3AuthMgr; +extern p3AuthMgr *authMgr; + +p3AuthMgr *getAuthMgr(); + +class pqiAuthDetails +{ + public: + pqiAuthDetails(); + + std::string id; + std::string name; + std::string email; + std::string location; + std::string org; + + std::string issuer; + + std::string fpr; /* fingerprint */ + std::list signers; + + uint32_t trustLvl; + uint32_t validLvl; + + bool ownsign; + bool trusted; // means valid in pgp world. +}; + + +class p3AuthMgr +{ + public: + +virtual ~p3AuthMgr() { return; } + + /* initialisation -> done by derived classes */ +virtual bool active() = 0; +virtual int InitAuth(const char *srvr_cert, const char *priv_key, + const char *passwd) = 0; +virtual bool CloseAuth() = 0; +virtual int setConfigDirectories(std::string confFile, std::string neighDir) = 0; + + /* get Certificate Ids */ + +virtual std::string OwnId() = 0; +virtual bool getAllList(std::list &ids) = 0; +virtual bool getAuthenticatedList(std::list &ids) = 0; +virtual bool getUnknownList(std::list &ids) = 0; + + /* get Details from the Certificates */ + +virtual bool isValid(std::string id) = 0; +virtual bool isAuthenticated(std::string id) = 0; +virtual std::string getName(std::string id) = 0; +virtual std::string getIssuerName(std::string id) { return getName(id); } // Default to same id. +virtual bool getDetails(std::string id, pqiAuthDetails &details) = 0; + + /* High Level Load/Save Configuration */ +virtual bool FinalSaveCertificates() = 0; +virtual bool CheckSaveCertificates() = 0; +virtual bool saveCertificates() = 0; +virtual bool loadCertificates() = 0; + + /* first party trust info */ +virtual bool isTrustingMe(std::string id) = 0; +virtual void addTrustingPeer(std::string id) = 0; + + /* Extra Fns for PGP, call std versions if not overloaded */ +virtual std::string PGPOwnId() { return OwnId(); } +virtual bool getPGPAllList(std::list &ids) { return getAllList(ids); }; + + /* Load/Save certificates */ + +virtual bool LoadCertificateFromString(std::string pem, std::string &id) = 0; +virtual std::string SaveCertificateToString(std::string id) = 0; +virtual bool LoadCertificateFromFile(std::string filename, std::string &id) = 0; +virtual bool SaveCertificateToFile(std::string id, std::string filename) = 0; + + /* specific OpenSSL ones -> careful with pointers.... + * save will allocate space, + */ +virtual bool LoadCertificateFromBinary(const uint8_t *ptr, uint32_t len, std::string &id) = 0; +virtual bool SaveCertificateToBinary(std::string id, uint8_t **ptr, uint32_t *len) = 0; + + /* Signatures */ +virtual bool AuthCertificate(std::string uid) = 0; +virtual bool SignCertificate(std::string id) = 0; +virtual bool RevokeCertificate(std::string id) = 0; +virtual bool TrustCertificate(std::string id, bool trust) = 0; + + /* Sign / Encrypt / Verify Data (TODO) */ +virtual bool SignData(std::string input, std::string &sign) = 0; +virtual bool SignData(const void *data, const uint32_t len, std::string &sign) = 0; +virtual bool SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen) = 0; +virtual bool SignDataBin(const void *data, const uint32_t len, + unsigned char *sign, unsigned int *signlen) = 0; + +virtual bool VerifySignBin(std::string pid, + const void *data, const uint32_t len, + unsigned char *sign, unsigned int signlen) = 0; + +//virtual bool encryptData(std::string recipientId, std::string plaindata, std::string &result); + +}; + + +class p3DummyAuthMgr: public p3AuthMgr +{ + public: + + p3DummyAuthMgr(); + p3DummyAuthMgr(std::string ownId, std::list peers); + + /* initialisation -> done by derived classes */ +virtual bool active(); +virtual int InitAuth(const char *srvr_cert, const char *priv_key, + const char *passwd); +virtual bool CloseAuth(); +virtual int setConfigDirectories(std::string confFile, std::string neighDir); + + /* get Certificate Ids */ + +virtual std::string OwnId(); +virtual bool getAllList(std::list &ids); +virtual bool getAuthenticatedList(std::list &ids); +virtual bool getUnknownList(std::list &ids); + + /* get Details from the Certificates */ + +virtual bool isValid(std::string id); +virtual bool isAuthenticated(std::string id); +virtual std::string getName(std::string id); +virtual bool getDetails(std::string id, pqiAuthDetails &details); + + /* High Level Load/Save Configuration */ +virtual bool FinalSaveCertificates(); +virtual bool CheckSaveCertificates(); +virtual bool saveCertificates(); +virtual bool loadCertificates(); + + /* first party trust info */ +virtual bool isTrustingMe(std::string id) ; +virtual void addTrustingPeer(std::string id) ; + + /* Load/Save certificates */ +virtual bool LoadCertificateFromString(std::string pem, std::string &id); +virtual std::string SaveCertificateToString(std::string id); +virtual bool LoadCertificateFromFile(std::string filename, std::string &id); +virtual bool SaveCertificateToFile(std::string id, std::string filename); + +virtual bool LoadCertificateFromBinary(const uint8_t *ptr, uint32_t len, std::string &id); +virtual bool SaveCertificateToBinary(std::string id, uint8_t **ptr, uint32_t *len); + /* Signatures */ + +virtual bool AuthCertificate(std::string uid); +virtual bool SignCertificate(std::string id); +virtual bool RevokeCertificate(std::string id); +virtual bool TrustCertificate(std::string id, bool trust); + +virtual bool SignData(std::string input, std::string &sign); +virtual bool SignData(const void *data, const uint32_t len, std::string &sign); +virtual bool SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen); +virtual bool SignDataBin(const void *data, const uint32_t len, + unsigned char *sign, unsigned int *signlen); + +virtual bool VerifySignBin(std::string pid, + const void *data, const uint32_t len, + unsigned char *sign, unsigned int signlen); + + std::string mOwnId; + std::map mPeerList; +}; + +#endif + + + + diff --git a/libretroshare/src/_pqi/p3cfgmgr.cc b/libretroshare/src/_pqi/p3cfgmgr.cc new file mode 100644 index 000000000..6985f1a8c --- /dev/null +++ b/libretroshare/src/_pqi/p3cfgmgr.cc @@ -0,0 +1,714 @@ +/* + * libretroshare/src/pqi: p3cfgmgr.cc + * + * 3P/PQI network interface for RetroShare. + * + * 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 "util/rsdir.h" +#include "rsiface/rspeers.h" +#include "pqi/p3cfgmgr.h" +#include "pqi/p3authmgr.h" +#include "pqi/pqibin.h" +#include "pqi/pqistore.h" +#include "pqi/pqinotify.h" +#include + +#include "serialiser/rsconfigitems.h" + +/**** + * #define CONFIG_DEBUG 1 + ***/ + +p3ConfigMgr::p3ConfigMgr(p3AuthMgr *am, std::string dir, std::string fname, std::string signame) + :mAuthMgr(am), basedir(dir), metafname(fname), metasigfname(signame), + mConfigSaveActive(true) +{ + + +} + +void p3ConfigMgr::tick() +{ + bool toSave = false; + + { + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + + /* iterate through and check if any have changed */ + std::map::iterator it; + for(it = configs.begin(); it != configs.end(); it++) + { + if (it->second->HasConfigChanged(0)) + { + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::tick() Config Changed - Element: "; + std::cerr << it->first; + std::cerr << std::endl; +#endif + + toSave = true; + } + } + + /* disable saving before exit */ + if (!mConfigSaveActive) + { + toSave = false; + } + } + + if (toSave) + { + saveConfiguration(); + } +} + + +void p3ConfigMgr::saveConfiguration() +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::saveConfiguration()"; + std::cerr << std::endl; +#endif + + RsConfigKeyValueSet *item = new RsConfigKeyValueSet(); + + std::map::iterator it; + for(it = configs.begin(); it != configs.end(); it++) + { + if (it->second->HasConfigChanged(1)) + { +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::saveConfiguration() Saving Element: "; + std::cerr << it->first; + std::cerr << std::endl; +#endif + it->second->saveConfiguration(); + } + /* save metaconfig */ + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::saveConfiguration() Element: "; + std::cerr << it->first << " Hash: " << it->second->Hash(); + std::cerr << std::endl; +#endif + if (it->second->Hash() == "") + { + /* skip if no hash */ + continue; + } + + RsTlvKeyValue kv; + { + std::ostringstream out; + out << it->first; + kv.key = out.str(); + } + kv.value = it->second->Hash(); + item->tlvkvs.pairs.push_back(kv); + } + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::saveConfiguration() Complete MetaConfigItem: "; + std::cerr << std::endl; + item->print(std::cerr, 20); + +#endif + /* construct filename */ + std::string filename1 = basedir; + std::string filename2 = basedir; + if (basedir != "") + { + filename1 += "/"; + filename2 += "/"; + } + filename1 += metasigfname; + filename2 += metafname; + + /* Write the data to a stream */ + uint32_t bioflags = BIN_FLAGS_WRITEABLE; + BinMemInterface *membio = new BinMemInterface(1000, bioflags); + RsSerialiser *rss = new RsSerialiser(); + rss->addSerialType(new RsGeneralConfigSerialiser()); + pqistore store(rss, "CONFIG", membio, BIN_FLAGS_WRITEABLE); + + store.SendItem(item); + + /* sign data */ + std::string signature; + mAuthMgr->SignData(membio->memptr(), membio->memsize(), signature); + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::saveConfiguration() MetaFile Signature:"; + std::cerr << std::endl; + std::cerr << signature; + std::cerr << std::endl; +#endif + + if (!membio->writetofile(filename2.c_str())) + { +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::saveConfiguration() Failed to Write MetaFile " << filename2 ; + std::cerr << std::endl; +#endif + } + + + /* write signature to configuration */ + BinMemInterface *signbio = new BinMemInterface(signature.c_str(), + signature.length(), BIN_FLAGS_READABLE); + + if (!signbio->writetofile(filename1.c_str())) + { +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::saveConfiguration() Failed to Write MetaSignFile" << filename1 ; + std::cerr << std::endl; +#endif + } + + delete signbio; + + +} + +void p3ConfigMgr::loadConfiguration() +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration()"; + std::cerr << std::endl; +#endif + + /* construct filename */ + std::string filename1 = basedir; + std::string filename2 = basedir; + if (basedir != "") + { + filename1 += "/"; + filename2 += "/"; + } + filename1 += metasigfname; + filename2 += metafname; + + /* write signature to configuration */ + BinMemInterface *signbio = new BinMemInterface(1000, BIN_FLAGS_READABLE); + + if (!signbio->readfromfile(filename1.c_str())) + { +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() Failed to Load MetaSignFile"; + std::cerr << std::endl; +#endif + + /* HACK to load the old one (with the wrong directory) + * THIS SHOULD BE REMOVED IN A COUPLE OF VERSIONS.... + * ONLY HERE TO CORRECT BAD MISTAKE IN EARLIER VERSIONS. + */ + + filename1 = metasigfname; + filename2 = metafname; + + if (!signbio->readfromfile(filename1.c_str())) + { +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() HACK: Failed to Load ALT MetaSignFile"; + std::cerr << std::endl; +#endif + } + else + { +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() HACK: Loaded ALT MetaSignFile"; + std::cerr << std::endl; +#endif + } + + } + + std::string oldsignature((char *) signbio->memptr(), signbio->memsize()); + delete signbio; + + BinMemInterface *membio = new BinMemInterface(1000, BIN_FLAGS_READABLE); + + if (!membio->readfromfile(filename2.c_str())) + { +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() Failed to Load MetaFile"; + std::cerr << std::endl; +#endif +// delete membio; +// return ; + } + + /* get signature */ + std::string signature; + mAuthMgr->SignData(membio->memptr(), membio->memsize(), signature); + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() New MetaFile Signature:"; + std::cerr << std::endl; + std::cerr << signature; + std::cerr << std::endl; +#endif + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() Orig MetaFile Signature:"; + std::cerr << std::endl; + std::cerr << oldsignature; + std::cerr << std::endl; +#endif + + if (signature != oldsignature) + { + /* Failed */ +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() Signature Check Failed"; + std::cerr << std::endl; +#endif + return; + } + + membio->fseek(0); /* go to start */ + RsSerialiser *rss = new RsSerialiser(); + rss->addSerialType(new RsGeneralConfigSerialiser()); + pqistore stream(rss, "CONFIG", membio, BIN_FLAGS_READABLE); + + RsItem *rsitem = stream.GetItem(); + + RsConfigKeyValueSet *item = dynamic_cast(rsitem); + if (!item) + { + delete rsitem; + return; + } + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() Loaded MetaConfigItem: "; + std::cerr << std::endl; + item->print(std::cerr, 20); + +#endif + + /* extract info from KeyValueSet */ + std::list::iterator it; + for(it = item->tlvkvs.pairs.begin(); it != item->tlvkvs.pairs.end(); it++) + { + /* find the configuration */ + uint32_t confId = atoi(it->key.c_str()); + std::string hashin = it->value; + + /*********************** HACK TO CHANGE CACHE CONFIG ID ********* + * REMOVE IN A MONTH OR TWO + */ + + if (confId == CONFIG_TYPE_CACHE_OLDID) + { + confId = CONFIG_TYPE_CACHE; + } + + /*********************** HACK TO CHANGE CACHE CONFIG ID *********/ + + std::map::iterator cit; + cit = configs.find(confId); + if (cit != configs.end()) + { +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() Element: "; + std::cerr << confId << " Hash: " << hashin; + std::cerr << std::endl; +#endif + (cit->second)->loadConfiguration(hashin); + /* force config to NOT CHANGED */ + cit->second->HasConfigChanged(0); + cit->second->HasConfigChanged(1); + } + } + + delete item; + +#ifdef CONFIG_DEBUG + std::cerr << "p3ConfigMgr::loadConfiguration() Done!"; + std::cerr << std::endl; +#endif + +} + +void p3ConfigMgr::addConfiguration(std::string file, pqiConfig *conf) +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + + /* construct filename */ + std::string filename = basedir; + if (basedir != "") + { + filename += "/"; + } + filename += file; + + conf->setFilename(filename); + configs[conf->Type()] = conf; +} + + +void p3ConfigMgr::completeConfiguration() +{ + saveConfiguration(); + + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + mConfigSaveActive = false; +} + +p3Config::p3Config(uint32_t t) + :pqiConfig(t) +{ + return; +} + +bool p3Config::loadConfiguration(std::string &loadHash) +{ + std::list load; + std::list::iterator it; + + std::string fname = Filename(); + + uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_READABLE; + uint32_t stream_flags = BIN_FLAGS_READABLE; + + BinInterface *bio = new BinFileInterface(fname.c_str(), bioflags); + pqistore stream(setupSerialiser(), "CONFIG", bio, stream_flags); + RsItem *item = NULL; + + while(NULL != (item = stream.GetItem())) + { +#ifdef CONFIG_DEBUG + std::cerr << "p3Config::loadConfiguration() loaded item:"; + std::cerr << std::endl; + item->print(std::cerr, 0); + std::cerr << std::endl; +#endif + load.push_back(item); + } + +#ifdef CONFIG_DEBUG + std::cerr << "p3Config::loadConfiguration() loaded " << load.size(); + std::cerr << " Elements from File: " << fname; + std::cerr << std::endl; +#endif + + /* check hash */ + std::string hashstr = bio->gethash(); + + if (hashstr != loadHash) + { + +#ifdef CONFIG_DEBUG + std::cerr << "p3Config::loadConfiguration() ERROR: Hash != MATCHloaded"; + std::cerr << std::endl; +#endif + + /* bad load */ + for(it = load.begin(); it != load.end(); it++) + { + delete (*it); + } + + setHash(""); + return false; + } + + setHash(hashstr); + + /* else okay */ + return loadList(load); +} + + +bool p3Config::saveConfiguration() +{ + + bool cleanup = true; + std::list toSave = saveList(cleanup); + + std::string fname = Filename(); + std::string fnametmp = Filename()+".tmp"; + + std::cerr << "Writting p3config file " << fname.c_str() << std::endl ; +#ifdef CONFIG_DEBUG + std::cerr << "p3Config::saveConfiguration() toSave " << toSave.size(); + std::cerr << " Elements to File: " << fname; + std::cerr << std::endl; +#endif + + uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_WRITEABLE; + uint32_t stream_flags = BIN_FLAGS_WRITEABLE; + + if (!cleanup) + stream_flags |= BIN_FLAGS_NO_DELETE; + + BinInterface *bio = new BinFileInterface(fnametmp.c_str(), bioflags); + pqistore *stream = new pqistore(setupSerialiser(), "CONFIG", bio, stream_flags); + + std::list::iterator it; + + bool written = true ; + + for(it = toSave.begin(); it != toSave.end(); it++) + { +#ifdef CONFIG_DEBUG + std::cerr << "p3Config::saveConfiguration() save item:"; + std::cerr << std::endl; + (*it)->print(std::cerr, 0); + std::cerr << std::endl; +#endif + written = written && stream->SendItem(*it); + +// std::cerr << "written = " << written << std::endl ; + } + + /* store the hash */ + setHash(bio->gethash()); + saveDone(); /* callback to inherited class to unlock any Mutexes + * protecting saveList() data + */ + + delete stream ; + + if(!written) + return false ; + + std::cerr << "renaming " << fnametmp.c_str() << " to " << fname.c_str() << std::endl ; + + if(!RsDirUtil::renameFile(fnametmp,fname)) + { + std::ostringstream errlog; +#ifdef WIN32 + errlog << "Error " << GetLastError() ; +#else + errlog << "Error " << errno ; +#endif + getPqiNotify()->AddSysMessage(0, RS_SYS_WARNING, "File rename error", "Error while renaming file " + fname + ": got error "+errlog.str()); + return false ; + } + + std::cerr << "Successfully wrote p3config file " << fname.c_str() << std::endl ; + /* else okay */ + return true; +} + + +/**************************** CONFIGURATION CLASSES ********************/ + +p3GeneralConfig::p3GeneralConfig() + :p3Config(CONFIG_TYPE_GENERAL) +{ + return; +} + + // General Configuration System +std::string p3GeneralConfig::getSetting(std::string opt) +{ +#ifdef CONFIG_DEBUG + std::cerr << "p3GeneralConfig::getSetting(" << opt << ")"; + std::cerr << std::endl; +#endif + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + + /* extract from config */ + std::map::iterator it; + if (settings.end() == (it = settings.find(opt))) + { + std::string nullstring; + return nullstring; + } + return it->second; +} + +void p3GeneralConfig::setSetting(std::string opt, std::string val) +{ +#ifdef CONFIG_DEBUG + std::cerr << "p3GeneralConfig::setSetting(" << opt << " = " << val << ")"; + std::cerr << std::endl; +#endif + { + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + + /* extract from config */ + std::map::iterator it; + if (settings.end() != (it = settings.find(opt))) + { + if (it->second == val) + { + /* no change */ + return; + } + } + + settings[opt] = val; + } + /* outside mutex */ + IndicateConfigChanged(); + + return; +} + +RsSerialiser *p3GeneralConfig::setupSerialiser() +{ + RsSerialiser *rss = new RsSerialiser(); + rss->addSerialType(new RsGeneralConfigSerialiser()); + return rss; +} + +std::list p3GeneralConfig::saveList(bool &cleanup) +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + +#ifdef CONFIG_DEBUG + std::cerr << "p3GeneralConfig::saveList() KV sets: " << settings.size(); + std::cerr << std::endl; +#endif + + cleanup = true; + std::list savelist; + + RsConfigKeyValueSet *item = new RsConfigKeyValueSet(); + std::map::iterator it; + for(it = settings.begin(); it != settings.end(); it++) + { + RsTlvKeyValue kv; + kv.key = it->first; + kv.value = it->second; + item->tlvkvs.pairs.push_back(kv); + + /* make sure we don't overload it */ + if (item->tlvkvs.TlvSize() > 4000) + { + savelist.push_back(item); + item = new RsConfigKeyValueSet(); + } + } + + if (item->tlvkvs.pairs.size() > 0) + { + savelist.push_back(item); + } + + return savelist; +} + + +bool p3GeneralConfig::loadList(std::list load) +{ +#ifdef CONFIG_DEBUG + std::cerr << "p3GeneralConfig::loadList() count: " << load.size(); + std::cerr << std::endl; +#endif + + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + + /* add into settings */ + RsConfigKeyValueSet *item = NULL; + std::list::iterator it; + std::list::iterator kit; + + for(it = load.begin(); it != load.end();) + { + item = dynamic_cast(*it); + if (item) + { + for(kit = item->tlvkvs.pairs.begin(); + kit != item->tlvkvs.pairs.end(); kit++) + { + settings[kit->key] = kit->value; + } + } + + /* cleanup */ + delete (*it); + it = load.erase(it); + } + + return true; +} + + +/**** MUTEX NOTE: + * have protected all, but think that + * only the Indication and hash really need it + */ + +pqiConfig::pqiConfig(uint32_t t) + :ConfInd(2), type(t) +{ + return; +} + +pqiConfig::~pqiConfig() +{ + return; +} + +uint32_t pqiConfig::Type() +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + return type; +} + +std::string pqiConfig::Filename() +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + return filename; +} + +std::string pqiConfig::Hash() +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + return hash; +} + +void pqiConfig::IndicateConfigChanged() +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + ConfInd.IndicateChanged(); +} + +bool pqiConfig::HasConfigChanged(uint16_t idx) +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + return ConfInd.Changed(idx); +} + +void pqiConfig::setFilename(std::string name) +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + filename = name; +} + +void pqiConfig::setHash(std::string h) +{ + RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/ + hash = h; +} + diff --git a/libretroshare/src/_pqi/p3cfgmgr.h b/libretroshare/src/_pqi/p3cfgmgr.h new file mode 100644 index 000000000..83377a27c --- /dev/null +++ b/libretroshare/src/_pqi/p3cfgmgr.h @@ -0,0 +1,228 @@ +/* + * libretroshare/src/pqi: p3cfgmgr.h + * + * 3P/PQI network interface for RetroShare. + * + * 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". + * + */ + + + +#ifndef P3CFGMGR_H +#define P3CFGMGR_H + +#include +#include + +#include "_pqi/pqi_base.h" +#include "_pqi/pqiindic.h" +#include "_pqi/pqinetwork.h" +#include "_util/rsthreads.h" +#include "_pqi/pqibin.h" +#include "_pqi/pqistore.h" +#include "_pqi/pqinotify.h" +#include "_pqi/p3authmgr.h" +#include "_util/rsdir.h" +#include "_rsiface/rspeers.h" +#include + +/***** Configuration Management ***** + * + * we need to store: + * (1) Certificates. + * (2) List of Friends / Net Configuration + * (3) Stun List. / DHT peers. + * (4) general config. + * + * + * At top level we need: + * + * - type / filename / size / hash - + * and the file signed... + * + * + */ + +/**** THESE are STORED in CONFIGURATION FILES.... + * Cannot be changed + * + *********************/ + +const uint32_t CONFIG_TYPE_GENERAL = 0x0001; +const uint32_t CONFIG_TYPE_PEERS = 0x0002; +const uint32_t CONFIG_TYPE_FSERVER = 0x0003; +const uint32_t CONFIG_TYPE_MSGS = 0x0004; +const uint32_t CONFIG_TYPE_CACHE_OLDID = 0x0005; + +/* new FileTransfer */ +const uint32_t CONFIG_TYPE_FT_SHARED = 0x0007; +const uint32_t CONFIG_TYPE_FT_EXTRA_LIST= 0x0008; +const uint32_t CONFIG_TYPE_FT_CONTROL = 0x0009; + +/* turtle router */ +const uint32_t CONFIG_TYPE_TURTLE = 0x0020; + +/* wish these ids where higher... + * may move when switch to v0.5 + */ +const uint32_t CONFIG_TYPE_RANK_LINK = 0x0011; +const uint32_t CONFIG_TYPE_CHAT = 0x0012; + +/* standard services */ +const uint32_t CONFIG_TYPE_QBLOG = 0x0101; +const uint32_t CONFIG_TYPE_FORUMS = 0x0102; +const uint32_t CONFIG_TYPE_CHANNELS = 0x0103; + +/* CACHE ID Must be at the END so that other configurations + * are loaded First (Cache Config --> Cache Loading) + */ +const uint32_t CONFIG_TYPE_CACHE = 0xff01; + +class p3ConfigMgr; +class p3AuthMgr; + +class pqiConfig +{ + public: + pqiConfig(uint32_t t); +virtual ~pqiConfig(); + +virtual bool loadConfiguration(std::string &loadHash) = 0; +virtual bool saveConfiguration() = 0; + +uint32_t Type(); +std::string Filename(); +std::string Hash(); + + protected: + +void IndicateConfigChanged(); +void setHash(std::string h); + + RsMutex cfgMtx; + + private: + +void setFilename(std::string name); +bool HasConfigChanged(uint16_t idx); + + Indicator ConfInd; + + uint32_t type; + std::string filename; + std::string hash; + + friend class p3ConfigMgr; + /* so it can access: + * setFilename() and HasConfigChanged() + */ +}; + + +/**** MUTEX NOTE + * None - because no-one calls any functions + * besides tick() when the system is running. + */ + +class p3ConfigMgr +{ + public: + p3ConfigMgr(p3AuthMgr *am, std::string bdir, std::string fname, std::string signame); + +void tick(); +void saveConfiguration(); +void loadConfiguration(); +void addConfiguration(std::string file, pqiConfig *conf); + + /* saves config, and disables further saving + * used for exiting the system + */ +void completeConfiguration(); + + private: + + + /* these are constants - so shouldn't need mutex */ + p3AuthMgr *mAuthMgr; + +const std::string basedir; +const std::string metafname; +const std::string metasigfname; + + RsMutex cfgMtx; /* below is protected */ + +bool mConfigSaveActive; +std::map configs; +}; + + +class p3Config: public pqiConfig +{ + public: + + p3Config(uint32_t t); + +virtual bool loadConfiguration(std::string &loadHash); +virtual bool saveConfiguration(); + + protected: + + /* Key Functions to be overloaded for Full Configuration */ +virtual RsSerialiser *setupSerialiser() = 0; +virtual std::list saveList(bool &cleanup) = 0; +virtual bool loadList(std::list load) = 0; +/** + * callback for mutex unlocking + * in derived classes (should only be needed if cleanup = false) + */ +virtual void saveDone() { return; } + +}; /* end of p3Config */ + + +class p3GeneralConfig: public p3Config +{ + public: + p3GeneralConfig(); + +// General Configuration System +std::string getSetting(std::string opt); +void setSetting(std::string opt, std::string val); + + protected: + + /* Key Functions to be overloaded for Full Configuration */ +virtual RsSerialiser *setupSerialiser(); +virtual std::list saveList(bool &cleanup); +virtual bool loadList(std::list load); + + private: + + /* protected by pqiConfig mutex as well! */ +std::map settings; +}; + + + + + + + +#endif // P3CFGMGR_H diff --git a/libretroshare/src/_pqi/p3connmgr.cc b/libretroshare/src/_pqi/p3connmgr.cc new file mode 100644 index 000000000..3fb74b696 --- /dev/null +++ b/libretroshare/src/_pqi/p3connmgr.cc @@ -0,0 +1,3387 @@ +/* + * libretroshare/src/pqi: p3connmgr.cc + * + * 3P/PQI network interface for RetroShare. + * + * 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 "pqi/p3connmgr.h" + + + + +const int p3connectzone = 3431; + + +/* Network setup States */ + +const uint32_t RS_NET_UNKNOWN = 0x0001; +const uint32_t RS_NET_UPNP_INIT = 0x0002; +const uint32_t RS_NET_UPNP_SETUP = 0x0003; +const uint32_t RS_NET_UDP_SETUP = 0x0004; +const uint32_t RS_NET_DONE = 0x0005; + + +/* Stun modes (TODO) */ +const uint32_t RS_STUN_DHT = 0x0001; +const uint32_t RS_STUN_DONE = 0x0002; +const uint32_t RS_STUN_LIST_MIN = 100; +const uint32_t RS_STUN_FOUND_MIN = 10; + +const uint32_t MAX_UPNP_INIT = 10; /* seconds UPnP timeout */ + +/**** + * #define CONN_DEBUG 1 + ***/ +/**** + * #define P3CONNMGR_NO_TCP_CONNECTIONS 1 + ***/ +/**** + * #define P3CONNMGR_NO_AUTO_CONNECTION 1 + ***/ + +const uint32_t P3CONNMGR_TCP_DEFAULT_DELAY = 2; /* 2 Seconds? is it be enough! */ +const uint32_t P3CONNMGR_UDP_DHT_DELAY = DHT_NOTIFY_PERIOD + 60; /* + 1 minute for DHT POST */ +const uint32_t P3CONNMGR_UDP_PROXY_DELAY = 30; /* 30 seconds (NOT IMPLEMENTED YET!) */ + +#define MAX_AVAIL_PERIOD (2 * DHT_NOTIFY_PERIOD) // If we haven't connected in 2 DHT periods. +#define MIN_RETRY_PERIOD (DHT_CHECK_PERIOD + 120) // just over DHT CHECK_PERIOD + +void printConnectState(peerConnectState &peer); + +peerConnectAddress::peerConnectAddress() + :delay(0), period(0), type(0), ts(0) +{ + sockaddr_clear(&addr); +} + + +peerAddrInfo::peerAddrInfo() + :found(false), type(0), ts(0) +{ + sockaddr_clear(&laddr); + sockaddr_clear(&raddr); +} + +peerConnectState::peerConnectState() + :id("unknown"), + netMode(RS_NET_MODE_UNKNOWN), visState(RS_VIS_STATE_STD), + lastcontact(0), + connecttype(0), + lastavailable(0), + lastattempt(0), + name("nameless"), state(0), actions(0), + source(0), + inConnAttempt(0) +{ + sockaddr_clear(&localaddr); + sockaddr_clear(&serveraddr); + + return; +} + + +p3ConnectMgr::p3ConnectMgr(p3AuthMgr *am) + :p3Config(CONFIG_TYPE_PEERS), + mAuthMgr(am), mNetStatus(RS_NET_UNKNOWN), + mStunStatus(0), mStunFound(0), mStunMoreRequired(true), + mStatusChanged(false) +{ + mUpnpAddrValid = false; + mStunAddrValid = false; + mStunAddrStable = false; + + /* setup basics of own state */ + if (am) + { + ownState.id = mAuthMgr->OwnId(); + ownState.name = mAuthMgr->getName(ownState.id); + ownState.netMode = RS_NET_MODE_UDP; + } + //use_extr_addr_finder = true ; + use_extr_addr_finder = false; + mExtAddrFinder = new ExtAddrFinder ; + + return; +} + +void p3ConnectMgr::getIPServersList(std::list& ip_servers) +{ + mExtAddrFinder->getIPServersList(ip_servers); +} + +void p3ConnectMgr::setIPServersEnabled(bool b) +{ + use_extr_addr_finder = b ; + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + std::cerr << "p3ConnectMgr: setIPServers to " << b << std::endl ; +} + +void p3ConnectMgr::setOwnNetConfig(uint32_t netMode, uint32_t visState) +{ + /* only change TRY flags */ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::setOwnNetConfig()" << std::endl; + std::cerr << "Existing netMode: " << ownState.netMode << " vis: " << ownState.visState; + std::cerr << std::endl; + std::cerr << "Input netMode: " << netMode << " vis: " << visState; + std::cerr << std::endl; +#endif + ownState.netMode &= ~(RS_NET_MODE_TRYMODE); + +#ifdef CONN_DEBUG + std::cerr << "After Clear netMode: " << ownState.netMode << " vis: " << ownState.visState; + std::cerr << std::endl; +#endif + + switch(netMode & RS_NET_MODE_ACTUAL) + { + case RS_NET_MODE_EXT: + ownState.netMode |= RS_NET_MODE_TRY_EXT; + break; + case RS_NET_MODE_UPNP: + ownState.netMode |= RS_NET_MODE_TRY_UPNP; + break; + default: + case RS_NET_MODE_UDP: + ownState.netMode |= RS_NET_MODE_TRY_UDP; + break; + } + + ownState.visState = visState; + +#ifdef CONN_DEBUG + std::cerr << "Final netMode: " << ownState.netMode << " vis: " << ownState.visState; + std::cerr << std::endl; +#endif + + /* if we've started up - then tweak Dht On/Off */ + if (mNetStatus != RS_NET_UNKNOWN) + { + enableNetAssistConnect(!(ownState.visState & RS_VIS_STATE_NODHT)); + } + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ +} + + +/***** Framework / initial implementation for a connection manager. + * + * This needs a state machine for Initialisation. + * + * Network state: + * RS_NET_UNKNOWN + * RS_NET_EXT_UNKNOWN * forwarded port (but Unknown Ext IP) * + * RS_NET_EXT_KNOWN * forwarded port with known IP/Port. * + * + * RS_NET_UPNP_CHECK * checking for UPnP * + * RS_NET_UPNP_KNOWN * confirmed UPnP ext Ip/port * + * + * RS_NET_UDP_UNKNOWN * not Ext/UPnP - to determine Ext IP/Port * + * RS_NET_UDP_KNOWN * have Stunned for Ext Addr * + * + * Transitions: + * + * RS_NET_UNKNOWN -(config)-> RS_NET_EXT_UNKNOWN + * RS_NET_UNKNOWN -(config)-> RS_NET_UPNP_UNKNOWN + * RS_NET_UNKNOWN -(config)-> RS_NET_UDP_UNKNOWN + * + * RS_NET_EXT_UNKNOWN -(DHT(ip)/Stun)-> RS_NET_EXT_KNOWN + * + * RS_NET_UPNP_UNKNOWN -(Upnp)-> RS_NET_UPNP_KNOWN + * RS_NET_UPNP_UNKNOWN -(timout/Upnp)-> RS_NET_UDP_UNKNOWN + * + * RS_NET_UDP_UNKNOWN -(stun)-> RS_NET_UDP_KNOWN + * + * + * STUN state: + * RS_STUN_INIT * done nothing * + * RS_STUN_DHT * looking up peers * + * RS_STUN_DONE * found active peer and stunned * + * + * + * Steps. + ******************************************************************* + * (1) Startup. + * - UDP port setup. + * - DHT setup. + * - Get Stun Keys -> add to DHT. + * - Feedback from DHT -> ask UDP to stun. + * + * (1) determine Network mode. + * If external Port.... Done: + * (2) + ******************************************************************* + * Stable operation: + * (1) tick and check peers. + * (2) handle callback. + * (3) notify of new/failed connections. + * + * + */ + +void p3ConnectMgr::netStatusReset() +{ + netFlagOk = true; + netFlagUpnpOk = false; + netFlagDhtOk = false; + netFlagExtOk = false; + netFlagUdpOk = false; + netFlagTcpOk = false; + netFlagResetReq = false; +} + +void p3ConnectMgr::netStartup() +{ + /* startup stuff */ + + /* StunInit gets a list of peers, and asks the DHT to find them... + * This is needed for all systems so startup straight away + */ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netStartup()" << std::endl; +#endif + + netDhtInit(); + netUdpInit(); + netStunInit(); + netStatusReset(); + + /* decide which net setup mode we're going into + */ + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + mNetInitTS = time(NULL); + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netStartup() tou_stunkeepalive() enabled" << std::endl; +#endif + tou_stunkeepalive(1); + + ownState.netMode &= ~(RS_NET_MODE_ACTUAL); + + switch(ownState.netMode & RS_NET_MODE_TRYMODE) + { + + case RS_NET_MODE_TRY_EXT: /* v similar to UDP */ + ownState.netMode |= RS_NET_MODE_EXT; + mNetStatus = RS_NET_UDP_SETUP; +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netStartup() disabling stunkeepalive() cos EXT" << std::endl; +#endif + tou_stunkeepalive(0); + mStunMoreRequired = false; /* only need to validate address (EXT) */ + + break; + + case RS_NET_MODE_TRY_UDP: + ownState.netMode |= RS_NET_MODE_UDP; + mNetStatus = RS_NET_UDP_SETUP; + break; + + case RS_NET_MODE_TRY_UPNP: + default: + /* Force it here (could be default!) */ + ownState.netMode |= RS_NET_MODE_TRY_UPNP; + ownState.netMode |= RS_NET_MODE_UDP; /* set to UDP, upgraded is UPnP is Okay */ + mNetStatus = RS_NET_UPNP_INIT; + break; + } +} + + +void p3ConnectMgr::tick() +{ + netTick(); + statusTick(); + tickMonitors(); + +} + +bool p3ConnectMgr::shutdown() /* blocking shutdown call */ +{ + connMtx.lock(); /* LOCK MUTEX */ + + bool upnpActive = ownState.netMode & RS_NET_MODE_UPNP; + + connMtx.unlock(); /* UNLOCK MUTEX */ + + if (upnpActive) + { + netAssistFirewallShutdown(); + } + netAssistConnectShutdown(); + + return true; +} + + +void p3ConnectMgr::statusTick() +{ + /* iterate through peers ... + * if been available for long time ... remove flag + * if last attempt a while - retryConnect. + * etc. + */ + +#ifdef CONN_DEBUG + //std::cerr << "p3ConnectMgr::statusTick()" << std::endl; +#endif + std::list retryIds; + std::list::iterator it2; + + time_t now = time(NULL); + time_t oldavail = now - MAX_AVAIL_PERIOD; + time_t retry = now - MIN_RETRY_PERIOD; + + { + RsStackMutex stack(connMtx); /****** LOCK MUTEX ******/ + std::map::iterator it; + for(it = mFriendList.begin(); it != mFriendList.end(); it++) + { + if (it->second.state & RS_PEER_S_CONNECTED) + { + continue; + } + + if ((it->second.state & RS_PEER_S_ONLINE) && + (it->second.lastavailable < oldavail)) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::statusTick() ONLINE TIMEOUT for: "; + std::cerr << it->first; + std::cerr << std::endl; +#endif + it->second.state &= (~RS_PEER_S_ONLINE); + } + + if (it->second.lastattempt < retry) + { + retryIds.push_back(it->first); + } + } + } + +#ifndef P3CONNMGR_NO_AUTO_CONNECTION + + for(it2 = retryIds.begin(); it2 != retryIds.end(); it2++) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::statusTick() RETRY TIMEOUT for: "; + std::cerr << *it2; + std::cerr << std::endl; +#endif + /* retry it! */ + retryConnectTCP(*it2); + } + +#endif + +} + + +void p3ConnectMgr::netTick() +{ + +#ifdef CONN_DEBUG + //std::cerr << "p3ConnectMgr::netTick()" << std::endl; +#endif + + // Check whether we are stuck on loopback. This happens if RS starts when + // the computer is not yet connected to the internet. In such a case we + // periodically check for a local net address. + // + if(isLoopbackNet(&(ownState.localaddr.sin_addr))) + checkNetAddress() ; + + connMtx.lock(); /* LOCK MUTEX */ + + uint32_t netStatus = mNetStatus; + + connMtx.unlock(); /* UNLOCK MUTEX */ + + switch(netStatus) + { + case RS_NET_UNKNOWN: + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netTick() STATUS: UNKNOWN" << std::endl; +#endif + /* RS_NET_UNKNOWN -(config)-> RS_NET_EXT_INIT + * RS_NET_UNKNOWN -(config)-> RS_NET_UPNP_INIT + * RS_NET_UNKNOWN -(config)-> RS_NET_UDP_INIT + */ + netStartup(); + break; + + case RS_NET_UPNP_INIT: +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netTick() STATUS: UPNP_INIT" << std::endl; +#endif + netUpnpInit(); + break; + + case RS_NET_UPNP_SETUP: +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netTick() STATUS: UPNP_SETUP" << std::endl; +#endif + netUpnpCheck(); + break; + + case RS_NET_UDP_SETUP: +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netTick() STATUS: UDP_SETUP" << std::endl; +#endif + netUdpCheck(); + break; + + case RS_NET_DONE: +#ifdef CONN_DEBUG + //std::cerr << "p3ConnectMgr::netTick() STATUS: DONE" << std::endl; +#endif + stunCheck(); /* Keep on stunning until its happy */ + default: + break; + } + + return; +} + + +void p3ConnectMgr::netUdpInit() +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netUdpInit()" << std::endl; +#endif + connMtx.lock(); /* LOCK MUTEX */ + + struct sockaddr_in iaddr = ownState.localaddr; + + connMtx.unlock(); /* UNLOCK MUTEX */ + + /* open our udp port */ + tou_init((struct sockaddr *) &iaddr, sizeof(iaddr)); +} + + +void p3ConnectMgr::netDhtInit() +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netDhtInit()" << std::endl; +#endif + connMtx.lock(); /* LOCK MUTEX */ + + uint32_t vs = ownState.visState; + + connMtx.unlock(); /* UNLOCK MUTEX */ + + enableNetAssistConnect(!(vs & RS_VIS_STATE_NODHT)); +} + + +void p3ConnectMgr::netUpnpInit() +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netUpnpInit()" << std::endl; +#endif + uint16_t eport, iport; + + connMtx.lock(); /* LOCK MUTEX */ + + /* get the ports from the configuration */ + + mNetStatus = RS_NET_UPNP_SETUP; + iport = ntohs(ownState.localaddr.sin_port); + eport = ntohs(ownState.serveraddr.sin_port); + if ((eport < 1000) || (eport > 30000)) + { + eport = iport; + } + + connMtx.unlock(); /* UNLOCK MUTEX */ + + netAssistFirewallPorts(iport, eport); + enableNetAssistFirewall(true); +} + +void p3ConnectMgr::netUpnpCheck() +{ + /* grab timestamp */ + connMtx.lock(); /* LOCK MUTEX */ + + time_t delta = time(NULL) - mNetInitTS; + + connMtx.unlock(); /* UNLOCK MUTEX */ + + struct sockaddr_in extAddr; + int upnpState = netAssistFirewallActive(); + + if ((upnpState < 0) || + ((upnpState == 0) && (delta > MAX_UPNP_INIT))) + { + /* fallback to UDP startup */ + connMtx.lock(); /* LOCK MUTEX */ + + /* UPnP Failed us! */ + mUpnpAddrValid = false; + mNetStatus = RS_NET_UDP_SETUP; +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netUpnpCheck() enabling stunkeepalive() cos UDP" << std::endl; +#endif + tou_stunkeepalive(1); + + connMtx.unlock(); /* UNLOCK MUTEX */ + } + else if ((upnpState > 0) && netAssistExtAddress(extAddr)) + { + /* switch to UDP startup */ + connMtx.lock(); /* LOCK MUTEX */ + + /* Set Net Status flags .... + * we now have external upnp address. Golden! + * don't set netOk flag until have seen some traffic. + */ + + netFlagUpnpOk = true; + netFlagExtOk = true; + + mUpnpAddrValid = true; + mUpnpExtAddr = extAddr; + mNetStatus = RS_NET_UDP_SETUP; + /* Fix netMode & Clear others! */ + ownState.netMode = RS_NET_MODE_TRY_UPNP | RS_NET_MODE_UPNP; +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netUpnpCheck() disabling stunkeepalive() cos uPnP" << std::endl; +#endif + tou_stunkeepalive(0); + mStunMoreRequired = false; /* only need to validate address (UPNP) */ + + connMtx.unlock(); /* UNLOCK MUTEX */ + } +} + +void p3ConnectMgr::netUdpCheck() +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netUdpCheck()" << std::endl; +#endif + struct sockaddr_in tmpip ; + + if (udpExtAddressCheck() || (mUpnpAddrValid) || (use_extr_addr_finder && mExtAddrFinder->hasValidIP(&tmpip))) + { + bool extValid = false; + bool extAddrStable = false; + struct sockaddr_in extAddr; + uint32_t mode = 0; + + connMtx.lock(); /* LOCK MUTEX */ + + mNetStatus = RS_NET_DONE; + + /* get the addr from the configuration */ + struct sockaddr_in iaddr = ownState.localaddr; + + if(use_extr_addr_finder && mExtAddrFinder->hasValidIP(&tmpip)) + { + extValid = true; + extAddr = tmpip ; + extAddr.sin_port = iaddr.sin_port ; + extAddrStable = true; + } + else if (mUpnpAddrValid) + { + extValid = true; + extAddr = mUpnpExtAddr; + extAddrStable = true; + } + else if (mStunAddrValid) + { + extValid = true; + extAddr = mStunExtAddr; + extAddrStable = mStunAddrStable; + } + + if (extValid) + { + ownState.serveraddr = extAddr; + mode = RS_NET_CONN_TCP_LOCAL; + + if (!extAddrStable) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netUdpCheck() UDP Unstable :( "; + std::cerr << std::endl; + std::cerr << "p3ConnectMgr::netUdpCheck() We are unreachable"; + std::cerr << std::endl; + std::cerr << "netMode => RS_NET_MODE_UNREACHABLE"; + std::cerr << std::endl; +#endif + ownState.netMode &= ~(RS_NET_MODE_ACTUAL); + ownState.netMode |= RS_NET_MODE_UNREACHABLE; + tou_stunkeepalive(0); + mStunMoreRequired = false; /* no point -> unreachable (EXT) */ + + /* send a system warning message */ + pqiNotify *notify = getPqiNotify(); + if (notify) + { + std::string title = + "Warning: Bad Firewall Configuration"; + + std::string msg; + msg += " **** WARNING **** \n"; + msg += "Retroshare has detected that you are behind"; + msg += " a restrictive Firewall\n"; + msg += "\n"; + msg += "You cannot connect to other firewalled peers\n"; + msg += "\n"; + msg += "You can fix this by:\n"; + msg += " (1) opening an External Port\n"; + msg += " (2) enabling UPnP, or\n"; + msg += " (3) get a new (approved) Firewall/Router\n"; + + notify->AddSysMessage(0, RS_SYS_WARNING, title, msg); + } + + } + else if (mUpnpAddrValid || (ownState.netMode & RS_NET_MODE_EXT)) + { + mode |= RS_NET_CONN_TCP_EXTERNAL; + mode |= RS_NET_CONN_UDP_DHT_SYNC; + } + else // if (extAddrStable) + { + /* Check if extAddr == intAddr (Not Firewalled) */ + if ((0 == inaddr_cmp(iaddr, extAddr)) && + isExternalNet(&(extAddr.sin_addr))) + { + mode |= RS_NET_CONN_TCP_EXTERNAL; + } + + mode |= RS_NET_CONN_UDP_DHT_SYNC; + } + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + } + + connMtx.unlock(); /* UNLOCK MUTEX */ + + if (extValid) + { + netAssistSetAddress(iaddr, extAddr, mode); + } + else + { + /* mode = 0 for error */ + netAssistSetAddress(iaddr, extAddr, mode); + } + + /* flag unreachables! */ + if ((extValid) && (!extAddrStable)) + { + netUnreachableCheck(); + } + } +} + +void p3ConnectMgr::netUnreachableCheck() +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::netUnreachableCheck()" << std::endl; +#endif + std::map::iterator it; + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + for(it = mFriendList.begin(); it != mFriendList.end(); it++) + { + /* get last contact detail */ + if (it->second.state & RS_PEER_S_CONNECTED) + { +#ifdef CONN_DEBUG + std::cerr << "NUC() Ignoring Connected Peer" << std::endl; +#endif + continue; + } + + peerAddrInfo details; + switch(it->second.source) + { + case RS_CB_DHT: + details = it->second.dht; +#ifdef CONN_DEBUG + std::cerr << "NUC() Using DHT data" << std::endl; +#endif + break; + case RS_CB_DISC: + details = it->second.disc; +#ifdef CONN_DEBUG + std::cerr << "NUC() Using DISC data" << std::endl; +#endif + break; + case RS_CB_PERSON: + details = it->second.peer; +#ifdef CONN_DEBUG + std::cerr << "NUC() Using PEER data" << std::endl; +#endif + break; + default: + continue; + break; + } + + std::cerr << "NUC() Peer: " << it->first << std::endl; + + /* Determine Reachability (only advisory) */ + // if (ownState.netMode == RS_NET_MODE_UNREACHABLE) // MUST BE TRUE! + { + if (details.type & RS_NET_CONN_TCP_EXTERNAL) + { + /* reachable! */ + it->second.state &= (~RS_PEER_S_UNREACHABLE); +#ifdef CONN_DEBUG + std::cerr << "NUC() Peer EXT TCP - reachable" << std::endl; +#endif + } + else + { + /* unreachable */ + it->second.state |= RS_PEER_S_UNREACHABLE; +#ifdef CONN_DEBUG + std::cerr << "NUC() Peer !EXT TCP - unreachable" << std::endl; +#endif + } + } + } + +} + + +/******************************* UDP MAINTAINANCE ******************************** + * Interaction with the UDP is mainly for determining the External Port. + * + */ + +bool p3ConnectMgr::udpInternalAddress(struct sockaddr_in iaddr) +{ + return false; +} + +bool p3ConnectMgr::udpExtAddressCheck() +{ + /* three possibilities: + * (1) not found yet. + * (2) Found! + * (3) bad udp (port switching). + */ + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + uint8_t stable; + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::udpExtAddressCheck()" << std::endl; +#endif + + if (0 < tou_extaddr((struct sockaddr *) &addr, &len, &stable)) + { + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + + /* update UDP information */ + mStunExtAddr = addr; + mStunAddrValid = true; + mStunAddrStable = (stable != 0); + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::udpExtAddressCheck() Got "; + std::cerr << " addr: " << inet_ntoa(mStunExtAddr.sin_addr); + std::cerr << ":" << ntohs(mStunExtAddr.sin_port); + std::cerr << " stable: " << mStunAddrStable; + std::cerr << std::endl; +#endif + + /* update net Status flags .... + * we've got stun information via udp... + * so up is okay, and ext address is known stable or not. + */ + + if (mStunAddrStable) + netFlagExtOk = true; + netFlagUdpOk = true; + + return true; + } + return false; +} + +void p3ConnectMgr::udpStunPeer(std::string id, struct sockaddr_in &addr) +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::udpStunPeer()" << std::endl; +#endif + /* add it into udp stun list */ + tou_stunpeer((struct sockaddr *) &addr, sizeof(addr), id.c_str()); +} + +/********************************** STUN SERVERS *********************************** + * We maintain a list of stun servers. This is initialised with a set of random keys. + * + * This is gradually rolled over with time. We update with friends/friends of friends, + * and the lists that they provide (part of AutoDisc). + * + * max 100 entries? + */ + +void p3ConnectMgr::netStunInit() +{ + stunInit(); +} + +void p3ConnectMgr::stunInit() +{ + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + netAssistStun(true); + + /* push stun list to DHT */ + std::list::iterator it; + for(it = mStunList.begin(); it != mStunList.end(); it++) + { + netAssistAddStun(*it); + } + mStunStatus = RS_STUN_DHT; + mStunFound = 0; + mStunMoreRequired = true; +} + +bool p3ConnectMgr::stunCheck() +{ + /* check if we've got a Stun result */ + bool stunOk = false; + +#ifdef CONN_DEBUG + //std::cerr << "p3ConnectMgr::stunCheck()" << std::endl; +#endif + + { + RsStackMutex stack(connMtx); /********* LOCK STACK MUTEX ******/ + + /* if DONE -> return */ + if (mStunStatus == RS_STUN_DONE) + { + return true; + } + + if (mStunFound >= RS_STUN_FOUND_MIN) + { + mStunMoreRequired = false; + } + stunOk = (!mStunMoreRequired); + } + + + if (udpExtAddressCheck() && (stunOk)) + { + /* set external UDP address */ + netAssistStun(false); + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + mStunStatus = RS_STUN_DONE; + + return true; + } + return false; +} + +void p3ConnectMgr::stunStatus(std::string id, struct sockaddr_in raddr, uint32_t type, uint32_t flags) +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::stunStatus()"; + std::cerr << " id: " << RsUtil::BinToHex(id) << " raddr: " << inet_ntoa(raddr.sin_addr); + std::cerr << ":" << ntohs(raddr.sin_port); + std::cerr << std::endl; +#endif + + connMtx.lock(); /* LOCK MUTEX */ + + bool stillStunning = (mStunStatus == RS_STUN_DHT); + + connMtx.unlock(); /* UNLOCK MUTEX */ + + /* only useful if they have an exposed TCP/UDP port */ + if (type & RS_NET_CONN_TCP_EXTERNAL) + { + if (stillStunning) + { + connMtx.lock(); /* LOCK MUTEX */ + mStunFound++; + connMtx.unlock(); /* UNLOCK MUTEX */ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::stunStatus() Sending to UDP" << std::endl; +#endif + /* push to the UDP */ + udpStunPeer(id, raddr); + + } + + /* push to the stunCollect */ + stunCollect(id, raddr, flags); + } +} + +/* FLAGS + +ONLINE +EXT +UPNP +UDP +FRIEND +FRIEND_OF_FRIEND +OTHER + +*/ + +void p3ConnectMgr::stunCollect(std::string id, struct sockaddr_in addr, uint32_t flags) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::stunCollect() id: " << RsUtil::BinToHex(id) << std::endl; +#endif + + std::list::iterator it; + it = std::find(mStunList.begin(), mStunList.end(), id); + if (it == mStunList.end()) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::stunCollect() Id not in List" << std::endl; +#endif + /* add it in: + * if FRIEND / ONLINE or if list is short. + */ + if ((flags & RS_STUN_ONLINE) || (flags & RS_STUN_FRIEND)) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::stunCollect() Id added to Front" << std::endl; +#endif + /* push to the front */ + mStunList.push_front(id); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + } + else if (mStunList.size() < RS_STUN_LIST_MIN) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::stunCollect() Id added to Back" << std::endl; +#endif + /* push to the front */ + mStunList.push_back(id); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + } + } + else + { + /* if they're online ... move to the front + */ + if (flags & RS_STUN_ONLINE) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::stunCollect() Id moved to Front" << std::endl; +#endif + /* move to front */ + mStunList.erase(it); + mStunList.push_front(id); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + } + } + +} + +/******************************** Network Status ********************************* + * Configuration Loading / Saving. + */ + + +void p3ConnectMgr::addMonitor(pqiMonitor *mon) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + std::list::iterator it; + it = std::find(clients.begin(), clients.end(), mon); + if (it != clients.end()) + { + return; + } + + mon->setConnectionMgr(this); + clients.push_back(mon); + return; +} + +void p3ConnectMgr::removeMonitor(pqiMonitor *mon) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + std::list::iterator it; + it = std::find(clients.begin(), clients.end(), mon); + if (it == clients.end()) + { + return; + } + (*it)->setConnectionMgr(NULL); + clients.erase(it); + + return; +} + + +void p3ConnectMgr::tickMonitors() +{ + bool doStatusChange = false; + std::list actionList; + std::map::iterator it; + + { + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + if (mStatusChanged) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::tickMonitors() StatusChanged! List:" << std::endl; +#endif + /* assemble list */ + for(it = mFriendList.begin(); it != mFriendList.end(); it++) + { + if (it->second.actions) + { + /* add in */ + pqipeer peer; + peer.id = it->second.id; + peer.name = it->second.name; + peer.state = it->second.state; + peer.actions = it->second.actions; + + /* reset action */ + it->second.actions = 0; + + actionList.push_back(peer); + +#ifdef CONN_DEBUG + std::cerr << "Friend: " << peer.name; + std::cerr << " Id: " << peer.id; + std::cerr << " State: " << peer.state; + if (peer.state & RS_PEER_S_FRIEND) + std::cerr << " S:RS_PEER_S_FRIEND"; + if (peer.state & RS_PEER_S_ONLINE) + std::cerr << " S:RS_PEER_S_ONLINE"; + if (peer.state & RS_PEER_S_CONNECTED) + std::cerr << " S:RS_PEER_S_CONNECTED"; + std::cerr << " Actions: " << peer.actions; + if (peer.actions & RS_PEER_NEW) + std::cerr << " A:RS_PEER_NEW"; + if (peer.actions & RS_PEER_MOVED) + std::cerr << " A:RS_PEER_MOVED"; + if (peer.actions & RS_PEER_CONNECTED) + std::cerr << " A:RS_PEER_CONNECTED"; + if (peer.actions & RS_PEER_DISCONNECTED) + std::cerr << " A:RS_PEER_DISCONNECTED"; + if (peer.actions & RS_PEER_CONNECT_REQ) + std::cerr << " A:RS_PEER_CONNECT_REQ"; + + std::cerr << std::endl; +#endif + + /* notify GUI */ + if (peer.actions & RS_PEER_CONNECTED) + { + pqiNotify *notify = getPqiNotify(); + if (notify) + { + notify->AddPopupMessage(RS_POPUP_CONNECT, + peer.id, "Online: "); + + + notify->AddFeedItem(RS_FEED_ITEM_PEER_CONNECT, peer.id, "", ""); + } + } +#if 0 + if (peer.actions & RS_PEER_DISCONNECTED) + { + pqiNotify *notify = getPqiNotify(); + if (notify) + { + notify->AddFeedItem(RS_FEED_ITEM_PEER_DISCONNECT, peer.id, "", ""); + + + } + } +#endif + } + } + /* do the Others as well! */ + for(it = mOthersList.begin(); it != mOthersList.end(); it++) + { + if (it->second.actions) + { + /* add in */ + pqipeer peer; + peer.id = it->second.id; + peer.name = it->second.name; + peer.state = it->second.state; + peer.actions = it->second.actions; + + /* reset action */ + it->second.actions = 0; + +#ifdef CONN_DEBUG + std::cerr << "Other: " << peer.name; + std::cerr << " Id: " << peer.id; + std::cerr << " State: " << peer.state; + if (peer.state & RS_PEER_S_FRIEND) + std::cerr << " S:RS_PEER_S_FRIEND"; + if (peer.state & RS_PEER_S_ONLINE) + std::cerr << " S:RS_PEER_S_ONLINE"; + if (peer.state & RS_PEER_S_CONNECTED) + std::cerr << " S:RS_PEER_S_CONNECTED"; + std::cerr << " Actions: " << peer.actions; + if (peer.actions & RS_PEER_NEW) + std::cerr << " A:RS_PEER_NEW"; + if (peer.actions & RS_PEER_MOVED) + std::cerr << " A:RS_PEER_MOVED"; + if (peer.actions & RS_PEER_CONNECTED) + std::cerr << " A:RS_PEER_CONNECTED"; + if (peer.actions & RS_PEER_DISCONNECTED) + std::cerr << " A:RS_PEER_DISCONNECTED"; + if (peer.actions & RS_PEER_CONNECT_REQ) + std::cerr << " A:RS_PEER_CONNECT_REQ"; + + std::cerr << std::endl; +#endif + + actionList.push_back(peer); + } + } + mStatusChanged = false; + doStatusChange = true; + + } + } /****** UNLOCK STACK MUTEX ******/ + + /* NOTE - clients is accessed without mutex protection!!!! + * At the moment this is okay - as they are only added at the start. + * IF this changes ---- must fix with second Mutex. + */ + + if (doStatusChange) + { +#ifdef CONN_DEBUG + std::cerr << "Sending to " << clients.size() << " monitorClients"; + std::cerr << std::endl; +#endif + + /* send to all monitors */ + std::list::iterator mit; + for(mit = clients.begin(); mit != clients.end(); mit++) + { + (*mit)->statusChange(actionList); + } + } +} + + +const std::string p3ConnectMgr::getOwnId() +{ + if (mAuthMgr) + { + return mAuthMgr->OwnId(); + } + else + { + std::string nullStr; + return nullStr; + } +} + + +bool p3ConnectMgr::getOwnNetStatus(peerConnectState &state) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + state = ownState; + return true; +} + +bool p3ConnectMgr::isFriend(std::string id) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + return (mFriendList.end() != mFriendList.find(id)); +} + +bool p3ConnectMgr::isOnline(std::string id) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + std::map::iterator it; + if (mFriendList.end() != (it = mFriendList.find(id))) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::isOnline(" << id; + std::cerr << ") is Friend, Online: "; + std::cerr << (it->second.state & RS_PEER_S_CONNECTED); + std::cerr << std::endl; +#endif + return (it->second.state & RS_PEER_S_CONNECTED); + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::isOnline(" << id; + std::cerr << ") is Not Friend"; + std::cerr << std::endl; + std::cerr << "p3ConnectMgr::isOnline() OwnId: "; + std::cerr << mAuthMgr->OwnId(); + std::cerr << std::endl; +#endif + /* not a friend */ + } + + return false; +} + +bool p3ConnectMgr::getFriendNetStatus(std::string id, peerConnectState &state) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* check for existing */ + std::map::iterator it; + it = mFriendList.find(id); + if (it == mFriendList.end()) + { + return false; + } + + state = it->second; + return true; +} + + +bool p3ConnectMgr::getOthersNetStatus(std::string id, peerConnectState &state) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* check for existing */ + std::map::iterator it; + it = mOthersList.find(id); + if (it == mOthersList.end()) + { + return false; + } + + state = it->second; + return true; +} + + +void p3ConnectMgr::getOnlineList(std::list &peers) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* check for existing */ + std::map::iterator it; + for(it = mFriendList.begin(); it != mFriendList.end(); it++) + { + if (it->second.state & RS_PEER_S_CONNECTED) + { + peers.push_back(it->first); + } + } + return; +} + +void p3ConnectMgr::getFriendList(std::list &peers) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* check for existing */ + std::map::iterator it; + for(it = mFriendList.begin(); it != mFriendList.end(); it++) + { + peers.push_back(it->first); + } + return; +} + + +void p3ConnectMgr::getOthersList(std::list &peers) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* check for existing */ + std::map::iterator it; + for(it = mOthersList.begin(); it != mOthersList.end(); it++) + { + peers.push_back(it->first); + } + return; +} + + + +bool p3ConnectMgr::connectAttempt(std::string id, struct sockaddr_in &addr, + uint32_t &delay, uint32_t &period, uint32_t &type) + +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* check for existing */ + std::map::iterator it; + it = mFriendList.find(id); + if (it == mFriendList.end()) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::connectAttempt() FAILED Not in FriendList!"; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + + return false; + } + + if (it->second.connAddrs.size() < 1) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::connectAttempt() FAILED No ConnectAddresses"; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + return false; + } + + it->second.lastattempt = time(NULL); /* time of last connect attempt */ + it->second.inConnAttempt = true; + it->second.currentConnAddr = it->second.connAddrs.front(); + it->second.connAddrs.pop_front(); + + addr = it->second.currentConnAddr.addr; + delay = it->second.currentConnAddr.delay; + period = it->second.currentConnAddr.period; + type = it->second.currentConnAddr.type; + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::connectAttempt() Success: "; + std::cerr << " id: " << id; + std::cerr << std::endl; + std::cerr << " laddr: " << inet_ntoa(addr.sin_addr); + std::cerr << " lport: " << ntohs(addr.sin_port); + std::cerr << " delay: " << delay; + std::cerr << " period: " << period; + std::cerr << " type: " << type; + std::cerr << std::endl; +#endif + + return true; +} + + +/**************************** + * Update state, + * trigger retry if necessary, + * + * remove from DHT? + * + */ + +bool p3ConnectMgr::connectResult(std::string id, bool success, uint32_t flags) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* check for existing */ + std::map::iterator it; + it = mFriendList.find(id); + if (it == mFriendList.end()) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::connectResult() Failed, missing Friend "; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + return false; + } + + + it->second.inConnAttempt = false; + + if (success) + { + /* remove other attempts */ + it->second.connAddrs.clear(); + netAssistFriend(id, false); + + /* update address (will come through from DISC) */ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::connectResult() Connect!: "; + std::cerr << " id: " << id; + std::cerr << std::endl; + std::cerr << " Success: " << success; + std::cerr << " flags: " << flags; + std::cerr << std::endl; +#endif + + + /* change state */ + it->second.state |= RS_PEER_S_CONNECTED; + it->second.actions |= RS_PEER_CONNECTED; + mStatusChanged = true; + it->second.lastcontact = time(NULL); /* time of connect */ + it->second.connecttype = flags; + + return true; + } + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::connectResult() Disconnect/Fail: "; + std::cerr << " id: " << id; + std::cerr << std::endl; + std::cerr << " Success: " << success; + std::cerr << " flags: " << flags; + std::cerr << std::endl; +#endif + + /* if currently connected -> flag as failed */ + if (it->second.state & RS_PEER_S_CONNECTED) + { + it->second.state &= (~RS_PEER_S_CONNECTED); + it->second.actions |= RS_PEER_DISCONNECTED; + + it->second.lastcontact = time(NULL); /* time of disconnect */ + + netAssistFriend(id, true); + if (it->second.visState & RS_VIS_STATE_NODHT) + { + /* hidden from DHT world */ + } + else + { + //netAssistFriend(id, true); + } + + } + + + if (it->second.connAddrs.size() < 1) + { + return true; + } + + + it->second.actions |= RS_PEER_CONNECT_REQ; + mStatusChanged = true; + + return true; +} + + + + +/******************************** Feedback ...... ********************************* + * From various sources + */ + + +void p3ConnectMgr::peerStatus(std::string id, + struct sockaddr_in laddr, struct sockaddr_in raddr, + uint32_t type, uint32_t flags, uint32_t source) +{ + std::map::iterator it; + bool isFriend = true; + + time_t now = time(NULL); + + peerAddrInfo details; + details.type = type; + details.found = true; + details.laddr = laddr; + details.raddr = raddr; + details.ts = now; + + + { + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus()"; + std::cerr << " id: " << id; + std::cerr << " laddr: " << inet_ntoa(laddr.sin_addr); + std::cerr << " lport: " << ntohs(laddr.sin_port); + std::cerr << " raddr: " << inet_ntoa(raddr.sin_addr); + std::cerr << " rport: " << ntohs(raddr.sin_port); + std::cerr << " type: " << type; + std::cerr << " flags: " << flags; + std::cerr << " source: " << source; + std::cerr << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::peerStatus()"; + out << " id: " << id; + out << " laddr: " << inet_ntoa(laddr.sin_addr); + out << " lport: " << ntohs(laddr.sin_port); + out << " raddr: " << inet_ntoa(raddr.sin_addr); + out << " rport: " << ntohs(raddr.sin_port); + out << " type: " << type; + out << " flags: " << flags; + out << " source: " << source; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + + /* look up the id */ + it = mFriendList.find(id); + if (it == mFriendList.end()) + { + /* check Others list */ + isFriend = false; + it = mOthersList.find(id); + if (it == mOthersList.end()) + { + /* not found - ignore */ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() Peer Not Found - Ignore"; + std::cerr << std::endl; +#endif + return; + } +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() Peer is in mOthersList"; + std::cerr << std::endl; +#endif + } + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() Current Peer State:" << std::endl; + printConnectState(it->second); + std::cerr << std::endl; +#endif + + /* update the status */ + + /* if source is DHT */ + if (source == RS_CB_DHT) + { + /* DHT can tell us about + * 1) connect type (UDP/TCP/etc) + * 2) local/external address + */ + it->second.source = RS_CB_DHT; + it->second.dht = details; + + /* If we get a info -> then they are online */ + it->second.state |= RS_PEER_S_ONLINE; + it->second.lastavailable = now; + + /* if we are recieving these - the dht is definitely up. + */ + + netFlagDhtOk = true; + } + else if (source == RS_CB_DISC) + { + /* DISC can tell us about + * 1) connect type (UDP/TCP/etc) + * 2) local/external addresses + */ + it->second.source = RS_CB_DISC; + it->second.disc = details; + + if (flags & RS_NET_FLAGS_ONLINE) + { + it->second.actions |= RS_PEER_ONLINE; + it->second.state |= RS_PEER_S_ONLINE; + it->second.lastavailable = now; + mStatusChanged = true; + } + + /* not updating VIS status??? */ + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + } + else if (source == RS_CB_PERSON) + { + /* PERSON can tell us about + * 1) online / offline + * 2) connect address + * -> update all! + */ + + it->second.source = RS_CB_PERSON; + it->second.peer = details; + + it->second.localaddr = laddr; + it->second.serveraddr = raddr; + + it->second.state |= RS_PEER_S_ONLINE; + it->second.lastavailable = now; + + /* must be online to recv info (should be connected too!) + * but no need for action as should be connected already + */ + + it->second.netMode &= (~RS_NET_MODE_ACTUAL); /* clear actual flags */ + if (flags & RS_NET_FLAGS_EXTERNAL_ADDR) + { + it->second.netMode = RS_NET_MODE_EXT; + } + else if (flags & RS_NET_FLAGS_STABLE_UDP) + { + it->second.netMode = RS_NET_MODE_UDP; + } + else + { + it->second.netMode = RS_NET_MODE_UNREACHABLE; + } + + + /* always update VIS status */ + if (flags & RS_NET_FLAGS_USE_DISC) + { + it->second.visState &= (~RS_VIS_STATE_NODISC); + } + else + { + it->second.visState |= RS_VIS_STATE_NODISC; + } + + if (flags & RS_NET_FLAGS_USE_DHT) + { + it->second.visState &= (~RS_VIS_STATE_NODHT); + } + else + { + it->second.visState |= RS_VIS_STATE_NODHT; + } + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + } + + /* Determine Reachability (only advisory) */ + if (ownState.netMode & RS_NET_MODE_UDP) + { + if ((details.type & RS_NET_CONN_UDP_DHT_SYNC) || + (details.type & RS_NET_CONN_TCP_EXTERNAL)) + { + /* reachable! */ + it->second.state &= (~RS_PEER_S_UNREACHABLE); + } + else + { + /* unreachable */ + it->second.state |= RS_PEER_S_UNREACHABLE; + } + } + else if (ownState.netMode & RS_NET_MODE_UNREACHABLE) + { + if (details.type & RS_NET_CONN_TCP_EXTERNAL) + { + /* reachable! */ + it->second.state &= (~RS_PEER_S_UNREACHABLE); + } + else + { + /* unreachable */ + it->second.state |= RS_PEER_S_UNREACHABLE; + } + } + else + { + it->second.state &= (~RS_PEER_S_UNREACHABLE); + } + + if (!isFriend) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() NOT FRIEND "; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::peerStatus() NO CONNECT (not friend)"; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + return; + } + + /* if already connected -> done */ + if (it->second.state & RS_PEER_S_CONNECTED) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() PEER ONLINE ALREADY "; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::peerStatus() NO CONNECT (already connected!)"; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + + return; + } + + + /* are the addresses different? */ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus()"; + std::cerr << " id: " << id; + std::cerr << " laddr: " << inet_ntoa(laddr.sin_addr); + std::cerr << " lport: " << ntohs(laddr.sin_port); + std::cerr << " raddr: " << inet_ntoa(raddr.sin_addr); + std::cerr << " rport: " << ntohs(raddr.sin_port); + std::cerr << " type: " << type; + std::cerr << " flags: " << flags; + std::cerr << " source: " << source; + std::cerr << std::endl; +#endif + +#ifndef P3CONNMGR_NO_AUTO_CONNECTION + +#ifndef P3CONNMGR_NO_TCP_CONNECTIONS + + /* add in attempts ... local(TCP), remote(TCP) + * udp must come from notify + */ + + /* determine delay (for TCP connections) + * this is to ensure that simultaneous connections don't occur + * (which can fail). + * easest way is to compare ids ... and delay one of them + */ + + uint32_t tcp_delay = 0; + if (id > ownState.id) + { + tcp_delay = P3CONNMGR_TCP_DEFAULT_DELAY; + } + + /* if address is same -> try local */ + if ((isValidNet(&(details.laddr.sin_addr))) && + (sameNet(&(ownState.localaddr.sin_addr), &(details.laddr.sin_addr)))) + + { + /* add the local address */ + peerConnectAddress pca; + pca.ts = now; + pca.delay = tcp_delay; + pca.period = 0; + pca.type = RS_NET_CONN_TCP_LOCAL; + pca.addr = details.laddr; + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() ADDING TCP_LOCAL ADDR: "; + std::cerr << " id: " << id; + std::cerr << " laddr: " << inet_ntoa(pca.addr.sin_addr); + std::cerr << " lport: " << ntohs(pca.addr.sin_port); + std::cerr << " delay: " << pca.delay; + std::cerr << " period: " << pca.period; + std::cerr << " type: " << pca.type; + std::cerr << " source: " << source; + std::cerr << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::peerStatus() PushBack Local TCP Address: "; + out << " id: " << id; + out << " laddr: " << inet_ntoa(pca.addr.sin_addr); + out << ":" << ntohs(pca.addr.sin_port); + out << " type: " << pca.type; + out << " delay: " << pca.delay; + out << " period: " << pca.period; + out << " ts: " << pca.ts; + out << " source: " << source; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + + it->second.connAddrs.push_back(pca); + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() Not adding Local Connect (Diff Network)"; + std::cerr << " id: " << id; + std::cerr << " laddr: " << inet_ntoa(details.laddr.sin_addr); + std::cerr << ": " << ntohs(details.laddr.sin_port); + std::cerr << " own.laddr: " << inet_ntoa(ownState.localaddr.sin_addr); + std::cerr << ": " << ntohs(ownState.localaddr.sin_port); + std::cerr << std::endl; +#endif + } + + + if ((details.type & RS_NET_CONN_TCP_EXTERNAL) && + (isValidNet(&(details.raddr.sin_addr)))) + + { + /* add the remote address */ + peerConnectAddress pca; + pca.ts = now; + pca.delay = tcp_delay; + pca.period = 0; + pca.type = RS_NET_CONN_TCP_EXTERNAL; + pca.addr = details.raddr; + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() ADDING TCP_REMOTE ADDR: "; + std::cerr << " id: " << id; + std::cerr << " raddr: " << inet_ntoa(pca.addr.sin_addr); + std::cerr << " rport: " << ntohs(pca.addr.sin_port); + std::cerr << " delay: " << pca.delay; + std::cerr << " period: " << pca.period; + std::cerr << " type: " << pca.type; + std::cerr << " source: " << source; + std::cerr << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::peerStatus() PushBack Remote TCP Address: "; + out << " id: " << id; + out << " raddr: " << inet_ntoa(pca.addr.sin_addr); + out << ":" << ntohs(pca.addr.sin_port); + out << " type: " << pca.type; + out << " delay: " << pca.delay; + out << " period: " << pca.period; + out << " ts: " << pca.ts; + out << " source: " << source; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + + it->second.connAddrs.push_back(pca); + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() Not adding Remote Connect (Type != E or Invalid Network)"; + std::cerr << " id: " << id; + std::cerr << " raddr: " << inet_ntoa(details.raddr.sin_addr); + std::cerr << ": " << ntohs(details.raddr.sin_port); + std::cerr << " type: " << details.type; + std::cerr << std::endl; +#endif + } + +#endif // P3CONNMGR_NO_TCP_CONNECTIONS + + } /****** STACK UNLOCK MUTEX *******/ + + /* notify if they say we can, or we cannot connect ! */ + if (details.type & RS_NET_CONN_UDP_DHT_SYNC) + { + retryConnectNotify(id); + } +#else + } // P3CONNMGR_NO_AUTO_CONNECTION /****** STACK UNLOCK MUTEX *******/ +#endif // P3CONNMGR_NO_AUTO_CONNECTION + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + if (it->second.inConnAttempt) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() ALREADY IN CONNECT ATTEMPT: "; + std::cerr << " id: " << id; + std::cerr << std::endl; + + /* -> it'll automatically use the addresses */ + + std::cerr << "p3ConnectMgr::peerStatus() Resulting Peer State:" << std::endl; + printConnectState(it->second); + std::cerr << std::endl; +#endif + + return; + } + + + /* start a connection attempt */ + if (it->second.connAddrs.size() > 0) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() Started CONNECT ATTEMPT! "; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + + it->second.actions |= RS_PEER_CONNECT_REQ; + mStatusChanged = true; + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() No addr suitable for CONNECT ATTEMPT! "; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + } + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerStatus() Resulting Peer State:" << std::endl; + printConnectState(it->second); + std::cerr << std::endl; +#endif + +} + +void p3ConnectMgr::peerConnectRequest(std::string id, struct sockaddr_in raddr, + uint32_t source) +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest()"; + std::cerr << " id: " << id; + std::cerr << " raddr: " << inet_ntoa(raddr.sin_addr); + std::cerr << ":" << ntohs(raddr.sin_port); + std::cerr << " source: " << source; + std::cerr << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::peerConnectRequest()"; + out << " id: " << id; + out << " raddr: " << inet_ntoa(raddr.sin_addr); + out << ":" << ntohs(raddr.sin_port); + out << " source: " << source; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + + /******************** TCP PART *****************************/ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() Try TCP first"; + std::cerr << std::endl; +#endif + + retryConnectTCP(id); + + /******************** UDP PART *****************************/ + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + if (ownState.netMode & RS_NET_MODE_UNREACHABLE) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() Unreachable - no UDP connection"; + std::cerr << std::endl; +#endif + return; + } + + /* look up the id */ + std::map::iterator it; + bool isFriend = true; + it = mFriendList.find(id); + if (it == mFriendList.end()) + { + /* check Others list */ + isFriend = false; + it = mOthersList.find(id); + if (it == mOthersList.end()) + { + /* not found - ignore */ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() Peer Not Found - Ignore"; + std::cerr << std::endl; +#endif + return; + } +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() Peer is in mOthersList - Ignore"; + std::cerr << std::endl; +#endif + return; + } + + /* if already connected -> done */ + if (it->second.state & RS_PEER_S_CONNECTED) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() Already connected - Ignore"; + std::cerr << std::endl; +#endif + return; + } + + + time_t now = time(NULL); + /* this is a UDP connection request (DHT only for the moment!) */ + if (isValidNet(&(raddr.sin_addr))) + { + /* add the remote address */ + peerConnectAddress pca; + pca.ts = now; + pca.type = RS_NET_CONN_UDP_DHT_SYNC; + pca.delay = 0; + + if (source == RS_CB_DHT) + { + pca.period = P3CONNMGR_UDP_DHT_DELAY; +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() source = DHT "; + std::cerr << std::endl; +#endif + } + else if (source == RS_CB_PROXY) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() source = PROXY "; + std::cerr << std::endl; +#endif + pca.period = P3CONNMGR_UDP_PROXY_DELAY; + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() source = UNKNOWN "; + std::cerr << std::endl; +#endif + /* error! */ + pca.period = P3CONNMGR_UDP_PROXY_DELAY; + } + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() period = " << pca.period; + std::cerr << std::endl; +#endif + + pca.addr = raddr; + + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::peerConnectRequest() PushBack UDP Address: "; + out << " id: " << id; + out << " raddr: " << inet_ntoa(pca.addr.sin_addr); + out << ":" << ntohs(pca.addr.sin_port); + out << " type: " << pca.type; + out << " delay: " << pca.delay; + out << " period: " << pca.period; + out << " ts: " << pca.ts; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + + /* push to the back ... TCP ones should be tried first */ + it->second.connAddrs.push_back(pca); + } + + if (it->second.inConnAttempt) + { + /* -> it'll automatically use the addresses */ + return; + } + + /* start a connection attempt */ + if (it->second.connAddrs.size() > 0) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() Started CONNECT ATTEMPT! "; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + + it->second.actions |= RS_PEER_CONNECT_REQ; + mStatusChanged = true; + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::peerConnectRequest() No addr suitable for CONNECT ATTEMPT! "; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + } +} + + + +/*******************************************************************/ +/*******************************************************************/ + +bool p3ConnectMgr::addFriend(std::string id, uint32_t netMode, uint32_t visState, time_t lastContact) +{ + /* so three possibilities + * (1) already exists as friend -> do nothing. + * (2) is in others list -> move over. + * (3) is non-existant -> create new one. + */ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::addFriend() " << id; + std::cerr << std::endl; +#endif + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + + std::map::iterator it; + if (mFriendList.end() != mFriendList.find(id)) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::addFriend() Already Exists"; + std::cerr << std::endl; +#endif + /* (1) already exists */ + return true; + } + + /* check with the AuthMgr if its authorised */ + if (!mAuthMgr->isAuthenticated(id)) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::addFriend() Failed Authentication"; + std::cerr << std::endl; +#endif + /* no auth */ + return false; + } + + /* check if it is in others */ + if (mOthersList.end() != (it = mOthersList.find(id))) + { + /* (2) in mOthersList -> move over */ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::addFriend() Move from Others"; + std::cerr << std::endl; +#endif + + mFriendList[id] = it->second; + mOthersList.erase(it); + + it = mFriendList.find(id); + + /* setup state */ + it->second.state = RS_PEER_S_FRIEND; + it->second.actions = RS_PEER_NEW; + + /* setup connectivity parameters */ + it->second.visState = visState; + it->second.netMode = netMode; + it->second.lastcontact = lastContact; + + mStatusChanged = true; + + /* add peer to DHT (if not dark) */ + if (it->second.visState & RS_VIS_STATE_NODHT) + { + /* hidden from DHT world */ + netAssistFriend(id, false); + } + else + { + netAssistFriend(id, true); + } + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + return true; + } + + /* get details from AuthMgr */ + pqiAuthDetails detail; + if (!mAuthMgr->getDetails(id, detail)) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::addFriend() Failed to get Details"; + std::cerr << std::endl; +#endif + /* ERROR: no details */ + return false; + } + + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::addFriend() Creating New Entry"; + std::cerr << std::endl; +#endif + + /* create a new entry */ + peerConnectState pstate; + + pstate.id = id; + pstate.name = detail.name; + + pstate.state = RS_PEER_S_FRIEND; + pstate.actions = RS_PEER_NEW; + pstate.visState = visState; + pstate.netMode = netMode; + pstate.lastcontact = lastContact; + + /* addr & timestamps -> auto cleared */ + + mFriendList[id] = pstate; + + mStatusChanged = true; + + /* expect it to be a standard DHT */ + netAssistFriend(id, true); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + return true; +} + + +bool p3ConnectMgr::removeFriend(std::string id) +{ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::removeFriend() " << id; + std::cerr << std::endl; +#endif + + netAssistFriend(id, false); + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* move to othersList */ + bool success = false; + std::map::iterator it; + if (mFriendList.end() != (it = mFriendList.find(id))) + { + + peerConnectState peer = it->second; + + mFriendList.erase(it); + + peer.state &= (~RS_PEER_S_FRIEND); + peer.state &= (~RS_PEER_S_CONNECTED); + peer.state &= (~RS_PEER_S_ONLINE); + peer.actions = RS_PEER_MOVED; + peer.inConnAttempt = false; + mOthersList[id] = peer; + mStatusChanged = true; + + success = true; + } + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + return success; +} + + + +bool p3ConnectMgr::addNeighbour(std::string id) +{ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::addNeighbour() " << id; + std::cerr << std::endl; +#endif + + /* so three possibilities + * (1) already exists as friend -> do nothing. + * (2) already in others list -> do nothing. + * (3) is non-existant -> create new one. + */ + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + std::map::iterator it; + if (mFriendList.end() == mFriendList.find(id)) + { + /* (1) already exists */ + return false; + } + + if (mOthersList.end() == mOthersList.find(id)) + { + /* (2) already exists */ + return true; + } + + /* check with the AuthMgr if its valid */ + if (!mAuthMgr->isValid(id)) + { + /* no auth */ + return false; + } + + /* get details from AuthMgr */ + pqiAuthDetails detail; + if (!mAuthMgr->getDetails(id, detail)) + { + /* no details */ + return false; + } + + /* create a new entry */ + peerConnectState pstate; + + pstate.id = id; + pstate.name = detail.name; + + pstate.state = 0; + pstate.actions = 0; //RS_PEER_NEW; + pstate.visState = RS_VIS_STATE_STD; + pstate.netMode = RS_NET_MODE_UNKNOWN; + + /* addr & timestamps -> auto cleared */ + mOthersList[id] = pstate; + + return true; +} + + +/*******************************************************************/ +/*******************************************************************/ + /*************** External Control ****************/ +bool p3ConnectMgr::retryConnect(std::string id) +{ + retryConnectTCP(id); + retryConnectNotify(id); + + return true; +} + + +bool p3ConnectMgr::retryConnectTCP(std::string id) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* push addresses onto stack */ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP()"; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + + /* look up the id */ + std::map::iterator it; + if (mFriendList.end() == (it = mFriendList.find(id))) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() Peer is not Friend"; + std::cerr << std::endl; +#endif + return false; + } + + /* if already connected -> done */ + if (it->second.state & RS_PEER_S_CONNECTED) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() Peer Already Connected"; + std::cerr << std::endl; +#endif + return true; + } + + /* are the addresses different? */ + + time_t now = time(NULL); + std::list::iterator cit; + + /* add in attempts ... local(TCP), remote(TCP) + */ + +#ifndef P3CONNMGR_NO_TCP_CONNECTIONS + + /* if address is same -> try local */ + if ((isValidNet(&(it->second.localaddr.sin_addr))) && + (sameNet(&(ownState.localaddr.sin_addr), + &(it->second.localaddr.sin_addr)))) + + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() Local Address Valid: "; + std::cerr << inet_ntoa(it->second.localaddr.sin_addr); + std::cerr << ":" << ntohs(it->second.localaddr.sin_port); + std::cerr << std::endl; +#endif + + bool localExists = false; + if ((it->second.inConnAttempt) && + (it->second.currentConnAddr.type == RS_NET_CONN_TCP_LOCAL)) + { + localExists = true; + } + + for(cit = it->second.connAddrs.begin(); + (!localExists) && (cit != it->second.connAddrs.begin()); cit++) + { + if (cit->type == RS_NET_CONN_TCP_LOCAL) + { + localExists = true; + } + } + + /* check if there is a local one on there already */ + + if (!localExists) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() Adding Local Addr to Queue"; + std::cerr << std::endl; +#endif + + /* add the local address */ + peerConnectAddress pca; + pca.ts = now; + pca.type = RS_NET_CONN_TCP_LOCAL; + pca.addr = it->second.localaddr; + + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::retryConnectTCP() PushBack Local TCP Address: "; + out << " id: " << id; + out << " raddr: " << inet_ntoa(pca.addr.sin_addr); + out << ":" << ntohs(pca.addr.sin_port); + out << " type: " << pca.type; + out << " delay: " << pca.delay; + out << " period: " << pca.period; + out << " ts: " << pca.ts; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + + it->second.connAddrs.push_back(pca); + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() Local Addr already in Queue"; + std::cerr << std::endl; +#endif + } + } + + /* otherwise try external ... (should check flags) */ + //if ((isValidNet(&(it->second.serveraddr.sin_addr))) && + // (it->second.netMode = RS_NET_MODE_EXT)) + + /* always try external */ + if (isValidNet(&(it->second.serveraddr.sin_addr))) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() Ext Address Valid: "; + std::cerr << inet_ntoa(it->second.serveraddr.sin_addr); + std::cerr << ":" << ntohs(it->second.serveraddr.sin_port); + std::cerr << std::endl; +#endif + + + bool remoteExists = false; + if ((it->second.inConnAttempt) && + (it->second.currentConnAddr.type == RS_NET_CONN_TCP_EXTERNAL)) + { + remoteExists = true; + } + + for(cit = it->second.connAddrs.begin(); + (!remoteExists) && (cit != it->second.connAddrs.begin()); cit++) + { + if (cit->type == RS_NET_CONN_TCP_EXTERNAL) + { + remoteExists = true; + } + } + + /* check if there is a local one on there already */ + + if (!remoteExists) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() Adding Ext Addr to Queue"; + std::cerr << std::endl; +#endif + + /* add the remote address */ + peerConnectAddress pca; + pca.ts = now; + pca.type = RS_NET_CONN_TCP_EXTERNAL; + pca.addr = it->second.serveraddr; + + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::retryConnectTCP() PushBack Ext TCP Address: "; + out << " id: " << id; + out << " raddr: " << inet_ntoa(pca.addr.sin_addr); + out << ":" << ntohs(pca.addr.sin_port); + out << " type: " << pca.type; + out << " delay: " << pca.delay; + out << " period: " << pca.period; + out << " ts: " << pca.ts; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + + it->second.connAddrs.push_back(pca); + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() Ext Addr already in Queue"; + std::cerr << std::endl; +#endif + } + } + +#endif // P3CONNMGR_NO_TCP_CONNECTIONS + + /* flag as last attempt to prevent loop */ + it->second.lastattempt = time(NULL); + + if (it->second.inConnAttempt) + { + /* -> it'll automatically use the addresses */ + return true; + } + + /* start a connection attempt */ + if (it->second.connAddrs.size() > 0) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() Started CONNECT ATTEMPT! "; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + + it->second.actions |= RS_PEER_CONNECT_REQ; + mStatusChanged = true; + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectTCP() No addr suitable for CONNECT ATTEMPT! "; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + } + return true; +} + + +bool p3ConnectMgr::retryConnectNotify(std::string id) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* push addresses onto stack */ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectNotify()"; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + + /* look up the id */ + std::map::iterator it; + + if (mFriendList.end() == (it = mFriendList.find(id))) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectNotify() Peer is not Friend"; + std::cerr << std::endl; +#endif + return false; + } + + /* if already connected -> done */ + if (it->second.state & RS_PEER_S_CONNECTED) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectNotify() Peer Already Connected"; + std::cerr << std::endl; +#endif + return true; + } + + /* flag as last attempt to prevent loop */ + it->second.lastattempt = time(NULL); + + if (ownState.netMode & RS_NET_MODE_UNREACHABLE) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectNotify() UNREACHABLE so no Notify!"; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::retryConnectNotify() Notifying Peer"; + std::cerr << " id: " << id; + std::cerr << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3ConnectMgr::retryConnectNotify() Notifying Peer"; + out << " id: " << id; + rslog(RSL_WARNING, p3connectzone, out.str()); + } + + /* attempt UDP connection */ + netAssistNotify(id); + } + + return true; +} + + + + + +bool p3ConnectMgr::setLocalAddress(std::string id, struct sockaddr_in addr) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + if (id == mAuthMgr->OwnId()) + { + ownState.localaddr = addr; + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + return true; + } + + /* check if it is a friend */ + std::map::iterator it; + if (mFriendList.end() == (it = mFriendList.find(id))) + { + if (mOthersList.end() == (it = mOthersList.find(id))) + { + return false; + } + } + + /* "it" points to peer */ + it->second.localaddr = addr; + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + return true; +} + +bool p3ConnectMgr::setExtAddress(std::string id, struct sockaddr_in addr) +{ + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + + if (id == mAuthMgr->OwnId()) + { + ownState.serveraddr = addr; + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + return true; + } + + /* check if it is a friend */ + std::map::iterator it; + if (mFriendList.end() == (it = mFriendList.find(id))) + { + if (mOthersList.end() == (it = mOthersList.find(id))) + { + return false; + } + } + + /* "it" points to peer */ + it->second.serveraddr = addr; + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + return true; +} + +bool p3ConnectMgr::setNetworkMode(std::string id, uint32_t netMode) +{ + if (id == mAuthMgr->OwnId()) + { + uint32_t visState = ownState.visState; + setOwnNetConfig(netMode, visState); + + return true; + } + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* check if it is a friend */ + std::map::iterator it; + if (mFriendList.end() == (it = mFriendList.find(id))) + { + if (mOthersList.end() == (it = mOthersList.find(id))) + { + return false; + } + } + + /* "it" points to peer */ + it->second.netMode = netMode; + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + return false; +} + +bool p3ConnectMgr::setVisState(std::string id, uint32_t visState) +{ + if (id == mAuthMgr->OwnId()) + { + uint32_t netMode = ownState.netMode; + setOwnNetConfig(netMode, visState); + + return true; + } + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + /* check if it is a friend */ + std::map::iterator it; + bool isFriend = false; + if (mFriendList.end() == (it = mFriendList.find(id))) + { + if (mOthersList.end() == (it = mOthersList.find(id))) + { + return false; + } + } + else + { + isFriend = true; + } + + /* "it" points to peer */ + it->second.visState = visState; + if (isFriend) + { + /* toggle DHT state */ + if (it->second.visState & RS_VIS_STATE_NODHT) + { + /* hidden from DHT world */ + netAssistFriend(id, false); + } + else + { + netAssistFriend(id, true); + } + } + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + return false; +} + + + + +/*******************************************************************/ + +bool p3ConnectMgr::checkNetAddress() +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::checkNetAddress()"; + std::cerr << std::endl; +#endif + + std::list addrs = getLocalInterfaces(); + std::list::iterator it; + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + bool found = false; + for(it = addrs.begin(); (!found) && (it != addrs.end()); it++) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::checkNetAddress() Local Interface: " << *it; + std::cerr << std::endl; +#endif + + // Ive added the 'isNotLoopbackNet' to prevent re-using the lo address if this was saved in the + // configuration. In such a case, lo should only be chosen from getPreferredInterface as a last resort + // fallback solution. + // + if ((!isLoopbackNet(&ownState.localaddr.sin_addr)) && (*it) == inet_ntoa(ownState.localaddr.sin_addr)) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::checkNetAddress() Matches Existing Address! FOUND = true"; + std::cerr << std::endl; +#endif + found = true; + } + } + /* check that we didn't catch 0.0.0.0 - if so go for prefered */ + if ((found) && (ownState.localaddr.sin_addr.s_addr == 0)) + { + found = false; + } + + if (!found) + { + ownState.localaddr.sin_addr = getPreferredInterface(); + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::checkNetAddress() Local Address Not Found: Using Preferred Interface: "; + std::cerr << inet_ntoa(ownState.localaddr.sin_addr); + std::cerr << std::endl; +#endif + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + } + if ((isPrivateNet(&(ownState.localaddr.sin_addr))) || + (isLoopbackNet(&(ownState.localaddr.sin_addr)))) + { + /* firewalled */ + //own_cert -> Firewalled(true); + } + else + { + //own_cert -> Firewalled(false); + } + + int port = ntohs(ownState.localaddr.sin_port); + if ((port < PQI_MIN_PORT) || (port > PQI_MAX_PORT)) + { + ownState.localaddr.sin_port = htons(PQI_DEFAULT_PORT); + } + + /* if localaddr = serveraddr, then ensure that the ports + * are the same (modify server)... this mismatch can + * occur when the local port is changed.... + */ + + if (ownState.localaddr.sin_addr.s_addr == + ownState.serveraddr.sin_addr.s_addr) + { + ownState.serveraddr.sin_port = + ownState.localaddr.sin_port; + } + + // ensure that address family is set, otherwise windows Barfs. + ownState.localaddr.sin_family = AF_INET; + ownState.serveraddr.sin_family = AF_INET; + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::checkNetAddress() Final Local Address: "; + std::cerr << inet_ntoa(ownState.localaddr.sin_addr); + std::cerr << ":" << ntohs(ownState.localaddr.sin_port); + std::cerr << std::endl; +#endif + + return 1; +} + + +/************************* p3config functions **********************/ +/*******************************************************************/ + /* Key Functions to be overloaded for Full Configuration */ + +RsSerialiser *p3ConnectMgr::setupSerialiser() +{ + RsSerialiser *rss = new RsSerialiser(); + rss->addSerialType(new RsPeerConfigSerialiser()); + rss->addSerialType(new RsGeneralConfigSerialiser()) ; + + return rss; +} + + +std::list p3ConnectMgr::saveList(bool &cleanup) +{ + /* create a list of current peers */ + std::list saveData; + cleanup = true; + + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + + RsPeerNetItem *item = new RsPeerNetItem(); + item->clear(); + + item->pid = getOwnId(); + if (ownState.netMode & RS_NET_MODE_TRY_EXT) + { + item->netMode = RS_NET_MODE_EXT; + } + else if (ownState.netMode & RS_NET_MODE_TRY_UPNP) + { + item->netMode = RS_NET_MODE_UPNP; + } + else + { + item->netMode = RS_NET_MODE_UDP; + } + + item->visState = ownState.visState; + item->lastContact = ownState.lastcontact; + item->localaddr = ownState.localaddr; + item->remoteaddr = ownState.serveraddr; + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::saveList() Own Config Item:"; + std::cerr << std::endl; + item->print(std::cerr, 10); + std::cerr << std::endl; +#endif + + saveData.push_back(item); + + /* iterate through all friends and save */ + std::map::iterator it; + for(it = mFriendList.begin(); it != mFriendList.end(); it++) + { + item = new RsPeerNetItem(); + item->clear(); + + item->pid = it->first; + item->netMode = (it->second).netMode; + item->visState = (it->second).visState; + item->lastContact = (it->second).lastcontact; + item->localaddr = (it->second).localaddr; + item->remoteaddr = (it->second).serveraddr; + + saveData.push_back(item); +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::saveList() Peer Config Item:"; + std::cerr << std::endl; + item->print(std::cerr, 10); + std::cerr << std::endl; +#endif + } + + RsPeerStunItem *sitem = new RsPeerStunItem(); + + std::list::iterator sit; + uint32_t count = 0; + for(sit = mStunList.begin(); (sit != mStunList.end()) && + (count < RS_STUN_LIST_MIN); sit++, count++) + { + sitem->stunList.ids.push_back(*sit); + } + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::saveList() Peer Stun Item:"; + std::cerr << std::endl; + sitem->print(std::cerr, 10); + std::cerr << std::endl; +#endif + + saveData.push_back(sitem); + + // Now save config for network digging strategies + + RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ; + + RsTlvKeyValue kv; + kv.key = "USE_EXTR_IP_FINDER" ; + kv.value = (use_extr_addr_finder)?"TRUE":"FALSE" ; + vitem->tlvkvs.pairs.push_back(kv) ; + + std::cout << "Pushing item for use_extr_addr_finder = " << use_extr_addr_finder << std::endl ; + saveData.push_back(vitem); + + return saveData; +} + +bool p3ConnectMgr::loadList(std::list load) +{ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::loadList() Item Count: " << load.size(); + std::cerr << std::endl; +#endif + + /* load the list of peers */ + std::list::iterator it; + for(it = load.begin(); it != load.end(); it++) + { + RsPeerNetItem *pitem = dynamic_cast(*it); + RsPeerStunItem *sitem = dynamic_cast(*it); + RsConfigKeyValueSet *vitem = dynamic_cast(*it) ; + + if (pitem) + { + if (pitem->pid == getOwnId()) + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::loadList() Own Config Item:"; + std::cerr << std::endl; + pitem->print(std::cerr, 10); + std::cerr << std::endl; +#endif + /* add ownConfig */ + setOwnNetConfig(pitem->netMode, pitem->visState); + setLocalAddress(pitem->pid, pitem->localaddr); + setExtAddress(pitem->pid, pitem->remoteaddr); + } + else + { +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::loadList() Peer Config Item:"; + std::cerr << std::endl; + pitem->print(std::cerr, 10); + std::cerr << std::endl; +#endif + /* ************* */ + addFriend(pitem->pid, pitem->netMode, pitem->visState, pitem->lastContact); + setLocalAddress(pitem->pid, pitem->localaddr); + setExtAddress(pitem->pid, pitem->remoteaddr); + } + } + else if (sitem) + { + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::loadList() Stun Config Item:"; + std::cerr << std::endl; + sitem->print(std::cerr, 10); + std::cerr << std::endl; +#endif + std::list::iterator sit; + for(sit = sitem->stunList.ids.begin(); + sit != sitem->stunList.ids.end(); sit++) + { + mStunList.push_back(*sit); + } + } + else if(vitem) + { + RsStackMutex stack(connMtx); /****** STACK LOCK MUTEX *******/ + +#ifdef CONN_DEBUG + std::cerr << "p3ConnectMgr::loadList() General Variable Config Item:"; + std::cerr << std::endl; + vitem->print(std::cerr, 10); + std::cerr << std::endl; +#endif + if(vitem->tlvkvs.pairs.front().key == "USE_EXTR_IP_FINDER") + { + use_extr_addr_finder = (vitem->tlvkvs.pairs.front().value == "TRUE") ; + std::cerr << "setting use_extr_addr_finder to " << use_extr_addr_finder << std::endl ; + } + + } + + delete (*it); + } + return true; +} + + + +void printConnectState(peerConnectState &peer) +{ + +#ifdef CONN_DEBUG + std::cerr << "Friend: " << peer.name; + std::cerr << " Id: " << peer.id; + std::cerr << " State: " << peer.state; + if (peer.state & RS_PEER_S_FRIEND) + std::cerr << " S:RS_PEER_S_FRIEND"; + if (peer.state & RS_PEER_S_ONLINE) + std::cerr << " S:RS_PEER_S_ONLINE"; + if (peer.state & RS_PEER_S_CONNECTED) + std::cerr << " S:RS_PEER_S_CONNECTED"; + std::cerr << " Actions: " << peer.actions; + if (peer.actions & RS_PEER_NEW) + std::cerr << " A:RS_PEER_NEW"; + if (peer.actions & RS_PEER_MOVED) + std::cerr << " A:RS_PEER_MOVED"; + if (peer.actions & RS_PEER_CONNECTED) + std::cerr << " A:RS_PEER_CONNECTED"; + if (peer.actions & RS_PEER_DISCONNECTED) + std::cerr << " A:RS_PEER_DISCONNECTED"; + if (peer.actions & RS_PEER_CONNECT_REQ) + std::cerr << " A:RS_PEER_CONNECT_REQ"; + + std::cerr << std::endl; +#endif + return; +} + + + +bool p3ConnectMgr::addBootstrapStunPeers() +{ + std::string id; + struct sockaddr_in dummyaddr; + uint32_t flags = 0; + + /* only use the Bootstrap system now */ + + return true; +} + +/************************ INTERFACES ***********************/ + + +void p3ConnectMgr::addNetAssistFirewall(uint32_t id, pqiNetAssistFirewall *fwAgent) +{ + mFwAgents[id] = fwAgent; +} + + +bool p3ConnectMgr::enableNetAssistFirewall(bool on) +{ + std::map::iterator it; + for(it = mFwAgents.begin(); it != mFwAgents.end(); it++) + { + (it->second)->enable(on); + } + return true; +} + + +bool p3ConnectMgr::netAssistFirewallEnabled() +{ + std::map::iterator it; + for(it = mFwAgents.begin(); it != mFwAgents.end(); it++) + { + if ((it->second)->getEnabled()) + { + return true; + } + } + return false; +} + +bool p3ConnectMgr::netAssistFirewallActive() +{ + std::map::iterator it; + for(it = mFwAgents.begin(); it != mFwAgents.end(); it++) + { + if ((it->second)->getActive()) + { + return true; + } + } + return false; +} + +bool p3ConnectMgr::netAssistFirewallShutdown() +{ + std::map::iterator it; + for(it = mFwAgents.begin(); it != mFwAgents.end(); it++) + { + (it->second)->shutdown(); + } + return true; +} + +bool p3ConnectMgr::netAssistFirewallPorts(uint16_t iport, uint16_t eport) +{ + std::map::iterator it; + for(it = mFwAgents.begin(); it != mFwAgents.end(); it++) + { + (it->second)->setInternalPort(iport); + (it->second)->setExternalPort(eport); + } + return true; +} + + +bool p3ConnectMgr::netAssistExtAddress(struct sockaddr_in &extAddr) +{ + std::map::iterator it; + for(it = mFwAgents.begin(); it != mFwAgents.end(); it++) + { + if ((it->second)->getActive()) + { + if ((it->second)->getExternalAddress(extAddr)) + { + return true; + } + } + } + return false; +} + + +void p3ConnectMgr::addNetAssistConnect(uint32_t id, pqiNetAssistConnect *dht) +{ + mDhts[id] = dht; +} + +bool p3ConnectMgr::enableNetAssistConnect(bool on) +{ + std::map::iterator it; + for(it = mDhts.begin(); it != mDhts.end(); it++) + { + (it->second)->enable(on); + } + return true; +} + +bool p3ConnectMgr::netAssistConnectEnabled() +{ + std::map::iterator it; + for(it = mDhts.begin(); it != mDhts.end(); it++) + { + if ((it->second)->getEnabled()) + { + return true; + } + } + return false; +} + +bool p3ConnectMgr::netAssistConnectActive() +{ + std::map::iterator it; + for(it = mDhts.begin(); it != mDhts.end(); it++) + { + if ((it->second)->getActive()) + + { + return true; + } + } + return false; +} + +bool p3ConnectMgr::netAssistConnectShutdown() +{ + std::map::iterator it; + for(it = mDhts.begin(); it != mDhts.end(); it++) + { + (it->second)->shutdown(); + } + return true; +} + +bool p3ConnectMgr::netAssistFriend(std::string id, bool on) +{ + std::map::iterator it; + for(it = mDhts.begin(); it != mDhts.end(); it++) + { + if (on) + { + (it->second)->findPeer(id); + } + else + { + (it->second)->dropPeer(id); + } + } + return true; +} + +bool p3ConnectMgr::netAssistAddStun(std::string id) +{ + std::map::iterator it; + for(it = mDhts.begin(); it != mDhts.end(); it++) + { + (it->second)->addStun(id); + } + return true; +} + + +bool p3ConnectMgr::netAssistStun(bool on) +{ + std::map::iterator it; + for(it = mDhts.begin(); it != mDhts.end(); it++) + { + (it->second)->enableStun(on); + } + return true; +} + +bool p3ConnectMgr::netAssistNotify(std::string id) +{ + std::map::iterator it; + for(it = mDhts.begin(); it != mDhts.end(); it++) + { + (it->second)->notifyPeer(id); + } + return true; +} + + +bool p3ConnectMgr::netAssistSetAddress( struct sockaddr_in &laddr, + struct sockaddr_in &eaddr, + uint32_t mode) +{ + std::map::iterator it; + for(it = mDhts.begin(); it != mDhts.end(); it++) + { + (it->second)->setExternalInterface(laddr, eaddr, mode); + } + return true; +} + + +bool p3ConnectMgr::getUPnPState() +{ + return netAssistFirewallActive(); +} + +bool p3ConnectMgr::getUPnPEnabled() +{ + return netAssistFirewallEnabled(); +} + +bool p3ConnectMgr::getDHTEnabled() +{ + return netAssistConnectEnabled(); +} + + + +bool p3ConnectMgr::getNetStatusOk() +{ + return netFlagOk; +} + +bool p3ConnectMgr::getNetStatusUpnpOk() +{ + return netFlagUpnpOk; +} + +bool p3ConnectMgr::getNetStatusDhtOk() +{ + return netFlagDhtOk; +} + +bool p3ConnectMgr::getNetStatusExtOk() +{ + return netFlagExtOk; +} + +bool p3ConnectMgr::getNetStatusUdpOk() +{ + return netFlagUdpOk; +} + +bool p3ConnectMgr::getNetStatusTcpOk() +{ + return netFlagTcpOk; +} + +bool p3ConnectMgr::getNetResetReq() +{ + return netFlagResetReq; +} + + + diff --git a/libretroshare/src/_pqi/p3connmgr.h b/libretroshare/src/_pqi/p3connmgr.h new file mode 100644 index 000000000..e5e454eb5 --- /dev/null +++ b/libretroshare/src/_pqi/p3connmgr.h @@ -0,0 +1,404 @@ +/* + * libretroshare/src/pqi: p3connmgr.h + * + * 3P/PQI network interface for RetroShare. + * + * 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". + * + */ + +#ifndef P3CONNMNGR_H +#define P3CONNMNGR_H + +#include "_pqi/p3dhtmgr.h" // Only need it for constants. +#include "_tcponudp/tou.h" +#include "_tcponudp/extaddrfinder.h" + +#include "_util/rsprint.h" +#include "_util/rsdebug.h" + +#include "_serialiser/rsconfigitems.h" +#include "_pqi/pqinotify.h" + +#include "_pqi/pqimonitor.h" +#include "_pqi/p3authmgr.h" + +//#include "_pqi/p3dhtmgr.h" +//#include "_pqi/p3upnpmgr.h" +#include "_pqi/pqiassist.h" + +#include "_pqi/p3cfgmgr.h" + +#include "_util/rsthreads.h" + + +#include + +class ExtAddrFinder ; + + /* RS_VIS_STATE_XXXX + * determines how public this peer wants to be... + * + * STD = advertise to Peers / DHT checking etc + * GRAY = share with friends / but not DHT + * DARK = hidden from all + * BROWN? = hidden from friends / but on DHT + */ + +const uint32_t RS_VIS_STATE_NODISC = 0x0001; +const uint32_t RS_VIS_STATE_NODHT = 0x0002; + +const uint32_t RS_VIS_STATE_STD = 0x0000; +const uint32_t RS_VIS_STATE_GRAY = RS_VIS_STATE_NODHT; +const uint32_t RS_VIS_STATE_DARK = RS_VIS_STATE_NODISC | RS_VIS_STATE_NODHT; +const uint32_t RS_VIS_STATE_BROWN = RS_VIS_STATE_NODISC; + + + + + /* Startup Modes (confirmed later) */ +const uint32_t RS_NET_MODE_TRYMODE = 0x00f0; + +const uint32_t RS_NET_MODE_TRY_EXT = 0x0010; +const uint32_t RS_NET_MODE_TRY_UPNP = 0x0020; +const uint32_t RS_NET_MODE_TRY_UDP = 0x0040; + + /* Actual State */ +const uint32_t RS_NET_MODE_ACTUAL = 0x000f; + +const uint32_t RS_NET_MODE_UNKNOWN = 0x0000; +const uint32_t RS_NET_MODE_EXT = 0x0001; +const uint32_t RS_NET_MODE_UPNP = 0x0002; +const uint32_t RS_NET_MODE_UDP = 0x0004; +const uint32_t RS_NET_MODE_UNREACHABLE = 0x0008; + + +/* order of attempts ... */ +const uint32_t RS_NET_CONN_TCP_ALL = 0x000f; +const uint32_t RS_NET_CONN_UDP_ALL = 0x00f0; + +const uint32_t RS_NET_CONN_TCP_LOCAL = 0x0001; +const uint32_t RS_NET_CONN_TCP_EXTERNAL = 0x0002; +const uint32_t RS_NET_CONN_UDP_DHT_SYNC = 0x0010; +const uint32_t RS_NET_CONN_UDP_PEER_SYNC = 0x0020; /* coming soon */ + +/* extra flags */ +// not sure if needed yet. +//const uint32_t RS_NET_CONN_PEERAGE = 0x0f00; +//const uint32_t RS_NET_CONN_SERVER = 0x0100; /* TCP only */ +//const uint32_t RS_NET_CONN_PEER = 0x0200; /* all UDP */ + + +/* flags of peerStatus */ +const uint32_t RS_NET_FLAGS_USE_DISC = 0x0001; +const uint32_t RS_NET_FLAGS_USE_DHT = 0x0002; +const uint32_t RS_NET_FLAGS_ONLINE = 0x0004; +const uint32_t RS_NET_FLAGS_EXTERNAL_ADDR = 0x0008; +const uint32_t RS_NET_FLAGS_STABLE_UDP = 0x0010; +const uint32_t RS_NET_FLAGS_TRUSTS_ME = 0x0020; + +const uint32_t RS_TCP_STD_TIMEOUT_PERIOD = 5; /* 5 seconds! */ + +class peerAddrInfo +{ + public: + peerAddrInfo(); /* init */ + + bool found; + uint32_t type; + struct sockaddr_in laddr, raddr; + time_t ts; +}; + +class peerConnectAddress +{ + public: + peerConnectAddress(); /* init */ + + struct sockaddr_in addr; + uint32_t delay; /* to stop simultaneous connects */ + uint32_t period; /* UDP only */ + uint32_t type; + time_t ts; +}; + +class peerConnectState +{ + public: + peerConnectState(); /* init */ + + std::string id; + + uint32_t netMode; /* EXT / UPNP / UDP / INVALID */ + uint32_t visState; /* STD, GRAY, DARK */ + + struct sockaddr_in localaddr, serveraddr; + + time_t lastcontact; + + /***** Below here not stored permanently *****/ + + uint32_t connecttype; // RS_NET_CONN_TCP_ALL / RS_NET_CONN_UDP_ALL + time_t lastavailable; + time_t lastattempt; + + std::string name; + + uint32_t state; + uint32_t actions; + + uint32_t source; /* most current source */ + peerAddrInfo dht; + peerAddrInfo disc; + peerAddrInfo peer; + + /* a list of connect attempts to make (in order) */ + bool inConnAttempt; + peerConnectAddress currentConnAddr; + std::list connAddrs; + +}; + + +class p3ConnectMgr: public pqiConnectCb, public p3Config +{ + public: + + p3ConnectMgr(p3AuthMgr *authMgr); + +void tick(); + + /*************** Setup ***************************/ +void addNetAssistConnect(uint32_t type, pqiNetAssistConnect *); +void addNetAssistFirewall(uint32_t type, pqiNetAssistFirewall *); + +bool checkNetAddress(); /* check our address is sensible */ + + /*************** External Control ****************/ +bool shutdown(); /* blocking shutdown call */ + +bool retryConnect(std::string id); + +bool getUPnPState(); +bool getUPnPEnabled(); +bool getDHTEnabled(); + +bool getIPServersEnabled() { return use_extr_addr_finder ;} +void setIPServersEnabled(bool b) ; +void getIPServersList(std::list& ip_servers) ; + +bool getNetStatusOk(); +bool getNetStatusUpnpOk(); +bool getNetStatusDhtOk(); +bool getNetStatusExtOk(); +bool getNetStatusUdpOk(); +bool getNetStatusTcpOk(); +bool getNetResetReq(); + +void setOwnNetConfig(uint32_t netMode, uint32_t visState); +bool setLocalAddress(std::string id, struct sockaddr_in addr); +bool setExtAddress(std::string id, struct sockaddr_in addr); + +bool setNetworkMode(std::string id, uint32_t netMode); +bool setVisState(std::string id, uint32_t visState); + + /* add/remove friends */ +bool addFriend(std::string id, uint32_t netMode = RS_NET_MODE_UDP, + uint32_t visState = RS_VIS_STATE_STD , time_t lastContact = 0); + +bool removeFriend(std::string); +bool addNeighbour(std::string); + + /*************** External Control ****************/ + + /* access to network details (called through Monitor) */ +const std::string getOwnId(); +bool getOwnNetStatus(peerConnectState &state); + +bool isFriend(std::string id); +bool isOnline(std::string id); +bool getFriendNetStatus(std::string id, peerConnectState &state); +bool getOthersNetStatus(std::string id, peerConnectState &state); + +void getOnlineList(std::list &peers); +void getFriendList(std::list &peers); +void getOthersList(std::list &peers); + + + /**************** handle monitors *****************/ +void addMonitor(pqiMonitor *mon); +void removeMonitor(pqiMonitor *mon); + + /******* overloaded from pqiConnectCb *************/ +virtual void peerStatus(std::string id, + struct sockaddr_in laddr, struct sockaddr_in raddr, + uint32_t type, uint32_t flags, uint32_t source); +virtual void peerConnectRequest(std::string id, + struct sockaddr_in raddr, uint32_t source); +virtual void stunStatus(std::string id, struct sockaddr_in raddr, uint32_t type, uint32_t flags); + + /****************** Connections *******************/ +bool connectAttempt(std::string id, struct sockaddr_in &addr, + uint32_t &delay, uint32_t &period, uint32_t &type); +bool connectResult(std::string id, bool success, uint32_t flags); + + +protected: + /****************** Internal Interface *******************/ +virtual bool enableNetAssistFirewall(bool on); +virtual bool netAssistFirewallEnabled(); +virtual bool netAssistFirewallActive(); +virtual bool netAssistFirewallShutdown(); + +virtual bool enableNetAssistConnect(bool on); +virtual bool netAssistConnectEnabled(); +virtual bool netAssistConnectActive(); +virtual bool netAssistConnectShutdown(); + + /* Assist Firewall */ +bool netAssistExtAddress(struct sockaddr_in &extAddr); +bool netAssistFirewallPorts(uint16_t iport, uint16_t eport); + + /* Assist Connect */ +virtual bool netAssistFriend(std::string id, bool on); +virtual bool netAssistAddStun(std::string id); +virtual bool netAssistStun(bool on); +virtual bool netAssistNotify(std::string id); +virtual bool netAssistSetAddress( struct sockaddr_in &laddr, + struct sockaddr_in &eaddr, + uint32_t mode); + + + /* Internal Functions */ +void statusTick(); +void netTick(); +void netStartup(); + + /* startup the bits */ +void netDhtInit(); +void netUdpInit(); +void netStunInit(); + +void netStatusReset(); + + +void netInit(); + +void netExtInit(); +void netExtCheck(); + +void netUpnpInit(); +void netUpnpCheck(); + +void netUdpCheck(); +void netUnreachableCheck(); + + /* Udp / Stun functions */ +bool udpInternalAddress(struct sockaddr_in iaddr); +bool udpExtAddressCheck(); +void udpStunPeer(std::string id, struct sockaddr_in &addr); + +void stunInit(); +bool stunCheck(); +void stunCollect(std::string id, struct sockaddr_in addr, uint32_t flags); +bool addBootstrapStunPeers(); + + /* monitor control */ +void tickMonitors(); + + /* connect attempts */ +bool retryConnectTCP(std::string id); +bool retryConnectNotify(std::string id); + + /* temporary for testing */ +//virtual void loadConfiguration() { return; } + + protected: +/*****************************************************************/ +/*********************** p3config ******************************/ + /* Key Functions to be overloaded for Full Configuration */ + virtual RsSerialiser *setupSerialiser(); + virtual std::list saveList(bool &cleanup); + virtual bool loadList(std::list load); +/*****************************************************************/ + + +#if 0 + +void setupOwnNetConfig(RsPeerConfigItem *item); +void addPeer(RsPeerConfigItem *item); + +#endif + +private: + + p3AuthMgr *mAuthMgr; + + std::map mFwAgents; + std::map mDhts; + + RsMutex connMtx; /* protects below */ + + time_t mNetInitTS; + uint32_t mNetStatus; + + uint32_t mStunStatus; + uint32_t mStunFound; + bool mStunMoreRequired; + + bool mStatusChanged; + + std::list clients; + + ExtAddrFinder *mExtAddrFinder ; + bool use_extr_addr_finder ; + + /* external Address determination */ + bool mUpnpAddrValid, mStunAddrValid; + bool mStunAddrStable; + struct sockaddr_in mUpnpExtAddr; + struct sockaddr_in mStunExtAddr; + + /* network status flags (read by rsiface) */ + bool netFlagOk; + bool netFlagUpnpOk; + bool netFlagDhtOk; + bool netFlagExtOk; + bool netFlagUdpOk; + bool netFlagTcpOk; + bool netFlagResetReq; + + + /* these are protected for testing */ +protected: + +void addPeer(std::string id, std::string name); /* tmp fn */ + + peerConnectState ownState; + + std::list mStunList; + std::map mFriendList; + std::map mOthersList; +}; + +#endif // P3CONNMNGR_H + + + + diff --git a/libretroshare/src/_pqi/p3dhtmgr.cc b/libretroshare/src/_pqi/p3dhtmgr.cc new file mode 100644 index 000000000..557d1d34e --- /dev/null +++ b/libretroshare/src/_pqi/p3dhtmgr.cc @@ -0,0 +1,1788 @@ +/* + * libretroshare/src/pqi: p3dhtmgr.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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 "_pqi/p3dhtmgr.h" + +const int p3dhtzone = 3892; + +/***** + * #define DHT_DEBUG 1 + * #define P3DHTMGR_USE_LOCAL_UDP_CONN 1 // For Testing only + ****/ + +/**** DHT State Variables **** + * TODO: + * (1) notify call in. + * (2) publish/search parameters. + * (3) callback. + * (4) example + * + */ + +/**** DHT State Variables ****/ + +#define DHT_STATE_OFF 0 +#define DHT_STATE_INIT 1 +#define DHT_STATE_CHECK_PEERS 2 +#define DHT_STATE_FIND_STUN 3 +#define DHT_STATE_ACTIVE 4 + +/* TIMEOUTS (Key ones in .h) */ +#define DHT_RESTART_PERIOD 300 /* 5 min */ +#define DHT_DEFAULT_PERIOD 300 /* Default period if no work to do */ +#define DHT_MIN_PERIOD 1 /* to ensure we don't get too many requests */ +#define DHT_SHORT_PERIOD 10 /* a short period */ + +#define DHT_DEFAULT_WAITTIME 1 /* Std sleep break period */ + +#define DHT_NUM_BOOTSTRAP_BINS 8 +#define DHT_MIN_BOOTSTRAP_REQ_PERIOD 30 + +void printDhtPeerEntry(dhtPeerEntry *ent, std::ostream &out); + +/* Interface class for DHT data */ + +dhtPeerEntry::dhtPeerEntry() + :state(DHT_PEER_INIT), lastTS(0), + notifyPending(0), notifyTS(0), + type(DHT_ADDR_INVALID) +{ + laddr.sin_addr.s_addr = 0; + laddr.sin_port = 0; + laddr.sin_family = 0; + + raddr.sin_addr.s_addr = 0; + raddr.sin_port = 0; + raddr.sin_family = 0; + + return; +} + +p3DhtMgr::p3DhtMgr(std::string id, pqiConnectCb *cb) + :pqiNetAssistConnect(id, cb), mStunRequired(true) +{ + /* setup own entry */ + dhtMtx.lock(); /* LOCK MUTEX */ + + ownEntry.id = id; + ownEntry.state = DHT_PEER_INIT; + ownEntry.type = DHT_ADDR_INVALID; + ownEntry.lastTS = 0; + + ownEntry.notifyPending = 0; + ownEntry.notifyTS = 0; + + ownEntry.hash1 = RsUtil::HashId(id, false); + ownEntry.hash2 = RsUtil::HashId(id, true); + + mDhtModifications = true; + mDhtOn = false; + mDhtState = DHT_STATE_OFF; + + mBootstrapAllowed = true; + mLastBootstrapListTS = 0; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + return; +} + + /* OVERLOADED from pqiNetAssistConnect + */ + +void p3DhtMgr::shutdown() +{ + /* ??? */ + +} + +void p3DhtMgr::restart() +{ + /* ??? */ +} + +void p3DhtMgr::enable(bool on) +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + mDhtModifications = true; + mDhtOn = on; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ +} + +bool p3DhtMgr::getEnabled() +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + bool on = mDhtOn; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + return on; +} + +bool p3DhtMgr::getActive() +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + bool act = dhtActive(); + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + return act; +} + +void p3DhtMgr::setBootstrapAllowed(bool on) +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + mBootstrapAllowed = on; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ +} + +bool p3DhtMgr::getBootstrapAllowed() +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + bool on = mBootstrapAllowed; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + return on; +} + +/******************************** PEER MANAGEMENT ********************************** + * + */ + /* set key data */ +bool p3DhtMgr::setExternalInterface( + struct sockaddr_in laddr, + struct sockaddr_in raddr, + uint32_t type) +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + mDhtModifications = true; + ownEntry.laddr = laddr; + ownEntry.raddr = raddr; + ownEntry.type = type; + ownEntry.state = DHT_PEER_ADDR_KNOWN; /* will force republish */ + ownEntry.lastTS = 0; /* will force republish */ + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::setExternalInterface()"; + std::cerr << " laddr: " << inet_ntoa(ownEntry.laddr.sin_addr); + std::cerr << " lport: " << ntohs(ownEntry.laddr.sin_port); + std::cerr << " raddr: " << inet_ntoa(ownEntry.raddr.sin_addr); + std::cerr << " rport: " << ntohs(ownEntry.raddr.sin_port); + std::cerr << " type: " << ownEntry.type; + std::cerr << " state: " << ownEntry.state; + std::cerr << std::endl; +#endif + + /* Log External Interface too */ + std::ostringstream out; + out << "p3DhtMgr::setExternalInterface()"; + out << " laddr: " << inet_ntoa(ownEntry.laddr.sin_addr); + out << " lport: " << ntohs(ownEntry.laddr.sin_port); + out << " raddr: " << inet_ntoa(ownEntry.raddr.sin_addr); + out << " rport: " << ntohs(ownEntry.raddr.sin_port); + out << " type: " << ownEntry.type; + out << " state: " << ownEntry.state; + + rslog(RSL_WARNING, p3dhtzone, out.str()); + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + checkOwnDHTKeys(); + return true; +} + + + /* add / remove peers */ +bool p3DhtMgr::findPeer(std::string id) +{ + RsStackMutex stack(dhtMtx); /***** LOCK MUTEX *****/ + + mDhtModifications = true; + + std::map::iterator it; + it = peers.find(id); + if (it != peers.end()) + { + /* reset some of it */ + it->second.state = DHT_PEER_INIT; + + // No point destroying a valid address!. + //it->second.type = DHT_ADDR_INVALID; + + // Reset notify + it->second.notifyPending = 0; + it->second.notifyTS = 0; + + return true; + } + + /* if they are not in the list -> add */ + dhtPeerEntry ent; + ent.id = id; + ent.state = DHT_PEER_INIT; + ent.type = DHT_ADDR_INVALID; + ent.lastTS = 0; + + ent.notifyPending = 0; + ent.notifyTS = 0; + + /* fill in hashes */ + ent.hash1 = RsUtil::HashId(id, false); + ent.hash2 = RsUtil::HashId(id, true); + + /* other fields don't matter */ + + peers[id] = ent; + + return true; +} + +bool p3DhtMgr::dropPeer(std::string id) +{ + RsStackMutex stack(dhtMtx); /***** LOCK MUTEX *****/ + + mDhtModifications = true; + + /* once we are connected ... don't worry about them anymore */ + std::map::iterator it; + it = peers.find(id); + if (it == peers.end()) + { + return false; + } + + /* leave it in there - just switch to off */ + it->second.state = DHT_PEER_OFF; + + return true; +} + + /* post DHT key saying we should connect */ +bool p3DhtMgr::notifyPeer(std::string id) +{ + RsStackMutex stack(dhtMtx); /***** LOCK MUTEX *****/ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::notifyPeer() " << id << std::endl; +#endif + + std::map::iterator it; + it = peers.find(id); + if (it == peers.end()) + { + return false; + } + /* ignore OFF peers */ + if (it->second.state == DHT_PEER_OFF) + { + return false; + } + + + time_t now = time(NULL); + + if (now - it->second.notifyTS < 2 * DHT_NOTIFY_PERIOD) + { + /* drop the notify (too soon) */ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::notifyPeer() TO SOON - DROPPING" << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3DhtMgr::notifyPeer() Id: " << id; + out << " TO SOON - DROPPING"; + rslog(RSL_WARNING, p3dhtzone, out.str()); + } + return false; + } + + it->second.notifyPending = RS_CONNECT_ACTIVE; + it->second.notifyTS = time(NULL); + + /* Trigger search if not found! */ + if (it->second.state != DHT_PEER_FOUND) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::notifyPeer() PEER NOT FOUND - Trigger search" << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3DhtMgr::notifyPeer() Id: " << id; + out << " PEER NOT FOUND - Trigger Search"; + rslog(RSL_WARNING, p3dhtzone, out.str()); + } + it->second.lastTS = 0; + } + + mDhtModifications = true; /* no wait! */ + + return true; +} + + /* extract current peer status */ +bool p3DhtMgr::getPeerStatus(std::string id, + struct sockaddr_in &laddr, + struct sockaddr_in &raddr, + uint32_t &type, uint32_t &state) +{ + RsStackMutex stack(dhtMtx); /* LOCK MUTEX */ + + std::map::iterator it; + it = peers.find(id); + + /* ignore OFF peers */ + if ((it == peers.end()) || (it->second.state == DHT_PEER_OFF)) + { + return false; + } + + laddr = it->second.laddr; + raddr = it->second.raddr; + type = it->second.type; + state = it->second.type; + + return true; +} + +/********************************* STUN HANDLING ********************************** + * add / cleanup / stun. + * + */ + + /* stun */ +bool p3DhtMgr::addStun(std::string id) +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + mDhtModifications = true; + + std::list::iterator it; + it = std::find(stunIds.begin(), stunIds.end(), id); + if (it != stunIds.end()) + { + dhtMtx.unlock(); /* UNLOCK MUTEX */ + return false; + } + stunIds.push_back(id); + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + return true; +} + +bool p3DhtMgr::enableStun(bool on) +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + mDhtModifications = true; + + if (!on) + { + /* clear up */ + stunIds.clear(); + } + + mStunRequired = on; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + return true; +} + + +void p3DhtMgr::run() +{ + /* + * + */ + + while(1) + { + checkDHTStatus(); + + +#ifdef DHT_DEBUG + status(std::cerr); +#endif + + dhtMtx.lock(); /* LOCK MUTEX */ + + uint32_t dhtState = mDhtState; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + int period = 60; /* default wait */ + switch(dhtState) + { + case DHT_STATE_INIT: +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() state = INIT -> wait" << std::endl; +#endif + period = 10; + break; + case DHT_STATE_CHECK_PEERS: +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() state = CHECK_PEERS -> do stuff" << std::endl; +#endif + checkPeerDHTKeys(); + checkStunState(); + period = DHT_MIN_PERIOD; + break; + case DHT_STATE_FIND_STUN: +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() state = FIND_STUN -> do stuff" << std::endl; +#endif + doStun(); + checkPeerDHTKeys(); /* keep on going - as we could be in this state for a while */ + checkStunState(); + period = DHT_MIN_PERIOD; + break; + case DHT_STATE_ACTIVE: + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() state = ACTIVE -> do stuff" << std::endl; +#endif + + period = checkOwnDHTKeys(); +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() checkOwnDHTKeys() period: " << period << std::endl; +#endif + int tmpperiod = checkNotifyDHT(); +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() checkNotifyDHT() period: " << tmpperiod << std::endl; +#endif + int tmpperiod2 = checkPeerDHTKeys(); +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() checkPeerDHTKeys() period: " << tmpperiod2 << std::endl; +#endif + if (tmpperiod < period) + period = tmpperiod; + if (tmpperiod2 < period) + period = tmpperiod2; + + /* finally we need to keep stun going */ + if (checkStunState_Active()) + { + /* still more stun to do */ + period = DHT_SHORT_PERIOD; + doStun(); + } + + } + break; + default: + case DHT_STATE_OFF: +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() state = OFF -> wait" << std::endl; +#endif + period = 60; + break; + } + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() sleeping for: " << period << std::endl; +#endif + + /* Breakable sleep loop */ + + bool toBreak = false; + int waittime = 1; + int i; + for(i = 0; i < period; i += waittime) + { + if (period-i > DHT_DEFAULT_WAITTIME) + { + waittime = DHT_DEFAULT_WAITTIME; + } + else + { + waittime = period-i; + } + + dhtMtx.lock(); /* LOCK MUTEX */ + + if (mDhtModifications) + { + mDhtModifications = false; + toBreak = true; + } + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + if (toBreak) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::run() breaking sleep" << std::endl; +#endif + + break; /* speed up config modifications */ + } + +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS + sleep(waittime); +#else + Sleep(1000 * waittime); +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + + } + } +} + + +int p3DhtMgr::checkOwnDHTKeys() +{ + int repubPeriod = 10000; + time_t now = time(NULL); + + /* in order of importance: + * (1) Check for Own Key publish. + * (2) Check for notification requests + * (3) Check for Peers + */ + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys()" << std::endl; +#endif + + dhtMtx.lock(); /* LOCK MUTEX */ + + dhtPeerEntry peer = ownEntry; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + /* publish ourselves if necessary */ + if (peer.state >= DHT_PEER_ADDR_KNOWN) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys() OWN ADDR KNOWN" << std::endl; +#endif + if ((peer.state < DHT_PEER_PUBLISHED) || + (now - peer.lastTS > DHT_PUBLISH_PERIOD)) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys() OWN ADDR REPUB" << std::endl; +#endif + +#ifdef DHT_DEBUG + std::cerr << "PUBLISH: "; + std::cerr << " hash1: " << RsUtil::BinToHex(peer.hash1); + std::cerr << " laddr: " << inet_ntoa(peer.laddr.sin_addr); + std::cerr << ":" << ntohs(peer.laddr.sin_port); + std::cerr << " raddr: " << inet_ntoa(peer.raddr.sin_addr); + std::cerr << ":" << ntohs(peer.raddr.sin_port); + std::cerr << " type: " << peer.type; + std::cerr << std::endl; +#endif + + { + /* Log */ + std::ostringstream out; + out << "p3DhtMgr::checkOwnDHTKeys() PUBLISH OWN ADDR:"; + out << " hash1: " << RsUtil::BinToHex(peer.hash1); + out << " laddr: " << inet_ntoa(peer.laddr.sin_addr); + out << " :" << ntohs(peer.laddr.sin_port); + out << " raddr: " << inet_ntoa(peer.raddr.sin_addr); + out << ":" << ntohs(peer.raddr.sin_port); + out << " type: " << peer.type; + + rslog(RSL_WARNING, p3dhtzone, out.str()); + } + + /* publish own key */ + if (dhtPublish(peer.hash1, peer.laddr, peer.raddr, peer.type, "")) + { + dhtMtx.lock(); /* LOCK MUTEX */ + + ownEntry.lastTS = now; + ownEntry.state = DHT_PEER_PUBLISHED; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + } + + /* dhtBootstrap -> if allowed and EXT port */ + if (peer.type & RS_NET_CONN_TCP_EXTERNAL) + { + dhtMtx.lock(); /* LOCK MUTEX */ + + bool doBootstrapPub = mBootstrapAllowed; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + if (doBootstrapPub) + { + dhtBootstrap(randomBootstrapId(), peer.hash1, ""); + } + } + + /* restart immediately */ + repubPeriod = DHT_MIN_PERIOD; + return repubPeriod; + } + else + { + if (now - peer.lastTS < DHT_PUBLISH_PERIOD) + { + repubPeriod = DHT_PUBLISH_PERIOD - + (now - peer.lastTS); + } + else + { + repubPeriod = 10; + } +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys() repub in: "; + std::cerr << repubPeriod << std::endl; +#endif + } + + + /* check for connect requests */ + //if ((peer.state == DHT_PEER_PUBLISHED) && + // (!(peer.type & RS_NET_CONN_TCP_EXTERNAL))) + if (peer.state == DHT_PEER_PUBLISHED) + { + if (now - peer.notifyTS >= DHT_NOTIFY_PERIOD) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys() check for Notify (rep=0)"; + std::cerr << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3DhtMgr::checkOwnDHTKeys() Checking for NOTIFY"; + rslog(RSL_WARNING, p3dhtzone, out.str()); + } + + if (dhtSearch(peer.hash1, DHT_MODE_NOTIFY)) + { + dhtMtx.lock(); /* LOCK MUTEX */ + + ownEntry.notifyTS = now; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + } + + /* restart immediately */ + repubPeriod = DHT_MIN_PERIOD; + return repubPeriod; + } + else + { + repubPeriod = DHT_NOTIFY_PERIOD - + (now - peer.notifyTS); + if (repubPeriod < DHT_MIN_PERIOD) + { + repubPeriod = DHT_MIN_PERIOD; + } +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys() check Notify in: "; + std::cerr << repubPeriod << std::endl; +#endif + } + } + else + { + if (peer.state != DHT_PEER_PUBLISHED) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys() No Notify until Published"; + std::cerr << std::endl; +#endif + } + else if (peer.type & RS_NET_CONN_TCP_EXTERNAL) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys() No Notify because have Ext Addr"; + std::cerr << std::endl; +#endif + } + else + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys() No Notify: Unknown Reason"; + std::cerr << std::endl; +#endif + } + } + + } + else + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkOwnDHTKeys() PEER ADDR UNKNOWN" << std::endl; +#endif + repubPeriod = 10; + } + return repubPeriod; +} + + +int p3DhtMgr::checkPeerDHTKeys() +{ + /* now loop through the peers */ + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkPeerDHTKeys()" << std::endl; +#endif + + dhtMtx.lock(); /* LOCK MUTEX */ + + /* iterate through and find min time and suitable candidate */ + std::map::iterator it,pit; + time_t now = time(NULL); + uint32_t period = 0; + uint32_t repeatPeriod = 6000; + + pit = peers.end(); + time_t pTS = now; + + for(it = peers.begin(); it != peers.end(); it++) + { + /* ignore OFF peers */ + if (it->second.state == DHT_PEER_OFF) + { + continue; + } + + time_t delta = now - it->second.lastTS; + if (it->second.state < DHT_PEER_FOUND) + { + period = DHT_SEARCH_PERIOD; + } + else /* (it->second.state == DHT_PEER_FOUND) */ + { + period = DHT_CHECK_PERIOD; + } +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkPeerDHTKeys() Peer: " << it->second.id; + std::cerr << " Period: " << period; + std::cerr << " Delta: " << delta; + std::cerr << std::endl; +#endif + + + if ((unsigned) delta >= period) + { + if (it->second.lastTS < pTS) + { + pit = it; + pTS = it->second.lastTS; + } + repeatPeriod = DHT_MIN_PERIOD; + } + else if (period - delta < repeatPeriod) + { + repeatPeriod = period - delta; + } + } + + /* now have - peer to handle, and period to next call */ + + if (pit == peers.end()) + { + dhtMtx.unlock(); /* UNLOCK MUTEX */ + return repeatPeriod; + } + + /* update timestamp + * clear FOUND or INIT state. + * */ + + pit->second.lastTS = now; + pit->second.state = DHT_PEER_SEARCH; + + dhtPeerEntry peer = (pit->second); + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + /* now search for the peer */ + dhtSearch(peer.hash1, DHT_MODE_SEARCH); + + /* results come through callback */ + return repeatPeriod; +} + + +int p3DhtMgr::checkNotifyDHT() +{ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkNotifyDHT()" << std::endl; +#endif + /* now loop through the peers */ + uint32_t notifyType = 0; + dhtPeerEntry peer; + dhtPeerEntry own; + + { + RsStackMutex stack(dhtMtx); /***** LOCK MUTEX *****/ + + /* iterate through and find min time and suitable candidate */ + std::map::iterator it; + time_t now = time(NULL); + int repeatPeriod = DHT_DEFAULT_PERIOD; + + /* find the first with a notify flag */ + for(it = peers.begin(); it != peers.end(); it++) + { + /* ignore OFF peers */ + if (it->second.state == DHT_PEER_OFF) + { + continue; + } + + if (it->second.notifyPending) + { + /* if very old drop it */ + if (now - it->second.notifyTS > DHT_NOTIFY_PERIOD) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkNotifyDHT() Dropping OLD Notify: "; + std::cerr << it->first << std::endl; +#endif + it->second.notifyPending = 0; + } + + if (it->second.state == DHT_PEER_FOUND) + { + notifyType = it->second.notifyPending; + break; + } + } + } + + /* now have - peer to handle */ + if (it == peers.end()) + { + return repeatPeriod; + } + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkNotifyDHT() Notify From: "; + std::cerr << it->first << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3DhtMgr::checkNotifyDHT() Notify Request for Known Peer: " << it->first; + rslog(RSL_WARNING, p3dhtzone, out.str()); + } + + /* update timestamp */ + it->second.notifyTS = now; + it->second.notifyPending = 0; + + peer = (it->second); + own = ownEntry; + + } /******* UNLOCK ******/ + + if (notifyType == RS_CONNECT_ACTIVE) + { + /* publish notification (publish Our Id) + * We publish the connection attempt on peers hash, + * using our alternative hash.. + * */ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkNotifyDHT() Posting Active Notify"; + std::cerr << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3DhtMgr::checkNotifyDHT() POST DHT (Active Notify) for Peer: " << peer.id; + rslog(RSL_WARNING, p3dhtzone, out.str()); + } + + dhtNotify(peer.hash1, own.hash2, ""); + } + + /* feedback to say we started it! */ +#ifdef P3DHTMGR_USE_LOCAL_UDP_CONN + mConnCb->peerConnectRequest(peer.id, peer.laddr, RS_CB_DHT); +#else + mConnCb->peerConnectRequest(peer.id, peer.raddr, RS_CB_DHT); +#endif + + return DHT_MIN_PERIOD; +} + + + +int p3DhtMgr::doStun() +{ + if (stunIds.size() < 1) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::doStun() Failed -> no Ids left" << std::endl; +#endif + return 0; + } + + dhtMtx.lock(); /* LOCK MUTEX */ + + bool stunRequired = mStunRequired; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + /* now loop through the peers */ + if (!stunRequired) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::doStun() not Required" << std::endl; +#endif + return 0; + } + + /* pop the front one */ + std::string activeStunId = stunIds.front(); + + stunIds.pop_front(); + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::doStun() STUN: " << RsUtil::BinToHex(activeStunId); + std::cerr << std::endl; +#endif + + /* look it up! */ + dhtSearch(activeStunId, DHT_MODE_SEARCH); + + return 1; +} + + + +int p3DhtMgr::checkStunState() +{ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkStunState()" << std::endl; +#endif + dhtMtx.lock(); /* LOCK MUTEX */ + + /* now loop through the peers */ + if (!mStunRequired) + { + mDhtState = DHT_STATE_ACTIVE; + } + + if (mDhtState == DHT_STATE_CHECK_PEERS) + { + /* check that they have all be searched for */ + std::map::iterator it; + for(it = peers.begin(); it != peers.end(); it++) + { + if (it->second.state == DHT_PEER_INIT) + { + break; + } + } + + if (it == peers.end()) + { + /* we've checked all peers */ + mDhtState = DHT_STATE_FIND_STUN; + } + } + else if (mDhtState == DHT_STATE_FIND_STUN) + { + /* if we run out of stun peers -> get some more */ + if (stunIds.size() < 1) + { +#ifdef DHT_DEBUG + std::cerr << "WARNING: out of Stun Peers - switching to Active Now" << std::endl; +#endif + mDhtState = DHT_STATE_ACTIVE; + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + /* this is a locked function */ + getDhtBootstrapList(); + + dhtMtx.lock(); /* LOCK MUTEX */ + } + } + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + return 1; +} + +int p3DhtMgr::checkStunState_Active() +{ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkStunState_Active()" << std::endl; +#endif + dhtMtx.lock(); /* LOCK MUTEX */ + + bool stunReq = mStunRequired; + bool moreIds = ((mStunRequired) && (stunIds.size() < 1)); + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + if (moreIds) + /* if we run out of stun peers -> get some more */ + { +#ifdef DHT_DEBUG + std::cerr << "WARNING: out of Stun Peers - getting more" << std::endl; +#endif + getDhtBootstrapList(); + } + + return stunReq; +} + +bool p3DhtMgr::getDhtBootstrapList() +{ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::getDHTBootstrapList()" << std::endl; +#endif + dhtMtx.lock(); /* LOCK MUTEX */ + + time_t now = time(NULL); + if (now - mLastBootstrapListTS < DHT_MIN_BOOTSTRAP_REQ_PERIOD) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::getDHTBootstrapList() Waiting: "; + std::cerr << DHT_MIN_BOOTSTRAP_REQ_PERIOD-(now-mLastBootstrapListTS); + std::cerr << " secs" << std::endl; +#endif + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + return false; + } + + mLastBootstrapListTS = now; + std::string bootId = randomBootstrapId(); + + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::getDHTBootstrapList() bootId: 0x"; + std::cerr << RsUtil::BinToHex(bootId) << std::endl; +#endif + + dhtSearch(bootId, DHT_MODE_SEARCH); + + return true; +} + + +std::string p3DhtMgr::BootstrapId(uint32_t bin) +{ + /* generate these from an equation! (makes it easy) + * Make sure that NUM_BOOTSTRAP_BINS doesn't affect ids + */ + + std::ostringstream genId; + genId << "BootstrapId"; + + uint32_t id = (bin % DHT_NUM_BOOTSTRAP_BINS) * 1234; + + genId << id; + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::BootstrapId() generatedId: "; + std::cerr << genId.str() << std::endl; +#endif + + /* now hash this to create a bootstrap Bin Id */ + std::string bootId = RsUtil::HashId(genId.str(), false); + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::BootstrapId() bootId: 0x"; + std::cerr << RsUtil::BinToHex(bootId) << std::endl; +#endif + + return bootId; +} + +std::string p3DhtMgr::randomBootstrapId() +{ + uint32_t rnd = DHT_NUM_BOOTSTRAP_BINS * (rand() / (RAND_MAX + 1.0)); + + return BootstrapId(rnd); +} + + + +void p3DhtMgr::checkDHTStatus() +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + bool isActive = (mDhtState != DHT_STATE_OFF); + + bool toShutdown = false; + bool toStartup = false; + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() mDhtState: " << mDhtState << std::endl; + std::cerr << "p3DhtMgr::checkDhtStatus() mDhtOn : " << mDhtOn << std::endl; +#endif + + if ((isActive) && (!mDhtOn)) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() Active & !mDhtOn -> toShutdown" << std::endl; +#endif + toShutdown = true; + } + + if ((!isActive) && (mDhtOn)) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() !Active & mDhtOn -> toStartup" << std::endl; +#endif + toStartup = true; + } + + /* restart if it has shutdown */ + if (isActive && mDhtOn) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() Active & mDhtOn" << std::endl; +#endif + if (dhtActive()) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() dhtActive() = true" << std::endl; +#endif + if (mDhtState == DHT_STATE_INIT) + { + mDhtState = DHT_STATE_CHECK_PEERS; +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() Switching to CHECK PEERS" << std::endl; +#endif + } + } + else + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() dhtActive() = false" << std::endl; +#endif + if (mDhtActiveTS - time(NULL) > DHT_RESTART_PERIOD) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() restart Period..." << std::endl; +#endif + toShutdown = true; + toStartup = true; + } + } + } + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + if (toShutdown) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() toShutdown = true -> shutdown()" << std::endl; +#endif + if (dhtShutdown()) + { + clearDhtData(); + + dhtMtx.lock(); /* LOCK MUTEX */ + + mDhtState = DHT_STATE_OFF; +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() mDhtState -> OFF" << std::endl; +#endif + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + } + } + + if (toStartup) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() toStartup = true -> dhtInit()" << std::endl; +#endif + if (dhtInit()) + { + + dhtMtx.lock(); /* LOCK MUTEX */ + + mDhtState = DHT_STATE_INIT; + mDhtActiveTS = time(NULL); + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::checkDhtStatus() mDhtState -> INIT" << std::endl; +#endif + + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + } + } +} + +void p3DhtMgr::clearDhtData() +{ + std::cerr << "p3DhtMgr::clearDhtData() DUMMY FN" << std::endl; +} + + +/****************************** REAL DHT INTERFACE ********************************* + * publish/search/result. + * + * dummy implementation for testing. + */ + +int p3DhtMgr::status(std::ostream &out) +{ + dhtMtx.lock(); /* LOCK MUTEX */ + + out << "p3DhtMgr::status() ************************************" << std::endl; + out << "mDhtState: " << mDhtState << std::endl; + out << "mDhtOn : " << mDhtOn << std::endl; + out << "dhtActive: " << dhtActive() << std::endl; + + /* now own state */ + out << "OWN DETAILS -------------------------------------------" << std::endl; + printDhtPeerEntry(&ownEntry, out); + out << "OWN DETAILS END----------------------------------------" << std::endl; + + /* now peers states */ + std::map::iterator it; + out << "PEER DETAILS ------------------------------------------" << std::endl; + for(it = peers.begin(); it != peers.end(); it++) + { + printDhtPeerEntry(&(it->second), out); + } + out << "PEER DETAILS END---------------------------------------" << std::endl; + + /* now stun states */ + out << "STUN DETAILS ------------------------------------------" << std::endl; + out << "Available Stun Ids: " << stunIds.size() << std::endl; + out << "STUN DETAILS END---------------------------------------" << std::endl; + + + out << "p3DhtMgr::status() END ********************************" << std::endl; + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + return 0; +} + + + + + +bool p3DhtMgr::dhtInit() +{ + std::cerr << "p3DhtMgr::dhtInit() DUMMY FN" << std::endl; + return true; +} + +bool p3DhtMgr::dhtShutdown() +{ + std::cerr << "p3DhtMgr::dhtShutdown() DUMMY FN" << std::endl; + return true; +} + +bool p3DhtMgr::dhtActive() +{ + std::cerr << "p3DhtMgr::dhtActive() DUMMY FN" << std::endl; + return true; +} + +bool p3DhtMgr::publishDHT(std::string key, std::string value, uint32_t ttl) +{ + std::cerr << "p3DhtMgr::publishDHT() DUMMY FN" << std::endl; + return false; +} + +bool p3DhtMgr::searchDHT(std::string idhash) +{ + std::cerr << "p3DhtMgr::searchDHT() DUMMY FN" << std::endl; + return false; +} + + + + +/****************************** INTERMEDIATE DHT INTERFACE ********************************* + * publish/search/result. + * + * Take the 'real' parameters and create the key/value parameters for the real dht. + */ + + +bool p3DhtMgr::dhtPublish(std::string idhash, + struct sockaddr_in &laddr, struct sockaddr_in &raddr, + uint32_t type, std::string sign) +{ + /* ... goes and searches */ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtPublish()" << std::endl; + + std::cerr << "PUBLISHing: idhash: " << RsUtil::BinToHex(idhash); + std::cerr << " laddr: " << inet_ntoa(laddr.sin_addr); + std::cerr << ":" << ntohs(laddr.sin_port); + std::cerr << " raddr: " << inet_ntoa(raddr.sin_addr); + std::cerr << ":" << ntohs(raddr.sin_port); + std::cerr << " type: " << type; + std::cerr << " sign: " << sign; + std::cerr << std::endl; +#endif + + /* Create a Value from addresses and type */ + /* to store the ip address and flags */ + + std::ostringstream out; + out << "RSDHT:" << std::setw(2) << std::setfill('0') << DHT_MODE_SEARCH << ": "; + out << "IPL=" << inet_ntoa(laddr.sin_addr) << ":" << ntohs(laddr.sin_port) << ", "; + out << "IPE=" << inet_ntoa(raddr.sin_addr) << ":" << ntohs(raddr.sin_port) << ", "; + out << "type=" << std::setw(4) << std::setfill('0') << std::hex << type << ", "; + +/******* + char valuearray[1024]; + snprintf(valuearray, 1024, "RSDHT:%02d: IPL=%s:%d, IPE=%s:%d, type=%04X,", + DHT_MODE_SEARCH, + inet_ntoa(laddr.sin_addr), + ntohs(laddr.sin_port), + inet_ntoa(raddr.sin_addr), + ntohs(raddr.sin_port), + type); + + std::string value = valuearray; +******/ + + std::string value = out.str(); + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtPublish()" << std::endl; + + std::cerr << "PUBLISH: key: " << RsUtil::BinToHex(idhash); + std::cerr << " value: " << value; + std::cerr << std::endl; +#endif + + /* call to the real DHT */ + return publishDHT(idhash, value, DHT_TTL_PUBLISH); +} + +bool p3DhtMgr::dhtNotify(std::string idhash, std::string ownIdHash, std::string sign) +{ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtNotify()" << std::endl; +#endif + + std::ostringstream value; + value << "RSDHT:" << std::setw(2) << std::setfill('0') << DHT_MODE_NOTIFY << ":"; + value << ownIdHash; + + /* call to the real DHT */ + return publishDHT(idhash, value.str(), DHT_TTL_NOTIFY); +} + +bool p3DhtMgr::dhtSearch(std::string idhash, uint32_t mode) +{ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtSearch()" << std::endl; +#endif + + /* call to the real DHT */ + return searchDHT(idhash); +} + + +bool p3DhtMgr::dhtBootstrap(std::string storehash, std::string ownIdHash, std::string sign) +{ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtBootstrap()" << std::endl; +#endif + + std::ostringstream value; + value << "RSDHT:" << std::setw(2) << std::setfill('0') << DHT_MODE_BOOTSTRAP << ":"; + value << ownIdHash; + + /* call to the real DHT */ + return publishDHT(storehash, value.str(), DHT_TTL_BOOTSTRAP); +} + + +/****************************** DHT FEEDBACK INTERFACE ********************************* + * Two functions... + * (1) The interpretation function. + * (2) callback handling. + * + */ + +bool p3DhtMgr::resultDHT(std::string key, std::string value) +{ + /* so .... two possibilities. + * + * RSDHT:01: IPL ... IPE ... TYPE ... (advertising) + * RSDHT:02: HASH (connect request) + * + */ + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() key: 0x" << RsUtil::BinToHex(key); + std::cerr << " value: 0x" << RsUtil::BinToHex(value) << std::endl; +#endif + + /* variables for dhtResult() call */ + struct sockaddr_in laddr; + struct sockaddr_in raddr; + std::string sign; + + + int32_t reqType; + uint32_t loc; + if (1 > sscanf(value.c_str(), "RSDHT:%d: %n", &reqType, &loc)) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() Not RSDHT msg -> discarding" << std::endl; +#endif + /* failed */ + return false; + } + + + dhtMtx.lock(); /* LOCK MUTEX */ + std::string ownhash = ownEntry.hash1; + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + switch(reqType) + { + case DHT_MODE_NOTIFY: + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() NOTIFY msg" << std::endl; +#endif + + if (ownhash != key) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() NOTIFY msg not for us -> discarding" << std::endl; +#endif + return false; + } + + /* get the hash */ + std::string notifyHash = value.substr(loc); +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() NOTIFY msg HASH:-> 0x" << RsUtil::BinToHex(notifyHash) << "<-" << std::endl; +#endif + /* call out */ + dhtResultNotify(notifyHash); + + break; + } + + case DHT_MODE_SEARCH: + { + + if (ownhash == key) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() SEARCH msg is OWN PUBLISH -> discarding" << std::endl; +#endif + return false; + } + + uint32_t a1,b1,c1,d1,e1; + uint32_t a2,b2,c2,d2,e2; + uint32_t flags; + + if (sscanf(&((value.c_str())[loc]), " IPL=%d.%d.%d.%d:%d, IPE=%d.%d.%d.%d:%d, type=%x", + &a1, &b1, &c1, &d1, &e1, &a2, &b2, &c2, &d2, &e2, &flags) != 11) + { + /* failed to scan */ +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() SEARCH parse failed of:" << (&((value.c_str())[loc])); + std::cerr << std::endl; +#endif + return false; + } + + std::ostringstream out1; + out1 << a1 << "." << b1 << "." << c1 << "." << d1; + inet_aton(out1.str().c_str(), &(laddr.sin_addr)); + laddr.sin_port = htons(e1); + laddr.sin_family = AF_INET; + + std::ostringstream out2; + out2 << a2 << "." << b2 << "." << c2 << "." << d2; + inet_aton(out2.str().c_str(), &(raddr.sin_addr)); + raddr.sin_port = htons(e2); + raddr.sin_family = AF_INET; + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() SEARCH laddr: " << out1.str() << ":" << e1; + std::cerr << " raddr: " << out2.str() << ":" << e2; + std::cerr << " flags: " << flags; + std::cerr << std::endl; +#endif + + return dhtResultSearch(key, laddr, raddr, flags, sign); + + break; + } + + case DHT_MODE_BOOTSTRAP: + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() BOOTSTRAP msg" << std::endl; +#endif + + /* get the hash */ + std::string bootId = value.substr(loc); +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::resultDHT() BOOTSTRAP msg, IdHash:->" << RsUtil::BinToHex(bootId) << "<-" << std::endl; +#endif + /* call out */ + dhtResultBootstrap(bootId); + + break; + } + + default: + + return false; + break; + } + + return false; +} + + + + +bool p3DhtMgr::dhtResultBootstrap(std::string idhash) +{ + RsStackMutex stack(dhtMtx); /***** LOCK MUTEX *****/ + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtResultBootstrap() from idhash: "; + std::cerr << RsUtil::BinToHex(idhash) << std::endl; +#endif + + /* Temp - to avoid duplication during testing */ + if (stunIds.end() == std::find(stunIds.begin(), stunIds.end(), idhash)) + { + stunIds.push_back(idhash); +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtResultBootstrap() adding to StunList"; + std::cerr << std::endl; +#endif + } + else + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtResultBootstrap() DUPLICATE not adding to List"; + std::cerr << std::endl; +#endif + } + + + return true; +} + + + + +bool p3DhtMgr::dhtResultNotify(std::string idhash) +{ + RsStackMutex stack(dhtMtx); /***** LOCK MUTEX *****/ + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtResultNotify() from idhash: "; + std::cerr << RsUtil::BinToHex(idhash) << std::endl; +#endif + std::map::iterator it; + time_t now = time(NULL); + + /* if notify - we must match on the second hash */ + for(it = peers.begin(); (it != peers.end()) && ((it->second).hash2 != idhash); it++); + + /* update data */ + std::string peerid; + + /* ignore OFF peers */ + if ((it != peers.end()) && (it->second.state != DHT_PEER_OFF)) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtResult() NOTIFY for id: " << it->first << std::endl; +#endif + { + /* Log */ + std::ostringstream out; + out << "p3DhtMgr::dhtResultNotify() NOTIFY from Id: " << it->first; + rslog(RSL_WARNING, p3dhtzone, out.str()); + } + + /* delay callback -> if they are not found */ + it->second.notifyTS = now; + it->second.notifyPending = RS_CONNECT_PASSIVE; + mDhtModifications = true; /* no wait! */ + + if (it->second.state != DHT_PEER_FOUND) + { + /* flag for immediate search */ + it->second.lastTS = 0; + } + } + else + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtResult() unknown NOTIFY "; + std::cerr << RsUtil::BinToHex(idhash) << std::endl; +#endif + } + + return true; +} + + +bool p3DhtMgr::dhtResultSearch(std::string idhash, + struct sockaddr_in &laddr, struct sockaddr_in &raddr, + uint32_t type, std::string sign) +{ + dhtMtx.lock(); /* LOCK MUTEX */ + +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtResultSearch() for idhash: "; + std::cerr << RsUtil::BinToHex(idhash) << std::endl; +#endif + std::map::iterator it; + bool doCb = false; + bool doStun = false; + uint32_t stunFlags = 0; + time_t now = time(NULL); + + dhtPeerEntry ent; + + /* if search - we must match on the second hash */ + for(it = peers.begin(); (it != peers.end()) && ((it->second).hash1 != idhash); it++); + + /* update data */ + /* ignore OFF peers */ + if ((it != peers.end()) && (it->second.state != DHT_PEER_OFF)) + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtResult() SEARCH for id: " << it->first << std::endl; +#endif + it->second.lastTS = now; + + { + /* Log */ + std::ostringstream out; + out << "p3DhtMgr::dhtSearchResult() for Id: " << it->first; + out << " laddr: " << inet_ntoa(laddr.sin_addr); + out << ":" << ntohs(laddr.sin_port); + out << " raddr: " << inet_ntoa(raddr.sin_addr); + out << ":" << ntohs(raddr.sin_port); + out << " type: " << ownEntry.type; + + rslog(RSL_WARNING, p3dhtzone, out.str()); + } + + /* update info .... always */ + it->second.state = DHT_PEER_FOUND; + it->second.laddr = laddr; + it->second.raddr = raddr; + it->second.type = type; + + /* Do callback all the time */ + ent = it->second; + doCb = true; + + if (it->second.notifyPending) + { + /* no wait if we have pendingNotification */ + mDhtModifications = true; + } + + /* do Stun always */ + doStun = true; + stunFlags = RS_STUN_FRIEND | RS_STUN_ONLINE; + } + else + { +#ifdef DHT_DEBUG + std::cerr << "p3DhtMgr::dhtResult() SEARCH(stun) for idhash: "; + std::cerr << RsUtil::BinToHex(idhash) << std::endl; +#endif + /* stun result? */ + doStun = true; + stunFlags = RS_STUN_ONLINE; + } + + dhtMtx.unlock(); /* UNLOCK MUTEX */ + + if (doCb) + { + mConnCb->peerStatus(ent.id, ent.laddr, ent.raddr, + ent.type, 0, RS_CB_DHT); + } + + if (doStun) + { + mConnCb->stunStatus(idhash, raddr, type, stunFlags); + } + + return true; +} + + + +/******************************** AUX FUNCTIONS ********************************** + * + */ + +void printDhtPeerEntry(dhtPeerEntry *ent, std::ostream &out) +{ + + out << "DhtEntry: ID: " << ent->id; + out << " State: " << ent->state; + out << " lastTS: " << ent->lastTS; + out << " notifyPending: " << ent->notifyPending; + out << " notifyTS: " << ent->notifyTS; + out << std::endl; + out << " laddr: " << inet_ntoa(ent->laddr.sin_addr) << ":" << ntohs(ent->laddr.sin_port); + out << " raddr: " << inet_ntoa(ent->raddr.sin_addr) << ":" << ntohs(ent->raddr.sin_port); + out << " type: " << ent->type; + out << std::endl; + out << " hash1: " << RsUtil::BinToHex(ent->hash1); + out << std::endl; + out << " hash2: " << RsUtil::BinToHex(ent->hash2); + out << std::endl; + return; +} + + diff --git a/libretroshare/src/_pqi/p3dhtmgr.h b/libretroshare/src/_pqi/p3dhtmgr.h new file mode 100644 index 000000000..cc99c00ae --- /dev/null +++ b/libretroshare/src/_pqi/p3dhtmgr.h @@ -0,0 +1,263 @@ +/* + * libretroshare/src/pqi: p3dhtmgr.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + + +#ifndef P3DHDHTMGR_H +#define P3DHDHTMGR_H + +/* Interface class for DHT data */ + +#include +#include +#include +#include +#include +#include + +#include "_pqi/pqinetwork.h" + +#include "_util/rsthreads.h" +#include "_pqi/pqimonitor.h" + +#include "_pqi/pqiassist.h" + + + + +#include "_pqi/p3connmgr.h" + +#include "_util/rsprint.h" +#include "_util/rsdebug.h" + +/* All other #defs are in .cc */ +#define DHT_ADDR_INVALID 0xff +#define DHT_ADDR_TCP 0x01 +#define DHT_ADDR_UDP 0x02 + + +/* for DHT peer STATE */ +#define DHT_PEER_OFF 0 +#define DHT_PEER_INIT 1 +#define DHT_PEER_SEARCH 2 +#define DHT_PEER_FOUND 3 + +/* for DHT peer STATE (ownEntry) */ +#define DHT_PEER_ADDR_KNOWN 4 +#define DHT_PEER_PUBLISHED 5 + +/* Interface with Real DHT Implementation */ +#define DHT_MODE_SEARCH 1 +#define DHT_MODE_PUBLISH 1 +#define DHT_MODE_NOTIFY 2 +#define DHT_MODE_BOOTSTRAP 3 + + +/* TIMEOUTS: Reference Values are set here... */ + +#define DHT_SEARCH_PERIOD 1800 /* PeerKeys: if we haven't found them: 30 min */ +#define DHT_CHECK_PERIOD 1800 /* PeerKeys: re-lookup peer: 30 min */ +#define DHT_PUBLISH_PERIOD 1800 /* OwnKey: 30 min */ +#define DHT_NOTIFY_PERIOD 300 /* 5 min - Notify Check period */ + +/* TTLs for DHTs posts */ +#define DHT_TTL_PUBLISH (DHT_PUBLISH_PERIOD + 120) // for a little overlap. +#define DHT_TTL_NOTIFY (DHT_NOTIFY_PERIOD + 60) // for time to find it... +#define DHT_TTL_BOOTSTRAP (DHT_PUBLISH_PERIOD) // To start with. + +class dhtPeerEntry +{ + public: + dhtPeerEntry(); + + std::string id; + uint32_t state; + time_t lastTS; + + uint32_t notifyPending; + time_t notifyTS; + + struct sockaddr_in laddr, raddr; + uint32_t type; /* ADDR_TYPE as defined above */ + + std::string hash1; /* SHA1 Hash of id */ + std::string hash2; /* SHA1 Hash of reverse Id */ +}; + +class p3DhtMgr: public pqiNetAssistConnect, public RsThread +{ + /* + */ + public: + p3DhtMgr(std::string id, pqiConnectCb *cb); + + /********** External DHT Interface ************************ + * These Functions are the external interface + * for the DHT, and must be non-blocking and return quickly + */ + + /* OVERLOADED From pqiNetAssistConnect. */ + +virtual void enable(bool on); +virtual void shutdown(); +virtual void restart(); + +virtual bool getEnabled(); /* on */ +virtual bool getActive(); /* actually working */ + +virtual void setBootstrapAllowed(bool on); +virtual bool getBootstrapAllowed(); + + /* set key data */ +virtual bool setExternalInterface(struct sockaddr_in laddr, + struct sockaddr_in raddr, uint32_t type); + + /* add / remove peers */ +virtual bool findPeer(std::string id); +virtual bool dropPeer(std::string id); + + /* post DHT key saying we should connect (callback when done) */ +virtual bool notifyPeer(std::string id); + + /* extract current peer status */ +virtual bool getPeerStatus(std::string id, + struct sockaddr_in &laddr, struct sockaddr_in &raddr, + uint32_t &type, uint32_t &mode); + + /* stun */ +virtual bool enableStun(bool on); +virtual bool addStun(std::string id); + //doneStun(); + + /********** Higher Level DHT Work Functions ************************ + * These functions translate from the strings/addresss to + * key/value pairs. + */ + public: + + /* results from DHT proper */ +virtual bool dhtResultNotify(std::string id); +virtual bool dhtResultSearch(std::string id, + struct sockaddr_in &laddr, struct sockaddr_in &raddr, + uint32_t type, std::string sign); + +virtual bool dhtResultBootstrap(std::string idhash); + + protected: + + /* can block briefly (called only from thread) */ +virtual bool dhtPublish(std::string id, + struct sockaddr_in &laddr, + struct sockaddr_in &raddr, + uint32_t type, std::string sign); + +virtual bool dhtNotify(std::string peerid, std::string ownId, + std::string sign); + +virtual bool dhtSearch(std::string id, uint32_t mode); + +virtual bool dhtBootstrap(std::string storehash, std::string ownIdHash, + std::string sign); /* to publish bootstrap */ + + + + /********** Actual DHT Work Functions ************************ + * These involve a very simple LOW-LEVEL interface ... + * + * publish + * search + * result + * + */ + + public: + + /* Feedback callback (handled here) */ +virtual bool resultDHT(std::string key, std::string value); + + protected: + +virtual bool dhtInit(); +virtual bool dhtShutdown(); +virtual bool dhtActive(); +virtual int status(std::ostream &out); + +virtual bool publishDHT(std::string key, std::string value, uint32_t ttl); +virtual bool searchDHT(std::string key); + + + + /********** Internal DHT Threading ************************ + * + */ + + public: + +virtual void run(); + + private: + + /* search scheduling */ +void checkDHTStatus(); +int checkStunState(); +int checkStunState_Active(); /* when in active state */ +int doStun(); +int checkPeerDHTKeys(); +int checkOwnDHTKeys(); +int checkNotifyDHT(); + +void clearDhtData(); + + /* IP Bootstrap */ +bool getDhtBootstrapList(); +std::string BootstrapId(uint32_t bin); +std::string randomBootstrapId(); + + /* other feedback through callback */ + // use pqiNetAssistConnect.. version pqiConnectCb *connCb; + + /* protected by Mutex */ + RsMutex dhtMtx; + + bool mDhtOn; /* User desired state */ + bool mDhtModifications; /* any user requests? */ + + dhtPeerEntry ownEntry; + time_t ownNotifyTS; + std::map peers; + + std::list stunIds; + bool mStunRequired; + + uint32_t mDhtState; + time_t mDhtActiveTS; + + bool mBootstrapAllowed; + time_t mLastBootstrapListTS; +}; + + +#endif // P3DHDHTMGR_H + + diff --git a/libretroshare/src/_pqi/p3notify.cc b/libretroshare/src/_pqi/p3notify.cc new file mode 100644 index 000000000..4069935bf --- /dev/null +++ b/libretroshare/src/_pqi/p3notify.cc @@ -0,0 +1,197 @@ +/* + * libretroshare/src/rsserver: p3notify.cc + * + * RetroShare C++ Interface. + * + * 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 "pqi/p3notify.h" + + +/* external reference point */ +RsNotify *rsNotify = NULL; + +pqiNotify *getPqiNotify() +{ + return ((p3Notify *) rsNotify); +} + + /* Output for retroshare-gui */ +bool p3Notify::NotifySysMessage(uint32_t &sysid, uint32_t &type, + std::string &title, std::string &msg) +{ + RsStackMutex stack(noteMtx); /************* LOCK MUTEX ************/ + if (pendingSysMsgs.size() > 0) + { + p3NotifySysMsg smsg = pendingSysMsgs.front(); + pendingSysMsgs.pop_front(); + + sysid = smsg.sysid; + type = smsg.type; + title = smsg.title; + msg = smsg.msg; + + return true; + } + + return false; +} + + /* Output for retroshare-gui */ +bool p3Notify::NotifyLogMessage(uint32_t &sysid, uint32_t &type, + std::string &title, std::string &msg) +{ + RsStackMutex stack(noteMtx); /************* LOCK MUTEX ************/ + if (pendingLogMsgs.size() > 0) + { + p3NotifyLogMsg smsg = pendingLogMsgs.front(); + pendingLogMsgs.pop_front(); + + sysid = smsg.sysid; + type = smsg.type; + title = smsg.title; + msg = smsg.msg; + + return true; + } + + return false; +} + + +bool p3Notify::NotifyPopupMessage(uint32_t &ptype, std::string &name, std::string &msg) +{ + RsStackMutex stack(noteMtx); /************* LOCK MUTEX ************/ + if (pendingPopupMsgs.size() > 0) + { + p3NotifyPopupMsg pmsg = pendingPopupMsgs.front(); + pendingPopupMsgs.pop_front(); + + ptype = pmsg.type; + name = pmsg.name; + msg = pmsg.msg; + + return true; + } + + return false; +} + + + /* Control over Messages */ +bool p3Notify::GetSysMessageList(std::map &list) +{ + return false; +} + +bool p3Notify::GetPopupMessageList(std::map &list) +{ + return false; +} + + +bool p3Notify::SetSysMessageMode(uint32_t sysid, uint32_t mode) +{ + return false; +} + +bool p3Notify::SetPopupMessageMode(uint32_t ptype, uint32_t mode) +{ + return false; +} + + + /* Input from libretroshare */ +bool p3Notify::AddPopupMessage(uint32_t ptype, std::string name, std::string msg) +{ + RsStackMutex stack(noteMtx); /************* LOCK MUTEX ************/ + + p3NotifyPopupMsg pmsg; + + pmsg.type = ptype; + pmsg.name = name; + pmsg.msg = msg; + + pendingPopupMsgs.push_back(pmsg); + + return true; +} + + +bool p3Notify::AddSysMessage(uint32_t sysid, uint32_t type, + std::string title, std::string msg) +{ + RsStackMutex stack(noteMtx); /************* LOCK MUTEX ************/ + + p3NotifySysMsg smsg; + + smsg.sysid = sysid; + smsg.type = type; + smsg.title = title; + smsg.msg = msg; + + pendingSysMsgs.push_back(smsg); + + return true; +} + +bool p3Notify::AddLogMessage(uint32_t sysid, uint32_t type, + std::string title, std::string msg) +{ + RsStackMutex stack(noteMtx); /************* LOCK MUTEX ************/ + + p3NotifyLogMsg smsg; + + smsg.sysid = sysid; + smsg.type = type; + smsg.title = title; + smsg.msg = msg; + + pendingLogMsgs.push_back(smsg); + + return true; +} + + +bool p3Notify::GetFeedItem(RsFeedItem &item) +{ + RsStackMutex stack(noteMtx); /************* LOCK MUTEX ************/ + if (pendingNewsFeed.size() > 0) + { + item = pendingNewsFeed.front(); + pendingNewsFeed.pop_front(); + + return true; + } + + return false; +} + + +bool p3Notify::AddFeedItem(uint32_t type, std::string id1, std::string id2, std::string id3) +{ + RsStackMutex stack(noteMtx); /************* LOCK MUTEX ************/ + pendingNewsFeed.push_back(RsFeedItem(type, id1, id2, id3)); + + return true; +} + diff --git a/libretroshare/src/_pqi/p3notify.h b/libretroshare/src/_pqi/p3notify.h new file mode 100644 index 000000000..34dfe8e45 --- /dev/null +++ b/libretroshare/src/_pqi/p3notify.h @@ -0,0 +1,104 @@ +#ifndef P3NOTIFY_H +#define P3NOTIFY_H + +/* + * libretroshare/src/rsserver: p3notify.h + * + * RetroShare C++ Interface. + * + * 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 "rsiface/rsnotify.h" +#include "pqi/pqinotify.h" + +#include "util/rsthreads.h" + +class p3NotifySysMsg +{ + public: + + uint32_t sysid; + uint32_t type; + std::string title; + std::string msg; +}; + +class p3NotifyLogMsg +{ + public: + + uint32_t sysid; + uint32_t type; + std::string title; + std::string msg; +}; + +class p3NotifyPopupMsg +{ + public: + + uint32_t type; + std::string name; + std::string msg; +}; + + +class p3Notify: public RsNotify, public pqiNotify +{ + public: + + p3Notify() { return; } +virtual ~p3Notify() { return; } + + /* Output for retroshare-gui */ +virtual bool NotifySysMessage(uint32_t &sysid, uint32_t &type, + std::string &title, std::string &msg); +virtual bool NotifyPopupMessage(uint32_t &ptype, std::string &name, std::string &msg); +virtual bool NotifyLogMessage(uint32_t &sysid, uint32_t &type, std::string &title, std::string &msg); + + /* Control over Messages */ +virtual bool GetSysMessageList(std::map &list); +virtual bool GetPopupMessageList(std::map &list); + +virtual bool SetSysMessageMode(uint32_t sysid, uint32_t mode); +virtual bool SetPopupMessageMode(uint32_t ptype, uint32_t mode); + +virtual bool GetFeedItem(RsFeedItem &item); + + /* Overloaded from pqiNotify */ +virtual bool AddPopupMessage(uint32_t ptype, std::string name, std::string msg); +virtual bool AddSysMessage(uint32_t sysid, uint32_t type, std::string title, std::string msg); +virtual bool AddLogMessage(uint32_t sysid, uint32_t type, std::string title, std::string msg); +virtual bool AddFeedItem(uint32_t type, std::string id1, std::string id2, std::string id3); + + private: + + RsMutex noteMtx; + + std::list pendingSysMsgs; + std::list pendingLogMsgs; + std::list pendingPopupMsgs; + std::list pendingNewsFeed; +}; + + +#endif // P3NOTIFY_H diff --git a/libretroshare/src/_pqi/p3upnpmgr.h b/libretroshare/src/_pqi/p3upnpmgr.h new file mode 100644 index 000000000..501ba2d1b --- /dev/null +++ b/libretroshare/src/_pqi/p3upnpmgr.h @@ -0,0 +1,59 @@ +/* + * libretroshare/src/pqi: p3upnpmgr.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2007 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". + * + */ + + +#ifndef P3UPNPMGR_H +#define P3UPNPMGR_H + +/* platform independent networking... */ +#include "util/rsthreads.h" +#include "pqi/pqinetwork.h" + +class p3UpnpMgr: public pqiNetAssistFirewall +{ +public: + +virtual ~p3UpnpMgr() { return; } + + /* External Interface */ +virtual void enable(bool on) = 0; /* launches thread to start it up */ +virtual void shutdown() = 0; /* blocking shutdown call */ +virtual void restart() = 0; /* must be called if ports change */ + +virtual bool getEnabled() = 0; +virtual bool getActive() = 0; + + /* the address that the listening port is on */ +virtual void setInternalPort(unsigned short iport_in) = 0; +virtual void setExternalPort(unsigned short eport_in) = 0; + + /* as determined by uPnP */ +virtual bool getInternalAddress(struct sockaddr_in &addr) = 0; +virtual bool getExternalAddress(struct sockaddr_in &addr) = 0; + +}; + +#endif /* P3UPNPMGR_H */ + diff --git a/libretroshare/src/_pqi/pqi.h b/libretroshare/src/_pqi/pqi.h new file mode 100644 index 000000000..92de40661 --- /dev/null +++ b/libretroshare/src/_pqi/pqi.h @@ -0,0 +1,86 @@ +/* + * libretroshare/src/pqi pqi.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + + +#ifndef PQI_H +#define PQI_H + +/* This just includes the standard headers required. + */ + + +#include "pqi/pqi_base.h" +#include "pqi/pqinetwork.h" +#include "serialiser/rsserial.h" +#include "serialiser/rsbaseitems.h" + +#include +#include +#include + +/********************** SEARCH INTERFACE ***************************/ +// this is an interface.... so should be +// classified as virtual = 0; + +class SearchInterface +{ +public: + SearchInterface() { return; } + +virtual ~SearchInterface() { return; } + + // Cache Requests +virtual int SearchSpecific(RsCacheRequest *) = 0; +virtual RsCacheRequest *RequestedSearch() = 0; + + // Cache Results +virtual int SendSearchResult(RsCacheItem *item) = 0; +virtual RsCacheItem *GetSearchResult() = 0; + + // FileTransfer. +virtual RsFileRequest *GetFileRequest() = 0; +virtual int SendFileRequest(RsFileRequest *) = 0; + +virtual RsFileData *GetFileData() = 0; +virtual int SendFileData(RsFileData *) = 0; + +}; + +class P3Interface: public SearchInterface +{ +public: + P3Interface() {return; } +virtual ~P3Interface() {return; } + +virtual int tick() { return 1; } +virtual int status() { return 1; } + +virtual int SendRsRawItem(RsRawItem *) = 0; +virtual RsRawItem *GetRsRawItem() = 0; + +}; + +#endif // PQI_H + diff --git a/libretroshare/src/_pqi/pqi.pri b/libretroshare/src/_pqi/pqi.pri index f9a0516ae..05321934d 100644 --- a/libretroshare/src/_pqi/pqi.pri +++ b/libretroshare/src/_pqi/pqi.pri @@ -1,7 +1,69 @@ INCLUDEPATH += $$PWD \ ../$$PWP DEPENDPATH += $$PWD +SOURCES = authgpg.cc \ + authssl.cc \ + authxpgp.cc \ + cleanupxpgp.cc \ + p3authmgr.cc \ + pqi/p3cfgmgr.cc \ + p3connmgr.cc \ + p3dhtmgr.cc \ + p3notify.cc \ + pqi_base.cc \ + pqiarchive.cc \ + pqibin.cc \ + pqihandler.cc \ + pqihash.cc \ + pqiindic.cc \ + pqiloopback.cc \ + pqimonitor.cc \ + pqinetwork.cc \ + pqiperson.cc \ + pqipersongrp.cc \ + pqisecurity.cc \ + pqiservice.cc \ + pqissl.cc \ + pqissllistener.cc \ + pqisslpersongrp.cc \ + pqissludp.cc \ + pqistore.cc \ + pqistreamer.cc \ + sslcert.cc \ + xpgpcert.cc -SOURCES = authgpg.cc - -HEADERS = authgpg.h +HEADERS = authgpg.h \ + authssl.h \ + authxpgp.h \ + cleanupxpgp.h \ + p3authmgr.h \ + p3cfgmgr.h \ + p3connmgr.h \ + p3dhtmgr.h \ + p3notify.h \ + p3upnpmgr.h \ + pqi.h \ + pqi_base.h \ + pqiarchive.h \ + pqiassist.h \ + pqibin.h \ + pqihandler.h \ + pqihash.h \ + pqiindic.h \ + pqilistener.h \ + pqiloopback.h \ + pqimonitor.h \ + pqinetwork.h \ + pqinotify.h \ + pqiperson.h \ + pqipersongrp.h \ + pqisecurity.h \ + pqiservice.h \ + pqissl.h \ + pqissllistener.h \ + pqisslpersongrp.h \ + pqissludp.h \ + pqistore.h \ + pqistreamer.h \ + sslcert.h \ + xpgpcert.h diff --git a/libretroshare/src/_pqi/pqi_base.cc b/libretroshare/src/_pqi/pqi_base.cc new file mode 100644 index 000000000..5f67b075f --- /dev/null +++ b/libretroshare/src/_pqi/pqi_base.cc @@ -0,0 +1,410 @@ +/* + * "$Id: pqi_base.cc,v 1.17 2007-03-31 09:41:32 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + +// CHANGED : REMOVED : REASON -> Not used in any file +#if 0 + +#include "pqi/pqi_base.h" + + + + + +static int next_search_id = 1; + +int getPQIsearchId() +{ + return next_search_id++; +} + + +// CHANID Operations. +int pqicid_clear(ChanId *cid) +{ + for(int i = 0; i < 10; i++) + { + cid -> route[i] = 0; + } + return 1; +} + +int pqicid_copy(const ChanId *cid, ChanId *newcid) +{ + for(int i = 0; i < 10; i++) + { + (newcid -> route)[i] = (cid -> route)[i]; + } + return 1; +} + +int pqicid_cmp(const ChanId *cid1, ChanId *cid2) +{ + int ret = 0; + for(int i = 0; i < 10; i++) + { + ret = cid1->route[i] - cid2->route[i]; + if (ret != 0) + { + return ret; + } + } + return 0; +} + + + + +int pqiroute_getshift(ChanId *id) +{ + int *array = id -> route; + int next = array[0]; + + // shift. + for(int i = 0; i < 10 - 1; i++) + { + array[i] = array[i+1]; + } + array[10 - 1] = 0; + + return next; +} + +int pqiroute_setshift(ChanId *id, int chan) +{ + int *array = id -> route; + + // shift. + for(int i = 10 - 1; i > 0; i--) + { + array[i] = array[i-1]; + } + array[0] = chan; + + return 1; + +} + +#endif + +// CHANGED : REMOVED : REASON -> Not declared +#if 0 /* removing old stuff */ +/****************** PERSON DETAILS ***********************/ + +Person::Person() + :dhtFound(false), dhtFlags(0), + lc_timestamp(0), lr_timestamp(0), + nc_timestamp(0), nc_timeintvl(5), + name("Unknown"), status(PERSON_STATUS_MANUAL) + + + { + for(int i = 0; i < (signed) sizeof(lastaddr); i++) + { + ((unsigned char *) (&lastaddr))[i] = 0; + ((unsigned char *) (&localaddr))[i] = 0; + ((unsigned char *) (&serveraddr))[i] = 0; + ((unsigned char *) (&dhtaddr))[i] = 0; + } + pqicid_clear(&cid); + + + return; + } + +Person::~Person() + { + } + + +int Person::cidpop() +{ + return pqiroute_getshift(&cid); +} + +void Person::cidpush(int id) +{ + pqiroute_setshift(&cid, id); + return; +} + +bool Person::Group(std::string in) + { + std::list::iterator it; + for(it = groups.begin(); it != groups.end(); it++) + { + if (in == (*it)) + { + return true; + } + } + return false; + } + + +int Person::addGroup(std::string in) + { + groups.push_back(in); + return 1; + } + +int Person::removeGroup(std::string in) + { + std::list::iterator it; + for(it = groups.begin(); it != groups.end(); it++) + { + if (in == (*it)) + { + groups.erase(it); + return 1; + } + } + return 0; + } + + + +bool Person::Valid() + { + return (status & PERSON_STATUS_VALID); + } + +void Person::Valid(bool b) + { + if (b) + status |= PERSON_STATUS_VALID; + else + status &= ~PERSON_STATUS_VALID; + } + +bool Person::Accepted() + { + return (status & PERSON_STATUS_ACCEPTED); + } + +void Person::Accepted(bool b) + { + if (b) + status |= PERSON_STATUS_ACCEPTED; + else + status &= ~PERSON_STATUS_ACCEPTED; + } + +bool Person::InUse() + { + return (status & PERSON_STATUS_INUSE); + } + +void Person::InUse(bool b) + { + if (b) + status |= PERSON_STATUS_INUSE; + else + status &= ~(PERSON_STATUS_INUSE); + } + + +bool Person::Listening() + { + return (status & PERSON_STATUS_LISTENING); + } + +void Person::Listening(bool b) + { + if (b) + status |= PERSON_STATUS_LISTENING; + else + status &= ~PERSON_STATUS_LISTENING; + } + +bool Person::Connected() + { + return (status & PERSON_STATUS_CONNECTED); + } + +void Person::Connected(bool b) + { + if (b) + status |= PERSON_STATUS_CONNECTED; + else + status &= ~PERSON_STATUS_CONNECTED; + } + +bool Person::WillListen() + { + return (status & PERSON_STATUS_WILL_LISTEN); + } + +void Person::WillListen(bool b) + { + if (b) + status |= PERSON_STATUS_WILL_LISTEN; + else + status &= ~PERSON_STATUS_WILL_LISTEN; + } + +bool Person::WillConnect() + { + return (status & PERSON_STATUS_WILL_CONNECT); + } + +void Person::WillConnect(bool b) + { + if (b) + status |= PERSON_STATUS_WILL_CONNECT; + else + status &= ~PERSON_STATUS_WILL_CONNECT; + } + +bool Person::Manual() + { + return (status & PERSON_STATUS_MANUAL); + } + +void Person::Manual(bool b) + { + if (b) + status |= PERSON_STATUS_MANUAL; + else + status &= ~PERSON_STATUS_MANUAL; + } + +bool Person::Firewalled() + { + return (status & PERSON_STATUS_FIREWALLED); + } + +void Person::Firewalled(bool b) + { + if (b) + status |= PERSON_STATUS_FIREWALLED; + else + status &= ~PERSON_STATUS_FIREWALLED; + } + +bool Person::Forwarded() + { + return (status & PERSON_STATUS_FORWARDED); + } + +void Person::Forwarded(bool b) + { + if (b) + status |= PERSON_STATUS_FORWARDED; + else + status &= ~PERSON_STATUS_FORWARDED; + } + +bool Person::Local() + { + return (status & PERSON_STATUS_LOCAL); + } + +void Person::Local(bool b) + { + if (b) + status |= PERSON_STATUS_LOCAL; + else + status &= ~PERSON_STATUS_LOCAL; + } + + +bool Person::Trusted() + { + return (status & PERSON_STATUS_TRUSTED); + } + +void Person::Trusted(bool b) + { + if (b) + status |= PERSON_STATUS_TRUSTED; + else + status &= ~PERSON_STATUS_TRUSTED; + } + + +unsigned int Person::Status() + { + return status; + } + + +void Person::Status(unsigned int s) + { + status = s; + } + +std::string Person::Name() + { + return name; + } + + +void Person::Name(std::string n) + { + name = n; + } + + /* Dynamic Address Foundation */ +bool Person::hasDHT() +{ + return dhtFound; +} + +void Person::setDHT(struct sockaddr_in addr, unsigned int flags) +{ + dhtFound = true; + dhtFlags = flags; + dhtaddr = addr; +} + +/* GUI Flags */ +bool Person::InChat() + { + return (status & PERSON_STATUS_INCHAT); + } + +void Person::InChat(bool b) + { + if (b) + status |= PERSON_STATUS_INCHAT; + else + status &= ~PERSON_STATUS_INCHAT; + } + +bool Person::InMessage() + { + return (status & PERSON_STATUS_INMSG); + } + +void Person::InMessage(bool b) + { + if (b) + status |= PERSON_STATUS_INMSG; + else + status &= ~PERSON_STATUS_INMSG; + } + + +#endif diff --git a/libretroshare/src/_pqi/pqi_base.h b/libretroshare/src/_pqi/pqi_base.h new file mode 100644 index 000000000..3880aa83a --- /dev/null +++ b/libretroshare/src/_pqi/pqi_base.h @@ -0,0 +1,461 @@ +/* + * "$Id: pqi_base.h,v 1.18 2007-05-05 16:10:05 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + +// CHANGED : REMOVED : REASON -> Not used in any file +#if 0 + +#ifndef PQI_BASE_H +#define PQI_BASE_H + +#include +#include +#include +#include +#include +#include +#include + +#include "_pqi/pqinetwork.h" + +/*** Base DataTypes: ****/ +#include "_serialiser/rsserial.h" + + +#define PQI_MIN_PORT 1024 +#define PQI_MAX_PORT 50000 +#define PQI_DEFAULT_PORT 7812 + + + +int getPQIsearchId(); + +// CHANGED: REMODED : Reason -> Not implemented +//int fixme(char *str, int n); + +// local functions. +int pqiroute_setshift(ChanId *item, int chan); +int pqiroute_getshift(ChanId *item); + +// these ones are also exported! +int pqicid_clear(ChanId *cid); +int pqicid_copy(const ChanId *cid, ChanId *newcid); +int pqicid_cmp(const ChanId *cid1, ChanId *cid2); + +// Helper functions for the PQInterface. +#endif + + +#if 0 /* removing old stuff */ + +struct chan_id +{ + int route[10]; +}; + +typedef unsigned long SearchId; /* unsigned 32 bits */ +typedef struct chan_id ChanId; + +/* basic worker fns on cids */ +int pqicid_clear(ChanId *cid); +int pqicid_copy(const ChanId *cid, ChanId *newcid); +int pqicid_cmp(const ChanId *cid1, ChanId *cid2); + + +// So the basic pqi interface defines +// +// 1) Person -> reference object +// 2) RsItem +// 3) PQInterface +// which defines: +// - send/recieve objs +// - send/recieve files +// +// This is then extended to +// 4) SInterface +// which seperates the data into providing a basic search interface. +// chat/messages/files/search objs. +// + +// TODO IN THIS CLASS. +// 1) Add serialisation.... (later) +// 2) reduce base interface to the minimum. +// 3) Convert cid -> std::stack. + +// Base for all Security/Id/Certificate schemes. +// The list of these must be maintained seperately +// from the RsItems.... + + +// Person - includes +// 1) name + signature (unique id) +// 2) address + timestamps of connections. +// 3) status +// 4) groups. +// +// only requires certificate. to complete... + +#define PERSON_STATUS_VALID 0x0001 +#define PERSON_STATUS_ACCEPTED 0x0002 +#define PERSON_STATUS_INUSE 0x0004 +#define PERSON_STATUS_LISTENING 0x0008 +#define PERSON_STATUS_CONNECTED 0x0010 +#define PERSON_STATUS_WILL_LISTEN 0x0020 +#define PERSON_STATUS_WILL_CONNECT 0x0040 + +#define PERSON_STATUS_MANUAL 0x0080 +#define PERSON_STATUS_FIREWALLED 0x0100 +#define PERSON_STATUS_FORWARDED 0x0200 +#define PERSON_STATUS_LOCAL 0x0400 +#define PERSON_STATUS_TRUSTED 0x0800 + +/* some extra flags for advising the gui.... + * have no affect on the pqi networking. + */ + +#define PERSON_STATUS_INCHAT 0x1000 +#define PERSON_STATUS_INMSG 0x2000 + +class Person +{ +public: + + Person(); +virtual ~Person(); + +std::string Name(); +void Name(std::string); + + // Signature needs to be defined. +virtual std::string Signature() { return Name(); }; + + // These operate on the status. +bool Valid(); +void Valid(bool); +bool Accepted(); +void Accepted(bool); +bool InUse(); +void InUse(bool); +bool Listening(); +void Listening(bool); +bool Connected(); +void Connected(bool); +bool WillListen(); +void WillListen(bool); +bool WillConnect(); +void WillConnect(bool); +bool Manual(); +void Manual(bool); +bool Firewalled(); +void Firewalled(bool); +bool Forwarded(); +void Forwarded(bool); +bool Local(); +void Local(bool); +bool Trusted(); +void Trusted(bool); + +/* GUI Flags */ +bool InChat(); +void InChat(bool); +bool InMessage(); +void InMessage(bool); + +unsigned int Status(); +void Status(unsigned int s); + +int addGroup(std::string); +int removeGroup(std::string); +bool Group(std::string); + +int cidpop(); +void cidpush(int id); + + /* Dynamic Address Foundation */ +bool hasDHT(); +std::string getDHTId(); +void setDHT(struct sockaddr_in addr, unsigned int flags); + + // public for the moment. + struct sockaddr_in lastaddr, localaddr, serveraddr; + std::string dynDNSaddr; + + bool dhtFound; + unsigned int dhtFlags; + struct sockaddr_in dhtaddr; + + time_t lc_timestamp; // last connect timestamp + time_t lr_timestamp; // last receive timestamp + + time_t nc_timestamp; // next connect timestamp. + time_t nc_timeintvl; // next connect time interval. + + ChanId cid; // to get to the correct pqissl. + + int trustLvl; /* new field */ + +private: + std::string name; + + unsigned int status; + std::list groups; +}; + +#endif /* removing old stuff */ + +class RateInterface +{ +// controlling data rates. +public: + + RateInterface() + :bw_in(0), bw_out(0), bwMax_in(0), bwMax_out(0) { return; } +virtual ~RateInterface() { return; } + +virtual float getRate(bool in) + { + if (in) + return bw_in; + return bw_out; + } + +virtual float getMaxRate(bool in) + { + if (in) + return bwMax_in; + return bwMax_out; + } + +virtual void setMaxRate(bool in, float val) + { + if (in) + bwMax_in = val; + else + bwMax_out = val; + return; + } + +protected: + +void setRate(bool in, float val) + { + if (in) + bw_in = val; + else + bw_out = val; + return; + } + + private: +float bw_in, bw_out, bwMax_in, bwMax_out; +}; + + +/*********************** PQI INTERFACE ******************************\ + * The basic exchange interface. + * This inherits the RateInterface, as Bandwidth control + * is a critical to a networked application. + * + */ +class NetInterface; + +class PQInterface: public RateInterface +{ +public: + PQInterface(std::string id) :peerId(id) { return; } +virtual ~PQInterface() { return; } + +virtual int SendItem(RsItem *) = 0; +virtual RsItem *GetItem() = 0; + +// also there are tick + person id functions. +virtual int tick() { return 0; } +virtual int status() { return 0; } +virtual std::string PeerId() { return peerId; } + + // the callback from NetInterface Connection Events. +virtual int notifyEvent(NetInterface *ni, int event) { return 0; } + + private: + + std::string peerId; +}; + + + +/**** Consts for pqiperson -> placed here for NetBinDummy usage() */ + +const uint32_t PQI_CONNECT_TCP = 0x0001; +const uint32_t PQI_CONNECT_UDP = 0x0002; + + +/********************** Binary INTERFACE **************************** + * This defines the binary interface used by Network/loopback/file + * interfaces + */ + +#define BIN_FLAGS_NO_CLOSE 0x0001 +#define BIN_FLAGS_READABLE 0x0002 +#define BIN_FLAGS_WRITEABLE 0x0004 +#define BIN_FLAGS_NO_DELETE 0x0008 +#define BIN_FLAGS_HASH_DATA 0x0010 + +class BinInterface +{ +public: + BinInterface() { return; } +virtual ~BinInterface() { return; } + +virtual int tick() = 0; + +virtual int senddata(void *data, int len) = 0; +virtual int readdata(void *data, int len) = 0; +virtual int netstatus() = 0; +virtual int isactive() = 0; +virtual bool moretoread() = 0; +virtual bool cansend() = 0; + + /* method for streamer to shutdown bininterface */ +virtual int close() = 0; + + /* if hashing data */ +virtual std::string gethash() = 0; +virtual uint64_t bytecount() { return 0; } + + /* used by pqistreamer to limit transfers */ +virtual bool bandwidthLimited() { return true; } +}; + + +/********************** Network INTERFACE *************************** + * This defines the Network interface used by sockets/SSL/XPGP + * interfaces + * + * NetInterface: very pure interface, so no tick.... + * + * It is passed a pointer to a PQInterface *parent, + * this is used to notify the system of Connect/Disconnect Events. + * + * Below are the Events for callback. + */ + +static const int NET_CONNECT_RECEIVED = 1; +static const int NET_CONNECT_SUCCESS = 2; +static const int NET_CONNECT_UNREACHABLE = 3; +static const int NET_CONNECT_FIREWALLED = 4; +static const int NET_CONNECT_FAILED = 5; + +static const uint32_t NET_PARAM_CONNECT_DELAY = 1; +static const uint32_t NET_PARAM_CONNECT_PERIOD = 2; +static const uint32_t NET_PARAM_CONNECT_TIMEOUT = 3; + +class NetInterface +{ +public: + NetInterface(PQInterface *p_in, std::string id) + :p(p_in), peerId(id) { return; } + +virtual ~NetInterface() + { return; } + +// virtual int tick() = 0; // Already defined for BinInterface. + +virtual int connect(struct sockaddr_in raddr) = 0; +virtual int listen() = 0; +virtual int stoplistening() = 0; +virtual int disconnect() = 0; +virtual int reset() = 0; +virtual std::string PeerId() { return peerId; } + +virtual bool connect_parameter(uint32_t type, uint32_t value) = 0; + +protected: +PQInterface *parent() { return p; } + +private: + PQInterface *p; + std::string peerId; +}; + + +class NetBinInterface: public NetInterface, public BinInterface +{ +public: + NetBinInterface(PQInterface *parent, std::string id) + :NetInterface(parent, id) + { return; } +virtual ~NetBinInterface() { return; } +}; + +#define CHAN_SIGN_SIZE 16 +#define CERTSIGNLEN 16 /* actual byte length of signature */ +#define PQI_PEERID_LENGTH 32 /* When expanded into a string */ + +#if 0 +class certsign +{ + public: + bool operator<(const certsign &ref) const; + bool operator==(const certsign &ref) const; + char data[CERTSIGNLEN]; +}; + + +// IDS aren't inportant in the base class. + +// EXCEPT FOR MAYBE THE FLAGS. + +// These are OR'ed together. +const int PQI_ITEM_FLAG_NEW = 0x001; +const int PQI_ITEM_FLAG_LOCAL = 0x002; +const int PQI_ITEM_FLAG_PRIVATE = 0x004; + +const int PQI_ITEM_TYPE_ITEM = 0x000; +const int PQI_ITEM_TYPE_FILEITEM = 0x001; +const int PQI_ITEM_TYPE_SEARCHITEM = 2; +const int PQI_ITEM_TYPE_CHATITEM = 3; +const int PQI_ITEM_TYPE_INFOITEM = 5; +const int PQI_ITEM_TYPE_COMMANDITEM = 6; +const int PQI_ITEM_TYPE_AUTODISCITEM = 7; + +const int PQI_ITEM_TYPE_TUNNELITEM = 777; +const int PQI_ITEM_TYPE_TUNNELINITITEM = 778; + +// Sub Type. +const int PQI_FI_SUBTYPE_GENERAL = 1; +const int PQI_FI_SUBTYPE_DATA = 2; +const int PQI_FI_SUBTYPE_CANCELRECV = 3; +const int PQI_FI_SUBTYPE_CANCELSEND = 4; +const int PQI_FI_SUBTYPE_REQUEST = 5; +const int PQI_FI_SUBTYPE_TRANSFER = 6; +const int PQI_FI_SUBTYPE_RATE = 7; +const int PQI_FI_SUBTYPE_ERROR = 8; + +const int PQI_FD_FLAG_ENDSTREAM = 1; + +#endif /* removing old stuff */ + + +#endif // PQI_BASE_H + diff --git a/libretroshare/src/_pqi/pqiarchive.cc b/libretroshare/src/_pqi/pqiarchive.cc new file mode 100644 index 000000000..9518fbdb5 --- /dev/null +++ b/libretroshare/src/_pqi/pqiarchive.cc @@ -0,0 +1,474 @@ +/* + * "$Id: pqiarchive.cc,v 1.5 2007-03-21 18:45:41 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + + +/* This is dependent on the sslroot at the moment. + * -> as we need to create/restore references to the Person. + * -> and store the signatures to do this. + */ + +/******************************************************************* + * pqiarchive provides an archive stream. + * This stores RsItem + Person Reference + Timestamp, + * + * and allows Objects to be replayed or restored, + * independently of the rest of the pqi system. + * + */ + +#include "_pqi/pqiarchive.h" + + +const int pqiarchivezone = 9326; + +const int PQIARCHIVE_TYPE_PQITEM = 0x0001; + +/* PeerId of PQInterface is not important ... as we are archiving + * packets from other people... + */ + +pqiarchive::pqiarchive(RsSerialiser *rss, BinInterface *bio_in, int bio_flags_in) + :PQInterface(""), rsSerialiser(rss), bio(bio_in), bio_flags(bio_flags_in), + nextPkt(NULL), nextPktTS(0), firstPktTS(0), initTS(0),realTime(false) +{ + { + std::ostringstream out; + out << "pqiarchive::pqiarchive()"; + out << " Initialisation!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqiarchivezone, out.str()); + } + + if (!bio_in) + { + std::ostringstream out; + out << "pqiarchive::pqiarchive()"; + out << " NULL bio, FATAL ERROR!" << std::endl; + pqioutput(PQL_ALERT, pqiarchivezone, out.str()); + exit(1); + } + + return; +} + +pqiarchive::~pqiarchive() +{ + { + std::ostringstream out; + out << "pqiarchive::~pqiarchive()"; + out << " Destruction!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqiarchivezone, out.str()); + } + + if (bio_flags & BIN_FLAGS_NO_CLOSE) + { + std::ostringstream out; + out << "pqiarchive::~pqiarchive()"; + out << " Not Closing BinInterface!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqiarchivezone, out.str()); + } + else if (bio) + { + std::ostringstream out; + out << "pqiarchive::~pqiarchive()"; + out << " Deleting BinInterface!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqiarchivezone, out.str()); + + delete bio; + } + + if (rsSerialiser) + delete rsSerialiser; + + if (nextPkt) + { + delete nextPkt; + } + return; +} + + +// Get/Send Items. +int pqiarchive::SendItem(RsItem *si) +{ + { + std::ostringstream out; + out << "pqiarchive::SendItem()" << std::endl; + si -> print(out); + pqioutput(PQL_DEBUG_BASIC, pqiarchivezone, out.str()); + } + + // check if this is a writing bio. + + if (!(bio_flags & BIN_FLAGS_WRITEABLE)) + { + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete si; + return -1; + } + +// std::cerr << "SendItem: si->PeerId()=" << si->PeerId() << std::endl ; + + int ret = writePkt(si); + return ret; /* 0 - failure, 1 - success*/ +} + +RsItem *pqiarchive::GetItem() +{ + { + std::ostringstream out; + out << "pqiarchive::GetItem()"; + pqioutput(PQL_DEBUG_ALL, pqiarchivezone, out.str()); + } + + // check if this is a reading bio. + if (!(bio_flags & BIN_FLAGS_READABLE)) + { + std::ostringstream out; + out << "pqiarchive::GetItem()"; + out << "Error Not Readable" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqiarchivezone, out.str()); + return NULL; + } + + // load if we dont have a packet. + if (!nextPkt) + { + if (!readPkt(&nextPkt, &nextPktTS)) + { + std::ostringstream out; + out << "pqiarchive::GetItem()"; + out << "Failed to ReadPkt" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqiarchivezone, out.str()); + return NULL; + } + } + + if (!nextPkt) return NULL; + + + /* if realtime - check delta */ + bool rtnPkt = true; + if ((realTime) && (initTS)) + { + int delta = time(NULL) - initTS; + int delta2 = nextPktTS - firstPktTS; + if (delta >= delta2) + { + rtnPkt = true; + } + else + { + rtnPkt = false; + } + } + + if (rtnPkt) + { + if (!initTS) + { + /* first read! */ + initTS = time(NULL); + firstPktTS = nextPktTS; + } + RsItem *outPkt = nextPkt; + nextPkt = NULL; + + if (outPkt != NULL) + { + std::ostringstream out; + out << "pqiarchive::GetItem() Returning:" << std::endl; + outPkt -> print(out); + pqioutput(PQL_DEBUG_BASIC, pqiarchivezone, out.str()); + } + return outPkt; + } + return NULL; +} + +// // PQInterface +int pqiarchive::tick() +{ + { + std::ostringstream out; + out << "pqiarchive::tick()"; + out << std::endl; + } + return 0; +} + +int pqiarchive::status() +{ + { + std::ostringstream out; + out << "pqiarchive::status()"; + pqioutput(PQL_DEBUG_ALL, pqiarchivezone, out.str()); + } + return 0; +} + +// +/**************** HANDLE OUTGOING TRANSLATION + TRANSMISSION ******/ + +int pqiarchive::writePkt(RsItem *pqi) +{ +// std::cerr << "writePkt, pqi->peerId()=" << pqi->PeerId() << std::endl ; + { + std::ostringstream out; + out << "pqiarchive::writePkt()"; + pqioutput(PQL_DEBUG_ALL, pqiarchivezone, out.str()); + } + + uint32_t pktsize = rsSerialiser->size(pqi); + void *ptr = malloc(pktsize); + if (!(rsSerialiser->serialise(pqi, ptr, &pktsize))) + { + std::ostringstream out; + out << "pqiarchive::writePkt() Null Pkt generated!"; + out << std::endl; + out << "Caused By: " << std::endl; + pqi -> print(out); + pqioutput(PQL_ALERT, pqiarchivezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + return 0; + } + + /* extract the extra details */ + uint32_t len = getRsItemSize(ptr); + if (len != pktsize) + { + std::ostringstream out; + out << "pqiarchive::writePkt() Length MisMatch: len: " << len; + out << " != pktsize: " << pktsize; + out << std::endl; + out << "Caused By: " << std::endl; + pqi -> print(out); + pqioutput(PQL_ALERT, pqiarchivezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + return 0; + } + + + if (!(bio->cansend())) + { + std::ostringstream out; + out << "pqiarchive::writePkt() BIO cannot write!"; + out << std::endl; + out << "Discarding: " << std::endl; + pqi -> print(out); + pqioutput(PQL_ALERT, pqiarchivezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + + return 0; + } + + // using the peerid from the item. + if (pqi->PeerId().length() != PQI_PEERID_LENGTH) + { + std::ostringstream out; + out << "pqiarchive::writePkt() Invalid peerId Length!"; + out << std::endl; + out << "Found " << pqi->PeerId().length() << " instead of " << PQI_PEERID_LENGTH << std::endl ; + out << "pqi->PeerId() = " << pqi->PeerId() << std::endl ; + out << "Caused By: " << std::endl; + pqi -> print(out); + pqioutput(PQL_ALERT, pqiarchivezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + return 0; + } + + std::ostringstream out; + out << "Writing Pkt Header" << std::endl; + struct pqiarchive_header hdr; + hdr.type = PQIARCHIVE_TYPE_PQITEM; + hdr.length = len; + hdr.ts = time(NULL); + memcpy(hdr.personSig, pqi->PeerId().c_str(), PQI_PEERID_LENGTH); + + // write packet header. + if (sizeof(hdr) != bio->senddata(&hdr, sizeof(hdr))) + { + out << "Trouble writing header!"; + out << std::endl; + pqioutput(PQL_ALERT, pqiarchivezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + + return 0; + } + + out << "Writing Pkt Body" << std::endl; + + // write packet. + if (len != bio->senddata(ptr, len)) + { + out << "Problems with Send Data!"; + out << std::endl; + pqioutput(PQL_ALERT, pqiarchivezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + + return 0; + } + + out << " Success!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqiarchivezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + + return 1; +} + +/* Reads a single packet from the input stream + * gets the timestamp as well. + * + */ + +int pqiarchive::readPkt(RsItem **item_out, long *ts_out) +{ + { + std::ostringstream out; + out << "pqiarchive::readPkt()"; + pqioutput(PQL_DEBUG_ALL, pqiarchivezone, out.str()); + } + + if ((!(bio->isactive())) || (!(bio->moretoread()))) + { + return 0; + } + + // header + struct pqiarchive_header hdr; + + // enough space to read any packet. + int maxlen = getRsPktMaxSize(); + void *block = malloc(maxlen); + + // initial read size: basic packet. + int blen = getRsPktBaseSize(); + + int tmplen; + + /* read in the header */ + if (sizeof(hdr) != bio->readdata(&hdr, sizeof(hdr))) + { + /* error */ + pqioutput(PQL_WARNING, pqiarchivezone, + "pqiarchive::readPkt() bad read(1)"); + + free(block); + return 0; + } + + /* we have the header */ + + // read the basic block (minimum packet size) + if (blen != (tmplen = bio->readdata(block, blen))) + { + pqioutput(PQL_WARNING, pqiarchivezone, + "pqiarchive::readPkt() bad read(2)"); + + free(block); + return 0; + } + + // workout how much more to read. + int extralen = getRsItemSize(block) - blen; + if (extralen > 0) + { + void *extradata = (void *) (((char *) block) + blen); + if (extralen != (tmplen = bio->readdata(extradata, extralen))) + { + + std::ostringstream out; + out << "pqiarchive::readPkt() "; + out << "Error Completing Read (read "; + out << tmplen << "/" << extralen << ")" << std::endl; + pqioutput(PQL_ALERT, pqiarchivezone, out.str()); + + free(block); + return 0; + } + } + + // create packet, based on header. + std::cerr << "Read Data Block -> Incoming Pkt("; + std::cerr << blen + extralen << ")" << std::endl; + uint32_t readbytes = extralen + blen; + + RsItem *item = rsSerialiser->deserialise(block, &readbytes); + free(block); + + if (item == NULL) + { + pqioutput(PQL_ALERT, pqiarchivezone, + "pqiarchive::readPkt() Failed to create Item from archive!"); + return 0; + } + + /* Cannot detect bad ids here anymore.... + * but removed dependancy on the sslroot! + */ + + std::string peerId = ""; + for(int i = 0; i < PQI_PEERID_LENGTH; i++) + { + peerId += hdr.personSig[i]; + } + item->PeerId(peerId); + + *item_out = item; + *ts_out = hdr.ts; + + return 1; +} + +/**** Hashing Functions ****/ +std::string pqiarchive::gethash() +{ + return bio->gethash(); +} + + + diff --git a/libretroshare/src/_pqi/pqiarchive.h b/libretroshare/src/_pqi/pqiarchive.h new file mode 100644 index 000000000..2ec20e983 --- /dev/null +++ b/libretroshare/src/_pqi/pqiarchive.h @@ -0,0 +1,95 @@ +/* + * "$Id: pqiarchive.h,v 1.3 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQIARCHIVE_H +#define PQIARCHIVE_H + +#include "_pqi/pqi.h" +#include "_serialiser/rsserial.h" +#include +#include + +#include +#include "_util/rsdebug.h" + +#include + +/******************************************************************* + * pqiarchive provides an archive stream. + * This stores RsItem + Person Reference + Timestamp, + * + * and allows Objects to be replayed or restored, + * independently of the rest of the pqi system. + * + */ + +struct pqiarchive_header +{ + uint32_t type; + uint32_t length; + uint32_t ts; + uint8_t personSig[PQI_PEERID_LENGTH]; +}; + +class pqiarchive: PQInterface +{ +public: + pqiarchive(RsSerialiser *rss, BinInterface *bio_in, int bio_flagsin); +virtual ~pqiarchive(); + +// PQInterface +virtual int SendItem(RsItem *); +virtual RsItem *GetItem(); + +virtual int tick(); +virtual int status(); + +virtual void setRealTime(bool r) { realTime = r; } + +std::string gethash(); + + private: +int writePkt(RsItem *item); +int readPkt(RsItem **item_out, long *ts); + + // Serialiser + RsSerialiser *rsSerialiser; + // Binary Interface for IO, initialisated at startup. + BinInterface *bio; + unsigned int bio_flags; // only BIN_NO_CLOSE at the moment. + + // Temp Storage for transient data..... + RsItem *nextPkt; + long nextPktTS; /* timestamp associated with nextPkt */ + long firstPktTS; /* timestamp associated with first read Pkt */ + long initTS; /* clock timestamp at first read */ + + bool realTime; +}; + + +#endif // PQIARCHIVE_H diff --git a/libretroshare/src/_pqi/pqiassist.h b/libretroshare/src/_pqi/pqiassist.h new file mode 100644 index 000000000..c5c495611 --- /dev/null +++ b/libretroshare/src/_pqi/pqiassist.h @@ -0,0 +1,126 @@ +/* + * libretroshare/src/pqi: pqiassist.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2007 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". + * + */ + + +#ifndef PQIASSIST_H +#define PQIASSIST_H + +#include +#include +#include "_pqi/pqinetwork.h" +#include "_pqi/pqimonitor.h" + +/********** + * This header file provides two interfaces for assisting + * the connections to friends. + * + * pqiNetAssistFirewall - which will provides interfaces + * to functionality like upnp and apple's equivalent. + * + * pqiNetAssistConnect - which will provides interfaces + * to other networks (DHT) etc that can provide information. + * These classes would be expected to use the pqiMonitor + * callback system to notify the connectionMgr. + * + ***/ + +class pqiNetAssist +{ + public: + +virtual ~pqiNetAssist() { return; } + + /* External Interface */ +virtual void enable(bool on) = 0; +virtual void shutdown() = 0; /* blocking call */ +virtual void restart() = 0; + +virtual bool getEnabled() = 0; +virtual bool getActive() = 0; + +}; + +class pqiNetAssistFirewall: public pqiNetAssist +{ + public: + +virtual ~pqiNetAssistFirewall() { return; } + + /* the address that the listening port is on */ +virtual void setInternalPort(unsigned short iport_in) = 0; +virtual void setExternalPort(unsigned short eport_in) = 0; + + /* as determined by uPnP */ +virtual bool getInternalAddress(struct sockaddr_in &addr) = 0; +virtual bool getExternalAddress(struct sockaddr_in &addr) = 0; + +}; + + + + +class pqiNetAssistConnect: public pqiNetAssist +{ + /* + */ + public: + pqiNetAssistConnect(std::string id, pqiConnectCb *cb) + :mPeerId(id), mConnCb(cb) { return; } + + /********** External DHT Interface ************************ + * These Functions are the external interface + * for the DHT, and must be non-blocking and return quickly + */ + +virtual void setBootstrapAllowed(bool on) = 0; +virtual bool getBootstrapAllowed() = 0; + + /* set key data */ +virtual bool setExternalInterface(struct sockaddr_in laddr, + struct sockaddr_in raddr, uint32_t type) = 0; + + /* add / remove peers */ +virtual bool findPeer(std::string id) = 0; +virtual bool dropPeer(std::string id) = 0; + + /* post DHT key saying we should connect (callback when done) */ +virtual bool notifyPeer(std::string id) = 0; + + /* extract current peer status */ +virtual bool getPeerStatus(std::string id, + struct sockaddr_in &laddr, struct sockaddr_in &raddr, + uint32_t &type, uint32_t &mode) = 0; + + /* stun */ +virtual bool enableStun(bool on) = 0; +virtual bool addStun(std::string id) = 0; + + protected: + std::string mPeerId; + pqiConnectCb *mConnCb; +}; + +#endif /*PQIASSIST_H */ + diff --git a/libretroshare/src/_pqi/pqibin.cc b/libretroshare/src/_pqi/pqibin.cc new file mode 100644 index 000000000..b5d7f97d1 --- /dev/null +++ b/libretroshare/src/_pqi/pqibin.cc @@ -0,0 +1,565 @@ +/* + * "$Id: pqibin.cc,v 1.4 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 "pqi/pqibin.h" + +BinFileInterface::BinFileInterface(const char *fname, int flags) + :bin_flags(flags), buf(NULL), hash(NULL), bcount(0) +{ + /* if read + write - open r+ */ + if ((bin_flags & BIN_FLAGS_READABLE) && + (bin_flags & BIN_FLAGS_WRITEABLE)) + { + buf = fopen(fname, "rb+"); + /* if the file don't exist */ + if (!buf) + { + buf = fopen(fname, "wb+"); + } + + } + else if (bin_flags & BIN_FLAGS_READABLE) + { + buf = fopen(fname, "rb"); + } + else if (bin_flags & BIN_FLAGS_WRITEABLE) + { + // This is enough to remove old file in Linux... + // but not in windows.... (what to do) + buf = fopen(fname, "wb"); + fflush(buf); /* this might help windows! */ + } + else + { + /* not read or write! */ + } + + if (buf) + { + /* no problem */ + + /* go to the end */ + fseek(buf, 0L, SEEK_END); + /* get size */ + size = ftell(buf); + /* back to the start */ + fseek(buf, 0L, SEEK_SET); + } + else + { + size = 0; + } + + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + hash = new pqihash(); + } +} + + +BinFileInterface::~BinFileInterface() +{ + if (buf) + { + fclose(buf); + } + + if (hash) + { + delete hash; + } +} + +int BinFileInterface::close() +{ + if (buf) + { + fclose(buf); + buf = NULL; + } + return 1; +} + +int BinFileInterface::senddata(void *data, int len) +{ + if (!buf) + return -1; + + if (1 != fwrite(data, len, 1, buf)) + { + return -1; + } + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + hash->addData(data, len); + bcount += len; + } + return len; +} + +int BinFileInterface::readdata(void *data, int len) +{ + if (!buf) + return -1; + + if (1 != fread(data, len, 1, buf)) + { + return -1; + } + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + hash->addData(data, len); + bcount += len; + } + return len; +} + +std::string BinFileInterface::gethash() +{ + std::string hashstr; + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + hash->Complete(hashstr); + } + return hashstr; +} + +uint64_t BinFileInterface::bytecount() +{ + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + return bcount; + } + return 0; +} + + +BinMemInterface::BinMemInterface(int defsize, int flags) + :bin_flags(flags), buf(NULL), size(defsize), + recvsize(0), readloc(0), hash(NULL), bcount(0) + { + buf = malloc(defsize); + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + hash = new pqihash(); + } + } + + +BinMemInterface::BinMemInterface(const void *data, const int defsize, int flags) + :bin_flags(flags), buf(NULL), size(defsize), + recvsize(0), readloc(0), hash(NULL), bcount(0) + { + buf = malloc(defsize); + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + hash = new pqihash(); + } + + /* just remove the const + * *BAD* but senddata don't change it anyway + */ + senddata((void *) data, defsize); + } + +BinMemInterface::~BinMemInterface() +{ + if (buf) + free(buf); + + if (hash) + delete hash; + return; +} + +int BinMemInterface::close() +{ + if (buf) + { + free(buf); + buf = NULL; + } + size = 0; + recvsize = 0; + readloc = 0; + return 1; +} + + /* some fns to mess with the memory */ +int BinMemInterface::fseek(int loc) + { + if (loc <= recvsize) + { + readloc = loc; + return 1; + } + return 0; + } + + +int BinMemInterface::senddata(void *data, const int len) + { + if(recvsize + len > size) + { + /* resize? */ + return -1; + } + memcpy((char *) buf + recvsize, data, len); + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + hash->addData(data, len); + bcount += len; + } + recvsize += len; + return len; + } + + +int BinMemInterface::readdata(void *data, int len) + { + if(readloc + len > recvsize) + { + /* no more stuff? */ + return -1; + } + memcpy(data, (char *) buf + readloc, len); + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + hash->addData(data, len); + bcount += len; + } + readloc += len; + return len; + } + + + +std::string BinMemInterface::gethash() + { + std::string hashstr; + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + hash->Complete(hashstr); + } + return hashstr; + } + + +uint64_t BinMemInterface::bytecount() +{ + if (bin_flags & BIN_FLAGS_HASH_DATA) + { + return bcount; + } + return 0; +} + +bool BinMemInterface::writetofile(const char *fname) +{ + FILE *fd = fopen(fname, "wb"); + if (!fd) + { + return false; + } + + if (1 != fwrite(buf, recvsize, 1, fd)) + { + fclose(fd); + return false; + } + + fclose(fd); + + return true; +} + +bool BinMemInterface::readfromfile(const char *fname) +{ + FILE *fd = fopen(fname, "rb"); + if (!fd) + { + return false; + } + + /* get size */ + ::fseek(fd, 0L, SEEK_END); + int fsize = ftell(fd); + + if (fsize > size) + { + /* not enough room */ + std::cerr << "BinMemInterface::readfromfile() not enough room"; + std::cerr << std::endl; + + fclose(fd); + return false; + } + + ::fseek(fd, 0L, SEEK_SET); + if (1 != fread(buf, fsize, 1, fd)) + { + /* not enough room */ + std::cerr << "BinMemInterface::readfromfile() failed fread"; + std::cerr << std::endl; + + fclose(fd); + return false; + } + + recvsize = fsize; + readloc = 0; + fclose(fd); + + return true; +} + + +/**************************************************************************/ + + +void printNetBinID(std::ostream &out, std::string id, uint32_t t) +{ + out << "NetBinId(" << id << ","; + if (t == PQI_CONNECT_TCP) + { + out << "TCP)"; + } + else + { + out << "UDP)"; + } +} + + +/************************** NetBinDummy ****************************** + * A test framework, + * + */ + +#include "pqi/pqiperson.h" + +const uint32_t DEFAULT_DUMMY_DELTA = 5; + +NetBinDummy::NetBinDummy(PQInterface *parent, std::string id, uint32_t t) + :NetBinInterface(parent, id), type(t), dummyConnected(false), + toConnect(false), connectDelta(DEFAULT_DUMMY_DELTA) +{ + return; +} + + // Net Interface +int NetBinDummy::connect(struct sockaddr_in raddr) +{ + std::cerr << "NetBinDummy::connect("; + std::cerr << inet_ntoa(raddr.sin_addr) << ":"; + std::cerr << htons(raddr.sin_port); + std::cerr << ") "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + std::cerr << "NetBinDummy::dummyConnect = true!"; + std::cerr << std::endl; + + if (type == PQI_CONNECT_TCP) + { + std::cerr << "NetBinDummy:: Not connecting TCP"; + std::cerr << std::endl; + if (parent()) + { + parent()->notifyEvent(this, CONNECT_FAILED); + } + } + else if (!dummyConnected) + { + toConnect = true; + connectTS = time(NULL) + connectDelta; + std::cerr << "NetBinDummy::toConnect = true, connect in: "; + std::cerr << connectDelta << " secs"; + std::cerr << std::endl; + } + else + { + std::cerr << "NetBinDummy:: Already Connected!"; + std::cerr << std::endl; + } + + return 1; +} + +int NetBinDummy::listen() +{ + std::cerr << "NetBinDummy::connect() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + return 1; +} + +int NetBinDummy::stoplistening() +{ + std::cerr << "NetBinDummy::stoplistening() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + return 1; +} + +int NetBinDummy::disconnect() +{ + std::cerr << "NetBinDummy::disconnect() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + std::cerr << "NetBinDummy::dummyConnect = false!"; + std::cerr << std::endl; + dummyConnected = false; + + if (parent()) + { + parent()->notifyEvent(this, CONNECT_FAILED); + } + + return 1; +} + +int NetBinDummy::reset() +{ + std::cerr << "NetBinDummy::reset() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + disconnect(); + + return 1; +} + + + // Bin Interface. +int NetBinDummy::tick() +{ + + if (toConnect) + { + if (connectTS < time(NULL)) + { + std::cerr << "NetBinDummy::tick() dummyConnected = true "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + dummyConnected = true; + toConnect = false; + if (parent()) + parent()->notifyEvent(this, CONNECT_SUCCESS); + } + else + { + std::cerr << "NetBinDummy::tick() toConnect "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + } + } + return 0; +} + +int NetBinDummy::senddata(void *data, int len) +{ + std::cerr << "NetBinDummy::senddata() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + if (dummyConnected) + return len; + return 0; +} + +int NetBinDummy::readdata(void *data, int len) +{ + std::cerr << "NetBinDummy::readdata() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + return 0; +} + +int NetBinDummy::netstatus() +{ + std::cerr << "NetBinDummy::netstatus() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + return 1; +} + +int NetBinDummy::isactive() +{ + std::cerr << "NetBinDummy::isactive() "; + printNetBinID(std::cerr, PeerId(), type); + if (dummyConnected) + std::cerr << " true "; + else + std::cerr << " false "; + std::cerr << std::endl; + + return dummyConnected; +} + +bool NetBinDummy::moretoread() +{ + std::cerr << "NetBinDummy::moretoread() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + return false; +} + +bool NetBinDummy::cansend() +{ + std::cerr << "NetBinDummy::cansend() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + return dummyConnected; +} + +int NetBinDummy::close() +{ + std::cerr << "NetBinDummy::close() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + return 1; +} + +std::string NetBinDummy::gethash() +{ + std::cerr << "NetBinDummy::gethash() "; + printNetBinID(std::cerr, PeerId(), type); + std::cerr << std::endl; + + return std::string(""); +} + diff --git a/libretroshare/src/_pqi/pqibin.h b/libretroshare/src/_pqi/pqibin.h new file mode 100644 index 000000000..7e815034e --- /dev/null +++ b/libretroshare/src/_pqi/pqibin.h @@ -0,0 +1,165 @@ +/* + * "$Id: pqibin.h,v 1.2 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQIBIN_H +#define PQIBIN_H + +#include "pqi/pqi_base.h" +#include "pqi/pqihash.h" +#include + +class BinFileInterface: public BinInterface +{ +public: + BinFileInterface(const char *fname, int flags); +virtual ~BinFileInterface(); + +virtual int tick() { return 1; } + +virtual int senddata(void *data, int len); +virtual int readdata(void *data, int len); +virtual int netstatus() { return 1;} +virtual int isactive() { return (buf != NULL);} +virtual bool moretoread() + { + if ((buf) && (bin_flags | BIN_FLAGS_READABLE)) + { + if ((size - ftell(buf)) > 0) + { + return true; + } + } + return false; + } + +virtual int close(); +virtual bool cansend() { return (bin_flags | BIN_FLAGS_WRITEABLE); } +virtual bool bandwidthLimited() { return false; } + + /* if HASHing is switched on */ +virtual std::string gethash(); +virtual uint64_t bytecount(); + +private: + int bin_flags; + FILE *buf; + int size; + pqihash *hash; + uint64_t bcount; +}; + + +class BinMemInterface: public BinInterface +{ +public: + BinMemInterface(int defsize, int flags); + BinMemInterface(const void *data, const int size, int flags); +virtual ~BinMemInterface(); + + /* Extra Interfaces */ +int fseek(int loc); +int memsize() { return recvsize; } +void *memptr() { return buf; } + + /* file interface */ +bool writetofile(const char *fname); +bool readfromfile(const char *fname); + +virtual int tick() { return 1; } + +virtual int senddata(void *data, int len); +virtual int readdata(void *data, int len); +virtual int netstatus() { return 1; } +virtual int isactive() { return 1; } +virtual bool moretoread() + { + if ((buf) && (bin_flags | BIN_FLAGS_READABLE )) + { + if (readloc < recvsize) + { + return true; + } + } + return false; + } + +virtual int close(); +virtual bool cansend() { return (bin_flags | BIN_FLAGS_WRITEABLE); } +virtual bool bandwidthLimited() { return false; } + +virtual std::string gethash(); +virtual uint64_t bytecount(); + + private: + int bin_flags; + void *buf; + int size; + int recvsize; + int readloc; + pqihash *hash; + uint64_t bcount; +}; + + +class NetBinDummy: public NetBinInterface +{ +public: + NetBinDummy(PQInterface *parent, std::string id, uint32_t t); +virtual ~NetBinDummy() { return; } + + // Net Interface +virtual int connect(struct sockaddr_in raddr); +virtual int listen(); +virtual int stoplistening(); +virtual int disconnect(); +virtual int reset(); +virtual bool connect_parameter(uint32_t type, uint32_t value) { return false; } + + // Bin Interface. +virtual int tick(); + +virtual int senddata(void *data, int len); +virtual int readdata(void *data, int len); +virtual int netstatus(); +virtual int isactive(); +virtual bool moretoread(); +virtual bool cansend(); +virtual int close(); + +virtual std::string gethash(); + + private: + uint32_t type; + bool dummyConnected; + bool toConnect; + uint32_t connectDelta; + time_t connectTS; +}; + + +#endif // PQIBIN_H + diff --git a/libretroshare/src/_pqi/pqihandler.cc b/libretroshare/src/_pqi/pqihandler.cc new file mode 100644 index 000000000..5bcb44250 --- /dev/null +++ b/libretroshare/src/_pqi/pqihandler.cc @@ -0,0 +1,705 @@ +/* + * "$Id: pqihandler.cc,v 1.12 2007-03-31 09:41:32 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 "pqi/pqihandler.h" + + +const int pqihandlerzone = 34283; + +/**** +#define DEBUG_TICK 1 +#define RSITEM_DEBUG 1 +****/ + +pqihandler::pqihandler(SecurityPolicy *Global) +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + // The global security.... + // if something is disabled here... + // cannot be enabled by module. + globsec = Global; + + { + std::ostringstream out; + out << "New pqihandler()" << std::endl; + out << "Security Policy: " << secpolicy_print(globsec); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, out.str()); + } + + // setup minimal total+individual rates. + rateIndiv_out = 0.01; + rateIndiv_in = 0.01; + rateMax_out = 0.01; + rateMax_in = 0.01; + return; +} + +int pqihandler::tick() +{ + int moreToTick = 0; + + { RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + // tick all interfaces... + std::map::iterator it; + for(it = mods.begin(); it != mods.end(); it++) + { + if (0 < ((it -> second) -> pqi) -> tick()) + { +#ifdef DEBUG_TICK + std::cerr << "pqihandler::tick() moreToTick from mod()" << std::endl; +#endif + moreToTick = 1; + } + } + // get the items, and queue them correctly + if (0 < locked_GetItems()) + { +#ifdef DEBUG_TICK + std::cerr << "pqihandler::tick() moreToTick from GetItems()" << std::endl; +#endif + moreToTick = 1; + } + } /****** UNLOCK ******/ + + UpdateRates(); + return moreToTick; +} + + +int pqihandler::status() +{ + std::map::iterator it; + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + { // for output + std::ostringstream out; + out << "pqihandler::status() Active Modules:" << std::endl; + + // display all interfaces... + for(it = mods.begin(); it != mods.end(); it++) + { + out << "\tModule [" << it -> first << "] Pointer <"; + out << (void *) ((it -> second) -> pqi) << ">" << std::endl; + } + + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, out.str()); + + } // end of output. + + + // status all interfaces... + for(it = mods.begin(); it != mods.end(); it++) + { + ((it -> second) -> pqi) -> status(); + } + return 1; +} + + + +bool pqihandler::AddSearchModule(SearchModule *mod) +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + // if peerid used -> error. + std::map::iterator it; + if (mod->peerid != mod->pqi->PeerId()) + { + // ERROR! + std::ostringstream out; + out << "ERROR peerid != PeerId!" << std::endl; + pqioutput(PQL_ALERT, pqihandlerzone, out.str()); + return false; + } + + if (mod->peerid == "") + { + // ERROR! + std::ostringstream out; + out << "ERROR peerid == NULL" << std::endl; + pqioutput(PQL_ALERT, pqihandlerzone, out.str()); + return false; + } + + if (mods.find(mod->peerid) != mods.end()) + { + // ERROR! + std::ostringstream out; + out << "ERROR PeerId Module already exists!" << std::endl; + pqioutput(PQL_ALERT, pqihandlerzone, out.str()); + return false; + } + + // check security. + if (mod -> sp == NULL) + { + // create policy. + mod -> sp = secpolicy_create(); + } + + // limit to what global security allows. + secpolicy_limit(globsec, mod -> sp); + + // store. + mods[mod->peerid] = mod; + return true; +} + +bool pqihandler::RemoveSearchModule(SearchModule *mod) +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + std::map::iterator it; + for(it = mods.begin(); it != mods.end(); it++) + { + if (mod == it -> second) + { + mods.erase(it); + return true; + } + } + return false; +} + +// dummy output check +int pqihandler::locked_checkOutgoingRsItem(RsItem *item, int global) +{ + pqioutput(PQL_WARNING, pqihandlerzone, + "pqihandler::checkOutgoingPQItem() NULL fn"); + return 1; +} + + + +// generalised output +int pqihandler::HandleRsItem(RsItem *item, int allowglobal) +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + std::map::iterator it; + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "pqihandler::HandleRsItem()"); + + /* simplified to no global! */ + if (allowglobal) + { + /* error */ + std::ostringstream out; + out << "pqihandler::HandleSearchItem()"; + out << " Cannot send out Global RsItem"; + pqioutput(PQL_ALERT, pqihandlerzone, out.str()); + delete item; + return -1; + } + + if (!locked_checkOutgoingRsItem(item, allowglobal)) + { + std::ostringstream out; + out << "pqihandler::HandleRsItem() checkOutgoingPQItem"; + out << " Failed on item: " << std::endl; + item -> print(out); + + pqioutput(PQL_ALERT, pqihandlerzone, out.str()); + delete item; + return -1; + } + + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "pqihandler::HandleRsItem() Sending to One Channel"); + + + // find module. + if ((it = mods.find(item->PeerId())) == mods.end()) + { + std::ostringstream out; + out << "pqihandler::HandleRsItem() Invalid chan!"; + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, out.str()); + + delete item; + return -1; + } + + // check security... is output allowed. + if(0 < secpolicy_check((it -> second) -> sp, 0, PQI_OUTGOING)) + { + std::ostringstream out; + out << "pqihandler::HandleRsItem() sending to chan:"; + out << it -> first << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, out.str()); + + // if yes send on item. + ((it -> second) -> pqi) -> SendItem(item); + return 1; + } + else + { + std::ostringstream out; + out << "pqihandler::HandleRsItem()"; + out << " Sec not approved"; + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, out.str()); + + delete item; + return -1; + } + + // if successfully sent to at least one. + return 1; +} + +int pqihandler::SearchSpecific(RsCacheRequest *ns) +{ + return HandleRsItem(ns, 0); +} + +int pqihandler::SendSearchResult(RsCacheItem *ns) +{ + return HandleRsItem(ns, 0); +} + +int pqihandler::SendFileRequest(RsFileRequest *ns) +{ + return HandleRsItem(ns, 0); +} + +int pqihandler::SendFileData(RsFileData *ns) +{ + return HandleRsItem(ns, 0); +} + +int pqihandler::SendRsRawItem(RsRawItem *ns) +{ + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "pqihandler::SendRsRawItem()"); + return HandleRsItem(ns, 0); +} + + +// inputs. This is a very basic +// system that is completely biased and slow... +// someone please fix. + +int pqihandler::locked_GetItems() +{ + std::map::iterator it; + + RsItem *item; + int count = 0; + + // loop through modules.... + for(it = mods.begin(); it != mods.end(); it++) + { + SearchModule *mod = (it -> second); + + // check security... is output allowed. + if(0 < secpolicy_check((it -> second) -> sp, + 0, PQI_INCOMING)) // PQI_ITEM_TYPE_ITEM, PQI_INCOMING)) + { + // if yes... attempt to read. + while((item = (mod -> pqi) -> GetItem()) != NULL) + { +#ifdef RSITEM_DEBUG + std::ostringstream out; + out << "pqihandler::GetItems() Incoming Item "; + out << " from: " << mod -> pqi << std::endl; + item -> print(out); + + pqioutput(PQL_DEBUG_BASIC, + pqihandlerzone, out.str()); +#endif + + if (item->PeerId() != (mod->pqi)->PeerId()) + { + /* ERROR */ + pqioutput(PQL_ALERT, + pqihandlerzone, "ERROR PeerIds dont match!"); + item->PeerId(mod->pqi->PeerId()); + } + + locked_SortnStoreItem(item); + count++; + } + } + else + { + // not allowed to recieve from here.... + while((item = (mod -> pqi) -> GetItem()) != NULL) + { + std::ostringstream out; + out << "pqihandler::GetItems() Incoming Item "; + out << " from: " << mod -> pqi << std::endl; + item -> print(out); + out << std::endl; + out << "Item Not Allowed (Sec Pol). deleting!"; + out << std::endl; + + pqioutput(PQL_DEBUG_BASIC, + pqihandlerzone, out.str()); + + delete item; + } + } + } + return count; +} + + + + +void pqihandler::locked_SortnStoreItem(RsItem *item) +{ + /* get class type / subtype out of the item */ + uint8_t vers = item -> PacketVersion(); + uint8_t cls = item -> PacketClass(); + uint8_t type = item -> PacketType(); + uint8_t subtype = item -> PacketSubType(); + + /* whole Version reserved for SERVICES/CACHES */ + if (vers == RS_PKT_VERSION_SERVICE) + { + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "SortnStore -> Service"); + in_service.push_back(item); + item = NULL; + return; + } + + if (vers != RS_PKT_VERSION1) + { + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "SortnStore -> Invalid VERSION! Deleting!"); + delete item; + item = NULL; + return; + } + + switch(cls) + { + case RS_PKT_CLASS_BASE: + switch(type) + { + case RS_PKT_TYPE_CACHE: + switch(subtype) + { + case RS_PKT_SUBTYPE_CACHE_REQUEST: + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "SortnStore -> Cache Request"); + in_search.push_back(item); + item = NULL; + break; + + case RS_PKT_SUBTYPE_CACHE_ITEM: + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "SortnStore -> Cache Result"); + in_result.push_back(item); + item = NULL; + break; + + default: + break; /* no match! */ + } + break; + + case RS_PKT_TYPE_FILE: + switch(subtype) + { + case RS_PKT_SUBTYPE_FI_REQUEST: + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "SortnStore -> File Request"); + in_request.push_back(item); + item = NULL; + break; + + case RS_PKT_SUBTYPE_FI_DATA: + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "SortnStore -> File Data"); + in_data.push_back(item); + item = NULL; + break; + + default: + break; /* no match! */ + } + break; + + default: + break; /* no match! */ + } + break; + + default: + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "SortnStore -> Unknown"); + break; + + } + + if (item) + { + pqioutput(PQL_DEBUG_BASIC, pqihandlerzone, + "SortnStore -> Deleting Unsorted Item"); + delete item; + } + + return; +} + + +// much like the input stuff. +RsCacheItem *pqihandler::GetSearchResult() +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + if (in_result.size() != 0) + { + RsCacheItem *fi = dynamic_cast(in_result.front()); + if (!fi) { delete in_result.front(); } + in_result.pop_front(); + return fi; + } + return NULL; +} + +RsCacheRequest *pqihandler::RequestedSearch() +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + if (in_search.size() != 0) + { + RsCacheRequest *fi = dynamic_cast(in_search.front()); + if (!fi) { delete in_search.front(); } + in_search.pop_front(); + return fi; + } + return NULL; +} + +RsFileRequest *pqihandler::GetFileRequest() +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + if (in_request.size() != 0) + { + RsFileRequest *fi = dynamic_cast(in_request.front()); + if (!fi) { delete in_request.front(); } + in_request.pop_front(); + return fi; + } + return NULL; +} + +RsFileData *pqihandler::GetFileData() +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + if (in_data.size() != 0) + { + RsFileData *fi = dynamic_cast(in_data.front()); + if (!fi) { delete in_data.front(); } + in_data.pop_front(); + return fi; + } + return NULL; +} + +RsRawItem *pqihandler::GetRsRawItem() +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + if (in_service.size() != 0) + { + RsRawItem *fi = dynamic_cast(in_service.front()); + if (!fi) { delete in_service.front(); } + in_service.pop_front(); + return fi; + } + return NULL; +} + +static const float MIN_RATE = 0.01; // 10 B/s + +// internal fn to send updates +int pqihandler::UpdateRates() +{ + std::map::iterator it; + int num_sm = mods.size(); + + float avail_in = getMaxRate(true); + float avail_out = getMaxRate(false); + + float used_bw_in = 0; + float used_bw_out = 0; + + /* Lock once rates have been retrieved */ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + int effectiveUploadsSm = 0; + int effectiveDownloadsSm = 0; + // loop through modules to get the used bandwith and the number of modules that are affectively transfering + //std::cerr << " Looping through modules" << std::endl; + for(it = mods.begin(); it != mods.end(); it++) + { + SearchModule *mod = (it -> second); + float crate_in = mod -> pqi -> getRate(true); + if (crate_in > 0.01 * avail_in || crate_in > 0.1) + { + effectiveDownloadsSm ++; + } + + float crate_out = mod -> pqi -> getRate(false); + if (crate_out > 0.01 * avail_out || crate_out > 0.1) + { + effectiveUploadsSm ++; + } + + used_bw_in += crate_in; + used_bw_out += crate_out; + } +// std::cerr << "Totals (In) Used B/W " << used_bw_in; +// std::cerr << " Available B/W " << avail_in; +// std::cerr << " Effective transfers " << effectiveDownloadsSm << std::endl; +// std::cerr << "Totals (Out) Used B/W " << used_bw_out; +// std::cerr << " Available B/W " << avail_out; +// std::cerr << " Effective transfers " << effectiveUploadsSm << std::endl; + + locked_StoreCurrentRates(used_bw_in, used_bw_out); + + //computing average rates for effective transfers + float max_in_effective = avail_in / num_sm; + if (effectiveDownloadsSm != 0) { + max_in_effective = avail_in / effectiveDownloadsSm; + } + float max_out_effective = avail_out / num_sm; + if (effectiveUploadsSm != 0) { + max_out_effective = avail_out / effectiveUploadsSm; + } + + //modify the outgoing rates if bandwith is not used well + float rate_out_modifier = 0; + if (used_bw_out / avail_out < 0.95) { + rate_out_modifier = 0.001 * avail_out; + } else if (used_bw_out / avail_out > 1.05) { + rate_out_modifier = - 0.001 * avail_out; + } + if (rate_out_modifier != 0) { + for(it = mods.begin(); it != mods.end(); it++) + { + SearchModule *mod = (it -> second); + mod -> pqi -> setMaxRate(false, mod -> pqi -> getMaxRate(false) + rate_out_modifier); + } + } + + //modify the incoming rates if bandwith is not used well + float rate_in_modifier = 0; + if (used_bw_in / avail_in < 0.95) { + rate_in_modifier = 0.001 * avail_in; + } else if (used_bw_in / avail_in > 1.05) { + rate_in_modifier = - 0.001 * avail_in; + } + if (rate_in_modifier != 0) { + for(it = mods.begin(); it != mods.end(); it++) + { + SearchModule *mod = (it -> second); + mod -> pqi -> setMaxRate(true, mod -> pqi -> getMaxRate(true) + rate_in_modifier); + } + } + + //cap the rates + for(it = mods.begin(); it != mods.end(); it++) + { + SearchModule *mod = (it -> second); + if (mod -> pqi -> getMaxRate(false) < max_out_effective) { + mod -> pqi -> setMaxRate(false, max_out_effective); + } + if (mod -> pqi -> getMaxRate(false) > avail_out) { + mod -> pqi -> setMaxRate(false, avail_out); + } + if (mod -> pqi -> getMaxRate(true) < max_in_effective) { + mod -> pqi -> setMaxRate(true, max_in_effective); + } + if (mod -> pqi -> getMaxRate(true) > avail_in) { + mod -> pqi -> setMaxRate(true, avail_in); + } + } + + return 1; +} + +void pqihandler::getCurrentRates(float &in, float &out) +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + in = rateTotal_in; + out = rateTotal_out; +} + +void pqihandler::locked_StoreCurrentRates(float in, float out) +{ + rateTotal_in = in; + rateTotal_out = out; +} + + +//inline void pqihandler::setMaxIndivRate(bool in, float val) +//{ +// RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ +// if (in) +// rateIndiv_in = val; +// else +// rateIndiv_out = val; +// return; +//} +// +//inline float pqihandler::getMaxIndivRate(bool in) +//{ +// RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ +// if (in) +// return rateIndiv_in; +// else +// return rateIndiv_out; +//} + +void pqihandler::setMaxRate(bool in, float val) +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + if (in) + rateMax_in = val; + else + rateMax_out = val; + return; +} + +float pqihandler::getMaxRate(bool in) +{ + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + if (in) + return rateMax_in; + else + return rateMax_out; +} + diff --git a/libretroshare/src/_pqi/pqihandler.h b/libretroshare/src/_pqi/pqihandler.h new file mode 100644 index 000000000..d91b815bd --- /dev/null +++ b/libretroshare/src/_pqi/pqihandler.h @@ -0,0 +1,126 @@ +/* + * "$Id: pqihandler.h,v 1.10 2007-03-31 09:41:32 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + +#ifndef PQIHANDLER_H +#define PQIHANDLER_H + +#include "_pqi/pqi.h" +#include "_pqi/pqisecurity.h" + +#include "_util/rsthreads.h" +#include +#include "_util/rsdebug.h" +#include + +#include +#include + +class SearchModule +{ + public: + std::string peerid; + PQInterface *pqi; + SecurityPolicy *sp; +}; + +// Presents a P3 Face to the world! +// and funnels data through to a PQInterface. +// +class pqihandler: public P3Interface +{ +public: + pqihandler(SecurityPolicy *Global); + bool AddSearchModule(SearchModule *mod); + bool RemoveSearchModule(SearchModule *mod); + + // P3Interface. + virtual int SearchSpecific(RsCacheRequest *ns); + virtual int SendSearchResult(RsCacheItem *); + + // inputs. + virtual RsCacheRequest * RequestedSearch(); + virtual RsCacheItem * GetSearchResult(); + + // file i/o + virtual int SendFileRequest(RsFileRequest *ns); + virtual int SendFileData(RsFileData *ns); + virtual RsFileRequest * GetFileRequest(); + virtual RsFileData * GetFileData(); + + // Rest of P3Interface + virtual int tick(); + virtual int status(); + + // Service Data Interface + virtual int SendRsRawItem(RsRawItem *); + virtual RsRawItem *GetRsRawItem(); + + // rate control. + //indiv rate is deprecated + //void setMaxIndivRate(bool in, float val); + //float getMaxIndivRate(bool in); + inline void setMaxRate(bool in, float val); + inline float getMaxRate(bool in); + + void getCurrentRates(float &in, float &out); + +protected: + /* check to be overloaded by those that can + * generates warnings otherwise + */ + + int HandleRsItem(RsItem *ns, int allowglobal); + + virtual int locked_checkOutgoingRsItem(RsItem *item, int global); + int locked_GetItems(); + void locked_SortnStoreItem(RsItem *item); + + RsMutex coreMtx; /* MUTEX */ + + std::map mods; + SecurityPolicy *globsec; + + // Temporary storage... + std::list in_result, in_search, + in_request, in_data, in_service; +private: + + // rate control. + int UpdateRates(); + void locked_StoreCurrentRates(float in, float out); + + float rateIndiv_in; + float rateIndiv_out; + float rateMax_in; + float rateMax_out; + + float rateTotal_in; + float rateTotal_out; + +}; + + + +#endif // PQIHANDLER_H diff --git a/libretroshare/src/_pqi/pqihash.cc b/libretroshare/src/_pqi/pqihash.cc new file mode 100644 index 000000000..17dc7c0f1 --- /dev/null +++ b/libretroshare/src/_pqi/pqihash.cc @@ -0,0 +1,48 @@ +#include "pqihash.h" + + +pqihash::pqihash() +{ + + sha_hash = new uint8_t[SHA_DIGEST_LENGTH]; + sha_ctx = new SHA_CTX; + SHA1_Init(sha_ctx); + doHash = true; +} + +pqihash::~pqihash() +{ + delete[] sha_hash; + delete sha_ctx; +} + + +void pqihash::addData(void *data, uint32_t len) +{ + if (doHash) + { + SHA1_Update(sha_ctx, data, len); + } +} + +void pqihash::Complete(std::string &hash) +{ + if (!doHash) + { + hash = endHash; + return; + } + + SHA1_Final(sha_hash, sha_ctx); + + std::ostringstream out; + for(int i = 0; i < SHA_DIGEST_LENGTH; i++) + { + out << std::setw(2) << std::setfill('0') << std::hex; + out << (unsigned int) (sha_hash[i]); + } + endHash = out.str(); + hash = endHash; + doHash = false; +} + diff --git a/libretroshare/src/_pqi/pqihash.h b/libretroshare/src/_pqi/pqihash.h new file mode 100644 index 000000000..101fe6a9b --- /dev/null +++ b/libretroshare/src/_pqi/pqihash.h @@ -0,0 +1,51 @@ +/* + * libretroshare/src/pqi: pqihash.h + * + * 3P/PQI network interface 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". + * + */ + +#ifndef PQIHASH_H +#define PQIHASH_H + +#include +#include +#include +#include + +class pqihash +{ +public: + pqihash(); + ~pqihash(); + + inline void addData(void *data, uint32_t len); + inline void Complete(std::string &hash); + +private: + bool doHash; + std::string endHash; + uint8_t *sha_hash; + SHA_CTX *sha_ctx; +}; + + +#endif // PQIHASH_H diff --git a/libretroshare/src/_pqi/pqiindic.cc b/libretroshare/src/_pqi/pqiindic.cc new file mode 100644 index 000000000..5008e5924 --- /dev/null +++ b/libretroshare/src/_pqi/pqiindic.cc @@ -0,0 +1,26 @@ +#include "pqiindic.h" + +Indicator::Indicator(uint16_t n = 1) : + num(n), + changeFlags(n) +{ + IndicateChanged(); +} + +void Indicator::IndicateChanged() +{ + for(uint16_t i = 0; i < num; i++) + changeFlags[i]=true; +} + +bool Indicator::Changed(uint16_t idx = 0) +{ + /* catch overflow */ + if (idx > num - 1) + return false; + + bool ans = changeFlags[idx]; + changeFlags[idx] = false; + return ans; +} + diff --git a/libretroshare/src/_pqi/pqiindic.h b/libretroshare/src/_pqi/pqiindic.h new file mode 100644 index 000000000..a1e202e2d --- /dev/null +++ b/libretroshare/src/_pqi/pqiindic.h @@ -0,0 +1,51 @@ +/* + * "$Id: pqiindic.h,v 1.3 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQIINDIC_H +#define PQIINDIC_H + +#include + +// This will indicate to num different sources +// when the event has occured. + +class Indicator +{ +public: + Indicator(uint16_t n = 1); + void IndicateChanged(); + bool Changed(uint16_t idx = 0); + +private: + uint16_t num; + std::vector changeFlags; + +}; + + + +#endif // PQIINDIC_H diff --git a/libretroshare/src/_pqi/pqilistener.h b/libretroshare/src/_pqi/pqilistener.h new file mode 100644 index 000000000..95bcee818 --- /dev/null +++ b/libretroshare/src/_pqi/pqilistener.h @@ -0,0 +1,48 @@ +/* + * libretroshare/src/pqi: pqilistener.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + +#ifndef PQILISTENER_H +#define PQILISTENER_H + +// operating system specific network header. +#include "pqi/pqinetwork.h" + +class pqilistener +{ +public: + + pqilistener() { return; } + virtual ~pqilistener() { return; } + + virtual int tick() { return 1; } + virtual int status() { return 1; } + virtual int setListenAddr(struct sockaddr_in addr) { return 1; } + virtual int setuplisten() { return 1; } + virtual int resetlisten() { return 1; } + +}; + + +#endif // PQILISTENER_H diff --git a/libretroshare/src/_pqi/pqiloopback.cc b/libretroshare/src/_pqi/pqiloopback.cc new file mode 100644 index 000000000..457548d33 --- /dev/null +++ b/libretroshare/src/_pqi/pqiloopback.cc @@ -0,0 +1,90 @@ +/* + * libretroshare/src/pqi: pqiloopback.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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 "pqi/pqi.h" +#include "pqi/pqiloopback.h" + +/*** +#define LOOPBACK_DEBUG 1 +***/ + +pqiloopback::pqiloopback(std::string id) + :PQInterface(id) +{ + setMaxRate(true, 0); + setMaxRate(false, 0); + setRate(true, 0); + setRate(false, 0); + + return; +} + +pqiloopback::~pqiloopback() +{ + return; +} + +int pqiloopback::SendItem(RsItem *i) +{ + +#ifdef LOOPBACK_DEBUG + std::cerr << "pqiloopback::SendItem()"; + std::cerr << std::endl; + i->print(std::cerr); + std::cerr << std::endl; +#endif + objs.push_back(i); + return 1; +} + +RsItem * pqiloopback::GetItem() +{ + if (objs.size() > 0) + { + RsItem *pqi = objs.front(); + objs.pop_front(); +#ifdef LOOPBACK_DEBUG + std::cerr << "pqiloopback::GetItem()"; + std::cerr << std::endl; + pqi->print(std::cerr); + std::cerr << std::endl; +#endif + return pqi; + } + return NULL; +} + +// PQI interface. +int pqiloopback::tick() +{ + return 0; +} + +int pqiloopback::status() +{ + return 0; +} + + diff --git a/libretroshare/src/_pqi/pqiloopback.h b/libretroshare/src/_pqi/pqiloopback.h new file mode 100644 index 000000000..d44f73517 --- /dev/null +++ b/libretroshare/src/_pqi/pqiloopback.h @@ -0,0 +1,55 @@ +/* + * libretroshare/src/pqi: pqiloopback.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + +#ifndef PQILOOPBACK_H +#define PQILOOPBACK_H + +// The standard data types and the search interface. +#include "_pqi/pqi.h" + +#include +#include +#include + +class pqiloopback: public PQInterface +{ +public: + pqiloopback(std::string id); + virtual ~pqiloopback(); + + // search Interface. + virtual int SendItem(RsItem *item); + virtual RsItem *GetItem(); + + // PQI interface. + virtual int tick(); + virtual int status(); + + virtual int notifyEvent(NetInterface *ni, int event) { return 0; } /* Not used */ + private: + std::list objs; +}; + +#endif //MRK_PQI_LOOPBACK_HEADER diff --git a/libretroshare/src/_pqi/pqimonitor.cc b/libretroshare/src/_pqi/pqimonitor.cc new file mode 100644 index 000000000..6b854f660 --- /dev/null +++ b/libretroshare/src/_pqi/pqimonitor.cc @@ -0,0 +1,85 @@ +/* + * libretroshare/src/pqi: pqimonitor.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2007-2008 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 "_pqi/pqimonitor.h" + + +/***** DUMMY Connect CB for testing *****/ + +#include + + +pqiConnectCbDummy::pqiConnectCbDummy() +{ + std::cerr << "pqiConnectCbDummy()" << std::endl; + return; +} + +pqiConnectCbDummy::~pqiConnectCbDummy() +{ + return; +} + +void pqiConnectCbDummy::peerStatus(std::string id, + struct sockaddr_in laddr, struct sockaddr_in raddr, + uint32_t type, uint32_t mode, uint32_t source) +{ + std::cerr << "pqiConnectCbDummy::peerStatus()"; + std::cerr << " id: " << id; + + std::cerr << " laddr: " << inet_ntoa(laddr.sin_addr); + std::cerr << " lport: " << ntohs(laddr.sin_port); + std::cerr << " raddr: " << inet_ntoa(raddr.sin_addr); + std::cerr << " rport: " << ntohs(raddr.sin_port); + + std::cerr << " type: " << type; + std::cerr << " mode: " << mode; + std::cerr << " source: " << source; + std::cerr << std::endl; +} + +void pqiConnectCbDummy::peerConnectRequest(std::string id, + struct sockaddr_in raddr, uint32_t source) +{ + std::cerr << "pqiConnectCbDummy::peerConnectRequest()"; + std::cerr << " id: " << id; + std::cerr << " raddr: " << inet_ntoa(raddr.sin_addr); + std::cerr << ":" << ntohs(raddr.sin_port); + std::cerr << " source: " << source; + std::cerr << std::endl; +} + +void pqiConnectCbDummy::stunStatus(std::string id, struct sockaddr_in raddr, + uint32_t type, uint32_t flags) +{ + std::cerr << "pqiConnectCbDummy::stunStatus()"; + std::cerr << " idhash: " << RsUtil::BinToHex(id) << " raddr: " << inet_ntoa(raddr.sin_addr); + std::cerr << ":" << ntohs(raddr.sin_port); + std::cerr << " type: " << type; + std::cerr << " flags: " << flags; + std::cerr << std::endl; +} + + diff --git a/libretroshare/src/_pqi/pqimonitor.h b/libretroshare/src/_pqi/pqimonitor.h new file mode 100644 index 000000000..7cb2eb2be --- /dev/null +++ b/libretroshare/src/_pqi/pqimonitor.h @@ -0,0 +1,138 @@ +/* + * libretroshare/src/pqi: pqimonitor.h + * + * 3P/PQI network interface for RetroShare. + * + * 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". + * + */ + +#ifndef PQIMONITOR_H +#define PQIMONITOR_H + +/**** Rough sketch of a Monitor class + * expect it to change significantly + * + */ + +#include "_pqi/pqinetwork.h" +#include "_util/rsprint.h" + +#include +#include +#include + +/************** Define Type/Mode/Source ***************/ + + +/* STATE MASK */ +const uint32_t RS_PEER_STATE_MASK = 0x00ff; +const uint32_t RS_PEER_ACTION_MASK = 0xff00; + +/* STATE */ +const uint32_t RS_PEER_S_FRIEND = 0x0001; +const uint32_t RS_PEER_S_ONLINE = 0x0002; /* heard from recently..*/ +const uint32_t RS_PEER_S_CONNECTED = 0x0004; +const uint32_t RS_PEER_S_UNREACHABLE = 0x0008; + +/* ACTIONS */ +const uint32_t RS_PEER_NEW = 0x0001; /* new Peer */ +const uint32_t RS_PEER_ONLINE = 0x0002; +const uint32_t RS_PEER_CONNECTED = 0x0004; +const uint32_t RS_PEER_MOVED = 0x0008; /* moved from F->O or O->F */ +const uint32_t RS_PEER_DISCONNECTED = 0x0010; +const uint32_t RS_PEER_CONNECT_REQ = 0x0020; + +/* Stun Status Flags */ +//const uint32_t RS_STUN_SRC_DHT = 0x0001; +//const uint32_t RS_STUN_SRC_PEER = 0x0002; +const uint32_t RS_STUN_ONLINE = 0x0010; +const uint32_t RS_STUN_FRIEND = 0x0020; +const uint32_t RS_STUN_FRIEND_OF_FRIEND = 0x0040; + + + +#define RS_CONNECT_PASSIVE 1 +#define RS_CONNECT_ACTIVE 2 + +#define RS_CB_DHT 1 /* from dht */ +#define RS_CB_DISC 2 /* from peers */ +#define RS_CB_PERSON 3 /* from connection */ +#define RS_CB_PROXY 4 /* via proxy */ + + +class pqipeer +{ +public: + std::string id; + std::string name; + uint32_t state; + uint32_t actions; +}; + +class p3ConnectMgr; + +class pqiMonitor +{ +public: + pqiMonitor() :mConnMgr(NULL) { return; } + virtual ~pqiMonitor() { return; } + + void setConnectionMgr(p3ConnectMgr *cm) { mConnMgr = cm; } + virtual void statusChange(const std::list &plist) = 0; + + //virtual void peerStatus(std::string id, uint32_t mode) = 0; + +protected: + p3ConnectMgr *mConnMgr; +}; + + + + +class pqiConnectCb +{ +public: + virtual ~pqiConnectCb() { return; } + virtual void peerStatus(std::string id,struct sockaddr_in laddr, struct sockaddr_in raddr, + uint32_t type, uint32_t flags, uint32_t source) = 0; + virtual void peerConnectRequest(std::string id,struct sockaddr_in raddr, uint32_t source) = 0; + virtual void stunStatus(std::string id, struct sockaddr_in raddr, + uint32_t type, uint32_t flags) = 0; +}; + + +/**** DUMMY CB FOR TESTING (just prints) ****/ +class pqiConnectCbDummy: public pqiConnectCb +{ +public: + pqiConnectCbDummy(); + virtual ~pqiConnectCbDummy(); + + virtual void peerStatus(std::string id, + struct sockaddr_in laddr, struct sockaddr_in raddr, + uint32_t type, uint32_t mode, uint32_t source); + + virtual void peerConnectRequest(std::string id, + struct sockaddr_in raddr, uint32_t source); + virtual void stunStatus(std::string id, struct sockaddr_in raddr, uint32_t type, uint32_t flags); +}; + +#endif // PQIMONITOR_H + diff --git a/libretroshare/src/_pqi/pqinetwork.cc b/libretroshare/src/_pqi/pqinetwork.cc new file mode 100644 index 000000000..70aa08df5 --- /dev/null +++ b/libretroshare/src/_pqi/pqinetwork.cc @@ -0,0 +1,866 @@ +/* + * "$Id: pqinetwork.cc,v 1.18 2007-04-15 18:45:18 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 "_pqi/pqinetwork.h" +#include "util/rsnet.h" + +#include +#include +#include + +#include "util/rsdebug.h" +#include +#include +static const int pqinetzone = 96184; + +/***** + * #define NET_DEBUG 1 + ****/ + +#ifdef WINDOWS_SYS /* Windows - define errno */ + +int errno; + +#else /* Windows - define errno */ + +#include + +#endif + +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS + +std::ostream &showSocketError(std::ostream &out) +{ + int err = errno; + out << "\tSocket Error(" << err << ") : "; + out << socket_errorType(err) << std::endl; + return out; +} + +std::string socket_errorType(int err) +{ + if (err == EBADF) + { + return std::string("EBADF"); + } + else if (err == EINVAL) + { + return std::string("EINVAL"); + } + else if (err == EFAULT) + { + return std::string("EFAULT"); + } + else if (err == ENOTSOCK) + { + return std::string("ENOTSOCK"); + } + else if (err == EISCONN) + { + return std::string("EISCONN"); + } + else if (err == ECONNREFUSED) + { + return std::string("ECONNREFUSED"); + } + else if (err == ETIMEDOUT) + { + return std::string("ETIMEDOUT"); + } + else if (err == ENETUNREACH) + { + return std::string("ENETUNREACH"); + } + else if (err == EADDRINUSE) + { + return std::string("EADDRINUSE"); + } + else if (err == EINPROGRESS) + { + return std::string("EINPROGRESS"); + } + else if (err == EALREADY) + { + return std::string("EALREADY"); + } + else if (err == EAGAIN) + { + return std::string("EAGAIN"); + } + else if (err == EISCONN) + { + return std::string("EISCONN"); + } + else if (err == ENOTCONN) + { + return std::string("ENOTCONN"); + } + + return std::string("UNKNOWN ERROR CODE"); +} + +#include +#include + +std::list getLocalInterfaces() +{ + std::list addrs; + + int sock = 0; + struct ifreq ifreq; + + struct if_nameindex *iflist = if_nameindex(); + struct if_nameindex *ifptr = iflist; + + //need a socket for ioctl() + if( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + pqioutput(PQL_ALERT, pqinetzone, + "Cannot Determine Local Addresses!"); + exit(1); + } + + if (!ifptr) + { + pqioutput(PQL_ALERT, pqinetzone, + "getLocalInterfaces(): ERROR if_nameindex == NULL"); + } + + // loop through the interfaces. + //for(; *(char *)ifptr != 0; ifptr++) + for(; ifptr->if_index != 0; ifptr++) + { + //copy in the interface name to look up address of + strncpy(ifreq.ifr_name, ifptr->if_name, IF_NAMESIZE); + + if(ioctl(sock, SIOCGIFADDR, &ifreq) != 0) + { + std::ostringstream out; + out << "Cannot Determine Address for Iface: "; + out << ifptr -> if_name << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqinetzone, out.str()); + } + else + { + struct sockaddr_in *aptr = + (struct sockaddr_in *) &ifreq.ifr_addr; + const char *astr=inet_ntoa(aptr -> sin_addr); + + std::ostringstream out; + out << "Iface: "; + out << ifptr -> if_name << std::endl; + out << " Address: " << astr; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqinetzone, out.str()); + + addrs.push_back(astr); + } + } + // free socket -> or else run out of fds. + close(sock); + + if_freenameindex(iflist); + return addrs; +} + +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#else + +std::ostream &showSocketError(std::ostream &out) +{ + int err = WSAGetLastError(); + out << "\tSocket Error(" << err << ") : "; + out << socket_errorType(err) << std::endl; + return out; +} + + +std::string socket_errorType(int err) +{ + if (err == WSAEBADF) + { + return std::string("WSABADF"); + } + + + else if (err == WSAEINTR) + { + return std::string("WSAEINTR"); + } + else if (err == WSAEACCES) + { + return std::string("WSAEACCES"); + } + else if (err == WSAEFAULT) + { + return std::string("WSAEFAULT"); + } + else if (err == WSAEINVAL) + { + return std::string("WSAEINVAL"); + } + else if (err == WSAEMFILE) + { + return std::string("WSAEMFILE"); + } + else if (err == WSAEWOULDBLOCK) + { + return std::string("WSAEWOULDBLOCK"); + } + else if (err == WSAEINPROGRESS) + { + return std::string("WSAEINPROGRESS"); + } + else if (err == WSAEALREADY) + { + return std::string("WSAEALREADY"); + } + else if (err == WSAENOTSOCK) + { + return std::string("WSAENOTSOCK"); + } + else if (err == WSAEDESTADDRREQ) + { + return std::string("WSAEDESTADDRREQ"); + } + else if (err == WSAEMSGSIZE) + { + return std::string("WSAEMSGSIZE"); + } + else if (err == WSAEPROTOTYPE) + { + return std::string("WSAEPROTOTYPE"); + } + else if (err == WSAENOPROTOOPT) + { + return std::string("WSAENOPROTOOPT"); + } + else if (err == WSAENOTSOCK) + { + return std::string("WSAENOTSOCK"); + } + else if (err == WSAEISCONN) + { + return std::string("WSAISCONN"); + } + else if (err == WSAECONNREFUSED) + { + return std::string("WSACONNREFUSED"); + } + else if (err == WSAECONNRESET) + { + return std::string("WSACONNRESET"); + } + else if (err == WSAETIMEDOUT) + { + return std::string("WSATIMEDOUT"); + } + else if (err == WSAENETUNREACH) + { + return std::string("WSANETUNREACH"); + } + else if (err == WSAEADDRINUSE) + { + return std::string("WSAADDRINUSE"); + } + else if (err == WSAEAFNOSUPPORT) + { + return std::string("WSAEAFNOSUPPORT (normally UDP related!)"); + } + + return std::string("----WINDOWS OPERATING SYSTEM FAILURE----"); +} + +#include +//#include + +// A function to determine the interfaces on your computer.... +// No idea of how to do this in windows.... +// see if it compiles. +std::list getLocalInterfaces() +{ + std::list addrs; + + + /* USE MIB IPADDR Interface */ + PMIB_IPADDRTABLE iptable = NULL; + DWORD dwSize = 0; + + if (GetIpAddrTable(iptable, &dwSize, 0) != + ERROR_INSUFFICIENT_BUFFER) + { + pqioutput(PQL_ALERT, pqinetzone, + "Cannot Find Windoze Interfaces!"); + exit(0); + } + + iptable = (MIB_IPADDRTABLE *) malloc(dwSize); + GetIpAddrTable(iptable, &dwSize, 0); + + struct in_addr addr; + + for(unsigned int i = 0; i < iptable -> dwNumEntries; i++) + { + std::ostringstream out; + + out << "Iface(" << iptable->table[i].dwIndex << ") "; + addr.s_addr = iptable->table[i].dwAddr; + out << " => " << inet_ntoa(addr); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqinetzone, out.str()); + + addrs.push_back(inet_ntoa(addr)); + } + + return addrs; +} + +// implement the improved unix inet address fn. +// using old one. +int inet_aton(const char *name, struct in_addr *addr) +{ + return (((*addr).s_addr = inet_addr(name)) != INADDR_NONE); +} + + +// This returns in Net Byte Order. +// NB: Linux man page claims it is in Host Byte order, but +// this is blatantly wrong!..... (for Debian anyway) +// Making this consistent with the Actual behavior (rather than documented). +in_addr_t inet_netof(struct in_addr addr) +{ + return pqi_inet_netof(addr); +} + +// This returns in Host Byte Order. (as the man page says) +// Again, to be consistent with Linux. +in_addr_t inet_network(const char *inet_name) +{ + struct in_addr addr; + if (inet_aton(inet_name, &addr)) + { +#ifdef NET_DEBUG +// std::cerr << "inet_network(" << inet_name << ") : "; +// std::cerr << inet_ntoa(addr) << std::endl; +#endif + return ntohl(inet_netof(addr)); + } + return 0xffffffff; + //return -1; +} + + +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + +#include + + +// This returns in Net Byte Order. +// NB: Linux man page claims it is in Host Byte order, but +// this is blatantly wrong!..... (for Debian anyway) +// Making this consistent with the Actual behavior (rather than documented). +in_addr_t pqi_inet_netof(struct in_addr addr) +{ + // decide if A class address. + unsigned long haddr = ntohl(addr.s_addr); + unsigned long abit = haddr & 0xff000000UL; + unsigned long bbit = haddr & 0xffff0000UL; + unsigned long cbit = haddr & 0xffffff00UL; + +#ifdef NET_DEBUG + std::cerr << "inet_netof(" << inet_ntoa(addr) << ") "; +#endif + + if (!((haddr >> 31) | 0x0UL)) // MSB = 0 + { +#ifdef NET_DEBUG + std::cerr << " Type A " << std::endl; + std::cerr << "\tShifted(31): " << (haddr >> 31); + std::cerr << " Xord(0x0UL): " << + !((haddr >> 31) | 0x0UL) << std::endl; +#endif + + return htonl(abit); + } + else if (!((haddr >> 30) ^ 0x2UL)) // 2MSBs = 10 + { +#ifdef NET_DEBUG + std::cerr << " Type B " << std::endl; + std::cerr << "\tShifted(30): " << (haddr >> 30); + std::cerr << " Xord(0x2UL): " << + !((haddr >> 30) | 0x2UL) << std::endl; +#endif + + return htonl(bbit); + } + else if (!((haddr >> 29) ^ 0x6UL)) // 3MSBs = 110 + { +#ifdef NET_DEBUG + std::cerr << " Type C " << std::endl; + std::cerr << "\tShifted(29): " << (haddr >> 29); + std::cerr << " Xord(0x6UL): " << + !((haddr >> 29) | 0x6UL) << std::endl; +#endif + + return htonl(cbit); + } + else if (!((haddr >> 28) ^ 0xeUL)) // 4MSBs = 1110 + { +#ifdef NET_DEBUG + std::cerr << " Type Multicast " << std::endl; + std::cerr << "\tShifted(28): " << (haddr >> 28); + std::cerr << " Xord(0xeUL): " << + !((haddr >> 29) | 0xeUL) << std::endl; +#endif + + return addr.s_addr; // return full address. + } + else if (!((haddr >> 27) ^ 0x1eUL)) // 5MSBs = 11110 + { +#ifdef NET_DEBUG + std::cerr << " Type Reserved " << std::endl; + std::cerr << "\tShifted(27): " << (haddr >> 27); + std::cerr << " Xord(0x1eUL): " << + !((haddr >> 27) | 0x1eUL) << std::endl; +#endif + + return addr.s_addr; // return full address. + } + return htonl(abit); +} + +int sockaddr_cmp(struct sockaddr_in &addr1, struct sockaddr_in &addr2 ) +{ + if (addr1.sin_family != addr2.sin_family) + return addr1.sin_family - addr2.sin_family; + if (addr1.sin_addr.s_addr != addr2.sin_addr.s_addr) + return (addr1.sin_addr.s_addr - addr2.sin_addr.s_addr); + if (addr1.sin_port != addr2.sin_port) + return (addr1.sin_port - addr2.sin_port); + return 0; +} + +int inaddr_cmp(struct sockaddr_in addr1, struct sockaddr_in addr2 ) +{ +#ifdef NET_DEBUG + std::ostringstream out; + out << "inaddr_cmp(" << inet_ntoa(addr1.sin_addr); + out << "-" << addr1.sin_addr.s_addr; + out << "," << inet_ntoa(addr2.sin_addr); + out << "-" << addr2.sin_addr.s_addr << ")" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqinetzone, out.str()); +#endif + + + if (addr1.sin_addr.s_addr == addr2.sin_addr.s_addr) + { + return 0; + } + if (addr1.sin_addr.s_addr < addr2.sin_addr.s_addr) + return -1; + return 1; +} + +int inaddr_cmp(struct sockaddr_in addr1, unsigned long addr2) +{ +#ifdef NET_DEBUG + struct in_addr inaddr_tmp; + inaddr_tmp.s_addr = addr2; + + std::ostringstream out; + out << "inaddr_cmp2(" << inet_ntoa(addr1.sin_addr); + out << " vs " << inet_ntoa(inaddr_tmp); + out << " /or/ "; + out << std::hex << std::setw(10) << addr1.sin_addr.s_addr; + out << " vs " << std::setw(10) << addr2 << ")" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqinetzone, out.str()); +#endif + + if (addr1.sin_addr.s_addr == addr2) + { + return 0; + } + if (addr1.sin_addr.s_addr < addr2) + return -1; + return 1; +} + + +struct in_addr getPreferredInterface() // returns best addr. +{ + + std::list addrs = getLocalInterfaces(); + std::list::iterator it; + struct in_addr addr_zero = {0}, addr_loop = {0}, addr_priv = {0}, addr_ext = {0}, addr = {0}; + + bool found_zero = false; + bool found_loopback = false; + bool found_priv = false; + bool found_ext = false; + + // find the first of each of these. + // if ext - take first. + // if no ext -> first priv + // if no priv -> first loopback. + + for(it = addrs.begin(); it != addrs.end(); it++) + { + inet_aton((*it).c_str(), &addr); + // for windows silliness (returning 0.0.0.0 as valid addr!). + if (addr.s_addr == 0) + { + if (!found_zero) + { + found_zero = true; + addr_zero = addr; + } + } + else if (isLoopbackNet(&addr)) + { + if (!found_loopback) + { + found_loopback = true; + addr_loop = addr; + } + } + else if (isPrivateNet(&addr)) + { + if (!found_priv) + { + found_priv = true; + addr_priv = addr; + } + } + else + { + if (!found_ext) + { + found_ext = true; + addr_ext = addr; + return addr_ext; + } + } + } + + if (found_priv) + return addr_priv; + + // next bit can happen under windows, + // a general address is still + // preferable to a loopback device. + if (found_zero) + return addr_zero; + + if (found_loopback) + return addr_loop; + + // shound be 255.255.255.255 (error). + addr.s_addr = 0xffffffff; + return addr; +} + +bool sameNet(struct in_addr *addr, struct in_addr *addr2) +{ +#ifdef NET_DEBUG + std::cerr << "sameNet: " << inet_ntoa(*addr); + std::cerr << " VS " << inet_ntoa(*addr2); + std::cerr << std::endl; +#endif + struct in_addr addrnet, addrnet2; + + addrnet.s_addr = inet_netof(*addr); + addrnet2.s_addr = inet_netof(*addr2); + +#ifdef NET_DEBUG + std::cerr << " (" << inet_ntoa(addrnet); + std::cerr << " =?= " << inet_ntoa(addrnet2); + std::cerr << ")" << std::endl; +#endif + + in_addr_t address1 = htonl(addr->s_addr); + in_addr_t address2 = htonl(addr2->s_addr); + + // handle case for private net: 172.16.0.0/12 + if (address1>>20 == (172<<4 | 16>>4)) + { + return (address1>>20 == address2>>20); + } + + return (inet_netof(*addr) == inet_netof(*addr2)); +} + + +bool isSameSubnet(struct in_addr *addr1, struct in_addr *addr2) +{ + /* + * check that the (addr1 & 255.255.255.0) == (addr2 & 255.255.255.0) + */ + + unsigned long a1 = ntohl(addr1->s_addr); + unsigned long a2 = ntohl(addr2->s_addr); + + return ((a1 & 0xffffff00) == (a2 & 0xffffff00)); +} + +/* This just might be portable!!! will see!!! + * Unfortunately this is usable on winXP+, determined by: (_WIN32_WINNT >= 0x0501) + * but not older platforms.... which must use gethostbyname. + * + * include it for now..... + */ + +bool LookupDNSAddr(std::string name, struct sockaddr_in &addr) +{ + +#if 1 + char service[100]; + struct addrinfo hints_st; + struct addrinfo *hints = &hints_st; + struct addrinfo *res; + + hints -> ai_flags = 0; // (cygwin don;t like these) AI_ADDRCONFIG | AI_NUMERICSERV; + hints -> ai_family = AF_INET; + hints -> ai_socktype = 0; + hints -> ai_protocol = 0; + hints -> ai_addrlen = 0; + hints -> ai_addr = NULL; + hints -> ai_canonname = NULL; + hints -> ai_next = NULL; + + /* get the port number */ + sprintf(service, "%d", ntohs(addr.sin_port)); + + /* set it to IPV4 */ +#ifdef NET_DEBUG + std::cerr << "LookupDNSAddr() name: " << name << " service: " << service << std::endl; +#endif + + int err = 0; + if (0 != (err = getaddrinfo(name.c_str(), service, hints, &res))) + { +#ifdef NET_DEBUG + std::cerr << "LookupDNSAddr() getaddrinfo failed!" << std::endl; + std::cerr << "Error: " << gai_strerror(err) << std::endl; +#endif + return false; + } + + if ((res) && (res->ai_family == AF_INET)) + { + addr = *((struct sockaddr_in *) res->ai_addr); + freeaddrinfo(res); + +#ifdef NET_DEBUG + std::cerr << "LookupDNSAddr() getaddrinfo found address" << std::endl; + std::cerr << "addr: " << inet_ntoa(addr.sin_addr) << std::endl; + std::cerr << "port: " << ntohs(addr.sin_port) << std::endl; +#endif + return true; + } + +#ifdef NET_DEBUG + std::cerr << "getaddrinfo failed - no address" << std::endl; +#endif + +#endif +#ifdef NET_DEBUG + //std::cerr << "getaddrinfo disabled" << std::endl; +#endif + return false; +} + +/************************************************************* + * Socket Library Wrapper Functions + * to get over the crapness of the windows. + * + */ + +int unix_close(int fd) +{ + int ret; +/******************* WINDOWS SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS // ie UNIX + ret = close(fd); +#else + +#ifdef NET_DEBUG + std::cerr << "unix_close()" << std::endl; +#endif + ret = closesocket(fd); + /* translate error */ +#endif +/******************* WINDOWS SPECIFIC PART ******************/ + return ret; +} + +int unix_socket(int domain, int type, int protocol) +{ + int osock = socket(PF_INET, SOCK_STREAM, 0); + +/******************* WINDOWS SPECIFIC PART ******************/ +#ifdef WINDOWS_SYS // WINDOWS + +#ifdef NET_DEBUG + std::cerr << "unix_socket()" << std::endl; +#endif + + if ((unsigned) osock == INVALID_SOCKET) + { + // Invalidate socket Unix style. + osock = -1; + errno = WinToUnixError(WSAGetLastError()); + } +#endif +/******************* WINDOWS SPECIFIC PART ******************/ + return osock; +} + + +int unix_fcntl_nonblock(int fd) +{ + int ret; + +/******************* WINDOWS SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS // ie UNIX + ret = fcntl(fd, F_SETFL, O_NONBLOCK); + +#ifdef NET_DEBUG + std::cerr << "unix_fcntl_nonblock():" << ret << " errno:" << errno << std::endl; +#endif + +#else + unsigned long int on = 1; + ret = ioctlsocket(fd, FIONBIO, &on); + +#ifdef NET_DEBUG + std::cerr << "unix_fcntl_nonblock()" << std::endl; +#endif + if (ret != 0) + { + /* store unix-style error + */ + ret = -1; + errno = WinToUnixError(WSAGetLastError()); + } +#endif +/******************* WINDOWS SPECIFIC PART ******************/ + return ret; +} + + +int unix_connect(int fd, const struct sockaddr *serv_addr, socklen_t addrlen) +{ + int ret = connect(fd, serv_addr, addrlen); + +/******************* WINDOWS SPECIFIC PART ******************/ +#ifdef WINDOWS_SYS // WINDOWS + +#ifdef NET_DEBUG + std::cerr << "unix_connect()" << std::endl; +#endif + if (ret != 0) + { + errno = WinToUnixError(WSAGetLastError()); + ret = -1; + } +#endif +/******************* WINDOWS SPECIFIC PART ******************/ + return ret; +} + + +int unix_getsockopt_error(int sockfd, int *err) +{ + int ret; + *err = 1; +/******************* WINDOWS SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS // ie UNIX + socklen_t optlen = 4; + ret=getsockopt(sockfd, SOL_SOCKET, SO_ERROR, err, &optlen); +#else // WINDOWS_SYS + int optlen = 4; + ret=getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *) err, &optlen); + /* translate */ +#ifdef NET_DEBUG + std::cerr << "unix_getsockopt_error() returned: " << (int) err << std::endl; +#endif + if (*err != 0) + { + *err = WinToUnixError(*err); + } +#endif +/******************* WINDOWS SPECIFIC PART ******************/ + return ret; +} + +/******************* WINDOWS SPECIFIC PART ******************/ +#ifdef WINDOWS_SYS // ie WINDOWS. + +int WinToUnixError(int error) +{ +#ifdef NET_DEBUG + std::cerr << "WinToUnixError(" << error << ")" << std::endl; +#endif + switch(error) + { + case WSAEINPROGRESS: + return EINPROGRESS; + break; + case WSAEWOULDBLOCK: + return EINPROGRESS; + break; + case WSAENETUNREACH: + return ENETUNREACH; + break; + case WSAETIMEDOUT: + return ETIMEDOUT; + break; + case WSAEHOSTDOWN: + return EHOSTDOWN; + break; + case WSAECONNREFUSED: + return ECONNREFUSED; + break; + case WSAECONNRESET: + return ECONNRESET; + break; + default: +#ifdef NET_DEBUG + std::cerr << "WinToUnixError(" << error << ") Code Unknown!"; + std::cerr << std::endl; +#endif + break; + } + return ECONNREFUSED; /* sensible default? */ +} + +#endif +/******************* WINDOWS SPECIFIC PART ******************/ + diff --git a/libretroshare/src/_pqi/pqinetwork.h b/libretroshare/src/_pqi/pqinetwork.h new file mode 100644 index 000000000..f93b5d00f --- /dev/null +++ b/libretroshare/src/_pqi/pqinetwork.h @@ -0,0 +1,138 @@ +/* + * "$Id: pqinetwork.h,v 1.15 2007-04-15 18:45:18 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQINETWORK_H +#define PQINETWORK_H + + +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS + +#include +#include +#include +#include + +#include + +//socket blocking/options. +#include +#include + +#else + +/* This defines the platform to be WinXP or later... + * and is needed for getaddrinfo.... (not used anymore) + * + */ + +#define _WIN32_WINNT 0x0501 + +#include "util/rsnet.h" /* more generic networking header */ + +#include +#include +typedef int socklen_t; +//typedef unsigned long in_addr_t; + +// Some Network functions that are missing from windows. + +in_addr_t inet_netof(struct in_addr addr); +in_addr_t inet_network(const char *inet_name); +int inet_aton(const char *name, struct in_addr *addr); + +extern int errno; /* Define extern errno, to duplicate unix behaviour */ + +/* define the Unix Error Codes that we use... + * NB. we should make the same, but not necessary + */ +#define EAGAIN 11 +#define EWOULDBLOCK EAGAIN + +#define EUSERS 87 +#define ENOTSOCK 88 + +#define EOPNOTSUPP 95 + +#define EADDRINUSE 98 +#define EADDRNOTAVAIL 99 +#define ENETDOWN 100 +#define ENETUNREACH 101 + +#define ECONNRESET 104 + +#define ETIMEDOUT 110 +#define ECONNREFUSED 111 +#define EHOSTDOWN 112 +#define EHOSTUNREACH 113 +#define EALREADY 114 +#define EINPROGRESS 115 + +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + +#include +#include +#include + +// Same def - different functions... + +std::ostream &showSocketError(std::ostream &out); + +std::string socket_errorType(int err); +int sockaddr_cmp(struct sockaddr_in &addr1, struct sockaddr_in &addr2 ); +int inaddr_cmp(struct sockaddr_in addr1, struct sockaddr_in addr2 ); +int inaddr_cmp(struct sockaddr_in addr1, unsigned long); + +struct in_addr getPreferredInterface(); // returns best addr. +std::list getLocalInterfaces(); // returns all possible addrs. + + // checks (addr1 & 255.255.255.0) == (addr2 & 255.255.255.0) +bool isSameSubnet(struct in_addr *addr1, struct in_addr *addr2); +bool sameNet(struct in_addr *addr, struct in_addr *addr2); + +in_addr_t pqi_inet_netof(struct in_addr addr); // our implementation. + +bool LookupDNSAddr(std::string name, struct sockaddr_in &addr); + +/* universal socket interface */ + +int unix_close(int sockfd); +int unix_socket(int domain, int type, int protocol); +int unix_fcntl_nonblock(int sockfd); +int unix_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen); +int unix_getsockopt_error(int sockfd, int *err); + +#ifdef WINDOWS_SYS // WINDOWS +/******************* WINDOWS SPECIFIC PART ******************/ +int WinToUnixError(int error); +#endif + + + +#endif // PQINETWORK_H + diff --git a/libretroshare/src/_pqi/pqinotify.h b/libretroshare/src/_pqi/pqinotify.h new file mode 100644 index 000000000..b7219680e --- /dev/null +++ b/libretroshare/src/_pqi/pqinotify.h @@ -0,0 +1,51 @@ +#ifndef PQI_NOTIFY_INTERFACE_H +#define PQI_NOTIFY_INTERFACE_H + +/* + * libretroshare/src/rsserver: pqinotify.h + * + * RetroShare C++ Interface. + * + * 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 "_rsiface/rsnotify.h" /* for ids */ + + /* Interface for System Notification: Implemented in rsserver */ + /* Global Access -> so we don't need everyone to have a pointer to this! */ + + +class pqiNotify +{ +public: + + pqiNotify() { return; } + virtual ~pqiNotify() { return; } + + /* Input from libretroshare */ + virtual bool AddPopupMessage(uint32_t ptype, std::string name, std::string msg) = 0; + virtual bool AddSysMessage(uint32_t sysid, uint32_t type, std::string title, std::string msg) = 0; + virtual bool AddLogMessage(uint32_t sysid, uint32_t type, std::string title, std::string msg) = 0; + virtual bool AddFeedItem(uint32_t type, std::string id1, std::string id2, std::string id3) = 0; +}; + +extern pqiNotify *getPqiNotify(); + +#endif diff --git a/libretroshare/src/_pqi/pqiperson.cc b/libretroshare/src/_pqi/pqiperson.cc new file mode 100644 index 000000000..a882d87d6 --- /dev/null +++ b/libretroshare/src/_pqi/pqiperson.cc @@ -0,0 +1,479 @@ +/* + * libretroshare/src/pqi pqiperson.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 "_pqi/pqiperson.h" + + +const int pqipersonzone = 82371; + + +/**** + * #define PERSON_DEBUG + ****/ + +pqiconnect::pqiconnect(RsSerialiser *rss, NetBinInterface *ni_in) : + pqistreamer(rss, ni_in->PeerId(), ni_in, 0), // pqistreamer will cleanup NetInterface. + NetInterface(NULL, ni_in->PeerId()), // No need for callback. + ni(ni_in) +{ + if (!ni_in) + { + std::cerr << "pqiconnect::pqiconnect() NetInterface == NULL, FATAL!"; + std::cerr << std::endl; + exit(1); + } +} + +int pqiconnect::connect(struct sockaddr_in raddr) +{ + return ni->connect(raddr); +} + +int pqiconnect::listen() +{ + return ni -> listen(); +} + +int pqiconnect::stoplistening() +{ + return ni -> stoplistening(); +} + +int pqiconnect::reset() +{ + return ni -> reset(); +} + +int pqiconnect::disconnect() +{ + return ni -> reset(); +} + +bool pqiconnect::connect_parameter(uint32_t type, uint32_t value) +{ + return ni -> connect_parameter(type, value); +} + +std::string pqiconnect::PeerId() +{ + if (ni) + { + return ni->PeerId(); + } + else + { + return PQInterface::PeerId(); + } +} + +bool pqiconnect::thisNetInterface(NetInterface *ni_in) +{ + return (ni_in == ni); +} + +pqiperson::pqiperson(std::string id, pqipersongrp *pg) + :PQInterface(id), active(false), activepqi(NULL), + inConnectAttempt(false), waittimes(0), + pqipg(pg) +{ + + /* must check id! */ + + return; +} + +pqiperson::~pqiperson() +{ + // clean up the children. + std::map::iterator it; + for(it = kids.begin(); it != kids.end(); it++) + { + pqiconnect *pc = (it->second); + delete pc; + } + kids.clear(); +} + + + // The PQInterface interface. +int pqiperson::SendItem(RsItem *i) +{ + std::ostringstream out; + out << "pqiperson::SendItem()"; + if (active) + { + out << " Active: Sending On"; + return activepqi -> SendItem(i); + } + else + { + out << " Not Active: Used to put in ToGo Store"; + out << std::endl; + out << " Now deleting..."; + delete i; + } + pqioutput(PQL_DEBUG_BASIC, pqipersonzone, out.str()); + return 0; // queued. +} + +RsItem *pqiperson::GetItem() +{ + if (active) + return activepqi -> GetItem(); + // else not possible. + return NULL; +} + +int pqiperson::status() +{ + if (active) + return activepqi -> status(); + return -1; +} + + // tick...... +int pqiperson::tick() +{ + int activeTick = 0; + + { + std::ostringstream out; + out << "pqiperson::tick() Id: " << PeerId() << " "; + if (active) + out << "***Active***"; + else + out << ">>InActive<<"; + + out << std::endl; + out << "Activepqi: " << activepqi << " inConnectAttempt: "; + + if (inConnectAttempt) + out << "In Connection Attempt"; + else + out << " Not Connecting "; + out << std::endl; + + + // tick the children. + std::map::iterator it; + for(it = kids.begin(); it != kids.end(); it++) + { + if (0 < (it->second) -> tick()) + { + activeTick = 1; + } + out << "\tTicking Child: " << (it->first) << std::endl; + } + + pqioutput(PQL_DEBUG_ALL, pqipersonzone, out.str()); + } // end of pqioutput. + + return activeTick; +} + +// callback function for the child - notify of a change. +// This is only used for out-of-band info.... +// otherwise could get dangerous loops. +int pqiperson::notifyEvent(NetInterface *ni, int newState) +{ + { + std::ostringstream out; + out << "pqiperson::notifyEvent() Id: " << PeerId(); + out << std::endl; + out << "Message: " << newState << " from: " << ni << std::endl; + + pqioutput(PQL_DEBUG_BASIC, pqipersonzone, out.str()); + } + + /* find the pqi, */ + pqiconnect *pqi = NULL; + uint32_t type = 0; + std::map::iterator it; + + /* start again */ + int i = 0; + for(it = kids.begin(); it != kids.end(); it++) + { + std::ostringstream out; + out << "pqiperson::connectattempt() Kid# "; + out << i << " of " << kids.size(); + out << std::endl; + out << " type: " << (it->first); + out << " ni: " << (it->second)->ni; + out << " in_ni: " << ni; + pqioutput(PQL_DEBUG_BASIC, pqipersonzone, out.str()); + i++; + + if ((it->second)->thisNetInterface(ni)) + { + pqi = (it->second); + type = (it->first); + } + } + + if (!pqi) + { + pqioutput(PQL_WARNING, pqipersonzone, "Unknown notfyEvent Source!"); + return -1; + } + + + switch(newState) + { + case CONNECT_RECEIVED: + case CONNECT_SUCCESS: + + /* notify */ + if (pqipg) + pqipg->notifyConnect(PeerId(), type, true); + + if ((active) && (activepqi != pqi)) // already connected - trouble + { + pqioutput(PQL_WARNING, pqipersonzone, + "CONNECT_SUCCESS+active->trouble: shutdown EXISTING->switch to new one!"); + + // This is the RESET that's killing the connections..... + activepqi -> reset(); + // this causes a recursive call back into this fn. + // which cleans up state. + // we only do this if its not going to mess with new conn. + } + + /* now install a new one. */ + { + + pqioutput(PQL_WARNING, pqipersonzone, + "CONNECT_SUCCESS->marking so! (resetting others)"); + // mark as active. + active = true; + activepqi = pqi; + inConnectAttempt = false; + + /* reset all other children? (clear up long UDP attempt) */ + for(it = kids.begin(); it != kids.end(); it++) + { + if (it->second != activepqi) + { + it->second->reset(); + } + } + return 1; + } + break; + case CONNECT_UNREACHABLE: + case CONNECT_FIREWALLED: + case CONNECT_FAILED: + + + if (active) + { + if (activepqi == pqi) + { + pqioutput(PQL_WARNING, pqipersonzone, + "CONNECT_FAILED->marking so!"); + active = false; + activepqi = NULL; + } + else + { + pqioutput(PQL_WARNING, pqipersonzone, + "CONNECT_FAIL+not activepqi->strange!"); + // probably UDP connect has failed, + // TCP connection has been made since attempt started. + return -1; + } + } + else + { + pqioutput(PQL_WARNING, pqipersonzone, + "CONNECT_FAILED+NOT active -> try connect again"); + } + + /* notify up (But not if we are actually active: rtn -1 case above) */ + if (pqipg) + pqipg->notifyConnect(PeerId(), type, false); + + return 1; + + break; + default: + break; + } + return -1; +} + +/***************** Not PQInterface Fns ***********************/ + +int pqiperson::reset() +{ + { + std::ostringstream out; + out << "pqiperson::reset() Id: " << PeerId(); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqipersonzone, out.str()); + } + + std::map::iterator it; + for(it = kids.begin(); it != kids.end(); it++) + { + (it->second) -> reset(); + } + + activepqi = NULL; + active = false; + + return 1; +} + +int pqiperson::addChildInterface(uint32_t type, pqiconnect *pqi) +{ + { + std::ostringstream out; + out << "pqiperson::addChildInterface() : Id " << PeerId() << " " << type; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqipersonzone, out.str()); + } + + kids[type] = pqi; + return 1; +} + +/***************** PRIVATE FUNCTIONS ***********************/ +// functions to iterate over the connects and change state. + + +int pqiperson::listen() +{ + { + std::ostringstream out; + out << "pqiperson::listen() Id: " << PeerId(); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqipersonzone, out.str()); + } + + if (!active) + { + std::map::iterator it; + for(it = kids.begin(); it != kids.end(); it++) + { + // set them all listening. + (it->second) -> listen(); + } + } + return 1; +} + + +int pqiperson::stoplistening() +{ + { + std::ostringstream out; + out << "pqiperson::stoplistening() Id: " << PeerId(); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqipersonzone, out.str()); + } + + std::map::iterator it; + for(it = kids.begin(); it != kids.end(); it++) + { + // set them all listening. + (it->second) -> stoplistening(); + } + return 1; +} + +int pqiperson::connect(uint32_t type, struct sockaddr_in raddr, uint32_t delay, uint32_t period, uint32_t timeout) +{ +#ifdef PERSON_DEBUG + { + std::ostringstream out; + out << "pqiperson::connect() Id: " << PeerId(); + out << " type: " << type; + out << " addr: " << inet_ntoa(raddr.sin_addr); + out << ":" << ntohs(raddr.sin_port); + out << " delay: " << delay; + out << " period: " << period; + out << " timeout: " << timeout; + out << std::endl; + std::cerr << out.str(); + //pqioutput(PQL_DEBUG_BASIC, pqipersonzone, out.str()); + } +#endif + + std::map::iterator it; + + it = kids.find(type); + if (it == kids.end()) + { +#ifdef PERSON_DEBUG + std::ostringstream out; + out << "pqiperson::connect()"; + out << " missing pqiconnect"; + out << std::endl; + std::cerr << out.str(); + //pqioutput(PQL_DEBUG_BASIC, pqipersonzone, out.str()); +#endif + return 0; + } + + /* set the parameters */ + (it->second)->reset(); + +#ifdef PERSON_DEBUG + std::cerr << "pqiperson::connect() setting connect_parameters" << std::endl; +#endif + (it->second)->connect_parameter(NET_PARAM_CONNECT_DELAY, delay); + (it->second)->connect_parameter(NET_PARAM_CONNECT_PERIOD, period); + (it->second)->connect_parameter(NET_PARAM_CONNECT_TIMEOUT, timeout); + + (it->second)->connect(raddr); + + // flag if we started a new connectionAttempt. + inConnectAttempt = true; + + return 1; +} + + +float pqiperson::getRate(bool in) +{ + // get the rate from the active one. + if ((!active) || (activepqi == NULL)) + return 0; + return activepqi -> getRate(in); +} + +void pqiperson::setMaxRate(bool in, float val) +{ + // set to all of them. (and us) + PQInterface::setMaxRate(in, val); + // clean up the children. + std::map::iterator it; + for(it = kids.begin(); it != kids.end(); it++) + { + (it->second) -> setMaxRate(in, val); + } +} + diff --git a/libretroshare/src/_pqi/pqiperson.h b/libretroshare/src/_pqi/pqiperson.h new file mode 100644 index 000000000..b9702abbb --- /dev/null +++ b/libretroshare/src/_pqi/pqiperson.h @@ -0,0 +1,122 @@ +/* + * libretroshare/src/pqi pqiperson.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQIPERSON_H +#define PQIPERSON_H + + +#include "_pqi/pqipersongrp.h" +#include "_pqi/pqi.h" +#include "_util/rsdebug.h" + +#include + + +#include + +class pqiperson; + +static const int CONNECT_RECEIVED = 1; +static const int CONNECT_SUCCESS = 2; +static const int CONNECT_UNREACHABLE = 3; +static const int CONNECT_FIREWALLED = 4; +static const int CONNECT_FAILED = 5; + +#include "pqi/pqistreamer.h" + +class pqiconnect: public pqistreamer, public NetInterface +{ +public: + pqiconnect(RsSerialiser *rss, NetBinInterface *ni_in); + virtual ~pqiconnect() { return; } + + // presents a virtual NetInterface -> passes to ni. + virtual int connect(struct sockaddr_in raddr); + virtual int listen(); + virtual int stoplistening(); + virtual int reset(); + virtual int disconnect(); + virtual bool connect_parameter(uint32_t type, uint32_t value); + + // get the contact from the net side! + virtual std::string PeerId(); + + // to check if our interface. + virtual bool thisNetInterface(NetInterface *ni_in); + //protected: + NetBinInterface *ni; +protected: +}; + + +class pqipersongrp; + +class pqiperson: public PQInterface +{ +public: + pqiperson(std::string id, pqipersongrp *ppg); + virtual ~pqiperson(); // must clean up children. + + // control of the connection. + int reset(); + int listen(); + int stoplistening(); + int connect(uint32_t type, struct sockaddr_in raddr, uint32_t delay, uint32_t period, uint32_t timeout); + + // add in connection method. + int addChildInterface(uint32_t type, pqiconnect *pqi); + + // The PQInterface interface. + virtual int SendItem(RsItem *); + virtual RsItem *GetItem(); + + virtual int status(); + virtual int tick(); + + // overloaded callback function for the child - notify of a change. + int notifyEvent(NetInterface *ni, int event); + + // PQInterface for rate control overloaded.... + virtual float getRate(bool in); + virtual void setMaxRate(bool in, float val); + +private: + + std::map kids; + bool active; + pqiconnect *activepqi; + bool inConnectAttempt; + int waittimes; + +private: /* Helper functions */ + pqipersongrp *pqipg; /* parent for callback */ +}; + + + +#endif // PQIPERSON_H + diff --git a/libretroshare/src/_pqi/pqipersongrp.cc b/libretroshare/src/_pqi/pqipersongrp.cc new file mode 100644 index 000000000..a33c7e82f --- /dev/null +++ b/libretroshare/src/_pqi/pqipersongrp.cc @@ -0,0 +1,508 @@ +/* + * libretroshare/src/pqi: pqipersongrp.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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 "_pqi/pqipersongrp.h" + +#include +#include + +const int pqipersongrpzone = 354; + +/**** + *#define PGRP_DEBUG 1 + ****/ + +/* MUTEX NOTES: + * Functions like GetRsRawItem() lock itself (pqihandler) and + * likewise ServiceServer and ConfigMgr mutex themselves. + * This means the only things we need to worry about are: + * pqilistener and when accessing pqihandlers data. + */ + +// handle the tunnel services. +int pqipersongrp::tickServiceRecv() +{ + RsRawItem *pqi = NULL; + int i = 0; + { + std::ostringstream out; + out << "pqipersongrp::tickTunnelServer()"; + pqioutput(PQL_DEBUG_ALL, pqipersongrpzone, out.str()); + } + + //p3ServiceServer::tick(); + + while(NULL != (pqi = GetRsRawItem())) + { + ++i; + pqioutput(PQL_DEBUG_BASIC, pqipersongrpzone, + "pqipersongrp::tickTunnelServer() Incoming TunnelItem"); + incoming(pqi); + } + + if (0 < i) + { + return 1; + } + return 0; +} + +// handle the tunnel services. +int pqipersongrp::tickServiceSend() +{ + RsRawItem *pqi = NULL; + int i = 0; + { + std::ostringstream out; + out << "pqipersongrp::tickServiceSend()"; + pqioutput(PQL_DEBUG_ALL, pqipersongrpzone, out.str()); + } + + p3ServiceServer::tick(); + + while(NULL != (pqi = outgoing())) /* outgoing has own locking */ + { + ++i; + pqioutput(PQL_DEBUG_BASIC, pqipersongrpzone, + "pqipersongrp::tickTunnelServer() OutGoing RsItem"); + + SendRsRawItem(pqi); /* Locked by pqihandler */ + } + if (0 < i) + { + return 1; + } + return 0; +} + + + // init +pqipersongrp::pqipersongrp(SecurityPolicy *glob, unsigned long flags) + :pqihandler(glob), pqil(NULL), config(NULL), initFlags(flags) +{ +} + + +int pqipersongrp::tick() +{ + /* could limit the ticking of listener / tunnels to 1/sec... + * but not to important. + */ + + { RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + if (pqil) + { + pqil -> tick(); + } + } /* UNLOCKED */ + + int i = 0; + + if (tickServiceSend()) + { + i = 1; +#ifdef PGRP_DEBUG + std::cerr << "pqipersongrp::tick() moreToTick from tickServiceSend()" << std::endl; +#endif + } + + if (pqihandler::tick()) /* does actual Send/Recv */ + { + i = 1; +#ifdef PGRP_DEBUG + std::cerr << "pqipersongrp::tick() moreToTick from pqihandler::tick()" << std::endl; +#endif + } + + + if (tickServiceRecv()) + { + i = 1; +#ifdef PGRP_DEBUG + std::cerr << "pqipersongrp::tick() moreToTick from tickServiceRecv()" << std::endl; +#endif + } + + return i; +} + +int pqipersongrp::status() +{ + { RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + if (pqil) + { + pqil -> status(); + } + } /* UNLOCKED */ + + return pqihandler::status(); +} + + +/* Initialise pqilistener */ +int pqipersongrp::init_listener() +{ + /* extract our information from the p3ConnectMgr */ + if (initFlags & PQIPERSON_NO_LISTENER) + { + pqil = NULL; + } + else + { + /* extract details from + */ + peerConnectState state; + mConnMgr->getOwnNetStatus(state); + + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + pqil = createListener(state.localaddr); + } + return 1; +} + +int pqipersongrp::restart_listener() +{ + // stop it, + // change the address. + // restart. + bool haveListener = false; + { RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + haveListener = (pqil != NULL); + } /* UNLOCKED */ + + + if (haveListener) + { + peerConnectState state; + mConnMgr->getOwnNetStatus(state); + + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + pqil -> resetlisten(); + pqil -> setListenAddr(state.localaddr); + pqil -> setuplisten(); + } + return 1; +} + +/* NOT bothering to protect Config with a mutex.... it is not going to change + * and has its own internal mutexs. + */ +int pqipersongrp::setConfig(p3GeneralConfig *cfg) +{ + config = cfg; + return 1; +} + +static const std::string pqih_ftr("PQIH_FTR"); + +int pqipersongrp::save_config() +{ + char line[512]; + sprintf(line, "%f %f", getMaxRate(true), getMaxRate(false)); + if (config) + { + config -> setSetting(pqih_ftr, std::string(line)); + } + return 1; +} + +int pqipersongrp::load_config() +{ + std::string line; + if (config) + { + line = config -> getSetting(pqih_ftr); + } + + float mri, mro; + + if (2 == sscanf(line.c_str(), "%f %f", &mri, &mro)) + { + setMaxRate(true, mri); + setMaxRate(false, mro); + } + else + { + pqioutput(PQL_DEBUG_BASIC, pqipersongrpzone, + "pqipersongrp::load_config() Loading Default Rates!"); + + setMaxRate(true, 500.0); + setMaxRate(false, 500.0); + } + + return 1; +} + + +void pqipersongrp::statusChange(const std::list &plist) +{ + + /* iterate through, only worry about the friends */ + std::list::const_iterator it; + for(it = plist.begin(); it != plist.end(); it++) + { + if (it->state & RS_PEER_S_FRIEND) + { + /* now handle add/remove */ + if ((it->actions & RS_PEER_NEW) + || (it->actions & RS_PEER_MOVED)) + { + addPeer(it->id); + } + + if (it->actions & RS_PEER_CONNECT_REQ) + { + connectPeer(it->id); + } + } + else /* Not Friend */ + { + if (it->actions & RS_PEER_MOVED) + { + removePeer(it->id); + } + } + } +} + + + +int pqipersongrp::addPeer(std::string id) +{ + { + std::ostringstream out; + out << "pqipersongrp::addPeer() PeerId: " << id; + pqioutput(PQL_DEBUG_BASIC, pqipersongrpzone, out.str()); + } + +#ifdef PGRP_DEBUG + std::cerr << " pqipersongrp::addPeer() id: " << id; + std::cerr << std::endl; +#endif + + SearchModule *sm = NULL; + { RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + std::map::iterator it; + it = mods.find(id); + if (it != mods.end()) + { + pqioutput(PQL_DEBUG_BASIC, pqipersongrpzone, + "pqipersongrp::addPeer() Peer already in Use!"); + return -1; + } + + pqiperson *pqip = createPerson(id, pqil); + + // attach to pqihandler + sm = new SearchModule(); + sm -> peerid = id; + sm -> pqi = pqip; + sm -> sp = secpolicy_create(); + + // reset it to start it working. + pqip -> reset(); + pqip -> listen(); + } /* UNLOCKED */ + + return AddSearchModule(sm); +} + + +int pqipersongrp::removePeer(std::string id) +{ + std::map::iterator it; + +#ifdef PGRP_DEBUG + std::cerr << " pqipersongrp::removePeer() id: " << id; + std::cerr << std::endl; +#endif + + RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + + it = mods.find(id); + if (it != mods.end()) + { + SearchModule *mod = it->second; + // Don't duplicate remove!!! + //RemoveSearchModule(mod); + secpolicy_delete(mod -> sp); + pqiperson *p = (pqiperson *) mod -> pqi; + p -> reset(); + delete p; + mods.erase(it); + } + return 1; +} + +int pqipersongrp::connectPeer(std::string id) +{ + /* get status from p3connectMgr */ +#ifdef PGRP_DEBUG + std::cerr << " pqipersongrp::connectPeer() id: " << id << " does nothing yet! "; + std::cerr << std::endl; +#endif + + { RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + std::map::iterator it; + it = mods.find(id); + if (it == mods.end()) + { + return 0; + } + /* get the connect attempt details from the p3connmgr... */ + SearchModule *mod = it->second; + pqiperson *p = (pqiperson *) mod -> pqi; + + + /* get address from p3connmgr */ + if (!mConnMgr) + return 0; + + struct sockaddr_in addr; + uint32_t delay; + uint32_t period; + uint32_t timeout; + uint32_t type; + + if (!mConnMgr->connectAttempt(id, addr, delay, period, type)) + { +#ifdef PGRP_DEBUG + std::cerr << " pqipersongrp::connectPeer() No Net Address"; + std::cerr << std::endl; +#endif + return 0; + } + +#ifdef PGRP_DEBUG + std::cerr << " pqipersongrp::connectPeer() connectAttempt data id: " << id; + std::cerr << " addr: " << inet_ntoa(addr.sin_addr) << ":" << ntohs(addr.sin_port); + std::cerr << " delay: " << delay; + std::cerr << " period: " << period; + std::cerr << " type: " << type; + std::cerr << std::endl; +#endif + + + uint32_t ptype; + if (type & RS_NET_CONN_TCP_ALL) + { + ptype = PQI_CONNECT_TCP; + timeout = RS_TCP_STD_TIMEOUT_PERIOD; +#ifdef PGRP_DEBUG + std::cerr << " pqipersongrp::connectPeer() connecting with TCP: Timeout :" << timeout; + std::cerr << std::endl; +#endif + } + else if (type & RS_NET_CONN_UDP_ALL) + { + ptype = PQI_CONNECT_UDP; + timeout = period * 2; +#ifdef PGRP_DEBUG + std::cerr << " pqipersongrp::connectPeer() connecting with UDP: Timeout :" << timeout; + std::cerr << std::endl; +#endif + } + else + return 0; + + p->connect(ptype, addr, delay, period, timeout); + + } /* UNLOCKED */ + + + /* */ + return 1; +} + +bool pqipersongrp::notifyConnect(std::string id, uint32_t ptype, bool success) +{ + uint32_t type = 0; + if (ptype == PQI_CONNECT_TCP) + { + type = RS_NET_CONN_TCP_ALL; + } + else + { + type = RS_NET_CONN_UDP_ALL; + } + + + if (mConnMgr) + mConnMgr->connectResult(id, success, type); + + return (NULL != mConnMgr); +} + +/******************************** DUMMY Specific features ***************************/ + +#include "pqi/pqibin.h" + +pqilistener * pqipersongrpDummy::createListener(struct sockaddr_in laddr) +{ + pqilistener *listener = new pqilistener(); + return listener; +} + + +pqiperson * pqipersongrpDummy::createPerson(std::string id, pqilistener *listener) +{ + { + std::ostringstream out; + out << "pqipersongrpDummy::createPerson() PeerId: " << id; + pqioutput(PQL_DEBUG_BASIC, pqipersongrpzone, out.str()); + } + + pqiperson *pqip = new pqiperson(id, this); + + // TCP + NetBinDummy *d1 = new NetBinDummy(pqip, id, PQI_CONNECT_TCP); + + RsSerialiser *rss = new RsSerialiser(); + rss->addSerialType(new RsFileItemSerialiser()); + rss->addSerialType(new RsCacheItemSerialiser()); + rss->addSerialType(new RsServiceSerialiser()); + + pqiconnect *pqic = new pqiconnect(rss, d1); + + pqip -> addChildInterface(PQI_CONNECT_TCP, pqic); + + // UDP. + NetBinDummy *d2 = new NetBinDummy(pqip, id, PQI_CONNECT_UDP); + + RsSerialiser *rss2 = new RsSerialiser(); + rss2->addSerialType(new RsFileItemSerialiser()); + rss2->addSerialType(new RsCacheItemSerialiser()); + rss2->addSerialType(new RsServiceSerialiser()); + + pqiconnect *pqic2 = new pqiconnect(rss2, d2); + + pqip -> addChildInterface(PQI_CONNECT_UDP, pqic2); + + return pqip; +} + +/******************************** DUMMY Specific features ***************************/ + diff --git a/libretroshare/src/_pqi/pqipersongrp.h b/libretroshare/src/_pqi/pqipersongrp.h new file mode 100644 index 000000000..675722660 --- /dev/null +++ b/libretroshare/src/_pqi/pqipersongrp.h @@ -0,0 +1,117 @@ +/* + * libretroshare/src/pqi: pqipersongrp.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + + +#ifndef PQIPERSONGRP_H +#define PQIPERSONGRP_H + +#include "pqi/pqihandler.h" +#include "pqi/pqiperson.h" +#include "pqi/pqilistener.h" +#include "pqi/pqiservice.h" +#include "pqi/pqimonitor.h" +#include "pqi/p3cfgmgr.h" + + +// So this is a specific implementation +// +// it is designed to have one pqilistensocket + a series of pqisockets +// +// as an added bonus, we are going to +// make this a pqitunnelserver, to which services can be attached. + +const unsigned long PQIPERSON_NO_LISTENER = 0x0001; + +const unsigned long PQIPERSON_ALL_BW_LIMITED = 0x0010; + +class pqipersongrp: public pqihandler, public pqiMonitor, public p3ServiceServer +{ +public: + pqipersongrp(SecurityPolicy *, unsigned long flags); + + /*************************** Setup *************************/ + /* pqilistener */ + int init_listener(); + int restart_listener(); + + int setConfig(p3GeneralConfig *cfg); + int save_config(); + int load_config(); + + /*************** pqiMonitor callback ***********************/ + virtual void statusChange(const std::list &plist); + + /******************* Peer Control **************************/ + virtual int addPeer(std::string id); /* can be overloaded for testing */ + int removePeer(std::string id); + int connectPeer(std::string id); + + /*** callback from children ****/ + bool notifyConnect(std::string id, uint32_t type, bool success); + + // tick interfaces. + virtual int tick(); + virtual int status(); + +protected: + + /********* FUNCTIONS to OVERLOAD for specialisation ********/ + virtual pqilistener *createListener(struct sockaddr_in laddr) = 0; + virtual pqiperson *createPerson(std::string id, pqilistener *listener) = 0; + /********* FUNCTIONS to OVERLOAD for specialisation ********/ + + /* Overloaded RsItem Check + * checks item->cid vs Person + */ + virtual int checkOutgoingRsItem(RsItem *item, int global) { (void)item; (void)global; return 1; } + +private: + + // The tunnelserver operation. + int tickServiceRecv(); + int tickServiceSend(); + + pqilistener *pqil; + p3GeneralConfig *config; + unsigned long initFlags; +}; + +class pqipersongrpDummy: public pqipersongrp +{ +public: + pqipersongrpDummy(SecurityPolicy *pol, unsigned long flags) + :pqipersongrp(pol, flags) { return; } + +protected: + + /********* FUNCTIONS to OVERLOAD for specialisation ********/ + virtual pqilistener *createListener(struct sockaddr_in laddr); + virtual pqiperson *createPerson(std::string id, pqilistener *listener); + /********* FUNCTIONS to OVERLOAD for specialisation ********/ +}; + + + +#endif // PQIPERSONGRP_H diff --git a/libretroshare/src/_pqi/pqisecurity.cc b/libretroshare/src/_pqi/pqisecurity.cc new file mode 100644 index 000000000..c53cf6fb8 --- /dev/null +++ b/libretroshare/src/_pqi/pqisecurity.cc @@ -0,0 +1,74 @@ +/* + * "$Id: pqisecurity.cc,v 1.3 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "_pqi/pqisecurity.h" +#include // malloc + + +// Can keep the structure hidden.... +// but won't at the moment. + +// functions for checking what is allowed... +// currently these are all dummies. + + +std::string secpolicy_print(SecurityPolicy *) +{ + return std::string("secpolicy_print() Implement Me Please!"); +} + +SecurityPolicy *secpolicy_create() +{ + return (SecurityPolicy *) malloc(sizeof(SecurityPolicy)); +} + +int secpolicy_delete(SecurityPolicy *p) +{ + free(p); + return 1; +} + + +int secpolicy_limit(SecurityPolicy *limiter, + SecurityPolicy *alter) +{ + return 1; +} + +int secpolicy_check(SecurityPolicy *, int type_transaction, + int direction) +{ + return 1; +} + + +#ifdef __cplusplus +} +#endif diff --git a/libretroshare/src/_pqi/pqisecurity.h b/libretroshare/src/_pqi/pqisecurity.h new file mode 100644 index 000000000..bffc37065 --- /dev/null +++ b/libretroshare/src/_pqi/pqisecurity.h @@ -0,0 +1,59 @@ +/* + * "$Id: pqisecurity.h,v 1.3 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQISECURITY_H +#define PQISECURITY_H + +#define PQI_INCOMING 2 +#define PQI_OUTGOING 5 + + +#ifdef __cplusplus +extern "C" { +#endif + +//structure. +typedef struct sec_policy +{ + int searchable; // flags indicate how searchable we are.. +} SecurityPolicy; + +// functions for checking what is allowed... +// + +std::string secpolicy_print(SecurityPolicy *); +SecurityPolicy *secpolicy_create(); +int secpolicy_delete(SecurityPolicy *); +int secpolicy_limit(SecurityPolicy *limiter, SecurityPolicy *alter); +int secpolicy_check(SecurityPolicy *, int type_transaction, + int direction); + +#ifdef __cplusplus +} +#endif +#endif // PQISECURITY_H + diff --git a/libretroshare/src/_pqi/pqiservice.cc b/libretroshare/src/_pqi/pqiservice.cc new file mode 100644 index 000000000..fb3f84fa5 --- /dev/null +++ b/libretroshare/src/_pqi/pqiservice.cc @@ -0,0 +1,223 @@ +/* + * libretroshare/src/pqi pqiservice.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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 "pqi/pqiservice.h" + +#include + +const int pqiservicezone = 60478; + +/**** + * #define SERVICE_DEBUG 1 + ****/ + +p3ServiceServer::p3ServiceServer() +{ + RsStackMutex stack(srvMtx); /********* LOCKED *********/ + +#ifdef SERVICE_DEBUG + pqioutput(PQL_DEBUG_BASIC, pqiservicezone, + "p3ServiceServer::p3ServiceServer()"); +#endif + + rrit = services.begin(); + return; +} + +int p3ServiceServer::addService(pqiService *ts) +{ + RsStackMutex stack(srvMtx); /********* LOCKED *********/ + +#ifdef SERVICE_DEBUG + pqioutput(PQL_DEBUG_BASIC, pqiservicezone, + "p3ServiceServer::addService()"); +#endif + + std::map::iterator it; + it = services.find(ts -> getType()); + if (it != services.end()) + { + // it exists already! + return -1; + } + + services[ts -> getType()] = ts; + rrit = services.begin(); + return 1; +} + +int p3ServiceServer::incoming(RsRawItem *item) +{ + RsStackMutex stack(srvMtx); /********* LOCKED *********/ + +#ifdef SERVICE_DEBUG + pqioutput(PQL_DEBUG_BASIC, pqiservicezone, + "p3ServiceServer::incoming()"); + + { + std::ostringstream out; + out << "p3ServiceServer::incoming() PacketId: "; + out << std::hex << item -> PacketId() << std::endl; + out << "Looking for Service: "; + out << (item -> PacketId() & 0xffffff00) << std::dec << std::endl; + + out << "Item:" << std::endl; + item -> print(out); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqiservicezone, out.str()); + } +#endif + + std::map::iterator it; + it = services.find(item -> PacketId() & 0xffffff00); + if (it == services.end()) + { +#ifdef SERVICE_DEBUG + pqioutput(PQL_DEBUG_BASIC, pqiservicezone, + "p3ServiceServer::incoming() Service: No Service - deleting"); +#endif + + // delete it. + delete item; + + // it exists already! + return -1; + } + + { +#ifdef SERVICE_DEBUG + std::ostringstream out; + out << "p3ServiceServer::incoming() Sending to"; + out << it -> second << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqiservicezone, out.str()); +#endif + + return (it->second) -> receive(item); + } + + delete item; + return -1; +} + + + +RsRawItem *p3ServiceServer::outgoing() +{ + RsStackMutex stack(srvMtx); /********* LOCKED *********/ + +#ifdef SERVICE_DEBUG + pqioutput(PQL_DEBUG_ALL, pqiservicezone, + "p3ServiceServer::outgoing()"); +#endif + + if (rrit != services.end()) + { + rrit++; + } + else + { + rrit = services.begin(); + } + + std::map::iterator sit = rrit; + // run to the end. + RsRawItem *item; + + // run through to the end, + for(;rrit != services.end();rrit++) + { + if (NULL != (item = (rrit -> second) -> send())) + { + +#ifdef SERVICE_DEBUG + std::ostringstream out; + out << "p3ServiceServer::outgoing() Got Item From:"; + out << rrit -> second << std::endl; + + item -> print(out); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqiservicezone, out.str()); +#endif + + return item; + } + } + + // from the beginning to where we started. + for(rrit = services.begin();rrit != sit; rrit++) + { + if (NULL != (item = (rrit -> second) -> send())) + { + +#ifdef SERVICE_DEBUG + std::ostringstream out; + out << "p3ServiceServer::outgoing() Got Item From:"; + out << rrit -> second << std::endl; + + item -> print(out); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqiservicezone, out.str()); +#endif + + return item; + } + } + return NULL; +} + + + +int p3ServiceServer::tick() +{ + + RsStackMutex stack(srvMtx); /********* LOCKED *********/ + +#ifdef SERVICE_DEBUG + pqioutput(PQL_DEBUG_ALL, pqiservicezone, + "p3ServiceServer::tick()"); +#endif + + std::map::iterator it; + + // from the beginning to where we started. + for(it = services.begin();it != services.end(); it++) + { + +#ifdef SERVICE_DEBUG + std::ostringstream out; + out << "p3ServiceServer::service id:" << it -> first; + out << " -> Service: " << it -> second; + out << std::endl; + pqioutput(PQL_DEBUG_ALL, pqiservicezone, out.str()); +#endif + + // now we should actually tick the service. + (it -> second) -> tick(); + } + return 1; +} + + + diff --git a/libretroshare/src/_pqi/pqiservice.h b/libretroshare/src/_pqi/pqiservice.h new file mode 100644 index 000000000..7553e27e5 --- /dev/null +++ b/libretroshare/src/_pqi/pqiservice.h @@ -0,0 +1,101 @@ +/* + * libretroshare/src/pqi pqiservice.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + + +#ifndef PQISERVICE_H +#define PQISERVICE_H + +#include "_pqi/pqi_base.h" +#include "_util/rsthreads.h" +#include "_util/rsdebug.h" + +// PQI Service, is a generic lower layer on which services can run on. +// +// these packets get passed through the +// server, to a service that is registered. +// +// example services: +// proxytunnel. -> p3proxy. +// sockettunnel +// -> either broadcast (attach to +// open socket instead +// of broadcast address) +// -> or requested/signon. +// +// games, +// voice +// video +// +// +// DataType is defined in the serialiser directory. + +class RsRawItem; + +class pqiService +{ +protected: + + pqiService(uint32_t t) // our type of packets. + :type(t) { return; } + + virtual ~pqiService() { return; } + +public: + // + virtual int receive(RsRawItem *) = 0; + virtual RsRawItem * send() = 0; + + uint32_t getType() { return type; } + + virtual int tick() { return 0; } + +private: + uint32_t type; +}; + +#include + + +class p3ServiceServer +{ +public: + p3ServiceServer(); + + int addService(pqiService *); + + int incoming(RsRawItem *); + RsRawItem *outgoing(); + + int tick(); + +private: + + RsMutex srvMtx; + std::map services; + std::map::iterator rrit; +}; + + +#endif // PQISERVICE_H diff --git a/libretroshare/src/_pqi/pqissl.cc b/libretroshare/src/_pqi/pqissl.cc new file mode 100644 index 000000000..3cb824fff --- /dev/null +++ b/libretroshare/src/_pqi/pqissl.cc @@ -0,0 +1,1671 @@ +/* + * "$Id: pqissl.cc,v 1.28 2007-03-17 19:32:59 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 "_pqi/pqissl.h" +#include "_pqi/pqinetwork.h" + +#include "_util/rsnet.h" +#include "_util/rsdebug.h" + +#include +#include +#include + +#include + +#include "_pqi/pqissllistener.h" + +const int pqisslzone = 37714; + +/********* +#define WAITING_NOT 0 +#define WAITING_LOCAL_ADDR 1 +#define WAITING_REMOTE_ADDR 2 +#define WAITING_SOCK_CONNECT 3 +#define WAITING_SSL_CONNECTION 4 +#define WAITING_SSL_AUTHORISE 5 +#define WAITING_FAIL_INTERFACE 6 + + +#define PQISSL_PASSIVE 0x00 +#define PQISSL_ACTIVE 0x01 + +const int PQISSL_LOCAL_FLAG = 0x01; +const int PQISSL_REMOTE_FLAG = 0x02; +const int PQISSL_UDP_FLAG = 0x02; +***********/ + +static const int PQISSL_MAX_READ_ZERO_COUNT = 20; +static const int PQISSL_SSL_CONNECT_TIMEOUT = 30; + +/********** PQI SSL STUFF ****************************************** + * + * A little note on the notifyEvent(FAILED).... + * + * this is called from + * (1) reset if needed! + * (2) Determine_Remote_Address (when all options have failed). + * + * reset() is only called when a TCP/SSL connection has been + * established, and there is an error. If there is a failed TCP + * connection, then an alternative address can be attempted. + * + * reset() is called from + * (1) destruction. + * (2) disconnect() + * (3) bad waiting state. + * + * // TCP/or SSL connection already established.... + * (5) pqissl::SSL_Connection_Complete() <- okay -> cos we made a TCP connection already. + * (6) pqissl::accept() <- okay cos something went wrong. + * (7) moretoread()/cansend() <- okay cos + * + */ + +pqissl::pqissl(pqissllistener *l, PQInterface *parent, p3AuthMgr *am, p3ConnectMgr *cm) + :NetBinInterface(parent, parent->PeerId()), + waiting(WAITING_NOT), active(false), certvalid(false), + sslmode(PQISSL_ACTIVE), ssl_connection(NULL), sockfd(-1), + pqil(l), // no init for remote_addr. + readpkt(NULL), pktlen(0), + attempt_ts(0), + net_attempt(0), net_failure(0), net_unreachable(0), + sameLAN(false), n_read_zero(0), + mConnectDelay(0), mConnectTS(0), + mConnectTimeout(0), mTimeoutTS(0), + +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + mAuthMgr((AuthXPGP *) am), mConnMgr(cm) +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + mAuthMgr((AuthSSL *) am), mConnMgr(cm) +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + +{ + /* set address to zero */ + sockaddr_clear(&remote_addr); + + { + std::ostringstream out; + out << "pqissl for PeerId: " << PeerId(); + rslog(RSL_ALERT, pqisslzone, out.str()); + } + + if (!(mAuthMgr->isAuthenticated(PeerId()))) + { + rslog(RSL_ALERT, pqisslzone, + "pqissl::Warning Certificate Not Approved!"); + + rslog(RSL_ALERT, pqisslzone, + "\t pqissl will not initialise...."); + + } + + return; +} + + pqissl::~pqissl() +{ + rslog(RSL_ALERT, pqisslzone, + "pqissl::~pqissl -> destroying pqissl"); + stoplistening(); /* remove from pqissllistener only */ + reset(); + return; +} + + +/********** Implementation of NetInterface *************************/ + +int pqissl::connect(struct sockaddr_in raddr) +{ + // reset failures + net_failure = 0; + remote_addr = raddr; + remote_addr.sin_family = AF_INET; + + return ConnectAttempt(); +} + +// tells pqilistener to listen for us. +int pqissl::listen() +{ + if (pqil) + { + return pqil -> addlistenaddr(PeerId(), this); + } + return 0; +} + +int pqissl::stoplistening() +{ + if (pqil) + { + pqil -> removeListenPort(PeerId()); + } + return 1; +} + +int pqissl::disconnect() +{ + return reset(); +} + +/* BinInterface version of reset() for pqistreamer */ +int pqissl::close() +{ + return reset(); +} + +// put back on the listening queue. +int pqissl::reset() +{ + std::ostringstream out; + std::ostringstream outAlert; + + /* a reset shouldn't cause us to stop listening + * only reasons for stoplistening() are; + * + * (1) destruction. + * (2) connection. + * (3) WillListen state change + * + */ + + outAlert << "pqissl::reset():" << PeerId(); + rslog(RSL_ALERT, pqisslzone, outAlert.str()); + + + out << "pqissl::reset() State Before Reset:" << std::endl; + out << "\tActive: " << (int) active << std::endl; + out << "\tsockfd: " << sockfd << std::endl; + out << "\twaiting: " << waiting << std::endl; + out << "\tssl_con: " << ssl_connection << std::endl; + out << std::endl; + + bool neededReset = false; + + if (ssl_connection != NULL) + { + out << "pqissl::reset() Shutting down SSL Connection"; + out << std::endl; + SSL_shutdown(ssl_connection); + + neededReset = true; + } + + if (sockfd > 0) + { + out << "pqissl::reset() Shutting down (active) socket"; + out << std::endl; + net_internal_close(sockfd); + sockfd = -1; + neededReset = true; + } + active = false; + sockfd = -1; + waiting = WAITING_NOT; + ssl_connection = NULL; + sameLAN = false; + n_read_zero = 0; + + if (neededReset) + { + out << "pqissl::reset() Reset Required!" << std::endl; + out << "pqissl::reset() Will Attempt notifyEvent(FAILED)"; + out << std::endl; + } + + out << "pqissl::reset() Complete!" << std::endl; + rslog(RSL_DEBUG_BASIC, pqisslzone, out.str()); + + // notify people of problem! + // but only if we really shut something down. + if (neededReset) + { + // clean up the streamer + if (parent()) + { + parent() -> notifyEvent(this, NET_CONNECT_FAILED); + } + } + return 1; +} + +bool pqissl::connect_parameter(uint32_t type, uint32_t value) +{ + { + std::ostringstream out; + out << "pqissl::connect_parameter() Peer: " << PeerId(); + out << " type: " << type << "value: " << value; + rslog(RSL_DEBUG_ALL, pqisslzone, out.str()); + } + + if (type == NET_PARAM_CONNECT_DELAY) + { + std::ostringstream out; + out << "pqissl::connect_parameter() Peer: " << PeerId(); + out << " DELAY: " << value; + rslog(RSL_WARNING, pqisslzone, out.str()); + + + mConnectDelay = value; + return true; + } + else if (type == NET_PARAM_CONNECT_TIMEOUT) + { + std::ostringstream out; + out << "pqissl::connect_parameter() Peer: " << PeerId(); + out << " TIMEOUT: " << value; + rslog(RSL_WARNING, pqisslzone, out.str()); + + mConnectTimeout = value; + return true; + } + return false; + //return NetInterface::connect_parameter(type, value); +} + + +/********** End of Implementation of NetInterface ******************/ +/********** Implementation of BinInterface ************************** + * Only status() + tick() are here ... as they are really related + * to the NetInterface, and not the BinInterface, + * + */ + +/* returns ... + * -1 if inactive. + * 0 if connecting. + * 1 if connected. + */ + +int pqissl::status() +{ + int alg; + + std::ostringstream out; + out << "pqissl::status()"; + + if (active) + { + out << " active: " << std::endl; + // print out connection. + out << "Connected TO : " << PeerId(); + out << std::endl; + // print out cipher. + out << "\t\tSSL Cipher:" << SSL_get_cipher(ssl_connection); + out << " (" << SSL_get_cipher_bits(ssl_connection, &alg); + out << ":" << alg << ") "; + out << "Vers:" << SSL_get_cipher_version(ssl_connection); + out << std::endl; + out << std::endl; + + } + else + { + out << " Waiting for connection!" << std::endl; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, out.str()); + + if (active) + { + return 1; + } + else if (waiting > 0) + { + return 0; + } + return -1; +} + + // tick...... +int pqissl::tick() +{ + //pqistreamer::tick(); + + // continue existing connection attempt. + if (!active) + { + // if we are waiting.. continue the connection (only) + if (waiting > 0) + { + std::ostringstream out; + out << "pqissl::tick() "; + out << "Continuing Connection Attempt!"; + rslog(RSL_DEBUG_BASIC, pqisslzone, out.str()); + + ConnectAttempt(); + return 1; + } + } + return 1; +} + +/********** End of Implementation of BinInterface ******************/ +/********** Internals of SSL Connection ****************************/ + + +int pqissl::ConnectAttempt() +{ + switch(waiting) + { + case WAITING_NOT: + + sslmode = PQISSL_ACTIVE; /* we're starting this one */ + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::ConnectAttempt() STATE = Not Waiting, starting connection"); + + case WAITING_DELAY: + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::ConnectAttempt() STATE = Waiting Delay, starting connection"); + + return Delay_Connection(); + //return Initiate_Connection(); /* now called by Delay_Connection() */ + + break; + + case WAITING_SOCK_CONNECT: + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::ConnectAttempt() STATE = Waiting Sock Connect"); + + return Initiate_SSL_Connection(); + break; + + case WAITING_SSL_CONNECTION: + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::ConnectAttempt() STATE = Waiting SSL Connection"); + + return Authorise_SSL_Connection(); + break; + + case WAITING_SSL_AUTHORISE: + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::ConnectAttempt() STATE = Waiting SSL Authorise"); + + return Authorise_SSL_Connection(); + break; + case WAITING_FAIL_INTERFACE: + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::ConnectAttempt() Failed - Retrying"); + + return Failed_Connection(); + break; + + + default: + rslog(RSL_ALERT, pqisslzone, + "pqissl::ConnectAttempt() STATE = Unknown - Reset"); + + reset(); + break; + } + rslog(RSL_ALERT, pqisslzone, "pqissl::ConnectAttempt() Unknown"); + + return -1; +} + + +/****************************** FAILED ATTEMPT ****************************** + * Determine the Remote Address. + * + * Specifics: + * TCP / UDP + * TCP - check for which interface to use. + * UDP - check for request proxies.... + * + * X509 / XPGP - Same. + * + */ + +int pqissl::Failed_Connection() +{ + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::ConnectAttempt() Failed - Notifying"); + + if (parent()) + { + parent() -> notifyEvent(this, NET_CONNECT_UNREACHABLE); + } + waiting = WAITING_NOT; + + return 1; +} + +/****************************** MAKE CONNECTION ***************************** + * Open Socket and Initiate Connection. + * + * Specifics: + * TCP / UDP + * TCP - socket()/connect() + * UDP - tou_socket()/tou_connect() + * + * X509 / XPGP - Same. + * + */ + +int pqissl::Delay_Connection() +{ + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Delay_Connection() Attempting Outgoing Connection...."); + + if (waiting == WAITING_NOT) + { + waiting = WAITING_DELAY; + + /* set delay */ + if (mConnectDelay == 0) + { + return Initiate_Connection(); + } + + /* set Connection TS. + */ + { + std::ostringstream out; + out << "pqissl::Delay_Connection() "; + out << " Delaying Connection to "; + out << PeerId() << " for "; + out << mConnectDelay; + out << " seconds"; + rslog(RSL_DEBUG_BASIC, pqisslzone, out.str()); + } + + + mConnectTS = time(NULL) + mConnectDelay; + return 0; + } + else if (waiting == WAITING_DELAY) + { + { + std::ostringstream out; + out << "pqissl::Delay_Connection() "; + out << " Connection to "; + out << PeerId() << " starting in "; + out << mConnectTS - time(NULL); + out << " seconds"; + rslog(RSL_DEBUG_BASIC, pqisslzone, out.str()); + } + + if (time(NULL) > mConnectTS) + { + return Initiate_Connection(); + } + return 0; + } + + rslog(RSL_WARNING, pqisslzone, + "pqissl::Initiate_Connection() Already Attempt in Progress!"); + return -1; +} + + +int pqissl::Initiate_Connection() +{ + int err; + struct sockaddr_in addr = remote_addr; + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Initiate_Connection() Attempting Outgoing Connection...."); + + if (waiting != WAITING_DELAY) + { + rslog(RSL_WARNING, pqisslzone, + "pqissl::Initiate_Connection() Already Attempt in Progress!"); + return -1; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Initiate_Connection() Opening Socket"); + + // open socket connection to addr. + int osock = unix_socket(PF_INET, SOCK_STREAM, 0); + + { + std::ostringstream out; + out << "pqissl::Initiate_Connection() osock = " << osock; + rslog(RSL_DEBUG_BASIC, pqisslzone, out.str()); + } + + if (osock < 0) + { + std::ostringstream out; + out << "pqissl::Initiate_Connection()"; + out << "Failed to open socket!" << std::endl; + out << "Socket Error:" << socket_errorType(errno) << std::endl; + rslog(RSL_WARNING, pqisslzone, out.str()); + + net_internal_close(osock); + waiting = WAITING_FAIL_INTERFACE; + return -1; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Initiate_Connection() Making Non-Blocking"); + + err = unix_fcntl_nonblock(osock); + if (err < 0) + { + std::ostringstream out; + out << "pqissl::Initiate_Connection()"; + out << "Error: Cannot make socket NON-Blocking: "; + out << err << std::endl; + rslog(RSL_WARNING, pqisslzone, out.str()); + + waiting = WAITING_FAIL_INTERFACE; + net_internal_close(osock); + return -1; + } + + { + std::ostringstream out; + out << "pqissl::Initiate_Connection() "; + out << "Connecting To: "; + out << PeerId() << " via: "; + out << inet_ntoa(addr.sin_addr); + out << ":" << ntohs(addr.sin_port); + rslog(RSL_WARNING, pqisslzone, out.str()); + } + + if (addr.sin_addr.s_addr == 0) + { + std::ostringstream out; + out << "pqissl::Initiate_Connection() "; + out << "Invalid (0.0.0.0) Remote Address,"; + out << " Aborting Connect."; + out << std::endl; + rslog(RSL_WARNING, pqisslzone, out.str()); + waiting = WAITING_FAIL_INTERFACE; + net_internal_close(osock); + return -1; + } + + + mTimeoutTS = time(NULL) + mConnectTimeout; + //std::cerr << "Setting Connect Timeout " << mConnectTimeout << " Seconds into Future " << std::endl; + + if (0 != (err = unix_connect(osock, (struct sockaddr *) &addr, sizeof(addr)))) + { + std::ostringstream out; + out << "pqissl::Initiate_Connection() connect returns:"; + out << err << " -> errno: " << errno << " error: "; + out << socket_errorType(errno) << std::endl; + + if (errno == EINPROGRESS) + { + // set state to waiting..... + waiting = WAITING_SOCK_CONNECT; + sockfd = osock; + + out << " EINPROGRESS Waiting for Socket Connection"; + rslog(RSL_DEBUG_BASIC, pqisslzone, out.str()); + + return 0; + } + else if ((errno == ENETUNREACH) || (errno == ETIMEDOUT)) + { + out << "ENETUNREACHABLE: cert: " << PeerId(); + rslog(RSL_WARNING, pqisslzone, out.str()); + + // Then send unreachable message. + net_internal_close(osock); + osock=-1; + //reset(); + + waiting = WAITING_FAIL_INTERFACE; + // removing unreachables... + //net_unreachable |= net_attempt; + + return -1; + } + + /* IF we get here ---- we Failed for some other reason. + * Should abandon this interface + * Known reasons to get here: EINVAL (bad address) + */ + + out << "Error: Connection Failed: " << errno; + out << " - " << socket_errorType(errno) << std::endl; + + net_internal_close(osock); + osock=-1; + waiting = WAITING_FAIL_INTERFACE; + + rslog(RSL_WARNING, pqisslzone, out.str()); + // extra output for the moment. + //std::cerr << out.str(); + + return -1; + } + else + { + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Init_Connection() connect returned 0"); + } + + waiting = WAITING_SOCK_CONNECT; + sockfd = osock; + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Initiate_Connection() Waiting for Socket Connect"); + + return 1; +} + + +/****************************** CHECK SOCKET ***************************** + * Check the Socket. + * + * select() and getsockopt(). + * + * Specifics: + * TCP / UDP + * TCP - select()/getsockopt() + * UDP - tou_error() + * + * X509 / XPGP - Same. + * + */ + +int pqissl::Basic_Connection_Complete() +{ + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Basic_Connection_Complete()..."); + + /* new TimeOut code. */ + if (time(NULL) > mTimeoutTS) + { + std::ostringstream out; + out << "pqissl::Basic_Connection_Complete() Connection Timed Out. "; + out << "Peer: " << PeerId() << " Period: "; + out << mConnectTimeout; + + rslog(RSL_WARNING, pqisslzone, out.str()); + /* as sockfd is valid, this should close it all up */ + + reset(); + return -1; + } + + + if (waiting != WAITING_SOCK_CONNECT) + { + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Basic_Connection_Complete() Wrong Mode"); + return -1; + } + // use select on the opened socket. + // Interestingly - This code might be portable.... + + fd_set ReadFDs, WriteFDs, ExceptFDs; + FD_ZERO(&ReadFDs); + FD_ZERO(&WriteFDs); + FD_ZERO(&ExceptFDs); + + FD_SET(sockfd, &ReadFDs); + FD_SET(sockfd, &WriteFDs); + FD_SET(sockfd, &ExceptFDs); + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Basic_Connection_Complete() Selecting ...."); + + int sr = 0; + if (0 > (sr = select(sockfd + 1, + &ReadFDs, &WriteFDs, &ExceptFDs, &timeout))) + { + // select error. + rslog(RSL_WARNING, pqisslzone, + "pqissl::Basic_Connection_Complete() Select ERROR(1)"); + + net_internal_close(sockfd); + sockfd=-1; + //reset(); + waiting = WAITING_FAIL_INTERFACE; + return -1; + } + + { + std::ostringstream out; + out << "pqissl::Basic_Connection_Complete() Select "; + out << " returned " << sr; + rslog(RSL_DEBUG_BASIC, pqisslzone, out.str()); + } + + + if (FD_ISSET(sockfd, &ExceptFDs)) + { + // error - reset socket. + // this is a definite bad socket!. + + rslog(RSL_WARNING, pqisslzone, + "pqissl::Basic_Connection_Complete() Select ERROR(2)"); + + net_internal_close(sockfd); + sockfd=-1; + //reset(); + waiting = WAITING_FAIL_INTERFACE; + return -1; + } + + if (FD_ISSET(sockfd, &WriteFDs)) + { + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Basic_Connection_Complete() Can Write!"); + } + else + { + // happens frequently so switched to debug msg. + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Basic_Connection_Complete() Not Yet Ready!"); + return 0; + } + + if (FD_ISSET(sockfd, &ReadFDs)) + { + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Basic_Connection_Complete() Can Read!"); + } + else + { + // not ready return -1; + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Basic_Connection_Complete() Cannot Read!"); + } + + int err = 1; + if (0==unix_getsockopt_error(sockfd, &err)) + { + if (err == 0) + { + + { + std::ostringstream out; + out << "pqissl::Basic_Connection_Complete()"; + out << " TCP Connection Complete: cert: "; + out << PeerId(); + out << " on osock: " << sockfd; + rslog(RSL_WARNING, pqisslzone, out.str()); + } + return 1; + } + else if (err == EINPROGRESS) + { + + std::ostringstream out; + out << "pqissl::Basic_Connection_Complete()"; + out << " EINPROGRESS: cert: " << PeerId(); + rslog(RSL_WARNING, pqisslzone, out.str()); + + return 0; + } + else if ((err == ENETUNREACH) || (err == ETIMEDOUT)) + { + std::ostringstream out; + out << "pqissl::Basic_Connection_Complete()"; + out << " ENETUNREACH/ETIMEDOUT: cert: "; + out << PeerId(); + rslog(RSL_WARNING, pqisslzone, out.str()); + + // Then send unreachable message. + net_internal_close(sockfd); + sockfd=-1; + //reset(); + + waiting = WAITING_FAIL_INTERFACE; + // removing unreachables... + //net_unreachable |= net_attempt; + + return -1; + } + else if ((err == EHOSTUNREACH) || (err == EHOSTDOWN)) + { + std::ostringstream out; + out << "pqissl::Basic_Connection_Complete()"; + out << " EHOSTUNREACH/EHOSTDOWN: cert: "; + out << PeerId(); + rslog(RSL_WARNING, pqisslzone, out.str()); + + // Then send unreachable message. + net_internal_close(sockfd); + sockfd=-1; + //reset(); + waiting = WAITING_FAIL_INTERFACE; + + return -1; + } + else if ((err == ECONNREFUSED)) + { + std::ostringstream out; + out << "pqissl::Basic_Connection_Complete()"; + out << " ECONNREFUSED: cert: "; + out << PeerId(); + rslog(RSL_WARNING, pqisslzone, out.str()); + + // Then send unreachable message. + net_internal_close(sockfd); + sockfd=-1; + //reset(); + waiting = WAITING_FAIL_INTERFACE; + + return -1; + } + + std::ostringstream out; + out << "Error: Connection Failed UNKNOWN ERROR: " << err; + out << " - " << socket_errorType(err); + rslog(RSL_WARNING, pqisslzone, out.str()); + + net_internal_close(sockfd); + sockfd=-1; + //reset(); // which will send Connect Failed, + return -1; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Basic_Connection_Complete() BAD GETSOCKOPT!"); + waiting = WAITING_FAIL_INTERFACE; + + return -1; +} + + +int pqissl::Initiate_SSL_Connection() +{ + int err; + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Initiate_SSL_Connection() Checking Basic Connection"); + + if (0 >= (err = Basic_Connection_Complete())) + { + return err; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Initiate_SSL_Connection() Basic Connection Okay"); + + // setup timeout value. + ssl_connect_timeout = time(NULL) + PQISSL_SSL_CONNECT_TIMEOUT; + + // Perform SSL magic. + // library already inited by sslroot(). + SSL *ssl = SSL_new(mAuthMgr->getCTX()); + if (ssl == NULL) + { + rslog(RSL_ALERT, pqisslzone, + "pqissl::Initiate_SSL_Connection() SSL_new failed!"); + + exit(1); + return -1; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Initiate_SSL_Connection() SSL Connection Okay"); + + ssl_connection = ssl; + + net_internal_SSL_set_fd(ssl, sockfd); + if (err < 1) + { + std::ostringstream out; + out << "pqissl::Initiate_SSL_Connection() SSL_set_fd failed!"; + out << std::endl; + printSSLError(ssl, err, SSL_get_error(ssl, err), + ERR_get_error(), out); + + rslog(RSL_ALERT, pqisslzone, out.str()); + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Initiate_SSL_Connection() Waiting for SSL Connection"); + + waiting = WAITING_SSL_CONNECTION; + return 1; +} + +int pqissl::SSL_Connection_Complete() +{ + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::SSL_Connection_Complete()??? ... Checking"); + + if (waiting == WAITING_SSL_AUTHORISE) + { + rslog(RSL_ALERT, pqisslzone, + "pqissl::SSL_Connection_Complete() Waiting = W_SSL_AUTH"); + + return 1; + } + if (waiting != WAITING_SSL_CONNECTION) + { + rslog(RSL_ALERT, pqisslzone, + "pqissl::SSL_Connection_Complete() Still Waiting.."); + + return -1; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::SSL_Connection_Complete() Attempting SSL_connect"); + + /* if we are passive - then accept! */ + int err; + + if (sslmode) + { + rslog(RSL_DEBUG_BASIC, pqisslzone, "--------> Active Connect!"); + err = SSL_connect(ssl_connection); + } + else + { + rslog(RSL_DEBUG_BASIC, pqisslzone, "--------> Passive Accept!"); + err = SSL_accept(ssl_connection); + } + + if (err != 1) + { + int serr = SSL_get_error(ssl_connection, err); + if ((serr == SSL_ERROR_WANT_READ) + || (serr == SSL_ERROR_WANT_WRITE)) + { + rslog(RSL_DEBUG_BASIC, pqisslzone, + "Waiting for SSL handshake!"); + + waiting = WAITING_SSL_CONNECTION; + return 0; + } + + + std::ostringstream out; + out << "pqissl::SSL_Connection_Complete()" << std::endl; + out << "Issues with SSL Connect(" << err << ")!" << std::endl; + printSSLError(ssl_connection, err, serr, + ERR_get_error(), out); + + rslog(RSL_WARNING, pqisslzone, + out.str()); + + // attempt real error. + Extract_Failed_SSL_Certificate(); + + reset(); + waiting = WAITING_FAIL_INTERFACE; + + return -1; + } + // if we get here... success v quickly. + + { + std::ostringstream out; + out << "pqissl::SSL_Connection_Complete() Success!: Peer: " << PeerId(); + rslog(RSL_WARNING, pqisslzone, out.str()); + } + + waiting = WAITING_SSL_AUTHORISE; + return 1; +} + +int pqissl::Extract_Failed_SSL_Certificate() +{ + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Extract_Failed_SSL_Certificate()"); + + // Get the Peer Certificate.... +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + XPGP *peercert = SSL_get_peer_pgp_certificate(ssl_connection); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + X509 *peercert = SSL_get_peer_certificate(ssl_connection); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + if (peercert == NULL) + { + rslog(RSL_WARNING, pqisslzone, + "pqissl::Extract_Failed_SSL_Certificate() Peer Didnt Give Cert"); + return -1; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Extract_Failed_SSL_Certificate() Have Peer Cert - Registering"); + + // save certificate... (and ip locations) + // false for outgoing.... + // we actually connected to remote_addr, + // which could be + // (pqissl's case) sslcert->serveraddr or sslcert->localaddr. +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + mAuthMgr->FailedCertificateXPGP(peercert, false); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + mAuthMgr->FailedCertificate(peercert, false); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + return 1; +} + + + + +int pqissl::Authorise_SSL_Connection() +{ + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Authorise_SSL_Connection()"); + ssl_connect_timeout = time(NULL) + PQISSL_SSL_CONNECT_TIMEOUT; + + if (time(NULL) > ssl_connect_timeout) + { + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Authorise_SSL_Connection() Connection Timed Out!"); + /* as sockfd is valid, this should close it all up */ + reset(); + } + + int err; + if (0 >= (err = SSL_Connection_Complete())) + { + return err; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Authorise_SSL_Connection() SSL_Connection_Complete"); + + // reset switch. + waiting = WAITING_NOT; + +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + XPGP *peercert = SSL_get_peer_pgp_certificate(ssl_connection); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + X509 *peercert = SSL_get_peer_certificate(ssl_connection); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + if (peercert == NULL) + { + rslog(RSL_WARNING, pqisslzone, + "pqissl::Authorise_SSL_Connection() Peer Didnt Give Cert"); + + // Failed completely + reset(); + return -1; + } + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::Authorise_SSL_Connection() Have Peer Cert"); + + // save certificate... (and ip locations) + // false for outgoing.... + // we actually connected to remote_addr, + // which could be + // (pqissl's case) sslcert->serveraddr or sslcert->localaddr. + + bool certCorrect = false; +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + certCorrect = mAuthMgr->CheckCertificateXPGP(PeerId(), peercert); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + certCorrect = mAuthMgr->CheckCertificate(PeerId(), peercert); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + // check it's the right one. + if (certCorrect) + { + // then okay... + std::ostringstream out; + out << "pqissl::Authorise_SSL_Connection() Accepting Conn. Peer: " << PeerId(); + rslog(RSL_WARNING, pqisslzone, out.str()); + + accept(ssl_connection, sockfd, remote_addr); + return 1; + } + + { + std::ostringstream out; + out << "pqissl::Authorise_SSL_Connection() Something Wrong ... "; + out << " Shutdown. Peer: " << PeerId(); + rslog(RSL_WARNING, pqisslzone, out.str()); + } + + // else shutdown ssl connection. + + reset(); + return 0; +} + +int pqissl::accept(SSL *ssl, int fd, struct sockaddr_in foreign_addr) // initiate incoming connection. +{ + if (waiting != WAITING_NOT) + { + rslog(RSL_WARNING, pqisslzone, + "pqissl::accept() - Two connections in progress - Shut 1 down!"); + + // outgoing connection in progress. + // shut this baby down. + // + // Thought I should shut down one in progress, and continue existing one! + // But the existing one might be broke.... take second. + // all we need is to never stop listening. + + switch(waiting) + { + + case WAITING_SOCK_CONNECT: + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::accept() STATE = Waiting Sock Connect - close the socket"); + + break; + + case WAITING_SSL_CONNECTION: + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::accept() STATE = Waiting SSL Connection - close sockfd + ssl_conn"); + + break; + + case WAITING_SSL_AUTHORISE: + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::accept() STATE = Waiting SSL Authorise - close sockfd + ssl_conn"); + + break; + + case WAITING_FAIL_INTERFACE: + + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::accept() STATE = Failed, ignore?"); + + break; + + + default: + rslog(RSL_ALERT, pqisslzone, + "pqissl::accept() STATE = Unknown - ignore?"); + + reset(); + break; + } + + //waiting = WAITING_FAIL_INTERFACE; + //return -1; + } + + /* shutdown existing - in all cases use the new one */ + if ((ssl_connection) && (ssl_connection != ssl)) + { + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::accept() closing Previous/Existing ssl_connection"); + SSL_shutdown(ssl_connection); + } + + if ((sockfd > -1) && (sockfd != fd)) + { + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::accept() closing Previous/Existing sockfd"); + net_internal_close(sockfd); + } + + + // save ssl + sock. + + ssl_connection = ssl; + sockfd = fd; + + /* if we connected - then just writing the same over, + * but if from ssllistener then we need to save the address. + */ + remote_addr = foreign_addr; + + /* check whether it is on the same LAN */ + + peerConnectState details; + mConnMgr->getOwnNetStatus(details); + sameLAN = isSameSubnet(&(remote_addr.sin_addr), &(details.localaddr.sin_addr)); + + { + std::ostringstream out; + out << "pqissl::accept() Successful connection with: " << PeerId(); + out << std::endl; + out << "\t\tchecking for same LAN"; + out << std::endl; + out << "\t localaddr: " << inet_ntoa(details.localaddr.sin_addr); + out << std::endl; + out << "\t remoteaddr: " << inet_ntoa(remote_addr.sin_addr); + out << std::endl; + if (sameLAN) + { + out << "\tSAME LAN - no bandwidth restrictions!"; + } + else + { + out << "\tDifferent LANs - bandwidth restrictions!"; + } + out << std::endl; + + rslog(RSL_WARNING, pqisslzone, out.str()); + } + + // establish the ssl details. + // cipher name. + int alg; + int err; + + { + std::ostringstream out; + out << "SSL Cipher:" << SSL_get_cipher(ssl) << std::endl; + out << "SSL Cipher Bits:" << SSL_get_cipher_bits(ssl, &alg); + out << " - " << alg << std::endl; + out << "SSL Cipher Version:" << SSL_get_cipher_version(ssl) << std::endl; + rslog(RSL_DEBUG_BASIC, pqisslzone, out.str()); + } + + // make non-blocking / or check..... + if ((err = net_internal_fcntl_nonblock(sockfd)) < 0) + { + rslog(RSL_ALERT, pqisslzone, "Error: Cannot make socket NON-Blocking: "); + + active = false; + waiting = WAITING_FAIL_INTERFACE; + // failed completely. + reset(); + return -1; + } + else + { + rslog(RSL_DEBUG_BASIC, pqisslzone, "pqissl::accept() Socket Made Non-Blocking!"); + } + + // we want to continue listening - incase this socket is crap, and they try again. + //stoplistening(); + + active = true; + waiting = WAITING_NOT; + + // Notify the pqiperson.... (Both Connect/Receive) + if (parent()) + { + parent() -> notifyEvent(this, NET_CONNECT_SUCCESS); + } + return 1; +} + +/********** Implementation of BinInterface ************************** + * All the rest of the BinInterface. + * + */ + +int pqissl::senddata(void *data, int len) +{ + int tmppktlen ; + + tmppktlen = SSL_write(ssl_connection, data, len) ; + + if (len != tmppktlen) + { + std::ostringstream out; + out << "pqissl::senddata()"; + out << " Full Packet Not Sent!" << std::endl; + out << " -> Expected len(" << len << ") actually sent("; + out << tmppktlen << ")" << std::endl; + + int err = SSL_get_error(ssl_connection, tmppktlen); + // incomplete operations - to repeat.... + // handled by the pqistreamer... + if (err == SSL_ERROR_SYSCALL) + { + out << "SSL_write() SSL_ERROR_SYSCALL"; + out << std::endl; + out << "Socket Closed Abruptly.... Resetting PQIssl"; + out << std::endl; + std::cerr << out.str() ; + rslog(RSL_ALERT, pqisslzone, out.str()); + reset(); + return -1; + } + else if (err == SSL_ERROR_WANT_WRITE) + { + out << "SSL_write() SSL_ERROR_WANT_WRITE"; + out << std::endl; + rslog(RSL_ALERT, pqisslzone, out.str()); +// std::cerr << out.str() ; + return -1; + } + else if (err == SSL_ERROR_WANT_READ) + { + out << "SSL_write() SSL_ERROR_WANT_READ"; + out << std::endl; + rslog(RSL_ALERT, pqisslzone, out.str()); + std::cerr << out.str() ; + return -1; + } + else + { + out << "SSL_write() UNKNOWN ERROR: " << err; + out << std::endl; + printSSLError(ssl_connection, tmppktlen, err, ERR_get_error(), out); + out << std::endl; + out << "\tResetting!"; + out << std::endl; + std::cerr << out.str() ; + rslog(RSL_ALERT, pqisslzone, out.str()); + + reset(); + return -1; + } + } + return tmppktlen; +} + +int pqissl::readdata(void *data, int len) +{ + int total_len = 0 ; + + // There is a do, because packets can be splitted into multiple ssl buffers + // when they are larger than 16384 bytes. Such packets have to be read in + // multiple slices. + do + { + int tmppktlen ; + + tmppktlen = SSL_read(ssl_connection, (void*)((unsigned long int)data+(unsigned long int)total_len), len-total_len) ; + + // Need to catch errors..... + // + if (tmppktlen <= 0) // probably needs a reset. + { + std::ostringstream out; + + int error = SSL_get_error(ssl_connection, tmppktlen); + unsigned long err2 = ERR_get_error(); + + //printSSLError(ssl_connection, tmppktlen, error, err2, out); + + if ((error == SSL_ERROR_ZERO_RETURN) && (err2 == 0)) + { + /* this code will be called when + * (1) moretoread -> returns true. + + * (2) SSL_read fails. + * + * There are two ways this can happen: + * (1) there is a little data on the socket, but not enough + * for a full SSL record, so there legimitately is no error, and the moretoread() + * was correct, but the read fails. + * + * (2) the socket has been closed correctly. this leads to moretoread() -> true, + * and ZERO error.... we catch this case by counting how many times + * it occurs in a row (cos the other one will not). + */ + + ++n_read_zero; + out << "SSL_ERROR_ZERO_RETURN -- "; + out << std::endl; + out << " Has socket closed been properly closed? nReadZero: " << n_read_zero; + out << std::endl; + + if (PQISSL_MAX_READ_ZERO_COUNT < n_read_zero) + { + out << "Count passed Limit, shutting down!"; + reset(); + } + + rslog(RSL_ALERT, pqisslzone, out.str()); +// std::cerr << out.str() << std::endl ; + return -1; + } + + /* the only real error we expect */ + if (error == SSL_ERROR_SYSCALL) + { + out << "SSL_read() SSL_ERROR_SYSCALL"; + out << std::endl; + out << "Socket Closed Abruptly.... Resetting PQIssl"; + out << std::endl; + rslog(RSL_ALERT, pqisslzone, out.str()); + reset(); + std::cerr << out.str() << std::endl ; + return -1; + } + else if (error == SSL_ERROR_WANT_WRITE) + { + out << "SSL_read() SSL_ERROR_WANT_WRITE"; + out << std::endl; + rslog(RSL_ALERT, pqisslzone, out.str()); + std::cerr << out.str() << std::endl ; + return -1; + } + else if (error == SSL_ERROR_WANT_READ) // SSL_WANT_READ is not a crittical error. It's just a sign that + { // the internal SSL buffer is not ready to accept more data. So -1 +// out << "SSL_read() SSL_ERROR_WANT_READ"; // is returned, and the connexion will be retried as is on next +// out << std::endl; // call of readdata(). +// rslog(RSL_ALERT, pqisslzone, out.str()); + return -1; + } + else + { + out << "SSL_read() UNKNOWN ERROR: " << error; + out << std::endl; + out << "\tResetting!"; + rslog(RSL_ALERT, pqisslzone, out.str()); + std::cerr << out.str() << std::endl ; + reset(); + return -1; + } + + rslog(RSL_ALERT, pqisslzone, out.str()); + //exit(1); + } + else + total_len+=tmppktlen ; + } while(total_len < len) ; + + if (len != total_len) + { + std::ostringstream out; + out << "pqissl::readdata()"; + out << " Full Packet Not read!" << std::endl; + out << " -> Expected len(" << len << ") actually read("; + out << total_len << ")" << std::endl; + std::cerr << out.str() ; + rslog(RSL_WARNING, pqisslzone, out.str()); + } + n_read_zero = 0; + return len;//tmppktlen; +} + + +// dummy function currently. +int pqissl::netstatus() +{ + return 1; +} + +int pqissl::isactive() +{ + return active; +} + +bool pqissl::bandwidthLimited() +{ + return (!sameLAN); +} + +bool pqissl::moretoread() +{ + { + std::ostringstream out; + out << "pqissl::moretoread()"; + out << " polling socket (" << sockfd << ")"; + rslog(RSL_DEBUG_ALL, pqisslzone, out.str()); + } + + fd_set ReadFDs, WriteFDs, ExceptFDs; + FD_ZERO(&ReadFDs); + FD_ZERO(&WriteFDs); + FD_ZERO(&ExceptFDs); + + FD_SET(sockfd, &ReadFDs); + FD_SET(sockfd, &WriteFDs); + FD_SET(sockfd, &ExceptFDs); + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + if (select(sockfd + 1, &ReadFDs, &WriteFDs, &ExceptFDs, &timeout) < 0) + { + rslog(RSL_ALERT, pqisslzone, + "pqissl::moretoread() Select ERROR!"); + return 0; + } + + if (FD_ISSET(sockfd, &ExceptFDs)) + { + // error - reset socket. + rslog(RSL_ALERT, pqisslzone, + "pqissl::moretoread() Select Exception ERROR!"); + + // this is a definite bad socket!. + // reset. + reset(); + return 0; + } + + if (FD_ISSET(sockfd, &WriteFDs)) + { + // write can work. + rslog(RSL_DEBUG_ALL, pqisslzone, + "pqissl::moretoread() Can Write!"); + } + else + { + // write can work. + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::moretoread() Can *NOT* Write!"); + } + + if (FD_ISSET(sockfd, &ReadFDs)) + { + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::moretoread() Data to Read!"); + return 1; + } + else + { + rslog(RSL_DEBUG_ALL, pqisslzone, + "pqissl::moretoread() No Data to Read!"); + return 0; + } + +} + +bool pqissl::cansend() +{ + rslog(RSL_DEBUG_ALL, pqisslzone, + "pqissl::cansend() polling socket!"); + + // Interestingly - This code might be portable.... + + fd_set ReadFDs, WriteFDs, ExceptFDs; + FD_ZERO(&ReadFDs); + FD_ZERO(&WriteFDs); + FD_ZERO(&ExceptFDs); + + FD_SET(sockfd, &ReadFDs); + FD_SET(sockfd, &WriteFDs); + FD_SET(sockfd, &ExceptFDs); + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + if (select(sockfd + 1, &ReadFDs, &WriteFDs, &ExceptFDs, &timeout) < 0) + { + // select error. + rslog(RSL_ALERT, pqisslzone, + "pqissl::cansend() Select Error!"); + + return 0; + } + + if (FD_ISSET(sockfd, &ExceptFDs)) + { + // error - reset socket. + rslog(RSL_ALERT, pqisslzone, + "pqissl::cansend() Select Exception!"); + + // this is a definite bad socket!. + // reset. + reset(); + return 0; + } + + if (FD_ISSET(sockfd, &WriteFDs)) + { + // write can work. + rslog(RSL_DEBUG_ALL, pqisslzone, + "pqissl::cansend() Can Write!"); + return 1; + } + else + { + // write can work. + rslog(RSL_DEBUG_BASIC, pqisslzone, + "pqissl::cansend() Can *NOT* Write!"); + + return 0; + } + +} + +std::string pqissl::gethash() +{ + std::string dummyhash; + return dummyhash; +} + +/********** End of Implementation of BinInterface ******************/ + + diff --git a/libretroshare/src/_pqi/pqissl.h b/libretroshare/src/_pqi/pqissl.h new file mode 100644 index 000000000..bea781f6e --- /dev/null +++ b/libretroshare/src/_pqi/pqissl.h @@ -0,0 +1,230 @@ +/* + * "$Id: pqissl.h,v 1.18 2007-03-11 14:54:22 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQISSL_H +#define PQISSL_H + +#include + +// operating system specific network header. +#include "pqi/pqinetwork.h" + +#include +#include + +#include "_pqi/pqi_base.h" + +#include "_pqi/p3connmgr.h" + +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) +#include "pqi/authxpgp.h" +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ +#include "pqi/authssl.h" +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + +#define WAITING_NOT 0 +#define WAITING_DELAY 1 +#define WAITING_SOCK_CONNECT 2 +#define WAITING_SSL_CONNECTION 3 +#define WAITING_SSL_AUTHORISE 4 +#define WAITING_FAIL_INTERFACE 5 + +#define PQISSL_PASSIVE 0x00 +#define PQISSL_ACTIVE 0x01 + +const int PQISSL_LOCAL_FLAG = 0x01; +const int PQISSL_REMOTE_FLAG = 0x02; +const int PQISSL_DNS_FLAG = 0x04; + +/* not sure about the value? */ +const int PQISSL_UDP_FLAG = 0x02; + + +/***************************** pqi Net SSL Interface ********************************* + * This provides the base SSL interface class, + * and handles most of the required functionality. + * + * there are a series of small fn's that can be overloaded + * to provide alternative behaviour.... + * + * Classes expected to inherit from this are: + * + * pqissllistener -> pqissllistener (tcp only) + * -> pqixpgplistener (tcp only) + * + * pqissl -> pqissltcp + * -> pqissludp + * -> pqixpgptcp + * -> pqixpgpudp + * + */ + +class pqissl; +class cert; + +class pqissllistener; + +class pqissl: public NetBinInterface +{ +public: + pqissl(pqissllistener *l, PQInterface *parent, + p3AuthMgr *am, p3ConnectMgr *cm); + virtual ~pqissl(); + + // NetInterface + virtual int connect(struct sockaddr_in raddr); + virtual int listen(); + virtual int stoplistening(); + virtual int reset(); + virtual int disconnect(); + + virtual bool connect_parameter(uint32_t type, uint32_t value); + + // BinInterface + virtual int tick(); + virtual int status(); + + virtual int senddata(void*, int); + virtual int readdata(void*, int); + virtual int netstatus(); + virtual int isactive(); + virtual bool moretoread(); + virtual bool cansend(); + + virtual int close(); /* BinInterface version of reset() */ + virtual std::string gethash(); /* not used here */ + virtual bool bandwidthLimited(); + +protected: + // A little bit of information to describe + // the SSL state, this is needed + // to allow full Non-Blocking Connect behaviour. + // This fn loops through the following fns. + // to complete an SSL. + + int ConnectAttempt(); + int waiting; + + virtual int Failed_Connection(); + + // Start up connection with delay... + virtual int Delay_Connection(); + + // These two fns are overloaded for udp/etc connections. + virtual int Initiate_Connection(); + virtual int Basic_Connection_Complete(); + + // These should be identical for all cases, + // differences are achieved via the net_internal_* fns. + int Initiate_SSL_Connection(); + int SSL_Connection_Complete(); + int Authorise_SSL_Connection(); + + int Extract_Failed_SSL_Certificate(); // try to get cert anyway. + +public: + + /* Completion of the SSL connection, + * this is public, so it can be called by + * the listener (should make friends??) + */ + + int accept(SSL *ssl, int fd, struct sockaddr_in foreign_addr); + +protected: + + //protected internal fns that are overloaded for udp case. + virtual int net_internal_close(int fd) { return unix_close(fd); } + virtual int net_internal_SSL_set_fd(SSL *ssl, int fd) { return SSL_set_fd(ssl, fd); } + virtual int net_internal_fcntl_nonblock(int fd) { return unix_fcntl_nonblock(fd);} + + + /* data */ + bool active; + bool certvalid; + + // addition for udp (tcp version == ACTIVE). + int sslmode; + + SSL *ssl_connection; + int sockfd; + + pqissllistener *pqil; + struct sockaddr_in remote_addr; + + void *readpkt; + int pktlen; + + int attempt_ts; + + // Some flags to indicate + // the status of the various interfaces + // (local), (server) + unsigned int net_attempt; + unsigned int net_failure; + unsigned int net_unreachable; + + bool sameLAN; /* flag use to allow high-speed transfers */ + + int n_read_zero; /* a counter to determine if the connection is really dead */ + + int ssl_connect_timeout; /* timeout to ensure that we don't get stuck (can happen on udp!) */ + + uint32_t mConnectDelay; + time_t mConnectTS; + uint32_t mConnectTimeout; + time_t mTimeoutTS; + + /* Need Certificate specific functions here! */ + /**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + + AuthXPGP *mAuthMgr; + +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + AuthSSL *mAuthMgr; + +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + p3ConnectMgr *mConnMgr; + +private: + // ssl only fns. + int connectInterface(sockaddr_in&); + +}; + + + + +#endif // PQISSL_H diff --git a/libretroshare/src/_pqi/pqissllistener.cc b/libretroshare/src/_pqi/pqissllistener.cc new file mode 100644 index 000000000..3766588f6 --- /dev/null +++ b/libretroshare/src/_pqi/pqissllistener.cc @@ -0,0 +1,733 @@ +/* + * "$Id: pqissllistener.cc,v 1.3 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 "_pqi/pqissl.h" +#include "_pqi/pqissllistener.h" +#include "_pqi/pqinetwork.h" + +#include +#include + +#include "_util/rsdebug.h" +#include + +const int pqissllistenzone = 49787; + + +/************************ PQI SSL LISTEN BASE **************************** + * + * This provides all of the basic connection stuff, + * and calls completeConnection afterwards... + * + */ + + +pqissllistenbase::pqissllistenbase(struct sockaddr_in addr, p3AuthMgr *am, p3ConnectMgr *cm) + :laddr(addr), active(false), +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + mAuthMgr((AuthXPGP *) am), mConnMgr(cm) +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + mAuthMgr((AuthSSL *) am), mConnMgr(cm) +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + +{ + if (!(mAuthMgr -> active())) + { + pqioutput(PQL_ALERT, pqissllistenzone, + "SSL-CTX-CERT-ROOT not initialised!"); + + exit(1); + } + + setuplisten(); + return; +} + +pqissllistenbase::~pqissllistenbase() +{ + return; +} + +int pqissllistenbase::tick() +{ + status(); + // check listen port. + acceptconnection(); + return continueaccepts(); +} + +int pqissllistenbase::status() +{ + std::ostringstream out; + out << "pqissllistenbase::status(): "; + out << " Listening on port: " << ntohs(laddr.sin_port) << std::endl; + pqioutput(PQL_DEBUG_ALL, pqissllistenzone, out.str()); + return 1; +} + + + +int pqissllistenbase::setuplisten() +{ + int err; + if (active) + return -1; + + lsock = socket(PF_INET, SOCK_STREAM, 0); +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS // ie UNIX + if (lsock < 0) + { + pqioutput(PQL_ALERT, pqissllistenzone, + "pqissllistenbase::setuplisten() Cannot Open Socket!"); + + return -1; + } + + err = fcntl(lsock, F_SETFL, O_NONBLOCK); + if (err < 0) + { + std::ostringstream out; + out << "Error: Cannot make socket NON-Blocking: "; + out << err << std::endl; + pqioutput(PQL_ERROR, pqissllistenzone, out.str()); + + return -1; + } + +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#else //WINDOWS_SYS + if ((unsigned) lsock == INVALID_SOCKET) + { + std::ostringstream out; + out << "pqissllistenbase::setuplisten()"; + out << " Cannot Open Socket!" << std::endl; + out << "Socket Error:"; + out << socket_errorType(WSAGetLastError()) << std::endl; + pqioutput(PQL_ALERT, pqissllistenzone, out.str()); + + return -1; + } + + // Make nonblocking. + unsigned long int on = 1; + if (0 != (err = ioctlsocket(lsock, FIONBIO, &on))) + { + std::ostringstream out; + out << "pqissllistenbase::setuplisten()"; + out << "Error: Cannot make socket NON-Blocking: "; + out << err << std::endl; + out << "Socket Error:"; + out << socket_errorType(WSAGetLastError()) << std::endl; + pqioutput(PQL_ALERT, pqissllistenzone, out.str()); + + return -1; + } +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + + // setup listening address. + + // fill in fconstant bits. + + laddr.sin_family = AF_INET; + + { + std::ostringstream out; + out << "pqissllistenbase::setuplisten()"; + out << "\tAddress Family: " << (int) laddr.sin_family; + out << std::endl; + out << "\tSetup Address: " << inet_ntoa(laddr.sin_addr); + out << std::endl; + out << "\tSetup Port: " << ntohs(laddr.sin_port); + + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, out.str()); + } + + if (0 != (err = bind(lsock, (struct sockaddr *) &laddr, sizeof(laddr)))) + { + std::ostringstream out; + out << "pqissllistenbase::setuplisten()"; + out << " Cannot Bind to Local Address!" << std::endl; + showSocketError(out); + pqioutput(PQL_ALERT, pqissllistenzone, out.str()); + + exit(1); + return -1; + } + else + { + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, + "pqissllistenbase::setuplisten() Bound to Address."); + } + + if (0 != (err = listen(lsock, 100))) + { + std::ostringstream out; + out << "pqissllistenbase::setuplisten()"; + out << "Error: Cannot Listen to Socket: "; + out << err << std::endl; + showSocketError(out); + pqioutput(PQL_ALERT, pqissllistenzone, out.str()); + + exit(1); + return -1; + } + else + { + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, + "pqissllistenbase::setuplisten() Listening to Socket"); + } + active = true; + return 1; +} + +int pqissllistenbase::setListenAddr(struct sockaddr_in addr) +{ + laddr = addr; + return 1; +} + +int pqissllistenbase::resetlisten() +{ + if (active) + { + // close ports etc. +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS // ie UNIX + shutdown(lsock, SHUT_RDWR); + close(lsock); +#else //WINDOWS_SYS + closesocket(lsock); +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + + active = false; + return 1; + } + + return 0; +} + + +int pqissllistenbase::acceptconnection() +{ + if (!active) + return 0; + // check port for any socets... + pqioutput(PQL_DEBUG_ALL, pqissllistenzone, "pqissllistenbase::accepting()"); + + // These are local but temp variables... + // can't be arsed making them all the time. + struct sockaddr_in remote_addr; + socklen_t addrlen = sizeof(remote_addr); + int fd = accept(lsock, (struct sockaddr *) &remote_addr, &addrlen); + int err = 0; + +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS // ie UNIX + if (fd < 0) + { + pqioutput(PQL_DEBUG_ALL, pqissllistenzone, + "pqissllistenbase::acceptconnnection() Nothing to Accept!"); + return 0; + } + + err = fcntl(fd, F_SETFL, O_NONBLOCK); + if (err < 0) + { + std::ostringstream out; + out << "pqissllistenbase::acceptconnection()"; + out << "Error: Cannot make socket NON-Blocking: "; + out << err << std::endl; + pqioutput(PQL_ERROR, pqissllistenzone, out.str()); + + close(fd); + return -1; + } + +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#else //WINDOWS_SYS + if ((unsigned) fd == INVALID_SOCKET) + { + pqioutput(PQL_DEBUG_ALL, pqissllistenzone, + "pqissllistenbase::acceptconnnection() Nothing to Accept!"); + return 0; + } + + // Make nonblocking. + unsigned long int on = 1; + if (0 != (err = ioctlsocket(fd, FIONBIO, &on))) + { + std::ostringstream out; + out << "pqissllistenbase::acceptconnection()"; + out << "Error: Cannot make socket NON-Blocking: "; + out << err << std::endl; + out << "Socket Error:"; + out << socket_errorType(WSAGetLastError()) << std::endl; + pqioutput(PQL_ALERT, pqissllistenzone, out.str()); + + closesocket(fd); + return 0; + } +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + + { + std::ostringstream out; + out << "Accepted Connection from "; + out << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port); + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, out.str()); + } + + // Negotiate certificates. SSL stylee. + // Allow negotiations for secure transaction. + + SSL *ssl = SSL_new(mAuthMgr -> getCTX()); + SSL_set_fd(ssl, fd); + + return continueSSL(ssl, remote_addr, true); // continue and save if incomplete. +} + +int pqissllistenbase::continueSSL(SSL *ssl, struct sockaddr_in remote_addr, bool addin) +{ + // attempt the accept again. + int fd = SSL_get_fd(ssl); + int err = SSL_accept(ssl); + if (err <= 0) + { + int ssl_err = SSL_get_error(ssl, err); + int err_err = ERR_get_error(); + + { + std::ostringstream out; + out << "pqissllistenbase::continueSSL() "; + out << "Issues with SSL Accept(" << err << ")!" << std::endl; + printSSLError(ssl, err, ssl_err, err_err, out); + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, out.str()); + } + + if ((ssl_err == SSL_ERROR_WANT_READ) || + (ssl_err == SSL_ERROR_WANT_WRITE)) + { + std::ostringstream out; + out << "pqissllistenbase::continueSSL() "; + out << " Connection Not Complete!"; + out << std::endl; + + if (addin) + { + out << "pqissllistenbase::continueSSL() "; + out << "Adding SSL to incoming!"; + + // add to incomingqueue. + incoming_ssl[ssl] = remote_addr; + } + + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, out.str()); + + // zero means still continuing.... + return 0; + } + + /* we have failed -> get certificate if possible */ + Extract_Failed_SSL_Certificate(ssl, &remote_addr); + + // other wise delete ssl connection. + // kill connection.... + // so it will be removed from cache. + SSL_shutdown(ssl); + + // close socket??? +/************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS // ie UNIX + shutdown(fd, SHUT_RDWR); + close(fd); +#else //WINDOWS_SYS + closesocket(fd); +#endif +/************************** WINDOWS/UNIX SPECIFIC PART ******************/ + // free connection. + SSL_free(ssl); + + std::ostringstream out; + out << "Read Error on the SSL Socket"; + out << std::endl; + out << "Shutting it down!" << std::endl; + pqioutput(PQL_WARNING, pqissllistenzone, out.str()); + + // failure -1, pending 0, sucess 1. + return -1; + } + + // if it succeeds + if (0 < completeConnection(fd, ssl, remote_addr)) + { + return 1; + } + + /* else we shut it down! */ + pqioutput(PQL_WARNING, pqissllistenzone, + "pqissllistenbase::completeConnection() Failed!"); + + // delete ssl connection. + SSL_shutdown(ssl); + + // close socket??? +/************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS // ie UNIX + shutdown(fd, SHUT_RDWR); + close(fd); +#else //WINDOWS_SYS + closesocket(fd); +#endif +/************************** WINDOWS/UNIX SPECIFIC PART ******************/ + // free connection. + SSL_free(ssl); + + std::ostringstream out; + out << "Shutting it down!" << std::endl; + pqioutput(PQL_WARNING, pqissllistenzone, out.str()); + + // failure -1, pending 0, sucess 1. + return -1; +} + + +int pqissllistenbase::Extract_Failed_SSL_Certificate(SSL *ssl, struct sockaddr_in *inaddr) +{ + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, + "pqissllistenbase::Extract_Failed_SSL_Certificate()"); + + // Get the Peer Certificate.... +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + XPGP *peercert = SSL_get_peer_pgp_certificate(ssl); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + X509 *peercert = SSL_get_peer_certificate(ssl); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + if (peercert == NULL) + { + pqioutput(PQL_WARNING, pqissllistenzone, + "pqissllistenbase::Extract_Failed_SSL_Certificate() Peer Didnt Give Cert"); + return -1; + } + + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, + "pqissllistenbase::Extract_Failed_SSL_Certificate() Have Peer Cert - Registering"); + + // save certificate... (and ip locations) + // false for outgoing.... +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + mAuthMgr->FailedCertificateXPGP(peercert, true); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + mAuthMgr->FailedCertificate(peercert, true); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + return 1; +} + + +int pqissllistenbase::continueaccepts() +{ + + // for each of the incoming sockets.... call continue. + std::map::iterator it, itd; + + for(it = incoming_ssl.begin(); it != incoming_ssl.end();) + { + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, + "pqissllistenbase::continueaccepts() Continuing SSL"); + if (0 != continueSSL(it->first, it->second, false)) + { + pqioutput(PQL_DEBUG_ALERT, pqissllistenzone, + "pqissllistenbase::continueaccepts() SSL Complete/Dead!"); + + /* save and increment -> so we can delete */ + itd = it++; + incoming_ssl.erase(itd); + } + else + { + it++; + } + } + return 1; +} + + +/************************ PQI SSL LISTENER **************************** + * + * This is the standard pqissl listener interface.... + * + * this class only allows connections from + * specific certificates, which are pre specified. + * + */ + +pqissllistener::pqissllistener(struct sockaddr_in addr, p3AuthMgr *am, p3ConnectMgr *cm) + :pqissllistenbase(addr, am, cm) +{ + return; +} + +pqissllistener::~pqissllistener() +{ + return; +} + +int pqissllistener::addlistenaddr(std::string id, pqissl *acc) +{ + std::map::iterator it; + + std::ostringstream out; + + out << "Adding to Cert Listening Addresses Id: " << id << std::endl; + out << "Current Certs:" << std::endl; + for(it = listenaddr.begin(); it != listenaddr.end(); it++) + { + out << id << std::endl; + if (it -> first == id) + { + out << "pqissllistener::addlistenaddr()"; + out << "Already listening for Certificate!"; + out << std::endl; + + pqioutput(PQL_DEBUG_ALERT, pqissllistenzone, out.str()); + return -1; + + } + } + + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, out.str()); + + // not there can accept it! + listenaddr[id] = acc; + return 1; +} + +int pqissllistener::removeListenPort(std::string id) +{ + // check where the connection is coming from. + // if in list of acceptable addresses, + // + // check if in list. + std::map::iterator it; + for(it = listenaddr.begin();it!=listenaddr.end();it++) + { + if (it->first == id) + { + listenaddr.erase(it); + + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, + "pqissllisten::removeListenPort() Success!"); + return 1; + } + } + + pqioutput(PQL_WARNING, pqissllistenzone, + "pqissllistener::removeListenPort() Failed to Find a Match"); + + return -1; +} + + +int pqissllistener::status() +{ + pqissllistenbase::status(); + // print certificates we are listening for. + std::map::iterator it; + + std::ostringstream out; + out << "pqissllistener::status(): "; + out << " Listening (" << ntohs(laddr.sin_port) << ") for Certs:" << std::endl; + for(it = listenaddr.begin(); it != listenaddr.end(); it++) + { + out << it -> first << std::endl; + } + pqioutput(PQL_DEBUG_ALL, pqissllistenzone, out.str()); + + return 1; +} + +int pqissllistener::completeConnection(int fd, SSL *ssl, struct sockaddr_in &remote_addr) +{ + + // Get the Peer Certificate.... +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + XPGP *peercert = SSL_get_peer_pgp_certificate(ssl); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + X509 *peercert = SSL_get_peer_certificate(ssl); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + if (peercert == NULL) + { + pqioutput(PQL_WARNING, pqissllistenzone, + "pqissllistener::completeConnection() Peer Did Not Provide Cert!"); + + // failure -1, pending 0, sucess 1. + // pqissllistenbase will shutdown! + return -1; + } + + // Check cert. + std::string newPeerId; + +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + bool certOk = mAuthMgr->ValidateCertificateXPGP(peercert, newPeerId); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + /**** + * As the validation is actually done before this... + * we should only need to call CheckCertificate here! + ****/ + + bool certOk = mAuthMgr->ValidateCertificate(peercert, newPeerId); + +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + bool found = false; + std::map::iterator it; + + // Let connected one through as well! if ((npc == NULL) || (npc -> Connected())) + if (!certOk) + { + pqioutput(PQL_WARNING, pqissllistenzone, + "pqissllistener::completeConnection() registerCertificate Failed!"); + + // bad - shutdown. + // pqissllistenbase will shutdown! +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + XPGP_free(peercert); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + X509_free(peercert); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + return -1; + } + else + { + std::ostringstream out; + + out << "pqissllistener::continueSSL()" << std::endl; + out << "checking: " << newPeerId << std::endl; + // check if cert is in our list..... + for(it = listenaddr.begin();(found!=true) && (it!=listenaddr.end());) + { + out << "\tagainst: " << it->first << std::endl; + if (it -> first == newPeerId) + { + out << "\t\tMatch!"; + found = true; + } + else + { + it++; + } + } + + pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, out.str()); + } + + if (found == false) + { + std::ostringstream out; + out << "No Matching Certificate/Already Connected"; + out << " for Connection:" << inet_ntoa(remote_addr.sin_addr); + out << std::endl; + out << "pqissllistenbase: Will shut it down!" << std::endl; + pqioutput(PQL_WARNING, pqissllistenzone, out.str()); +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + XPGP_free(peercert); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + X509_free(peercert); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + return -1; + } + + /* Certificate consumed! */ +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + bool certKnown = mAuthMgr->CheckCertificateXPGP(it->first, peercert); +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + bool certKnown = mAuthMgr->CheckCertificate(it->first, peercert); +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + + if (certKnown == false) + { + std::ostringstream out; + out << "Failed Final Check"; + out << " for Connection:" << inet_ntoa(remote_addr.sin_addr); + out << std::endl; + out << "pqissllistenbase: Will shut it down!" << std::endl; + pqioutput(PQL_WARNING, pqissllistenzone, out.str()); + return -1; + } + + pqissl *pqis = it -> second; + + // dont remove from the list of certificates. + // want to allow a new connection to replace a faulty one! + // listenaddr.erase(it); + + // timestamp + // done in sslroot... npc -> lr_timestamp = time(NULL); + + // hand off ssl conection. + pqis -> accept(ssl, fd, remote_addr); + return 1; +} + + + + diff --git a/libretroshare/src/_pqi/pqissllistener.h b/libretroshare/src/_pqi/pqissllistener.h new file mode 100644 index 000000000..c83924e2f --- /dev/null +++ b/libretroshare/src/_pqi/pqissllistener.h @@ -0,0 +1,131 @@ +/* + * "$Id: pqissllistener.h,v 1.2 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQISSLLISTENER_H +#define PQISSLLISTENER_H + +#include + +// operating system specific network header. +#include "_pqi/pqinetwork.h" + +#include +#include + +#include "_pqi/pqi_base.h" +#include "_pqi/pqilistener.h" + +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) +#include "_pqi/authxpgp.h" +#else /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ +#include "_pqi/authssl.h" +#endif /* X509 Certificates */ +/**************** PQI_USE_XPGP ******************/ + +/***************************** pqi Net SSL Interface ********************************* + */ + +class pqissl; + +class pqissllistenbase: public pqilistener +{ +public: + pqissllistenbase(struct sockaddr_in addr, p3AuthMgr *am, p3ConnectMgr *cm); + virtual ~pqissllistenbase(); + + /*************************************/ + /* LISTENER INTERFACE **/ + + virtual int tick(); + virtual int status(); + virtual int setListenAddr(struct sockaddr_in addr); + virtual int setuplisten(); + virtual int resetlisten(); + + /*************************************/ + + int acceptconnection(); + int continueaccepts(); + int continueSSL(SSL *ssl, struct sockaddr_in remote_addr, bool); + + + virtual int completeConnection(int sockfd, SSL *in_connection, struct sockaddr_in &raddr) = 0; + +protected: + + struct sockaddr_in laddr; + +private: + + // fn to get cert, anyway + int Extract_Failed_SSL_Certificate(SSL *ssl, struct sockaddr_in *inaddr); + + bool active; + int lsock; + std::map incoming_ssl; + +protected: + +/**************** PQI_USE_XPGP ******************/ +#if defined(PQI_USE_XPGP) + + AuthXPGP *mAuthMgr; + +#else /* X509 Certificates */ + +/**************** PQI_USE_XPGP ******************/ + AuthSSL *mAuthMgr; +#endif /* X509 Certificates */ + + /**************** PQI_USE_XPGP ******************/ + p3ConnectMgr *mConnMgr; + +}; + + +class pqissllistener: public pqissllistenbase +{ +public: + pqissllistener(struct sockaddr_in addr, p3AuthMgr *am, p3ConnectMgr *cm); + virtual ~pqissllistener(); + + int addlistenaddr(std::string id, pqissl *acc); + int removeListenPort(std::string id); + + //virtual int tick(); + virtual int status(); + + virtual int completeConnection(int sockfd, SSL *in_connection, struct sockaddr_in &raddr); + +private: + std::map listenaddr; +}; + + +#endif // PQISSLLISTENER_H diff --git a/libretroshare/src/_pqi/pqisslpersongrp.cc b/libretroshare/src/_pqi/pqisslpersongrp.cc new file mode 100644 index 000000000..ed960ab22 --- /dev/null +++ b/libretroshare/src/_pqi/pqisslpersongrp.cc @@ -0,0 +1,105 @@ +/* + * libretroshare/src/pqi: pqisslpersongrp.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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 "util/rsdebug.h" + +#include "_pqi/pqisslpersongrp.h" +#include "_pqi/p3authmgr.h" + + +const int pqipersongrpzone = 354; + +/**** + * #define PQI_DISABLE_UDP 1 + ***/ + +/********************************** SSL Specific features ***************************/ + +#include "_pqi/pqissl.h" +#include "_pqi/pqissllistener.h" + +#ifndef PQI_DISABLE_UDP + #include "_pqi/pqissludp.h" +#endif + +pqilistener * pqisslpersongrp::createListener(struct sockaddr_in laddr) +{ + p3AuthMgr *authMgr = getAuthMgr(); + pqilistener *listener = new pqissllistener(laddr, authMgr, mConnMgr); + return listener; +} + +pqiperson * pqisslpersongrp::createPerson(std::string id, pqilistener *listener) +{ + { + std::ostringstream out; + out << "pqipersongrp::createPerson() PeerId: " << id; + pqioutput(PQL_DEBUG_BASIC, pqipersongrpzone, out.str()); + } + + p3AuthMgr *authMgr = getAuthMgr(); + pqiperson *pqip = new pqiperson(id, this); + pqissl *pqis = new pqissl((pqissllistener *) listener, pqip, authMgr, mConnMgr); + + /* construct the serialiser .... + * Needs: + * * FileItem + * * FileData + * * ServiceGeneric + */ + + RsSerialiser *rss = new RsSerialiser(); + rss->addSerialType(new RsFileItemSerialiser()); + rss->addSerialType(new RsCacheItemSerialiser()); + rss->addSerialType(new RsServiceSerialiser()); + + pqiconnect *pqisc = new pqiconnect(rss, pqis); + + pqip -> addChildInterface(PQI_CONNECT_TCP, pqisc); + +#ifndef PQI_DISABLE_UDP + pqissludp *pqius = new pqissludp(pqip, authMgr, mConnMgr); + + RsSerialiser *rss2 = new RsSerialiser(); + rss2->addSerialType(new RsFileItemSerialiser()); + rss2->addSerialType(new RsCacheItemSerialiser()); + rss2->addSerialType(new RsServiceSerialiser()); + + pqiconnect *pqiusc = new pqiconnect(rss2, pqius); + + // add a ssl + proxy interface. + // Add Proxy First. + pqip -> addChildInterface(PQI_CONNECT_UDP, pqiusc); +#endif + + return pqip; +} + + +/********************************** SSL Specific features ***************************/ + + diff --git a/libretroshare/src/_pqi/pqisslpersongrp.h b/libretroshare/src/_pqi/pqisslpersongrp.h new file mode 100644 index 000000000..2483b5f59 --- /dev/null +++ b/libretroshare/src/_pqi/pqisslpersongrp.h @@ -0,0 +1,48 @@ +/* + * libretroshare/src/pqi: pqisslpersongrp.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + + + +#ifndef PQISSLPERSONGRP_H +#define PQISSLPERSONGRP_H + +#include "pqi/pqipersongrp.h" + +class pqisslpersongrp: public pqipersongrp +{ +public: + pqisslpersongrp(SecurityPolicy *pol, unsigned long flags) + :pqipersongrp(pol, flags) { } + +protected: + + /********* FUNCTIONS to OVERLOAD for specialisation ********/ + virtual pqilistener *createListener(struct sockaddr_in laddr); + virtual pqiperson *createPerson(std::string id, pqilistener *listener); + /********* FUNCTIONS to OVERLOAD for specialisation ********/ +}; + + +#endif // PQISSLPERSONGRP_H diff --git a/libretroshare/src/_pqi/pqissludp.cc b/libretroshare/src/_pqi/pqissludp.cc new file mode 100644 index 000000000..04fc05360 --- /dev/null +++ b/libretroshare/src/_pqi/pqissludp.cc @@ -0,0 +1,513 @@ +/* + * "$Id: pqissludp.cc,v 1.16 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 "_pqi/pqissludp.h" +#include "_pqi/pqinetwork.h" + +#include "_tcponudp/tou.h" +#include "_tcponudp/bio_tou.h" + +#include +#include + +#include + +#include "_util/rsdebug.h" +#include "_util/rsnet.h" + +const int pqissludpzone = 3144; + + /* a final timeout, to ensure this never blocks completely + * 300 secs to complete udp/tcp/ssl connection. + * This is long as the udp connect can take some time. + */ + +static const uint32_t PQI_SSLUDP_DEF_CONN_PERIOD = 300; /* 5 minutes? */ + +/********** PQI SSL UDP STUFF **************************************/ + +pqissludp::pqissludp(PQInterface *parent, p3AuthMgr *am, p3ConnectMgr *cm) + :pqissl(NULL, parent, am, cm), tou_bio(NULL), + listen_checktime(0), mConnectPeriod(PQI_SSLUDP_DEF_CONN_PERIOD) +{ + sockaddr_clear(&remote_addr); + return; +} + + +pqissludp::~pqissludp() +{ + rslog(RSL_ALERT, pqissludpzone, + "pqissludp::~pqissludp -> destroying pqissludp"); + + /* must call reset from here, so that the + * virtual functions will still work. + * -> as they stop working in base class destructor. + * + * This means that reset() will be called twice, but this should + * be harmless. + */ + stoplistening(); /* remove from p3proxy listenqueue */ + reset(); + + if (tou_bio) // this should be in the reset? + { + BIO_free(tou_bio); + } + return; +} + +int pqissludp::reset() +{ + /* reset for next time.*/ + return pqissl::reset(); +} + + + /* <===================== UDP Difference *******************/ + // The Proxy Version takes a few more step + // + // connectInterface is sent via message from the proxy. + // and is set here. + /* <===================== UDP Difference *******************/ + +int pqissludp::attach() +{ + sockfd = tou_socket(0,0,0); + if (0 > sockfd) + { + rslog(RSL_WARNING, pqissludpzone, + "pqissludp::attach() failed to create a socket"); + return -1; + } + + // setup remote address + rslog(RSL_WARNING, pqissludpzone, + "pqissludp::attach() Opened Local Udp Socket"); + + return 1; +} + + +// The Address determination is done centrally +int pqissludp::Initiate_Connection() +{ + int err; + + attach(); /* open socket */ + remote_addr.sin_family = AF_INET; + + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::Initiate_Connection() Attempting Outgoing Connection...."); + + /* decide if we're active or passive */ + if (PeerId() < mConnMgr->getOwnId()) + { + sslmode = PQISSL_ACTIVE; + } + else + { + sslmode = PQISSL_PASSIVE; + } + + if (waiting != WAITING_DELAY) + { + rslog(RSL_WARNING, pqissludpzone, + "pqissludp::Initiate_Connection() Already Attempt in Progress!"); + return -1; + } + + if (sockfd < 0) + { + rslog(RSL_ALERT, pqissludpzone, + "pqissludp::Initiate_Connection() Socket Creation Failed!"); + waiting = WAITING_FAIL_INTERFACE; + return -1; + } + + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::Initiate_Connection() Opening Socket"); + + { + std::ostringstream out; + out << "pqissludp::Initiate_Connection() "; + out << "Connecting To: " << PeerId(); + out << " via: " << inet_ntoa(remote_addr.sin_addr) << ":"; + out << ntohs(remote_addr.sin_port) << " "; + if (sslmode) + { + out << "ACTIVE Connect (SSL_Connect)"; + } + else + { + out << "PASSIVE Connect (SSL_Accept)"; + } + rslog(RSL_WARNING, pqissludpzone, out.str()); + } + + if (remote_addr.sin_addr.s_addr == 0) + { + std::ostringstream out; + out << "pqissludp::Initiate_Connection() "; + out << "Invalid (0.0.0.0) Remote Address,"; + out << " Aborting Connect."; + out << std::endl; + rslog(RSL_WARNING, pqissludpzone, out.str()); + waiting = WAITING_FAIL_INTERFACE; + + reset(); + return -1; + } + + mTimeoutTS = time(NULL) + mConnectTimeout; + //std::cerr << "Setting Connect Timeout " << mConnectTimeout << " Seconds into Future " << std::endl; + //std::cerr << " Connect Period is:" << mConnectPeriod << std::endl; + + /* <===================== UDP Difference *******************/ + if (0 != (err = tou_connect(sockfd, (struct sockaddr *) &remote_addr, + sizeof(remote_addr), mConnectPeriod))) + /* <===================== UDP Difference *******************/ + { + int tou_err = tou_errno(sockfd); + + std::ostringstream out; + out << "pqissludp::Initiate_Connection()"; + + if ((tou_err == EINPROGRESS) || (tou_err == EAGAIN)) + { + // set state to waiting..... + waiting = WAITING_SOCK_CONNECT; + + out << " EINPROGRESS Waiting for Socket Connection"; + rslog(RSL_WARNING, pqissludpzone, out.str()); + + return 0; + } + else if ((tou_err == ENETUNREACH) || (tou_err == ETIMEDOUT)) + { + out << "ENETUNREACHABLE: cert: " << PeerId(); + out << std::endl; + + // Then send unreachable message. + waiting = WAITING_FAIL_INTERFACE; + net_unreachable |= net_attempt; + } + + out << "Error: Connection Failed: " << tou_err; + out << " - " << socket_errorType(tou_err) << std::endl; + + rslog(RSL_WARNING, pqissludpzone, out.str()); + + reset(); + + return -1; + } + else + { + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::Init_Connection() connect returned 0"); + } + + waiting = WAITING_SOCK_CONNECT; + + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::Initiate_Connection() Waiting for Socket Connect"); + + return 1; +} + +/********* VERY DIFFERENT **********/ +int pqissludp::Basic_Connection_Complete() +{ + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::Basic_Connection_Complete()..."); + + + if (time(NULL) > mTimeoutTS) + { + std::ostringstream out; + out << "pqissludp::Basic_Connection_Complete() Connection Timed Out. "; + out << "Peer: " << PeerId() << " Period: "; + out << mConnectTimeout; + + rslog(RSL_WARNING, pqissludpzone, out.str()); + + /* as sockfd is valid, this should close it all up */ + + reset(); + return -1; + } + + + if (waiting != WAITING_SOCK_CONNECT) + { + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::Basic_Connection_Complete() Wrong Mode"); + return -1; + } + + + /* new approach is to check for an error */ + /* check for an error */ + int err; + if (0 != (err = tou_errno(sockfd))) + { + if (err == EINPROGRESS) + { + + std::ostringstream out; + out << "pqissludp::Basic_Connection_Complete() "; + out << "EINPROGRESS: cert: " << PeerId(); + rslog(RSL_DEBUG_BASIC, pqissludpzone, out.str()); + + } + else if ((err == ENETUNREACH) || (err == ETIMEDOUT)) + { + std::ostringstream out; + out << "pqissludp::Basic_Connection_Complete() "; + out << "ENETUNREACH/ETIMEDOUT: cert: "; + out << PeerId(); + rslog(RSL_WARNING, pqissludpzone, out.str()); + + /* is the second one needed? */ + std::ostringstream out2; + out2 << "pqissludp::Basic_Connection_Complete() "; + out2 << "Error: Connection Failed: " << err; + out2 << " - " << socket_errorType(err); + rslog(RSL_DEBUG_BASIC, pqissludpzone, out2.str()); + + net_unreachable |= net_attempt; + + reset(); + + // Then send unreachable message. + waiting = WAITING_FAIL_INTERFACE; + + return -1; + } + } + + /* <===================== UDP Difference *******************/ + if (tou_connected(sockfd)) + /* <===================== UDP Difference *******************/ + { + std::ostringstream out; + out << "pqissludp::Basic_Connection_Complete() "; + out << "Connection Complete: cert: "; + out << PeerId(); + rslog(RSL_WARNING, pqissludpzone, out.str()); + + return 1; + } + else + { + // not ready return -1; + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::Basic_Connection_Complete() Not Yet Ready!"); + return 0; + } + + return -1; +} + + +/* New Internal Functions required to generalise tcp/udp version + * of the programs + */ + +// used everywhere +int pqissludp::net_internal_close(int fd) +{ + rslog(RSL_ALERT, pqissludpzone, + "pqissludp::net_internal_close() -> tou_close()"); + return tou_close(fd); +} + +// install udp BIO. +int pqissludp::net_internal_SSL_set_fd(SSL *ssl, int fd) +{ + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::net_internal_SSL_set_fd()"); + + /* create the bio's */ + tou_bio =BIO_new(BIO_s_tou_socket()); + + /* attach the fd's to the BIO's */ + BIO_set_fd(tou_bio, fd, BIO_NOCLOSE); + SSL_set_bio(ssl, tou_bio, tou_bio); + return 1; +} + +int pqissludp::net_internal_fcntl_nonblock(int fd) +{ + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::net_internal_fcntl_nonblock()"); + return 0; +} + + +/* These are identical to pqinetssl version */ +//int pqissludp::status() + +int pqissludp::tick() +{ + pqissl::tick(); + return 1; +} + + // listen fns call the udpproxy. +int pqissludp::listen() +{ + { + std::ostringstream out; + out << "pqissludp::listen() (NULLOP)"; + rslog(RSL_DEBUG_BASIC, pqissludpzone, out.str()); + } + return 1; //udpproxy->listen(); +} + +int pqissludp::stoplistening() +{ + { + std::ostringstream out; + out << "pqissludp::stoplistening() (NULLOP)"; + rslog(RSL_DEBUG_BASIC, pqissludpzone, out.str()); + } + return 1; //udpproxy->stoplistening(); +} + + +bool pqissludp::connect_parameter(uint32_t type, uint32_t value) +{ + //std::cerr << "pqissludp::connect_parameter() type: " << type << "value: " << value << std::endl; + if (type == NET_PARAM_CONNECT_PERIOD) + { + std::ostringstream out; + out << "pqissludp::connect_parameter() Peer: " << PeerId() << " PERIOD: " << value; + rslog(RSL_WARNING, pqissludpzone, out.str()); + + mConnectPeriod = value; + return true; + } + return pqissl::connect_parameter(type, value); +} + +/********** PQI STREAMER OVERLOADING *********************************/ + +bool pqissludp::moretoread() +{ + { + std::ostringstream out; + out << "pqissludp::moretoread()"; + out << " polling socket (" << sockfd << ")"; + rslog(RSL_DEBUG_ALL, pqissludpzone, out.str()); + } + + /* check for more to read first ... if nothing... check error + */ + /* <===================== UDP Difference *******************/ + if (tou_maxread(sockfd)) + /* <===================== UDP Difference *******************/ + { + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::moretoread() Data to Read!"); + return 1; + } + + /* else check the error */ + rslog(RSL_DEBUG_ALL, pqissludpzone, + "pqissludp::moretoread() No Data to Read!"); + + int err; + if (0 != (err = tou_errno(sockfd))) + { + if ((err == EAGAIN) || (err == EINPROGRESS)) + { + + std::ostringstream out; + out << "pqissludp::moretoread() "; + out << "EAGAIN/EINPROGRESS: cert " << PeerId(); + rslog(RSL_DEBUG_BASIC, pqissludpzone, out.str()); + return 0; + + } + else if ((err == ENETUNREACH) || (err == ETIMEDOUT)) + { + std::ostringstream out; + out << "pqissludp::moretoread() "; + out << "ENETUNREACH/ETIMEDOUT: cert "; + out << PeerId(); + rslog(RSL_WARNING, pqissludpzone, out.str()); + + } + else if (err == EBADF) + { + std::ostringstream out; + out << "pqissludp::moretoread() "; + out << "EBADF: cert "; + out << PeerId(); + rslog(RSL_WARNING, pqissludpzone, out.str()); + + } + else + { + std::ostringstream out; + out << "pqissludp::moretoread() "; + out << " Unknown ERROR: " << err << ": cert "; + out << PeerId(); + rslog(RSL_WARNING, pqissludpzone, out.str()); + + } + + reset(); + return 0; + } + + /* otherwise - not error - strange! */ + rslog(RSL_DEBUG_BASIC, pqissludpzone, + "pqissludp::moretoread() No Data + No Error (really nothing)"); + + return 0; + + +} + +bool pqissludp::cansend() +{ + rslog(RSL_DEBUG_ALL, pqissludpzone, + "pqissludp::cansend() polling socket!"); + + /* <===================== UDP Difference *******************/ + return (0 < tou_maxwrite(sockfd)); + /* <===================== UDP Difference *******************/ + +} + + + diff --git a/libretroshare/src/_pqi/pqissludp.h b/libretroshare/src/_pqi/pqissludp.h new file mode 100644 index 000000000..d31befacd --- /dev/null +++ b/libretroshare/src/_pqi/pqissludp.h @@ -0,0 +1,104 @@ +/* + * "$Id: pqissludp.h,v 1.8 2007-02-18 21:46:49 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQISSLUDP_H +#define PQISSLUDP_H + +#include + +// operating system specific network header. +#include "pqi/pqinetwork.h" + +#include +#include + +#include "_pqi/pqissl.h" + + /* So pqissludp is the special firewall breaking protocol. + * This class will implement the basics of streaming + * ssl over udp using a tcponudp library.... + * and a small extension to ssl. + */ + +class pqissludp; +class cert; + +/* This provides a NetBinInterface, which is + * primarily inherited from pqissl. + * fns declared here are different -> all others are identical. + */ + +class pqissludp: public pqissl +{ +public: + pqissludp(PQInterface *parent, p3AuthMgr *am, p3ConnectMgr *cm); + + virtual ~pqissludp(); + + // NetInterface. + // listen fns call the udpproxy. + virtual int listen(); + virtual int stoplistening(); + virtual int tick(); + virtual int reset(); + + virtual bool connect_parameter(uint32_t type, uint32_t value); + + // BinInterface. + // These are reimplemented. + virtual bool moretoread(); + virtual bool cansend(); + /* UDP always through firewalls -> always bandwidth Limited */ + virtual bool bandwidthLimited() { return true; } + + // pqissludp specific. + // called to initiate a connection; + int attach(); + +protected: + + virtual int Initiate_Connection(); + virtual int Basic_Connection_Complete(); + + //protected internal fns that are overloaded for udp case. + virtual int net_internal_close(int fd); + virtual int net_internal_SSL_set_fd(SSL *ssl, int fd); + virtual int net_internal_fcntl_nonblock(int fd); + +private: + + BIO *tou_bio; // specific to ssludp. + + //int remote_timeout; + //int proxy_timeout; + + long listen_checktime; + + uint32_t mConnectPeriod; +}; + +#endif // PQISSLUDP_H diff --git a/libretroshare/src/_pqi/pqistore.cc b/libretroshare/src/_pqi/pqistore.cc new file mode 100644 index 000000000..deea342e7 --- /dev/null +++ b/libretroshare/src/_pqi/pqistore.cc @@ -0,0 +1,378 @@ +/* + * "$Id: pqistore.cc,v 1.5 2007-03-21 18:45:41 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + + +/* This is dependent on the sslroot at the moment. + * -> as we need to create/restore references to the Person. + * -> and store the signatures to do this. + */ + +/******************************************************************* + * pqistore provides an store stream. + * This stores RsItem + Person Reference + Timestamp, + * + * and allows Objects to be replayed or restored, + * independently of the rest of the pqi system. + * + */ + +#include "_pqi/pqistore.h" +#include "_serialiser/rsserial.h" +#include +#include + +#include +#include "_util/rsdebug.h" + +const int pqistorezone = 9511; + +pqistore::pqistore(RsSerialiser *rss, std::string srcId, BinInterface *bio_in, int bio_flags_in) + :PQInterface(""), rsSerialiser(rss), bio(bio_in), bio_flags(bio_flags_in), + nextPkt(NULL), mSrcId(srcId) +{ + { + std::ostringstream out; + out << "pqistore::pqistore()"; + out << " Initialisation!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistorezone, out.str()); + } + + if (!bio_in) + { + std::ostringstream out; + out << "pqistore::pqistore()"; + out << " NULL bio, FATAL ERROR!" << std::endl; + pqioutput(PQL_ALERT, pqistorezone, out.str()); + exit(1); + } + + return; +} + +pqistore::~pqistore() +{ + { + std::ostringstream out; + out << "pqistore::~pqistore()"; + out << " Destruction!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistorezone, out.str()); + } + + if (bio_flags & BIN_FLAGS_NO_CLOSE) + { + std::ostringstream out; + out << "pqistore::~pqistore()"; + out << " Not Closing BinInterface!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistorezone, out.str()); + } + else if (bio) + { + std::ostringstream out; + out << "pqistore::~pqistore()"; + out << " Deleting BinInterface!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistorezone, out.str()); + + delete bio; + } + + if (rsSerialiser) + delete rsSerialiser; + + if (nextPkt) + { + delete nextPkt; + } + return; +} + + +// Get/Send Items. +int pqistore::SendItem(RsItem *si) +{ + { + std::ostringstream out; + out << "pqistore::SendItem()" << std::endl; + si -> print(out); + pqioutput(PQL_DEBUG_BASIC, pqistorezone, out.str()); + } + + // check if this is a writing bio. + + if (!(bio_flags & BIN_FLAGS_WRITEABLE)) + { + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete si; + return -1; + } + +// std::cerr << "SendItem: si->PeerId()=" << si->PeerId() << std::endl ; + + int ret = writePkt(si); + return ret; /* 0 - failure, 1 - success*/ +} + +RsItem *pqistore::GetItem() +{ + { + std::ostringstream out; + out << "pqistore::GetItem()"; + pqioutput(PQL_DEBUG_ALL, pqistorezone, out.str()); + } + + // check if this is a reading bio. + if (!(bio_flags & BIN_FLAGS_READABLE)) + { + std::ostringstream out; + out << "pqistore::GetItem()"; + out << "Error Not Readable" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqistorezone, out.str()); + return NULL; + } + + // load if we dont have a packet. + if (!nextPkt) + { + if (!readPkt(&nextPkt)) + { + std::ostringstream out; + out << "pqistore::GetItem()"; + out << "Failed to ReadPkt" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqistorezone, out.str()); + return NULL; + } + } + + if (!nextPkt) return NULL; + + RsItem *outPkt = nextPkt; + nextPkt = NULL; + + if (outPkt != NULL) + { + std::ostringstream out; + out << "pqistore::GetItem() Returning:" << std::endl; + outPkt -> print(out); + pqioutput(PQL_DEBUG_BASIC, pqistorezone, out.str()); + } + return outPkt; +} + +// // PQInterface +int pqistore::tick() +{ + { + std::ostringstream out; + out << "pqistore::tick()"; + out << std::endl; + } + return 0; +} + +int pqistore::status() +{ + { + std::ostringstream out; + out << "pqistore::status()"; + pqioutput(PQL_DEBUG_ALL, pqistorezone, out.str()); + } + return 0; +} + +// +/**************** HANDLE OUTGOING TRANSLATION + TRANSMISSION ******/ + +int pqistore::writePkt(RsItem *pqi) +{ +// std::cerr << "writePkt, pqi->peerId()=" << pqi->PeerId() << std::endl ; + { + std::ostringstream out; + out << "pqistore::writePkt()"; + pqioutput(PQL_DEBUG_ALL, pqistorezone, out.str()); + } + + uint32_t pktsize = rsSerialiser->size(pqi); + void *ptr = malloc(pktsize); + if (!(rsSerialiser->serialise(pqi, ptr, &pktsize))) + { + std::ostringstream out; + out << "pqistore::writePkt() Null Pkt generated!"; + out << std::endl; + out << "Caused By: " << std::endl; + pqi -> print(out); + pqioutput(PQL_ALERT, pqistorezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + return 0; + } + + /* extract the extra details */ + uint32_t len = getRsItemSize(ptr); + if (len != pktsize) + { + std::ostringstream out; + out << "pqistore::writePkt() Length MisMatch: len: " << len; + out << " != pktsize: " << pktsize; + out << std::endl; + out << "Caused By: " << std::endl; + pqi -> print(out); + pqioutput(PQL_ALERT, pqistorezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + return 0; + } + + + if (!(bio->cansend())) + { + std::ostringstream out; + out << "pqistore::writePkt() BIO cannot write!"; + out << std::endl; + out << "Discarding: " << std::endl; + pqi -> print(out); + pqioutput(PQL_ALERT, pqistorezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + + return 0; + } + + std::ostringstream out; + out << "Writing Pkt Body" << std::endl; + // write packet. + if (len != bio->senddata(ptr, len)) + { + out << "Problems with Send Data!"; + out << std::endl; + pqioutput(PQL_ALERT, pqistorezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + + return 0; + } + + out << " Success!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqistorezone, out.str()); + + free(ptr); + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + delete pqi; + + return 1; +} + +/* Reads a single packet from the input stream + * gets the timestamp as well. + */ + +int pqistore::readPkt(RsItem **item_out) +{ + { + std::ostringstream out; + out << "pqistore::readPkt()"; + pqioutput(PQL_DEBUG_ALL, pqistorezone, out.str()); + } + + if ((!(bio->isactive())) || (!(bio->moretoread()))) + { + return 0; + } + + // enough space to read any packet. + int maxlen = getRsPktMaxSize(); + void *block = malloc(maxlen); + + // initial read size: basic packet. + int blen = getRsPktBaseSize(); + + int tmplen; + /* we have the header */ + + // read the basic block (minimum packet size) + if (blen != (tmplen = bio->readdata(block, blen))) + { + pqioutput(PQL_WARNING, pqistorezone, + "pqistore::readPkt() bad read(2)"); + + free(block); + return 0; + } + + // workout how much more to read. + int extralen = getRsItemSize(block) - blen; + if (extralen > 0) + { + void *extradata = (void *) (((char *) block) + blen); + if (extralen != (tmplen = bio->readdata(extradata, extralen))) + { + + std::ostringstream out; + out << "pqistore::readPkt() "; + out << "Error Completing Read (read "; + out << tmplen << "/" << extralen << ")" << std::endl; + pqioutput(PQL_ALERT, pqistorezone, out.str()); + + free(block); + return 0; + } + } + + // create packet, based on header. + std::cerr << "Read Data Block -> Incoming Pkt("; + std::cerr << blen + extralen << ")" << std::endl; + uint32_t readbytes = extralen + blen; + + RsItem *item = rsSerialiser->deserialise(block, &readbytes); + free(block); + + if (item == NULL) + { + pqioutput(PQL_ALERT, pqistorezone, + "pqistore::readPkt() Failed to create Item from store!"); + return 0; + } + + item->PeerId(mSrcId); + *item_out = item; + return 1; +} + +/**** Hashing Functions ****/ +std::string pqistore::gethash() +{ + return bio->gethash(); +} + + + diff --git a/libretroshare/src/_pqi/pqistore.h b/libretroshare/src/_pqi/pqistore.h new file mode 100644 index 000000000..dede0706f --- /dev/null +++ b/libretroshare/src/_pqi/pqistore.h @@ -0,0 +1,72 @@ +/* + * pqistore.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQISTORE_H +#define PQISTORE_H + +#include "_pqi/pqi.h" + +#include + +/******************************************************************* + * pqistore provides a stream to file. + * objects only - like pqistreamer as opposed to pqiarchive. + * + */ + +class pqistore: public PQInterface +{ +public: + pqistore(RsSerialiser *rss, std::string srcId, BinInterface *bio_in, int bio_flagsin); + virtual ~pqistore(); + + // PQInterface + virtual int SendItem(RsItem *); + virtual RsItem *GetItem(); + + virtual int tick(); + virtual int status(); + + std::string gethash(); + +private: + int writePkt(RsItem *item); + int readPkt(RsItem **item_out); + + // Serialiser + RsSerialiser *rsSerialiser; + // Binary Interface for IO, initialisated at startup. + BinInterface *bio; + unsigned int bio_flags; // only BIN_NO_CLOSE at the moment. + + // Temp Storage for transient data..... + RsItem *nextPkt; + std::string mSrcId; +}; + + +#endif //PQISTORE_H diff --git a/libretroshare/src/_pqi/pqistreamer.cc b/libretroshare/src/_pqi/pqistreamer.cc new file mode 100644 index 000000000..cc1062fe3 --- /dev/null +++ b/libretroshare/src/_pqi/pqistreamer.cc @@ -0,0 +1,910 @@ +/* + * "$Id: pqistreamer.cc,v 1.19 2007-02-18 21:46:50 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 +#include "_util/rsdebug.h" + +#include "_pqi/pqistreamer.h" +#include "_pqi/pqinotify.h" + +#include "_serialiser/rsserial.h" +#include "_serialiser/rsbaseitems.h" /***** For RsFileData *****/ + + +const int pqistreamerzone = 8221; + +const int PQISTREAM_ABS_MAX = 100000000; /* 100 MB/sec (actually per loop) */ + +/* This removes the print statements (which hammer pqidebug) */ +/*** +#define RSITEM_DEBUG 1 + ***/ + + +pqistreamer::pqistreamer(RsSerialiser *rss, std::string id, BinInterface *bio_in, int bio_flags_in) + :PQInterface(id), rsSerialiser(rss), bio(bio_in), bio_flags(bio_flags_in), + pkt_wpending(NULL), + totalRead(0), totalSent(0), + currRead(0), currSent(0), + avgReadCount(0), avgSentCount(0) +{ + avgLastUpdate = currReadTS = currSentTS = time(NULL); + + /* allocated once */ + pkt_rpend_size = getRsPktMaxSize(); + pkt_rpending = malloc(pkt_rpend_size); + reading_state = reading_state_initial ; + +// thread_id = pthread_self() ; + // avoid uninitialized (and random) memory read. + memset(pkt_rpending,0,pkt_rpend_size) ; + + // 100 B/s (minimal) + setMaxRate(true, 0.1); + setMaxRate(false, 0.1); + setRate(true, 0); + setRate(false, 0); + + { + std::ostringstream out; + out << "pqistreamer::pqistreamer()"; + out << " Initialisation!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + if (!bio_in) + { + std::ostringstream out; + out << "pqistreamer::pqistreamer()"; + out << " NULL bio, FATAL ERROR!" << std::endl; + pqioutput(PQL_ALERT, pqistreamerzone, out.str()); + exit(1); + } + + failed_read_attempts = 0 ; // reset failed read, as no packet is still read. + + return; +} + +pqistreamer::~pqistreamer() +{ + RsStackMutex stack(streamerMtx) ; // lock out_pkt and out_data + + { + std::ostringstream out; + out << "pqistreamer::~pqistreamer()"; + out << " Destruction!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + if (bio_flags & BIN_FLAGS_NO_CLOSE) + { + std::ostringstream out; + out << "pqistreamer::~pqistreamer()"; + out << " Not Closing BinInterface!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + else if (bio) + { + std::ostringstream out; + out << "pqistreamer::~pqistreamer()"; + out << " Deleting BinInterface!" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + + delete bio; + } + + /* clean up serialiser */ + if (rsSerialiser) + delete rsSerialiser; + + // clean up outgoing. (cntrl packets) + while(out_pkt.size() > 0) + { + void *pkt = out_pkt.front(); + out_pkt.pop_front(); + free(pkt); + } + + // clean up outgoing (data packets) + while(out_data.size() > 0) + { + void *pkt = out_data.front(); + out_data.pop_front(); + free(pkt); + } + + if (pkt_wpending) + { + free(pkt_wpending); + pkt_wpending = NULL; + } + + free(pkt_rpending); + + // clean up outgoing. + while(incoming.size() > 0) + { + RsItem *i = incoming.front(); + incoming.pop_front(); + delete i; + } + return; +} + + +// Get/Send Items. +int pqistreamer::SendItem(RsItem *si) +{ +#ifdef RSITEM_DEBUG + { + std::ostringstream out; + out << "pqistreamer::SendItem():" << std::endl; + si -> print(out); + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } +#endif + + return queue_outpqi(si); +} + +RsItem *pqistreamer::GetItem() +{ + { + std::ostringstream out; + out << "pqistreamer::GetItem()"; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + std::list::iterator it; + + it = incoming.begin(); + if (it == incoming.end()) { return NULL; } + + RsItem *osr = (*it); + incoming.erase(it); + return osr; +} + +// // PQInterface +int pqistreamer::tick() +{ +// std::cerr << "enterign tick, state = " << reading_state << std::endl ; + { + std::ostringstream out; + out << "pqistreamer::tick()"; + out << std::endl; + out << PeerId() << ": currRead/Sent: " << currRead << "/" << currSent; + out << std::endl; + + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + +// std::cerr << "calling bio-> tick, state = " << reading_state << std::endl ; + bio->tick(); +// std::cerr << "after bio-> tick, state = " << reading_state << std::endl ; + + /* short circuit everything is bio isn't active */ + if (!(bio->isactive())) + { + return 0; + } + + + /* must do both, as outgoing will catch some bad sockets, + * that incoming will not + */ + +// std::cerr << "calling handle incoming, state = " << reading_state << std::endl ; + handleincoming(); +// std::cerr << "returned from handle incoming, state = " << reading_state << std::endl ; + handleoutgoing(); +// std::cerr << "returned from handle outgoing, state = " << reading_state << std::endl ; + + /* give details of the packets */ + { + std::list::iterator it; + + std::ostringstream out; + out << "pqistreamer::tick() Queued Data:"; + out << " for " << PeerId(); + + if (bio->isactive()) + { + out << " (active)"; + } + else + { + out << " (waiting)"; + } + out << std::endl; + + { + RsStackMutex stack(streamerMtx) ; // lock out_pkt and out_data + int total = 0; + + for(it = out_pkt.begin(); it != out_pkt.end(); it++) + { + total += getRsItemSize(*it); + } + + out << "\t Out Packets [" << out_pkt.size() << "] => " << total; + out << " bytes" << std::endl; + + total = 0; + for(it = out_data.begin(); it != out_data.end(); it++) + { + total += getRsItemSize(*it); + } + + out << "\t Out Data [" << out_data.size() << "] => " << total; + out << " bytes" << std::endl; + + out << "\t Incoming [" << incoming.size() << "]"; + out << std::endl; + } + + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, out.str()); + } + + /* if there is more stuff in the queues */ + if ((incoming.size() > 0) || (out_pkt.size() > 0) || (out_data.size() > 0)) + { + return 1; + } + return 0; +} + +int pqistreamer::status() +{ + { + std::ostringstream out; + out << "pqistreamer::status()"; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + if (bio->isactive()) + { + std::ostringstream out; + out << "Data in:" << totalRead << " out:" << totalSent; + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, out.str()); + } + + return 0; +} + +// +/**************** HANDLE OUTGOING TRANSLATION + TRANSMISSION ******/ + +int pqistreamer::queue_outpqi(RsItem *pqi) +{ + RsStackMutex stack(streamerMtx) ; // lock out_pkt and out_data + + // This is called by different threads, and by threads that are not the handleoutgoing thread, + // so it should be protected by a mutex !! + + + { + std::ostringstream out; + out << "pqistreamer::queue_outpqi()"; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + /* decide which type of packet it is */ + RsFileData *dta = dynamic_cast(pqi); + bool isCntrl = (dta == NULL); + +// std::cerr << "Thread (queue_outpqi): thread = " << pthread_self() << "isCntrl=" << isCntrl << std::endl ; + + uint32_t pktsize = rsSerialiser->size(pqi); + void *ptr = malloc(pktsize); + +// std::cerr << "serializing packet of size " << pktsize << std::endl ; + if (rsSerialiser->serialise(pqi, ptr, &pktsize)) + { + if (isCntrl) + { + out_pkt.push_back(ptr); + } + else + { + out_data.push_back(ptr); + } + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + { + delete pqi; + } + return 1; + } + else + { + /* cleanup serialiser */ + free(ptr); + } + + std::ostringstream out; + out << "pqistreamer::queue_outpqi() Null Pkt generated!"; + out << std::endl; + out << "Caused By: " << std::endl; + pqi -> print(out); + pqioutput(PQL_ALERT, pqistreamerzone, out.str()); + + if (!(bio_flags & BIN_FLAGS_NO_DELETE)) + { + delete pqi; + } + return 1; // keep error internal. +} + +int pqistreamer::handleincomingitem(RsItem *pqi) +{ + { + std::ostringstream out; + out << "pqistreamer::handleincomingitem()"; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + // Use overloaded Contact function + pqi -> PeerId(PeerId()); + incoming.push_back(pqi); + return 1; +} + +int pqistreamer::handleoutgoing() +{ + RsStackMutex stack(streamerMtx) ; // lock out_pkt and out_data + + { + std::ostringstream out; + out << "pqistreamer::handleoutgoing()"; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + int maxbytes = outAllowedBytes(); + int sentbytes = 0; + int len; + int ss; + // std::cerr << "pqistreamer: maxbytes=" << maxbytes<< std::endl ; + + std::list::iterator it; + + // if not connection, or cannot send anything... pause. + if (!(bio->isactive())) + { + /* if we are not active - clear anything in the queues. */ + for(it = out_pkt.begin(); it != out_pkt.end(); ) + { + free(*it); + it = out_pkt.erase(it); + + std::ostringstream out; + out << "pqistreamer::handleoutgoing() Not active -> Clearing Pkt!"; + // std::cerr << out.str() ; + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, out.str()); + } + for(it = out_data.begin(); it != out_data.end(); ) + { + free(*it); + it = out_data.erase(it); + + std::ostringstream out; + out << "pqistreamer::handleoutgoing() Not active -> Clearing DPkt!"; + // std::cerr << out.str() ; + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, out.str()); + } + + /* also remove the pending packets */ + if (pkt_wpending) + { + free(pkt_wpending); + pkt_wpending = NULL; + } + + outSentBytes(sentbytes); + return 0; + } + + // a very simple round robin + + bool sent = true; + while(sent) // catch if all items sent. + { + sent = false; + + if ((!(bio->cansend())) || (maxbytes < sentbytes)) + { + outSentBytes(sentbytes); + return 0; + } + + // send a out_pkt., else send out_data. unless + // there is a pending packet. + if (!pkt_wpending) + { + if (out_pkt.size() > 0) + { + pkt_wpending = *(out_pkt.begin()); + out_pkt.pop_front(); + } + else if (out_data.size() > 0) + { + pkt_wpending = *(out_data.begin()); + out_data.pop_front(); + } + } + + if (pkt_wpending) + { + std::ostringstream out; + // write packet. + len = getRsItemSize(pkt_wpending); + +// std::cout << "Sending Out Pkt of size " << len << " !" << std::endl ; + + if (len != (ss = bio->senddata(pkt_wpending, len))) + { + out << "Problems with Send Data! (only " << ss << " bytes sent" << ", total pkt size=" << len ; + out << std::endl; +// std::cerr << out.str() ; + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, out.str()); + + outSentBytes(sentbytes); + // pkt_wpending will kept til next time. + // ensuring exactly the same data is written (openSSL requirement). + return -1; + } + +// out << " Success!" << ", sent " << len << " bytes" << std::endl; + // std::cerr << out.str() ; + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, out.str()); + + free(pkt_wpending); + pkt_wpending = NULL; + + sentbytes += len; + sent = true; + } + } + outSentBytes(sentbytes); + return 1; +} + + +/* Handles reading from input stream. + */ +int pqistreamer::handleincoming() +{ + int readbytes = 0; + static const int max_failed_read_attempts = 2000 ; + + { + std::ostringstream out; + out << "pqistreamer::handleincoming()"; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + if(!(bio->isactive())) + { + reading_state = reading_state_initial ; + inReadBytes(readbytes); + return 0; + } + + // enough space to read any packet. + int maxlen = pkt_rpend_size; + void *block = pkt_rpending; + + // initial read size: basic packet. + int blen = getRsPktBaseSize(); + + int maxin = inAllowedBytes(); + +// std::cerr << "reading state = " << reading_state << std::endl ; + switch(reading_state) + { + case reading_state_initial: /*std::cerr << "jumping to start" << std::endl; */ goto start_packet_read ; + case reading_state_packet_started: /*std::cerr << "jumping to middle" << std::endl;*/ goto continue_packet ; + } + +start_packet_read: + { // scope to ensure variable visibility + // read the basic block (minimum packet size) + int tmplen; +// std::cerr << "starting packet" << std::endl ; + memset(block,0,blen) ; // reset the block, to avoid uninitialized memory reads. + + if (blen != (tmplen = bio->readdata(block, blen))) + { + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, "pqistreamer::handleincoming() Didn't read BasePkt!"); + + inReadBytes(readbytes); + + // error.... (either blocked or failure) + if (tmplen == 0) + { + // most likely blocked! + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, "pqistreamer::handleincoming() read blocked"); +// std::cerr << "given up 1" << std::endl ; + return 0; + } + else if (tmplen < 0) + { + // Most likely it is that the packet is pending but could not be read by pqissl because of stream flow. + // So we return without an error, and leave the machine state in 'start_read'. + // + //pqioutput(PQL_WARNING, pqistreamerzone, "pqistreamer::handleincoming() Error in bio read"); +// std::cerr << "given up 2, state = " << reading_state << std::endl ; + return 0; + } + else // tmplen > 0 + { + // strange case....This should never happen as partial reads are handled by pqissl below. + std::ostringstream out; + out << "pqistreamer::handleincoming() Incomplete "; + out << "(Strange) read of " << tmplen << " bytes"; + pqioutput(PQL_ALERT, pqistreamerzone, out.str()); +// std::cerr << "given up 3" << std::endl ; + return -1; + } + } +// std::cerr << "block 0 : " << (int)(((unsigned char*)block)[0]) << " " << (int)(((unsigned char*)block)[1]) << " " << (int)(((unsigned char*)block)[2]) << " " +// << (int)(((unsigned char*)block)[3]) << " " +// << (int)(((unsigned char*)block)[4]) << " " +// << (int)(((unsigned char*)block)[5]) << " " +// << (int)(((unsigned char*)block)[6]) << " " +// << (int)(((unsigned char*)block)[7]) << " " << std::endl ; + + readbytes += blen; + reading_state = reading_state_packet_started ; + failed_read_attempts = 0 ; // reset failed read, as the packet has been totally read. + } +continue_packet: + { + // workout how much more to read. + int extralen = getRsItemSize(block) - blen; + +// std::cerr << "continuing packet state=" << reading_state << std::endl ; +// std::cerr << "block 1 : " << (int)(((unsigned char*)block)[0]) << " " << (int)(((unsigned char*)block)[1]) << " " << (int)(((unsigned char*)block)[2]) << " " << (int)(((unsigned char*)block)[3]) +// << (int)(((unsigned char*)block)[4]) << " " +// << (int)(((unsigned char*)block)[5]) << " " +// << (int)(((unsigned char*)block)[6]) << " " +// << (int)(((unsigned char*)block)[7]) << " " << std::endl ; + if (extralen > maxlen - blen) + { + pqioutput(PQL_ALERT, pqistreamerzone, "ERROR: Read Packet too Big!"); + + pqiNotify *notify = getPqiNotify(); + if (notify) + { + std::string title = + "Warning: Bad Packet Read"; + + std::ostringstream msgout; + msgout << " **** WARNING **** \n"; + msgout << "Retroshare has caught a BAD Packet Read"; + msgout << "\n"; + msgout << "This is normally caused by connecting to an"; + msgout << " OLD version of Retroshare"; + msgout << "\n"; + msgout << "(M:" << maxlen << " B:" << blen << " E:" << extralen << ")\n"; + msgout << "\n"; + msgout << "block = " + << (int)(((unsigned char*)block)[0]) << " " + << (int)(((unsigned char*)block)[1]) << " " + << (int)(((unsigned char*)block)[2]) << " " + << (int)(((unsigned char*)block)[3]) << "\n" ; + msgout << "\n"; + msgout << "Please get your friends to upgrade to the latest version"; + msgout << "\n"; + msgout << "\n"; + msgout << "If you are sure the error was not caused by an old version"; + msgout << "\n"; + msgout << "Please report the problem to Retroshare's developers"; + msgout << "\n"; + + std::string msg = msgout.str(); + notify->AddLogMessage(0, RS_SYS_WARNING, title, msg); + } + bio->close(); + reading_state = reading_state_initial ; // restart at state 1. + failed_read_attempts = 0 ; + return -1; + + // Used to exit now! exit(1); + } + + if (extralen > 0) + { + void *extradata = (void *) (((char *) block) + blen); + int tmplen ; + memset((void*)( &(((unsigned char *)block)[blen])),0,extralen) ; // reset the block, to avoid uninitialized memory reads. + + memset( extradata,0,extralen ) ; // for checking later + + if (extralen != (tmplen = bio->readdata(extradata, extralen))) + { + if(tmplen > 0) + std::cerr << "Incomplete packet read ! This is a real problem ;-)" << std::endl ; + + if(++failed_read_attempts > max_failed_read_attempts) + { + std::ostringstream out; + out << "Error Completing Read (read "; + out << tmplen << "/" << extralen << ")" << std::endl; + std::cerr << out.str() ; + pqioutput(PQL_ALERT, pqistreamerzone, out.str()); + + pqiNotify *notify = getPqiNotify(); + if (notify) + { + std::string title = "Warning: Error Completing Read"; + + std::ostringstream msgout; + msgout << " **** WARNING **** \n"; + msgout << "Retroshare has experienced an unexpected Read ERROR"; + msgout << "\n"; + msgout << "(M:" << maxlen << " B:" << blen; + msgout << " E:" << extralen << " R:" << tmplen << ")\n"; + msgout << "\n"; + msgout << "Please contact the developers."; + msgout << "\n"; + + std::string msg = msgout.str(); + std::cerr << msg << std::endl ; + std::cerr << "block = " + << (int)(((unsigned char*)block)[0]) << " " + << (int)(((unsigned char*)block)[1]) << " " + << (int)(((unsigned char*)block)[2]) << " " + << (int)(((unsigned char*)block)[3]) << " " + << (int)(((unsigned char*)block)[4]) << " " + << (int)(((unsigned char*)block)[5]) << " " + << (int)(((unsigned char*)block)[6]) << " " + << (int)(((unsigned char*)block)[7]) << " " + << std::endl ; + // notify->AddSysMessage(0, RS_SYS_WARNING, title, msg); + } + + bio->close(); + reading_state = reading_state_initial ; // restart at state 1. + failed_read_attempts = 0 ; + return -1; + } + else + { +// std::cerr << "given up 5, state = " << reading_state << std::endl ; + return 0 ; // this is just a SSL_WANT_READ error. Don't panic, we'll re-try the read soon. + // we assume readdata() returned either -1 or the complete read size. + } + } + + failed_read_attempts = 0 ; + readbytes += extralen; + } + + // create packet, based on header. + { + std::ostringstream out; + out << "Read Data Block -> Incoming Pkt("; + out << blen + extralen << ")" << std::endl; + // std::cerr << out.str() ; + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, out.str()); + } + + // std::cerr << "Deserializing packet of size " << pktlen <deserialise(block, &pktlen); + + if ((pkt != NULL) && (0 < handleincomingitem(pkt))) + pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, "Successfully Read a Packet!"); + else + pqioutput(PQL_ALERT, pqistreamerzone, "Failed to handle Packet!"); + + reading_state = reading_state_initial ; // restart at state 1. + failed_read_attempts = 0 ; // reset failed read, as the packet has been totally read. + } + + if(maxin > readbytes && bio->moretoread()) + goto start_packet_read ; + + inReadBytes(readbytes); + return 0; +} + + +/* BandWidth Management Assistance */ + +float pqistreamer::outTimeSlice() +{ + { + std::ostringstream out; + out << "pqistreamer::outTimeSlice()"; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + //fixme("pqistreamer::outTimeSlice()", 1); + return 1; +} + +// very simple..... +int pqistreamer::outAllowedBytes() +{ + int t = time(NULL); // get current timestep. + + /* allow a lot if not bandwidthLimited */ + if (!bio->bandwidthLimited()) + { + currSent = 0; + currSentTS = t; + return PQISTREAM_ABS_MAX; + } + + int dt = t - currSentTS; + // limiter -> for when currSentTs -> 0. + if (dt > 5) + dt = 5; + + int maxout = (int) (getMaxRate(false) * 1000.0); + currSent -= dt * maxout; + if (currSent < 0) + { + currSent = 0; + } + + currSentTS = t; + + { + std::ostringstream out; + out << "pqistreamer::outAllowedBytes() is "; + out << maxout - currSent << "/"; + out << maxout; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + + return maxout - currSent; +} + +int pqistreamer::inAllowedBytes() +{ + int t = time(NULL); // get current timestep. + + /* allow a lot if not bandwidthLimited */ + if (!bio->bandwidthLimited()) + { + currReadTS = t; + currRead = 0; + return PQISTREAM_ABS_MAX; + } + + int dt = t - currReadTS; + // limiter -> for when currReadTs -> 0. + if (dt > 5) + dt = 5; + + int maxin = (int) (getMaxRate(true) * 1000.0); + currRead -= dt * maxin; + if (currRead < 0) + { + currRead = 0; + } + + currReadTS = t; + + { + std::ostringstream out; + out << "pqistreamer::inAllowedBytes() is "; + out << maxin - currRead << "/"; + out << maxin; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + + return maxin - currRead; +} + + +static const float AVG_PERIOD = 5; // sec +static const float AVG_FRAC = 0.8; // for low pass filter. + +void pqistreamer::outSentBytes(int outb) +{ + { + std::ostringstream out; + out << "pqistreamer::outSentBytes(): "; + out << outb << "@" << getRate(false) << "kB/s" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + + totalSent += outb; + currSent += outb; + avgSentCount += outb; + + int t = time(NULL); // get current timestep. + if (t - avgLastUpdate > AVG_PERIOD) + { + float avgReadpSec = getRate(true); + float avgSentpSec = getRate(false); + + avgReadpSec *= AVG_FRAC; + avgReadpSec += (1.0 - AVG_FRAC) * avgReadCount / + (1000.0 * (t - avgLastUpdate)); + + avgSentpSec *= AVG_FRAC; + avgSentpSec += (1.0 - AVG_FRAC) * avgSentCount / + (1000.0 * (t - avgLastUpdate)); + + + /* pretend our rate is zero if we are + * not bandwidthLimited(). + */ + if (bio->bandwidthLimited()) + { + setRate(true, avgReadpSec); + setRate(false, avgSentpSec); + } + else + { + setRate(true, 0); + setRate(false, 0); + } + + + avgLastUpdate = t; + avgReadCount = 0; + avgSentCount = 0; + } + return; +} + +void pqistreamer::inReadBytes(int inb) +{ + { + std::ostringstream out; + out << "pqistreamer::inReadBytes(): "; + out << inb << "@" << getRate(true) << "kB/s" << std::endl; + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, out.str()); + } + + totalRead += inb; + currRead += inb; + avgReadCount += inb; + + return; +} + diff --git a/libretroshare/src/_pqi/pqistreamer.h b/libretroshare/src/_pqi/pqistreamer.h new file mode 100644 index 000000000..9ce8982f1 --- /dev/null +++ b/libretroshare/src/_pqi/pqistreamer.h @@ -0,0 +1,116 @@ +/* + * libretroshare/src/pqi pqistreamer.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-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". + * + */ + + +#ifndef PQISTREAMER_H +#define PQISTREAMER_H + +// Only dependent on the base stuff. +#include "_pqi/pqi_base.h" +#include "_util/rsthreads.h" + +#include + +// Fully implements the PQInterface. +// and communicates with peer etc via the BinInterface. +// +// The interface does not handle connection, just communication. +// possible bioflags: BIN_FLAGS_NO_CLOSE | BIN_FLAGS_NO_DELETE + +class pqistreamer: public PQInterface +{ +public: + pqistreamer(RsSerialiser *rss, std::string peerid, BinInterface *bio_in, int bio_flagsin); + virtual ~pqistreamer(); + + // PQInterface + virtual int SendItem(RsItem *); + virtual RsItem *GetItem(); + + virtual int tick(); + virtual int status(); + +private: + /* Implementation */ + + // to filter functions - detect filecancel/data and act! + int queue_outpqi( RsItem *i); + int handleincomingitem(RsItem *i); + + // ticked regularly (manages out queues and sending + // via above interfaces. + int handleoutgoing(); + int handleincoming(); + + // Bandwidth/Streaming Management. + float outTimeSlice(); + + int outAllowedBytes(); + void outSentBytes(int ); + + int inAllowedBytes(); + void inReadBytes(int ); + + // RsSerialiser - determines which packets can be serialised. + RsSerialiser *rsSerialiser; + // Binary Interface for IO, initialisated at startup. + BinInterface *bio; + unsigned int bio_flags; // BIN_FLAGS_NO_CLOSE | BIN_FLAGS_NO_DELETE + + void *pkt_wpending; // storage for pending packet to write. + int pkt_rpend_size; // size of pkt_rpending. + void *pkt_rpending; // storage for read in pending packets. + + enum { reading_state_packet_started=1, + reading_state_initial=0 } ; + + int reading_state ; + int failed_read_attempts ; + + // Temp Storage for transient data..... + std::list out_pkt; // Cntrl / Search / Results queue + std::list out_data; // FileData - secondary queue. + std::list incoming; + + // data for network stats. + int totalRead; + int totalSent; + + // these are representative (but not exact) + int currRead; + int currSent; + int currReadTS; // TS from which these are measured. + int currSentTS; + + int avgLastUpdate; // TS from which these are measured. + float avgReadCount; + float avgSentCount; + + RsMutex streamerMtx ; + // pthread_t thread_id; +}; + + +#endif // PQISTREAMER_H diff --git a/libretroshare/src/_pqi/sslcert.cc b/libretroshare/src/_pqi/sslcert.cc new file mode 100644 index 000000000..eb93c6d7b --- /dev/null +++ b/libretroshare/src/_pqi/sslcert.cc @@ -0,0 +1,2031 @@ +/* + * Core PQI networking: sslcert.cc + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 "sslcert.h" + +#include "_pqi.h" +#include "_pqinetwork.h" + +#include +#include +#include + +#include +#include + +#include "pqidebug.h" + +const int pqisslrootzone = 1211; + + +// other fns +std::string getCertName(cert *c) +{ + std::string name = c -> certificate -> name; + // strip out bad chars. + for(int i = 0; i < (signed) name.length(); i++) + { + if ((name[i] == '/') || (name[i] == ' ') || (name[i] == '=') || + (name[i] == '\\') || (name[i] == '\t') || (name[i] == '\n')) + { + name[i] = '_'; + } + } + return name; +} + + + +int pem_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + strncpy(buf, (char *)(password), size); + buf[size - 1] = '\0'; + return(strlen(buf)); +} + + +/* This class handles openssl library init/destruct. + * only one of these... handles + * the CTX and setup? + * + * it will also handle the certificates..... + * mantaining a library of recieved certs, + * and ip addresses that the connections come from... + * + */ + +// the single instance of this. +static sslroot instance_sslroot; + +sslroot *getSSLRoot() +{ + return &instance_sslroot; +} + +sslroot::sslroot() + :sslctx(NULL), init(0), certsChanged(1), + certsMajorChanged(1), pkey(NULL) +{ +} + +int sslroot::active() +{ + return init; +} + +// args: server cert, server private key, trusted certificates. + +int sslroot::initssl(const char *cert_file, const char *priv_key_file, + const char *CA_FILE, const char *passwd) +{ +static int initLib = 0; + if (!initLib) + { + initLib = 1; + SSL_load_error_strings(); + SSL_library_init(); + } + + + if (init == 1) + { + return 1; + } + + if ((cert_file == NULL) || + (priv_key_file == NULL) || + (passwd == NULL)) + { + fprintf(stderr, "sslroot::initssl() missing parameters!\n"); + return 0; + } + + + SSL_load_error_strings(); + SSL_library_init(); + // XXX TODO + // actions_to_seed_PRNG(); + + pqioutput(PQL_WARNING, pqisslrootzone, "SSL Library Init!"); + + // setup connection method + sslctx = SSL_CTX_new(SSLv23_method()); + + // setup cipher lists. + SSL_CTX_set_cipher_list(sslctx, "DEFAULT"); + + // certificates (Set Local Server Certificate). + FILE *ownfp = fopen(cert_file, "r"); + if (ownfp == NULL) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "Couldn't open Own Certificate!"); + return -1; + } + + X509 *x509 = PEM_read_X509(ownfp, NULL, NULL, NULL); + fclose(ownfp); + if (x509 != NULL) + { + SSL_CTX_use_certificate(sslctx, x509); + own_cert = makeCertificate(x509); + if (own_cert == NULL) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "Failed to Make Own Cert!"); + return -1; + } + addCertificate(own_cert); + } + else + { + return -1; + } + + + // SSL_CTX_use_certificate_chain_file(sslctx, cert_file_chain); + + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, "SSL Set Chain File"); + + SSL_CTX_load_verify_locations(sslctx, CA_FILE, 0); + + // enable verification of certificates (PEER) + SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, "SSL Verification Set"); + + // setup private key + FILE *pkfp = fopen(priv_key_file, "rb"); + if (pkfp == NULL) + { + pqioutput(PQL_ALERT, pqisslrootzone, "Couldn't Open PrivKey File!"); + closessl(); + return -1; + } + + pkey = PEM_read_PrivateKey(pkfp, NULL, NULL, (void *) passwd); + + SSL_CTX_use_PrivateKey(sslctx, pkey); + + if (1 != SSL_CTX_check_private_key(sslctx)) + { + std::ostringstream out; + out << "Issues With Private Key! - Doesn't match your Cert" << std::endl; + out << "Check your input key/certificate:" << std::endl; + out << priv_key_file << " & " << cert_file; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + + closessl(); + return -1; + } + + + // Load CA for clients. + STACK_OF(X509_NAME) *cert_names; + cert_names = SSL_load_client_CA_file(CA_FILE); + + if (cert_names != NULL) + { + SSL_CTX_set_client_CA_list(sslctx, cert_names); + } + else + { + std::ostringstream out; + out << "Couldn't Load Client CA files!" << std::endl; + out << "Check That (" << CA_FILE << ") is valid"; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + closessl(); + return -1; + } + + /* configure basics on the certificate. */ + std::string tagname; // = "LOCL:"; + own_cert -> Name(tagname + getX509CNString(own_cert -> certificate -> cert_info -> subject)); + + init = 1; + return 1; +} + + + +int sslroot::closessl() +{ + SSL_CTX_free(sslctx); + + // clean up private key.... + // remove certificates etc -> opposite of initssl. + init = 0; + return 1; +} + +/* Context handling */ +SSL_CTX *sslroot::getCTX() +{ + return sslctx; +} + +int sslroot::setConfigDirs(const char *cdir, const char *ndir) +{ + certdir = cdir; + neighbourdir = ndir; + return 1; +} + +static const unsigned int OPT_LEN = 16; +static const unsigned int VAL_LEN = 1000; + +int sslroot::saveCertificates() +{ + if (certfile.length() > 1) + return saveCertificates(certfile.c_str()); + return -1; +} + + +int sslroot::saveCertificates(const char *fname) +{ + // construct file name. + // + // create the file in memory - hash + sign. + // write out data to a file. + + std::string neighdir = certdir + "/" + neighbourdir + "/"; + std::string configname = certdir + "/"; + configname += fname; + + std::map::iterator mit; + + + std::string conftxt; + std::string empty(""); + unsigned int i; + + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + std::string neighfile = neighdir + getCertName(*it) + ".pqi"; + savecertificate((*it), neighfile.c_str()); + conftxt += "CERT "; + conftxt += getCertName(*it); + conftxt += "\n"; + conftxt += (*it) -> Hash(); + conftxt += "\n"; + } + + // Now add the options. + for(mit = settings.begin(); mit != settings.end(); mit++) + { + // only save the nonempty settings. + if (mit -> second != empty) { + conftxt += "OPT "; + for(i = 0; (i < OPT_LEN) && (i < mit -> first.length()); i++) + { + conftxt += mit -> first[i]; + } + conftxt += "\n"; + for(i = 0; i < VAL_LEN; i++) + { + if (i < mit -> second.length()) + { + conftxt += mit -> second[i]; + } + else + { + conftxt += '\0'; + } + } + conftxt += "\n"; + } + } + + // now work out signature of it all. This relies on the + // EVP library of openSSL..... We are going to use signing + // for the moment. + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + //OpenSSL_add_all_digests(); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "EVP_SignInit Failure!"); + } + + if (0 == EVP_SignUpdate(mdctx, conftxt.c_str(), conftxt.length())) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "EVP_SignUpdate Failure!"); + } + + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "EVP_SignFinal Failure!"); + } + + { + std::ostringstream out; + out << "Conf Signature is(" << signlen << "): "; + for(i = 0; i < signlen; i++) + { + out << std::hex << std::setw(2) << (int) signature[i]; + conftxt += signature[i]; + } + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + FILE *cfd = fopen(configname.c_str(), "wb"); + int wrec; + if (1 != (wrec = fwrite(conftxt.c_str(), conftxt.length(), 1, cfd))) + { + std::ostringstream out; + out << "Error writing: " << configname << std::endl; + out << "Wrote: " << wrec << "/" << 1 << " Records" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + EVP_MD_CTX_destroy(mdctx); + fclose(cfd); + + return 1; +} + +int sslroot::loadCertificates(const char *conf_fname) +{ + // open the configuration file. + // + // read in CERT + Hash. + + // construct file name. + // + // create the file in memory - hash + sign. + // write out data to a file. + + std::string neighdir = certdir + "/" + neighbourdir + "/"; + std::string configname = certdir + "/"; + configname += conf_fname; + + // save name for later save attempts. + certfile = conf_fname; + + std::string conftxt; + + unsigned int maxnamesize = 1024; + char name[maxnamesize]; + + int c; + unsigned int i; + + FILE *cfd = fopen(configname.c_str(), "rb"); + if (cfd == NULL) + { + std::ostringstream out; + out << "Unable to Load Configuration File!" << std::endl; + out << "File: " << configname << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + return -1; + } + + std::list fnames; + std::list hashes; + std::map::iterator mit; + std::map tmpsettings; + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char conf_signature[signlen]; + char *ret = NULL; + + for(ret = fgets(name, maxnamesize, cfd); + ((ret != NULL) && (!strncmp(name, "CERT ", 5))); + ret = fgets(name, maxnamesize, cfd)) + { + for(i = 5; (name[i] != '\n') && (i < (unsigned) maxnamesize); i++); + + if (name[i] == '\n') + { + name[i] = '\0'; + } + + // so the name is first.... + std::string fname = &(name[5]); + + // now read the + std::string hash; + std::string signature; + + for(i = 0; i < signlen; i++) + { + if (EOF == (c = fgetc(cfd))) + { + std::ostringstream out; + out << "Error Reading Signature of: "; + out << fname; + out << std::endl; + out << "ABorting Load!"; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + return -1; + } + unsigned char uc = (unsigned char) c; + signature += (unsigned char) uc; + } + if ('\n' != (c = fgetc(cfd))) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "Warning Mising seperator"); + } + + { + std::ostringstream out; + out << "Read fname:" << fname << std::endl; + out << "Signature:" << std::endl; + for(i = 0; i < signlen; i++) + { + out << std::hex << std::setw(2) << (int) signature[i]; + } + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + // push back..... + fnames.push_back(fname); + hashes.push_back(signature); + + conftxt += "CERT "; + conftxt += fname; + conftxt += "\n"; + conftxt += signature; + conftxt += "\n"; + + // be sure to write over a bit... + name[0] = 'N'; + name[1] = 'O'; + } + + // string already waiting! + for(; ((ret != NULL) && (!strncmp(name, "OPT ", 4))); + ret = fgets(name, maxnamesize, cfd)) + { + for(i = 4; (name[i] != '\n') && (i < OPT_LEN); i++); + // terminate the string. + name[i] = '\0'; + + // so the name is first.... + std::string opt = &(name[4]); + + // now read the + std::string val; // cleaned up value. + std::string valsign; // value in the file. + + for(i = 0; i < VAL_LEN; i++) + { + if (EOF == (c = fgetc(cfd))) + { + std::ostringstream out; + out << "Error Reading Value of: "; + out << opt; + out << std::endl; + out << "ABorting Load!"; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + return -1; + } + // remove zeros on strings... + if (c != '\0') + { + val += (unsigned char) c; + } + valsign += (unsigned char) c; + } + if ('\n' != (c = fgetc(cfd))) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "Warning Mising seperator"); + } + + { + std::ostringstream out; + out << "Read OPT:" << opt; + out << " Val:" << val << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + // push back..... + tmpsettings[opt] = val; + + conftxt += "OPT "; + conftxt += opt; + conftxt += "\n"; + conftxt += valsign; + conftxt += "\n"; + + // be sure to write over a bit... + name[0] = 'N'; + name[1] = 'O'; + } + + // only read up to the first newline symbol.... + // continue... + for(i = 0; (name[i] != '\n') && (i < signlen); i++); + + //printf("Stepping over [%d] %0x\n", i, name[i]); + + + if (i != signlen) + { + for(i++; i < signlen; i++) + { + c = fgetc(cfd); + if (c == EOF) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "Error Reading Conf Signature:"); + return 1; + } + unsigned char uc = (unsigned char) c; + name[i] = uc; + } + } + + { + std::ostringstream out; + out << "Configuration File Signature: " << std::endl; + for(i = 0; i < signlen; i++) + { + out << std::hex << std::setw(2) << (int) name[i]; + } + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + + // when we get here - should have the final signature in the buffer. + // check. + // + // compare signatures. + // instead of verifying with the public key.... + // we'll sign it again - and compare .... FIX LATER... + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit(mdctx, EVP_sha1())) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignInit Failure!"); + } + + if (0 == EVP_SignUpdate(mdctx, conftxt.c_str(), conftxt.length())) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignUpdate Failure!"); + } + + if (0 == EVP_SignFinal(mdctx, conf_signature, &signlen, pkey)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignFinal Failure!"); + } + + EVP_MD_CTX_destroy(mdctx); + fclose(cfd); + + { + std::ostringstream out; + out << "Recalced File Signature: " << std::endl; + for(i = 0; i < signlen; i++) + { + out << std::hex << std::setw(2) << (int) conf_signature[i]; + } + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + bool same = true; + for(i = 0; i < signlen; i++) + { + if ((unsigned char) name[i] != conf_signature[i]) + { + same = false; + } + } + + if (same == false) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "ERROR VALIDATING CONFIGURATION! -- PLEASE FIX!"); + return -1; + } + std::list::iterator it; + std::list::iterator it2; + for(it = fnames.begin(), it2 = hashes.begin(); it != fnames.end(); it++, it2++) + { + std::string neighfile = neighdir + (*it) + ".pqi"; + cert *nc = loadcertificate(neighfile.c_str(), (*it2)); + if (nc != NULL) + { + if (0 > addCertificate(nc)) + { + // cleanup. + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "Updated Certificate....but no need for addition"); + // X509_free(nc -> certificate); + //delete nc; + } + } + } + for(mit = tmpsettings.begin(); mit != tmpsettings.end(); mit++) + { + settings[mit -> first] = mit -> second; + } + return 1; +} + + +int sslroot::savecertificate(cert *c, const char *fname) +{ + // load certificates from file. + FILE *setfp = fopen(fname, "wb"); + if (setfp == NULL) + { + std::ostringstream out; + out << "sslroot::savecertificate() Bad File: " << fname; + out << " Cannot be Written!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + return -1; + } + + { + std::ostringstream out; + out << "Writing out Cert...:" << c -> Name() << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + PEM_write_X509(setfp, c -> certificate); + + // writing out details.... + + // read in a line..... + int size = 1024; + char line[size]; + std::list::iterator it; + + int i; + + // This will need to be made portable..... + + struct sockaddr_in *addr_inet; + struct sockaddr_in *addr_inet2; + struct sockaddr_in *addr_inet3; + + int pos_status = 0; + int pos_addr = sizeof(int); + int pos_addr2 = pos_addr + sizeof(*addr_inet); + int pos_addr3 = pos_addr2 + sizeof(*addr_inet2); + + int pos_lcts = pos_addr3 + sizeof(*addr_inet3); + int pos_lrts = pos_lcts + sizeof(int); + int pos_ncts = pos_lrts + sizeof(int); + int pos_ncvl = pos_ncts + sizeof(int); + int pos_name = pos_ncvl + sizeof(int); + int pos_end = pos_name + 20; // \n. for readability. + + int *status = (int *) &(line[pos_status]); + addr_inet = (struct sockaddr_in *) &(line[pos_addr]); + addr_inet2 = (struct sockaddr_in *) &(line[pos_addr2]); + addr_inet3 = (struct sockaddr_in *) &(line[pos_addr3]); + int *lcts = (int *) &(line[pos_lcts]); + int *lrts = (int *) &(line[pos_lrts]); + char *ncts = &(line[pos_ncts]); + char *ncvl = &(line[pos_ncvl]); + char *name = &(line[pos_name]); + char *end = &(line[pos_end]); + + for(i = 0; i < 1024; i++) + line[i] = 0; + + *status = c -> Status(); + *addr_inet = c -> lastaddr; + *addr_inet2 = c -> localaddr; + *addr_inet3 = c -> serveraddr; + + *lcts = c -> lc_timestamp; + *lrts = c -> lr_timestamp; + *ncts = c -> nc_timestamp; + *ncvl = c -> nc_timeintvl; + + std::string tmpname = c -> Name(); + for(i = 0; (i < (signed) tmpname.length()) && (i < 20 - 1); i++) + { + name[i] = tmpname[i]; + } + name[20 - 1] = '\0'; + end[0] = '\n'; + + + if (1 != fwrite(line, size,1, setfp)) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "Error Writing Peer Record!"); + return -1; + } + fclose(setfp); + + // then reopen to generate hash. + setfp = fopen(fname, "rb"); + if (setfp == NULL) + { + std::ostringstream out; + out << "sslroot::savecertificate() Bad File: " << fname; + out << " Opened for ReHash!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + return -1; + } + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + int maxsize = 10240; + int rbytes; + char inall[maxsize]; + if (0 == (rbytes = fread(inall, 1, maxsize, setfp))) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "Error Writing Peer Record!"); + return -1; + } + + { + std::ostringstream out; + out << "Read " << rbytes << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + // so we read rbytes. + // hash. + //OpenSSL_add_all_digests(); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignInit Failure!"); + } + + if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignUpdate Failure!"); + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignFinal Failure!"); + } + + std::string signstr; + { + std::ostringstream out; + out << "Cert + Setting Signature is(" << signlen << "): "; + for(i = 0; i < (signed) signlen; i++) + { + out << std::hex << std::setw(2) << (int) signature[i]; + signstr += signature[i]; + } + out << std::dec << std::endl; + + c -> Hash(signstr); + out << "Stored Hash Length: " << (c -> Hash()).length() << std::endl; + out << "Real Hash Length: " << signlen << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + fclose(setfp); + + EVP_MD_CTX_destroy(mdctx); + + return 1; +} + +cert *sslroot::loadcertificate(const char *fname, std::string hash) +{ + // if there is a hash - check that the file matches it before loading. + FILE *pcertfp; + if (hash.length() > 1) + { + pcertfp = fopen(fname, "rb"); + // load certificates from file. + if (pcertfp == NULL) + { + std::ostringstream out; + out << "sslroot::loadcertificate() Bad File: " << fname; + out << " Cannot be Hashed!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + return NULL; + } + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + int maxsize = 10240; + int rbytes; + char inall[maxsize]; + if (0 == (rbytes = fread(inall, 1, maxsize, pcertfp))) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "Error Reading Peer Record!"); + return NULL; + } + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignInit Failure!"); + } + + if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignUpdate Failure!"); + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignFinal Failure!"); + } + + fclose(pcertfp); + EVP_MD_CTX_destroy(mdctx); + + bool same = true; + if (signlen != hash.length()) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "Different Length Signatures... Cannot Load Cert!"); + return NULL; + } + + for(int i = 0; i < (signed) signlen; i++) + { + if (signature[i] != (unsigned char) hash[i]) + { + same = false; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "Invalid Signature... Cannot Load Certificate!"); + return NULL; + } + } + + { + std::ostringstream out; + out << "Verified Signature for: " << fname; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + + } + else + { + pqioutput(PQL_ALERT, pqisslrootzone, "Not checking cert signature"); + } + + pcertfp = fopen(fname, "rb"); + + // load certificates from file. + if (pcertfp == NULL) + { + std::ostringstream out; + out << "sslroot::loadcertificate() Bad File: " << fname; + out << " Cannot be Read!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + return NULL; + } + + + X509 *pc; + cert *npc = NULL; + + if ((pc = PEM_read_X509(pcertfp, NULL, NULL, NULL)) != NULL) + { + // read a certificate. + std::ostringstream out; + out << "Loaded Certificate: "; + out << pc -> name << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + + npc = makeCertificate(pc); + if (npc == NULL) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, "Failed to Create Cert!"); + return NULL; + } + } + else // (pc == NULL) + { + unsigned long err = ERR_get_error(); + std::ostringstream out; + out << "Read Failed .... CODE(" << err << ")" << std::endl; + out << ERR_error_string(err, NULL) << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + return NULL; + } + + // Now we try to read in 1024 bytes..... + // if successful, then have settings! + + // read in a line..... + int size = 1024; + char line[size]; + + // Data arrangment. + // so far + // ------------ + // 4 - (int) status + // 8 - sockaddr + // 8 - sockaddr + // 8 - sockaddr + // 4 - lc_timestamp + // 4 - lr_timestamp + // 4 - nc_timestamp + // 4 - nc_timeintvl + // 20 - name. + // 1 - end + + // This will need to be made portable..... + + struct sockaddr_in *addr_inet; + struct sockaddr_in *addr_inet2; + struct sockaddr_in *addr_inet3; + + //int pos_status = 0; + int pos_addr = sizeof(int); + int pos_addr2 = pos_addr + sizeof(*addr_inet); + int pos_addr3 = pos_addr2 + sizeof(*addr_inet2); + int pos_lcts = pos_addr3 + sizeof(*addr_inet3); + + int pos_lrts = pos_lcts + sizeof(int); + int pos_ncts = pos_lrts + sizeof(int); + int pos_ncvl = pos_ncts + sizeof(int); + int pos_name = pos_ncvl + sizeof(int); + //int pos_end = pos_name + 20; // \n. for readability. + + int *status = (int *) line; + addr_inet = (struct sockaddr_in *) &(line[pos_addr]); + addr_inet2 = (struct sockaddr_in *) &(line[pos_addr2]); + addr_inet3 = (struct sockaddr_in *) &(line[pos_addr3]); + int *lcts = (int *) &(line[pos_lcts]); + int *lrts = (int *) &(line[pos_lrts]); + char *ncts = &(line[pos_ncts]); + char *ncvl = &(line[pos_ncvl]); + char *name = &(line[pos_name]); + //char *end = &(line[pos_end]); + + // end of data structures.... + + if (1 != (signed) fread(line, size,1, pcertfp)) + { + pqioutput(PQL_WARNING, pqisslrootzone, + "Error Reading Setting: Only Cert Retrieved"); + return npc; + } + + + // fill in the data. + cert *c = npc; + c -> Status(*status); + // but ensure that inUse is not set. + c -> InUse(false); + + c -> lastaddr = *addr_inet; + c -> localaddr = *addr_inet2; + c -> serveraddr = *addr_inet3; + + c -> lc_timestamp = *lcts; + c -> lr_timestamp = *lrts; + c -> nc_timestamp = *ncts; + c -> nc_timeintvl = *ncvl; + + + name[20 - 1] = '\0'; + c -> Name(std::string(name)); + + // save the hash. + c -> Hash(hash); + + fclose(pcertfp); + + // small hack - as the timestamps seem wrong..... + // could be a saving thing - or a bug.... + c -> lc_timestamp = 0; + c -> lr_timestamp = 0; + + // reset these. as well. + c -> nc_timestamp = 0; + c -> nc_timeintvl = 5; + + return c; +} + + +int sslroot::printCertificate(cert *c, std::ostream &out) +{ + out << "Cert Name:" << (c -> certificate) -> name << std::endl; + //X509_print_fp(stderr, c -> certificate); + return 1; +} + +// This function will clean up X509 *c if necessary. + +cert *sslroot::makeCertificate(X509 *c) +{ + if (c == NULL) + { + return NULL; + } + + // At this point we check to see if there is a duplicate. + cert *dup = checkDuplicateX509(c); + cert *npc = NULL; + if (dup == NULL) + { + npc = new cert(); + npc -> certificate = c; + if (!addtosignmap(npc)) // only allow the cert if no dup + { + + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::makeCertificate() Failed to Get Signature - Not Allowed!"); + + // failed!... cannot add it!. + delete npc; + return NULL; + } + + allcerts.push_back(npc); + { + std::ostringstream out; + out << "sslroot::makeCertificate() For " << c -> name; + out << " A-Okay!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + // at this point we need to add to the signaturelist.... + + } + else if (c == dup -> certificate) + { + // identical - so okay. + npc = dup; + std::ostringstream out; + out << "sslroot::makeCertificate() For " << c -> name; + out << " Found Identical - A-Okay!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + else + { + std::ostringstream out; + out << "sslroot::makeCertificate() For " << c -> name; + out << " Cleaning up other X509!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + // clean up c. + X509_free(c); + npc = dup; + } + return npc; +} + + +cert *sslroot::checkDuplicateX509(X509 *x) +{ + if (x == NULL) + return NULL; + + // loop through and print - then check. + std::list::iterator it; + for(it = allcerts.begin(); it != allcerts.end(); it++) + { + if (0 == X509_cmp((*it) -> certificate, x)) + { + return (*it); + } + } + return NULL; +} + + +cert *sslroot::checkPeerX509(X509 *x) +{ + if (x == NULL) + return NULL; + + // loop through and print - then check. + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + if (0 == X509_cmp((*it) -> certificate, x)) + { + return (*it); + } + } + return NULL; +} + + + +cert *sslroot::findpeercert(const char *name) +{ + // loop through and print - then check. + //std::cerr << "Checking Certs for: " << name << std::endl; + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + char *certname = ((*it) -> certificate) -> name; + //std::cerr << "Cert Name:" << certname << std::endl; + if (strstr(certname, name) != NULL) + { + //std::cerr << "Matches!" << std::endl; + return (*it); + } + } + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::findpeercert() Failed!"); + return NULL; +} + +// returns zero for the same. +int sslroot::compareCerts(cert *a, cert *b) +{ + // std::cerr << "Comparing Certificates:" << std::endl; + //printCertificate(a); + //printCertificate(b); + //X509_print_fp(stderr, a -> certificate); + //X509_print_fp(stderr, b -> certificate); + + int val = X509_cmp(a -> certificate, b -> certificate); + + { + std::ostringstream out; + out << "Certificate Comparison Returned: " << val << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + return val; +} + +cert * sslroot::registerCertificate(X509 *nc, struct sockaddr_in raddr, bool in) +{ + if (nc == NULL) + return NULL; + + // shoud check all certs. + cert *c = checkDuplicateX509(nc); + if (c != NULL) + { + if (c -> certificate == nc) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::registerCertificate() Found Identical X509 cert"); + } + else + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::registerCertificate() Found Same X509 cert/diff mem - Clean"); + X509_free(nc); + } + + if (!c -> Connected()) + { + c -> lastaddr = raddr; + + if (in == true) + { + c -> lr_timestamp = time(NULL); + // likely to be server address + // (with default port) + // if null! + if (!isValidNet(&(c -> serveraddr.sin_addr))) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "Guessing Default Server Addr!"); + + c -> serveraddr = raddr; + c -> serveraddr.sin_port = + htons(PQI_DEFAULT_PORT); + } + } + else + { + c -> lc_timestamp = time(NULL); + // also likely to be servera address, + // but we can check and see if its local. + // can flag local + if (0 == inaddr_cmp(c -> localaddr, raddr)) + { + c -> Local(true); + // don't set serveraddr -> just ignore + } + else + { + c -> serveraddr = raddr; + c -> Firewalled(false); + } + } + } + else + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "WARNING: attempt to reg CONNECTED Cert!"); + } + return c; + } + + { + std::ostringstream out; + out << "sslroot::registerCertificate() Certificate Not Found!" << std::endl; + out << "Saving :" << nc -> name << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + cert *npc = makeCertificate(nc); + if (npc == NULL) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "Failed to Make Certificate"); + return NULL; + } + + npc -> Name(nc -> name); + + npc -> lastaddr = raddr; + if (in == true) + { + npc -> lr_timestamp = time(NULL); + // likely to be server address (with default port) + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, "Guessing Default Server Addr!"); + npc -> serveraddr = raddr; + npc -> serveraddr.sin_port = htons(PQI_DEFAULT_PORT); + } + else + { + npc -> lc_timestamp = time(NULL); + + // as it is a new cert... all fields are + // null and the earlier tests must be + // delayed until the discovery packets. + + // also likely to be server. + npc -> serveraddr = raddr; + } + + // push back onto collected. + npc -> nc_timestamp = 0; + collectedcerts.push_back(npc); + + // return NULL to indicate that it dosen't yet exist in dbase. + return NULL; +} + +cert * sslroot::getCollectedCert() +{ + if (collectedcerts.size() < 1) + return NULL; + + cert *c = collectedcerts.front(); + collectedcerts.pop_front(); + return c; +} + +bool sslroot::collectedCerts() +{ + return (collectedcerts.size() > 0); +} + + +int sslroot::removeCertificate(cert *c) +{ + if (c -> InUse()) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::removeCertificate() Failed: cert is in use."); + return -1; + } + + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + if (c == (*it)) + { + peercerts.erase(it); + + c -> InUse(false); + c -> Accepted(false); + + + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::removeCertificate() Success! Moved to Coll Certs"); + + collectedcerts.push_back(c); + + certsChanged.IndicateChanged(); + certsMajorChanged.IndicateChanged(); + return 1; + } + } + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::removeCertificate() Failed to Match Cert!"); + + return 0; +} + + +int sslroot::addCertificate(cert *c) +{ + c -> InUse(false); + // let most flags through. + //c -> Accepted(false); + //c -> WillConnect(false); + if (c -> certificate == NULL) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::addCertificate() certificate==NULL, Not Adding"); + return 0; + } + + cert *dup = checkPeerX509(c -> certificate); + if (dup != NULL) + { + std::ostringstream out; + out << "sslroot::addCertificate() Not Adding"; + out << "Certificate with duplicate...." << std::endl; + out << "\t\tTry RegisterCertificate() " << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + + return -1; + } + + // else put in in the list. + peercerts.push_back(c); + + certsChanged.IndicateChanged(); + certsMajorChanged.IndicateChanged(); + + return 1; +} + + +int sslroot::addUntrustedCertificate(cert *c) +{ + // blank it all. + c -> Status(PERSON_STATUS_MANUAL); + // set Tag to be their X509CN. + c -> Name(getX509CNString(c -> certificate -> cert_info -> subject)); + + return addCertificate(c); +} + + + +int sslroot::validateCertificate(cert *c) +{ + std::ostringstream out; + out << "sslroot::validateCertificate() Why Not!" << std::endl; + c -> Valid(true); + out << "Cert Status: " << c -> Status() << std::endl; + pqioutput(PQL_ALERT, pqisslrootzone, out.str()); + return 1; +} + +/***** REMOVED! + * + * +std::list sslroot::listCertificates() +{ + std::list names; + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + names.push_back(((*it) -> certificate) -> name); + } + return names; +} + * + * + *****/ + + +bool sslroot::CertsChanged() +{ + return certsChanged.Changed(0); +} + +bool sslroot::CertsMajorChanged() +{ + return certsMajorChanged.Changed(0); +} + +void sslroot::IndicateCertsChanged() +{ + certsChanged.IndicateChanged(); +} + + +std::list &sslroot::getCertList() +{ + return peercerts; +} + +std::string sslroot::getSetting(std::string opt) +{ + std::map::iterator it; + if (settings.end() != (it = settings.find(opt))) + { + // found setting. + std::ostringstream out; + out << "sslroot::getSetting(" << opt << ") = "; + out << it -> second << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + return it -> second; + } + // else return empty string. + + { + std::ostringstream out; + out << "sslroot::getSetting(" << opt; + out << ") Not There!" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + std::string empty(""); + return empty; +} + +void sslroot::setSetting(std::string opt, std::string val) +{ + // check settings.. + std::ostringstream out; + out << "sslroot::saveSetting(" << opt << ", "; + out << val << ")" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + + settings[opt] = val; + return; +} + +cert *sslroot::getOwnCert() +{ + return own_cert; +} + +int sslroot::checkNetAddress() +{ + std::list addrs = getLocalInterfaces(); + std::list::iterator it; + + bool found = false; + for(it = addrs.begin(); (!found) && (it != addrs.end()); it++) + { + if ((*it) == inet_ntoa(own_cert -> localaddr.sin_addr)) + { + found = true; + } + } + /* check that we didn't catch 0.0.0.0 - if so go for prefered */ + if ((found) && (own_cert -> localaddr.sin_addr.s_addr == 0)) + { + found = false; + } + + if (!found) + { + own_cert -> localaddr.sin_addr = getPreferredInterface(); + } + if ((isPrivateNet(&(own_cert -> localaddr.sin_addr))) || + (isLoopbackNet(&(own_cert -> localaddr.sin_addr)))) + { + own_cert -> Firewalled(true); + } + else + { + //own_cert -> Firewalled(false); + } + + int port = ntohs(own_cert -> localaddr.sin_port); + if ((port < PQI_MIN_PORT) || (port > PQI_MAX_PORT)) + { + own_cert -> localaddr.sin_port = htons(PQI_DEFAULT_PORT); + } + + /* if localaddr = serveraddr, then ensure that the ports + * are the same (modify server)... this mismatch can + * occur when the local port is changed.... + */ + + if (own_cert -> localaddr.sin_addr.s_addr == + own_cert -> serveraddr.sin_addr.s_addr) + { + own_cert -> serveraddr.sin_port = + own_cert -> localaddr.sin_port; + } + + // ensure that address family is set, otherwise windows Barfs. + own_cert -> localaddr.sin_family = AF_INET; + own_cert -> serveraddr.sin_family = AF_INET; + own_cert -> lastaddr.sin_family = AF_INET; + + return 1; +} + + + + +/********** SSL ERROR STUFF ******************************************/ + +int printSSLError(SSL *ssl, int retval, int err, unsigned long err2, + std::ostream &out) +{ + std::string reason; + + std::string mainreason = std::string("UNKNOWN ERROR CODE"); + if (err == SSL_ERROR_NONE) + { + mainreason = std::string("SSL_ERROR_NONE"); + } + else if (err == SSL_ERROR_ZERO_RETURN) + { + mainreason = std::string("SSL_ERROR_ZERO_RETURN"); + } + else if (err == SSL_ERROR_WANT_READ) + { + mainreason = std::string("SSL_ERROR_WANT_READ"); + } + else if (err == SSL_ERROR_WANT_WRITE) + { + mainreason = std::string("SSL_ERROR_WANT_WRITE"); + } + else if (err == SSL_ERROR_WANT_CONNECT) + { + mainreason = std::string("SSL_ERROR_WANT_CONNECT"); + } + else if (err == SSL_ERROR_WANT_ACCEPT) + { + mainreason = std::string("SSL_ERROR_WANT_ACCEPT"); + } + else if (err == SSL_ERROR_WANT_X509_LOOKUP) + { + mainreason = std::string("SSL_ERROR_WANT_X509_LOOKUP"); + } + else if (err == SSL_ERROR_SYSCALL) + { + mainreason = std::string("SSL_ERROR_SYSCALL"); + } + else if (err == SSL_ERROR_SSL) + { + mainreason = std::string("SSL_ERROR_SSL"); + } + out << "RetVal(" << retval; + out << ") -> SSL Error: " << mainreason << std::endl; + out << "\t + ERR Error: " << ERR_error_string(err2, NULL) << std::endl; + return 1; +} + +cert::cert() + :certificate(NULL), hash("") +{ + return; +} + +cert::~cert() +{ + return; +} + +std::string cert::Signature() +{ + if (certificate == NULL) + { + return Name(); + } + else + { + // get signature from cert.... + return Name(); + } +} + + +std::string cert::Hash() +{ + return hash; +} + + +void cert::Hash(std::string h) +{ + hash = h; + return; +} + + + +/********************* signature stuff *********************/ + +bool certsign::operator<(const certsign &ref) const +{ + //compare the signature. + if (0 > strncmp(data, ref.data, CERTSIGNLEN)) + return true; + return false; +} + + +bool certsign::operator==(const certsign &ref) const +{ + //compare the signature. + return (0 == strncmp(data, ref.data, CERTSIGNLEN)); +} + + +/* Fns for relating cert signatures to structures */ +cert *sslroot::findcertsign(certsign &sign) +{ + std::map::iterator it; + + std::ostringstream out; + out << "sslroot::findcertsign()" << std::endl; + for (it = signmap.begin(); it != signmap.end(); it++) + { + out << "Checking Vs " << it -> second -> Name(); + if (sign == it -> first) + { + out << "Match!"; + } + out << std::endl; + } + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + + if (signmap.end() != (it = signmap.find(sign))) + { + return it -> second; + } + return NULL; +} + +int sslroot::getcertsign(cert *c, certsign &sign) +{ + if ((c == NULL) || (c->certificate == NULL)) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::getcertsign() ERROR: NULL c || c->certificate"); + return 0; + } + + // get the signature from the cert, and copy to the array. + ASN1_BIT_STRING *signature = c -> certificate -> signature; + int signlen = ASN1_STRING_length(signature); + if (signlen < CERTSIGNLEN) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::getcertsign() ERROR: short Signature"); + return 0; + } + // else copy in the first CERTSIGNLEN. + unsigned char *signdata = ASN1_STRING_data(signature); + memcpy(sign.data, signdata, CERTSIGNLEN); + + return 1; +} + +int sslroot::addtosignmap(cert *c) +{ + certsign cs; + if (!getcertsign(c, cs)) + { + // error. + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::addsigntomap() ERROR: Fail to getcertsign()"); + return 0; + } + cert *c2 = findcertsign(cs); + if (c2 == NULL) + { + // add, and return okay. + signmap[cs] = c; + return 1; + } + if (c2 != c) + { + // error. + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::addsigntomap() ERROR: Duplicate Entry()"); + return 0; + } + + // else already exists. + return 1; +} + + + + +int sslroot::hashFile(std::string fname, unsigned char *hash, unsigned int hlen) +{ + // open the file. + // setup the hash. + + // pipe the file through. + + + return 1; +} + +int sslroot::hashDigest(char *data, unsigned int dlen, + unsigned char *hash, unsigned int hlen) +{ + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_DigestInit_ex(mdctx, EVP_sha1(), NULL)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_DigestInit Failure!"); + return -1; + } + + if (0 == EVP_DigestUpdate(mdctx, data, dlen)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_DigestUpdate Failure!"); + return -1; + } + + unsigned int signlen = hlen; + if (0 == EVP_DigestFinal_ex(mdctx, hash, &signlen)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_DigestFinal Failure!"); + return -1; + } + + EVP_MD_CTX_destroy(mdctx); + return signlen; +} + + + +int sslroot::signDigest(EVP_PKEY *key, char *data, unsigned int dlen, + unsigned char *sign, unsigned int slen) +{ + unsigned int signlen = EVP_PKEY_size(key); + + { + std::ostringstream out; + out << "sslroot::signDigest(" << (void *) key; + out << ", " << (void *) data << ", " << dlen << ", "; + out << (void *) sign << ", " << slen << ")" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + if (signlen > slen) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::signDigest() Sign Length too short"); + return -1; + } + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignInit Failure!"); + return -1; + } + + if (0 == EVP_SignUpdate(mdctx, data, dlen)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignUpdate Failure!"); + return -1; + } + + signlen = slen; + if (0 == EVP_SignFinal(mdctx, sign, &signlen, key)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_SignFinal Failure!"); + return -1; + } + + EVP_MD_CTX_destroy(mdctx); + return signlen; +} + + +int sslroot::verifyDigest(EVP_PKEY *key, char *data, unsigned int dlen, + unsigned char *enc, unsigned int elen) +{ + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_VerifyInit_ex(mdctx, EVP_sha1(), NULL)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_VerifyInit Failure!"); + return -1; + } + + if (0 == EVP_VerifyUpdate(mdctx, data, dlen)) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_VerifyUpdate Failure!"); + return -1; + } + + int vv; + if (0 > (vv = EVP_VerifyFinal(mdctx, enc, elen, key))) + { + pqioutput(PQL_ALERT, pqisslrootzone, "EVP_VerifyFinal Failure!"); + return -1; + } + if (vv == 1) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, "Verified Signature OKAY"); + } + else + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, "Failed Verification!"); + } + + EVP_MD_CTX_destroy(mdctx); + return vv; +} + +// Think both will fit in the one Structure. +int sslroot::generateKeyPair(EVP_PKEY *keypair, unsigned int keylen) +{ + RSA *rsa = RSA_generate_key(2048, 65537, NULL, NULL); + EVP_PKEY_assign_RSA(keypair, rsa); + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, "sslroot::generateKeyPair()"); + return 1; +} + + +std::string getX509NameString(X509_NAME *name) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + namestr += "\t"; + namestr += OBJ_nid2ln(OBJ_obj2nid(entry_obj)); + namestr += " : "; + + //namestr += entry_obj -> flags; + //namestr += entry_data -> length; + //namestr += entry_data -> type; + + //namestr += entry_data -> flags; + //entry -> set; + + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "NULL"; + } + + if (i + 1 < X509_NAME_entry_count(name)) + { + namestr += "\n"; + } + + } + return namestr; +} + + +std::string getX509CNString(X509_NAME *name) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + if (0 == strncmp("CN", OBJ_nid2sn(OBJ_obj2nid(entry_obj)), 2)) + { + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "Unknown"; + } + return namestr; + } + } + return namestr; +} + + +std::string getX509TypeString(X509_NAME *name, char *type, int len) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + if (0 == strncmp(type, OBJ_nid2sn(OBJ_obj2nid(entry_obj)), len)) + { + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "Unknown"; + } + return namestr; + } + } + return namestr; +} + +std::string getX509LocString(X509_NAME *name) +{ + return getX509TypeString(name, "L", 2); +} + +std::string getX509OrgString(X509_NAME *name) +{ + return getX509TypeString(name, "O", 2); +} + + +std::string getX509CountryString(X509_NAME *name) +{ + return getX509TypeString(name, "C", 2); +} + + +std::string convert_to_str(certsign &sign) +{ + std::ostringstream id; + for(int i = 0; i < CERTSIGNLEN; i++) + { + id << std::hex << std::setw(2) << std::setfill('0') + << (uint16_t) (((uint8_t *) (sign.data))[i]); + } + return id.str(); +} + +bool convert_to_certsign(std::string id, certsign &sign) +{ + char num[3]; + if (id.length() < CERTSIGNLEN * 2) + { + return false; + } + + for(int i = 0; i < CERTSIGNLEN; i++) + { + num[0] = id[i * 2]; + num[1] = id[i * 2 + 1]; + num[2] = '\0'; + int32_t val; + if (1 != sscanf(num, "%x", &val)) + { + return false; + } + sign.data[i] = (uint8_t) val; + } + return true; +} + + diff --git a/libretroshare/src/_pqi/sslcert.h b/libretroshare/src/_pqi/sslcert.h new file mode 100644 index 000000000..4c8266244 --- /dev/null +++ b/libretroshare/src/_pqi/sslcert.h @@ -0,0 +1,192 @@ +#ifndef SSLCERT_H +#define SSLCERT_H + +/* + * Core PQI networking: sslcert.h + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 +#include + +#include "_pqi_base.h" +#include "_pqinetwork.h" + +#include "_pqiindic.h" + + +// helper fns. +int printSSLError(SSL *ssl, int retval, int err, unsigned long err2, std::ostream &out); +std::string getX509NameString(X509_NAME *name); +std::string getX509CNString(X509_NAME *name); + +std::string getX509OrgString(X509_NAME *name); +std::string getX509LocString(X509_NAME *name); +std::string getX509CountryString(X509_NAME *name); + + +/* definitions -> functions to be defined */ +std::string convert_to_str(certsign &sign); +bool convert_to_certsign(std::string id, certsign &sign); + +class sslroot; + +class cert: public Person +{ +public: + cert(); + virtual ~cert(); + + virtual std::string Signature(); + std::string Hash(); + void Hash(std::string); + + X509 *certificate; + std::string hash; +}; + + +// returns pointer to static variable. +// which must be inited.. +sslroot *getSSLRoot(); + +class sslroot +{ + public: + sslroot(); + int active(); + int setcertdir(char *path); + int initssl(const char *srvr_cert, const char *priv_key, + const char *CA_file, const char *passwd); + int closessl(); + + /* Context handling */ + SSL_CTX *getCTX(); + + /* Certificate handling */ + int compareCerts(cert *a, cert *b); + + // network interface. + + // program interface. + int addCertificate(cert *c); + int addUntrustedCertificate(cert *c); + int removeCertificate(cert *); + + // Creation of Certificates.... (From X509) + // Core functions.... + cert *checkDuplicateX509(X509 *x); + cert *checkPeerX509(X509 *x); + cert *makeCertificate(X509 *c); + cert *registerCertificate(X509 *nc, struct sockaddr_in, bool in); + + int validateCertificate(cert *c); + + // depreciated... + cert *findpeercert(const char *name); + //int loadpeercert(const char *fname); + //int savepeercert(const char *fname); + + // Configuration Handling... + int setConfigDirs(const char *cdir, const char *ndir); + + // these save both the certificates + the settings. + int saveCertificates(const char *fname); + int saveCertificates(); + int loadCertificates(const char *fname); + + // with a hash check/recalc in there for good measure. + cert * loadcertificate(const char* fname, std::string hash); + int savecertificate(cert *c, const char* fname); + + // digest hashing /signing or encrypting interface. + int hashFile(std::string fname, unsigned char *hash, unsigned int hlen); + int hashDigest(char *data, unsigned int dlen, unsigned char *hash, unsigned int hlen); + int signDigest(EVP_PKEY *key, char *data, unsigned int dlen, unsigned char *hash, unsigned int hlen); + int verifyDigest(EVP_PKEY *key, char *data, unsigned int dlen, unsigned char *enc, unsigned int elen); + int generateKeyPair(EVP_PKEY *keypair, unsigned int keylen); + + + + int printCertificate(cert *, std::ostream &out); + /****** REMOVED! + * + * + std::list listCertificates(); + * + * + ****/ + + std::list &getCertList(); + + cert * getOwnCert(); + int checkNetAddress(); + + // extra list for certs that aren't in main list. + cert * getCollectedCert(); + bool collectedCerts(); + + bool CertsChanged(); + bool CertsMajorChanged(); + void IndicateCertsChanged(); + + std::string getSetting(std::string opt); + void setSetting(std::string opt, std::string val); + + + /* Fns for relating cert signatures to structures */ + cert *findcertsign(certsign &sign); + int getcertsign(cert *c, certsign &sign); + int addtosignmap(cert *); + +private: /* data */ + std::list peercerts; + std::list allcerts; + std::list collectedcerts; + + // whenever a cert is added, it should also be put in the map. + std::map signmap; + + // General Configuration System + // easy it put it here - so it can be signed easily. + std::map settings; + + std::string certdir; + std::string neighbourdir; + std::string certfile; + + SSL_CTX *sslctx; + int init; + + Indicator certsChanged; + Indicator certsMajorChanged; + + EVP_PKEY *pkey; + + cert *own_cert; +}; + +#endif // SSLCERT_H diff --git a/libretroshare/src/_pqi/xpgpcert.cc b/libretroshare/src/_pqi/xpgpcert.cc new file mode 100644 index 000000000..9449c5960 --- /dev/null +++ b/libretroshare/src/_pqi/xpgpcert.cc @@ -0,0 +1,2569 @@ +/* + * "$Id: xpgpcert.cc,v 1.18 2007-04-15 18:45:18 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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 "xpgpcert.h" + +#include "pqinetwork.h" + +#include +#include +#include + +#include +#include + +#include "pqidebug.h" + +const int pqisslrootzone = 1211; + +/** XPGP keyring interface ************ +int XPGP_add_certificate(XPGP_KEYRING *kr, XPGP *x); +int XPGP_auth_certficate(XPGP_KEYRING *kr, XPGP *x); +int XPGP_sign_certificate(XPGP_KEYRING *kr, XPGP *subj, XPGP *issuer); + +int XPGP_check_valid_certificate(XPGP *x); + +int XPGP_signer_trusted(XPGP_KEYRING *kr, XPGP *trusted); +int XPGP_signer_untrusted(XPGP_KEYRING *kr, XPGP *untrusted); + + int XPGP_copy_known_signatures(XPGP_KEYRING *kr, XPGP *dest, XPGP *src); + + * + * + */ + +unsigned char convertHexToChar(unsigned char a, unsigned char b); + + +// other fns +std::string getCertName(cert *c) +{ + std::string name = c -> certificate -> name; + // strip out bad chars. + for(int i = 0; i < (signed) name.length(); i++) + { + if ((name[i] == '/') || (name[i] == ' ') || (name[i] == '=') || + (name[i] == '\\') || (name[i] == '\t') || (name[i] == '\n')) + { + name[i] = '_'; + } + } + return name; +} + + + +int pem_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + strncpy(buf, (char *)(password), size); + buf[size - 1] = '\0'; + return(strlen(buf)); +} + + +/* This class handles openssl library init/destruct. + * only one of these... handles + * the CTX and setup? + * + * it will also handle the certificates..... + * mantaining a library of recieved certs, + * and ip addresses that the connections come from... + * + */ + +// the single instance of this. +static sslroot instance_sslroot; + +sslroot *getSSLRoot() +{ + return &instance_sslroot; +} + +sslroot::sslroot() + :sslctx(NULL), init(0), certsChanged(1), + certsMajorChanged(1), pkey(NULL) +{ +} + +int sslroot::active() +{ + return init; +} + +// args: server cert, server private key, trusted certificates. + +int sslroot::initssl(const char *cert_file, const char *priv_key_file, + const char *passwd) +{ +static int initLib = 0; + if (!initLib) + { + initLib = 1; + SSL_load_error_strings(); + SSL_library_init(); + } + + + if (init == 1) + { + return 1; + } + + if ((cert_file == NULL) || + (priv_key_file == NULL) || + (passwd == NULL)) + { + fprintf(stderr, "sslroot::initssl() missing parameters!\n"); + return 0; + } + + + // XXX TODO + // actions_to_seed_PRNG(); + + std::cerr << "SSL Library Init!" << std::endl; + + // setup connection method + sslctx = SSL_CTX_new(PGPv1_method()); + + // setup cipher lists. + SSL_CTX_set_cipher_list(sslctx, "DEFAULT"); + + // certificates (Set Local Server Certificate). + FILE *ownfp = fopen(cert_file, "r"); + if (ownfp == NULL) + { + std::cerr << "Couldn't open Own Certificate!" << std::endl; + return -1; + } + + + + // get xPGP certificate. + XPGP *xpgp = PEM_read_XPGP(ownfp, NULL, NULL, NULL); + fclose(ownfp); + + if (xpgp == NULL) + { + return -1; + } + SSL_CTX_use_pgp_certificate(sslctx, xpgp); + + // get private key + FILE *pkfp = fopen(priv_key_file, "rb"); + if (pkfp == NULL) + { + std::cerr << "Couldn't Open PrivKey File!" << std::endl; + closessl(); + return -1; + } + + pkey = PEM_read_PrivateKey(pkfp, NULL, NULL, (void *) passwd); + fclose(pkfp); + + if (pkey == NULL) + { + return -1; + } + SSL_CTX_use_pgp_PrivateKey(sslctx, pkey); + + if (1 != SSL_CTX_check_pgp_private_key(sslctx)) + { + std::cerr << "Issues With Private Key! - Doesn't match your Cert" << std::endl; + std::cerr << "Check your input key/certificate:" << std::endl; + std::cerr << priv_key_file << " & " << cert_file; + std::cerr << std::endl; + closessl(); + return -1; + } + + // make keyring. + pgp_keyring = createPGPContext(xpgp, pkey); + SSL_CTX_set_XPGP_KEYRING(sslctx, pgp_keyring); + + + // Setup the certificate. (after keyring is made!). + + own_cert = makeCertificateXPGP(xpgp); + if (own_cert == NULL) + { + std::cerr << "Failed to Make Own Cert!" << std::endl; + return -1; + } + addCertificate(own_cert); + + // enable verification of certificates (PEER) + SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + + std::cerr << "SSL Verification Set" << std::endl; + + + + /* configure basics on the certificate. */ + own_cert -> Name(getX509CNString(own_cert -> certificate -> subject -> subject)); + + init = 1; + return 1; +} + + + +int sslroot::closessl() +{ + SSL_CTX_free(sslctx); + + // clean up private key.... + // remove certificates etc -> opposite of initssl. + init = 0; + return 1; +} + +/* Context handling */ +SSL_CTX *sslroot::getCTX() +{ + return sslctx; +} + +int sslroot::setConfigDirs(const char *cdir, const char *ndir) +{ + certdir = cdir; + neighbourdir = ndir; + return 1; +} + +static const unsigned int OPT_LEN = 16; +static const unsigned int VAL_LEN = 1000; + +int sslroot::saveCertificates() +{ + if (certfile.length() > 1) + return saveCertificates(certfile.c_str()); + return -1; +} + + +int sslroot::saveCertificates(const char *fname) +{ + // construct file name. + // + // create the file in memory - hash + sign. + // write out data to a file. + + std::string neighdir = certdir + "/" + neighbourdir + "/"; + std::string configname = certdir + "/"; + configname += fname; + + std::map::iterator mit; + + + std::string conftxt; + std::string empty(""); + unsigned int i; + + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + std::string neighfile = neighdir + getCertName(*it) + ".pqi"; + savecertificate((*it), neighfile.c_str()); + conftxt += "CERT "; + conftxt += getCertName(*it); + conftxt += "\n"; + conftxt += (*it) -> Hash(); + conftxt += "\n"; + std::cerr << std::endl; + } + + // Now add the options. + for(mit = settings.begin(); mit != settings.end(); mit++) + { + // only save the nonempty settings. + if (mit -> second != empty) { + conftxt += "OPT "; + for(i = 0; (i < OPT_LEN) && (i < mit -> first.length()); i++) + { + conftxt += mit -> first[i]; + } + conftxt += "\n"; + for(i = 0; i < VAL_LEN; i++) + { + if (i < mit -> second.length()) + { + conftxt += mit -> second[i]; + } + else + { + conftxt += '\0'; + } + } + conftxt += "\n"; + std::cerr << std::endl; + } + } + + // now work out signature of it all. This relies on the + // EVP library of openSSL..... We are going to use signing + // for the moment. + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + //OpenSSL_add_all_digests(); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, conftxt.c_str(), conftxt.length())) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + } + + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + } + + std::cerr << "Conf Signature is(" << signlen << "): "; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", signature[i]); + conftxt += signature[i]; + } + std::cerr << std::endl; + + FILE *cfd = fopen(configname.c_str(), "wb"); + int wrec; + if (1 != (wrec = fwrite(conftxt.c_str(), conftxt.length(), 1, cfd))) + { + std::cerr << "Error writing: " << configname << std::endl; + std::cerr << "Wrote: " << wrec << "/" << 1 << " Records" << std::endl; + } + + EVP_MD_CTX_destroy(mdctx); + fclose(cfd); + + return 1; +} + +int sslroot::loadCertificates(const char *conf_fname) +{ + // open the configuration file. + // + // read in CERT + Hash. + + // construct file name. + // + // create the file in memory - hash + sign. + // write out data to a file. + + std::string neighdir = certdir + "/" + neighbourdir + "/"; + std::string configname = certdir + "/"; + configname += conf_fname; + + // save name for later save attempts. + certfile = conf_fname; + + std::string conftxt; + + unsigned int maxnamesize = 1024; + char name[maxnamesize]; + + int c; + unsigned int i; + + FILE *cfd = fopen(configname.c_str(), "rb"); + if (cfd == NULL) + { + std::cerr << "Unable to Load Configuration File!" << std::endl; + std::cerr << "File: " << configname << std::endl; + return -1; + } + + std::list fnames; + std::list hashes; + std::map::iterator mit; + std::map tmpsettings; + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char conf_signature[signlen]; + char *ret = NULL; + + for(ret = fgets(name, maxnamesize, cfd); + ((ret != NULL) && (!strncmp(name, "CERT ", 5))); + ret = fgets(name, maxnamesize, cfd)) + { + for(i = 5; (name[i] != '\n') && (i < (unsigned) maxnamesize); i++); + + if (name[i] == '\n') + { + name[i] = '\0'; + } + + // so the name is first.... + std::string fname = &(name[5]); + + // now read the + std::string hash; + std::string signature; + + for(i = 0; i < signlen; i++) + { + if (EOF == (c = fgetc(cfd))) + { + std::cerr << "Error Reading Signature of: "; + std::cerr << fname; + std::cerr << std::endl; + std::cerr << "ABorting Load!"; + std::cerr << std::endl; + return -1; + } + unsigned char uc = (unsigned char) c; + signature += (unsigned char) uc; + } + if ('\n' != (c = fgetc(cfd))) + { + std::cerr << "Warning Mising seperator" << std::endl; + } + + std::cerr << "Read fname:" << fname << std::endl; + std::cerr << "Signature:" << std::endl; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", (unsigned char) signature[i]); + } + std::cerr << std::endl; + std::cerr << std::endl; + + // push back..... + fnames.push_back(fname); + hashes.push_back(signature); + + conftxt += "CERT "; + conftxt += fname; + conftxt += "\n"; + conftxt += signature; + conftxt += "\n"; + + // be sure to write over a bit... + name[0] = 'N'; + name[1] = 'O'; + } + + // string already waiting! + for(; ((ret != NULL) && (!strncmp(name, "OPT ", 4))); + ret = fgets(name, maxnamesize, cfd)) + { + for(i = 4; (name[i] != '\n') && (i < OPT_LEN); i++); + // terminate the string. + name[i] = '\0'; + + // so the name is first.... + std::string opt = &(name[4]); + + // now read the + std::string val; // cleaned up value. + std::string valsign; // value in the file. + for(i = 0; i < VAL_LEN; i++) + { + if (EOF == (c = fgetc(cfd))) + { + std::cerr << "Error Reading Value of: "; + std::cerr << opt; + std::cerr << std::endl; + std::cerr << "ABorting Load!"; + std::cerr << std::endl; + return -1; + } + // remove zeros on strings... + if (c != '\0') + { + val += (unsigned char) c; + } + valsign += (unsigned char) c; + } + if ('\n' != (c = fgetc(cfd))) + { + std::cerr << "Warning Mising seperator" << std::endl; + } + + std::cerr << "Read OPT:" << opt; + std::cerr << " Val:" << val << std::endl; + + // push back..... + tmpsettings[opt] = val; + + conftxt += "OPT "; + conftxt += opt; + conftxt += "\n"; + conftxt += valsign; + conftxt += "\n"; + + // be sure to write over a bit... + name[0] = 'N'; + name[1] = 'O'; + } + + // only read up to the first newline symbol.... + // continue... + for(i = 0; (name[i] != '\n') && (i < signlen); i++); + + //printf("Stepping over [%d] %0x\n", i, name[i]); + + + if (i != signlen) + { + for(i++; i < signlen; i++) + { + c = fgetc(cfd); + if (c == EOF) + { + std::cerr << "Error Reading Conf Signature:"; + std::cerr << std::endl; + return 1; + } + unsigned char uc = (unsigned char) c; + name[i] = uc; + } + } + + std::cerr << "Configuration File Signature: " << std::endl; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", (unsigned char) name[i]); + } + std::cerr << std::endl; + + + // when we get here - should have the final signature in the buffer. + // check. + // + // compare signatures. + // instead of verifying with the public key.... + // we'll sign it again - and compare .... FIX LATER... + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit(mdctx, EVP_sha1())) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, conftxt.c_str(), conftxt.length())) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + } + + if (0 == EVP_SignFinal(mdctx, conf_signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + } + + EVP_MD_CTX_destroy(mdctx); + fclose(cfd); + + std::cerr << "Recalced File Signature: " << std::endl; + for(i = 0; i < signlen; i++) + { + fprintf(stderr, "%02x", conf_signature[i]); + } + std::cerr << std::endl; + + bool same = true; + for(i = 0; i < signlen; i++) + { + if ((unsigned char) name[i] != conf_signature[i]) + { + same = false; + } + } + + if (same == false) + { + std::cerr << "ERROR VALIDATING CONFIGURATION!" << std::endl; + std::cerr << "PLEASE FIX!" << std::endl; + return -1; + } + std::list::iterator it; + std::list::iterator it2; + for(it = fnames.begin(), it2 = hashes.begin(); it != fnames.end(); it++, it2++) + { + std::string neighfile = neighdir + (*it) + ".pqi"; + cert *nc = loadcertificate(neighfile.c_str(), (*it2)); + if (nc != NULL) + { + if (0 > addCertificate(nc)) + { + // cleanup. + std::cerr << "Updated Certificate....but no"; + std::cerr << " need for addition"; + std::cerr << std::endl; + // X509_free(nc -> certificate); + //delete nc; + } + } + } + for(mit = tmpsettings.begin(); mit != tmpsettings.end(); mit++) + { + settings[mit -> first] = mit -> second; + } + return 1; +} + + +const int PQI_SSLROOT_CERT_CONFIG_SIZE = 1024; + +int sslroot::savecertificate(cert *c, const char *fname) +{ + // load certificates from file. + FILE *setfp = fopen(fname, "wb"); + if (setfp == NULL) + { + std::cerr << "sslroot::savecertificate() Bad File: " << fname; + std::cerr << " Cannot be Written!" << std::endl; + return -1; + } + + std::cerr << "Writing out Cert...:" << c -> Name() << std::endl; + PEM_write_XPGP(setfp, c -> certificate); + + // writing out details.... + + // read in a line..... + int size = PQI_SSLROOT_CERT_CONFIG_SIZE; + char line[size]; + std::list::iterator it; + + int i; + + // This will need to be made portable..... + + struct sockaddr_in *addr_inet; + struct sockaddr_in *addr_inet2; + struct sockaddr_in *addr_inet3; + + int pos_status = 0; + int pos_addr = sizeof(int); + int pos_addr2 = pos_addr + sizeof(*addr_inet); + int pos_addr3 = pos_addr2 + sizeof(*addr_inet2); + + int pos_lcts = pos_addr3 + sizeof(*addr_inet3); + int pos_lrts = pos_lcts + sizeof(int); + int pos_ncts = pos_lrts + sizeof(int); + int pos_ncvl = pos_ncts + sizeof(int); + int pos_name = pos_ncvl + sizeof(int); + int pos_end = pos_name + 20; // \n. for readability. + + int *status = (int *) &(line[pos_status]); + addr_inet = (struct sockaddr_in *) &(line[pos_addr]); + addr_inet2 = (struct sockaddr_in *) &(line[pos_addr2]); + addr_inet3 = (struct sockaddr_in *) &(line[pos_addr3]); + int *lcts = (int *) &(line[pos_lcts]); + int *lrts = (int *) &(line[pos_lrts]); + int *ncts = (int *) &(line[pos_ncts]); + int *ncvl = (int *) &(line[pos_ncvl]); + char *name = &(line[pos_name]); + char *end = &(line[pos_end]); + + for(i = 0; i < 1024; i++) + line[i] = 0; + + *status = c -> Status(); + *addr_inet = c -> lastaddr; + *addr_inet2 = c -> localaddr; + *addr_inet3 = c -> serveraddr; + + *lcts = c -> lc_timestamp; + *lrts = c -> lr_timestamp; + *ncts = c -> nc_timestamp; + *ncvl = c -> nc_timeintvl; + + std::string tmpname = c -> Name(); + for(i = 0; (i < (signed) tmpname.length()) && (i < 20 - 1); i++) + { + name[i] = tmpname[i]; + } + name[20 - 1] = '\0'; + end[0] = '\n'; + + /* now convert it to hex */ + char config_hex[2 * size]; + for(i = 0; i < size; i++) + { + sprintf(&(config_hex[i * 2]), "%02x", + (unsigned int) ((unsigned char *) line)[i]); + } + + if (1 != fwrite(config_hex, size * 2,1, setfp)) + { + std::cerr << "Error Writing Peer Record!" << std::endl; + return -1; + } + fclose(setfp); + + // then reopen to generate hash. + setfp = fopen(fname, "rb"); + if (setfp == NULL) + { + std::cerr << "sslroot::savecertificate() Bad File: " << fname; + std::cerr << " Opened for ReHash!" << std::endl; + return -1; + } + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + int maxsize = 20480; + int rbytes; + char inall[maxsize]; + if (0 == (rbytes = fread(inall, 1, maxsize, setfp))) + { + std::cerr << "Error Writing Peer Record!" << std::endl; + return -1; + } + std::cerr << "Read " << rbytes << std::endl; + + // so we read rbytes. + // hash. + //OpenSSL_add_all_digests(); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + } + + std::cerr << "Saved Cert: " << c -> Name() << " Status: "; + std::cerr << std::hex << (unsigned int) c->Status() << std::dec << std::endl; + + std::cerr << "Cert + Setting Signature is(" << signlen << "): "; + std::string signstr; + for(i = 0; i < (signed) signlen; i++) + { + fprintf(stderr, "%02x", signature[i]); + signstr += signature[i]; + } + std::cerr << std::endl; + + c -> Hash(signstr); + std::cerr << "Stored Hash Length: " << (c -> Hash()).length() << std::endl; + std::cerr << "Real Hash Length: " << signlen << std::endl; + + fclose(setfp); + + EVP_MD_CTX_destroy(mdctx); + + return 1; +} + +cert *sslroot::loadcertificate(const char *fname, std::string hash) +{ + // if there is a hash - check that the file matches it before loading. + FILE *pcertfp; + + /* We only check a signature's hash if + * we are loading from a configuration file. + * Therefore we saved the file and it should be identical. + * and a direct load + verify will work. + * + * If however it has been transported by email.... + * Then we might have to correct the data (strip out crap) + * from the configuration at the end. (XPGP load should work!) + */ + + if (hash.length() > 1) + { + pcertfp = fopen(fname, "rb"); + // load certificates from file. + if (pcertfp == NULL) + { + std::cerr << "sslroot::loadcertificate() Bad File: " << fname; + std::cerr << " Cannot be Hashed!" << std::endl; + return NULL; + } + + unsigned int signlen = EVP_PKEY_size(pkey); + unsigned char signature[signlen]; + + int maxsize = 20480; /* should be enough for about 50 signatures */ + int rbytes; + char inall[maxsize]; + if (0 == (rbytes = fread(inall, 1, maxsize, pcertfp))) + { + std::cerr << "Error Reading Peer Record!" << std::endl; + return NULL; + } + //std::cerr << "Read " << rbytes << std::endl; + + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + std::cerr << "EVP_SignInit Failure!" << std::endl; + } + + if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) + { + std::cerr << "EVP_SignUpdate Failure!" << std::endl; + } + + if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) + { + std::cerr << "EVP_SignFinal Failure!" << std::endl; + } + + fclose(pcertfp); + EVP_MD_CTX_destroy(mdctx); + + bool same = true; + if (signlen != hash.length()) + { + std::cerr << "Different Length Signatures... "; + std::cerr << "Cannot Load Certificate!" << std::endl; + return NULL; + } + + for(int i = 0; i < (signed) signlen; i++) + { + if (signature[i] != (unsigned char) hash[i]) + { + same = false; + std::cerr << "Invalid Signature... "; + std::cerr << "Cannot Load Certificate!" << std::endl; + return NULL; + } + } + std::cerr << "Verified Signature for: " << fname; + std::cerr << std::endl; + + + } + else + { + std::cerr << "Not checking cert signature" << std::endl; + } + + pcertfp = fopen(fname, "rb"); + + // load certificates from file. + if (pcertfp == NULL) + { + std::cerr << "sslroot::loadcertificate() Bad File: " << fname; + std::cerr << " Cannot be Read!" << std::endl; + return NULL; + } + + + XPGP *pc; + cert *npc = NULL; + + if ((pc = PEM_read_XPGP(pcertfp, NULL, NULL, NULL)) != NULL) + { + // read a certificate. + std::cerr << "Loaded Certificate: "; + std::cerr << pc -> name << std::endl; + + npc = makeCertificateXPGP(pc); + if (npc == NULL) + { + std::cerr << "Failed to Create Cert!" << std::endl; + return NULL; + } + } + else // (pc == NULL) + { + unsigned long err = ERR_get_error(); + std::cerr << "Read Failed .... CODE(" << err << ")" << std::endl; + std::cerr << ERR_error_string(err, NULL) << std::endl; + return NULL; + } + + // Now we try to read in 1024 bytes..... + // if successful, then have settings! + + // read in a line..... + int size = PQI_SSLROOT_CERT_CONFIG_SIZE; + char config_hex[PQI_SSLROOT_CERT_CONFIG_SIZE * 4]; /* double for extra space */ + char line[PQI_SSLROOT_CERT_CONFIG_SIZE]; + + /* load as much as possible into the config_hex. + */ + + int rbytes = fread(config_hex, 1, size * 4, pcertfp); + bool configLoaded = false; + int i, j; + + if (rbytes < size * 2) + { + if ((hash.size() > 1) && (rbytes >= size)) + { + /* old format certificate (already verified) */ + std::cerr << "Loading Old Style Cert Config" << std::endl; + memcpy(line, config_hex, size); + configLoaded = true; + } + else + { + std::cerr << "Error Reading Setting: Only Cert Retrieved" << std::endl; + return npc; + } + } + + /* if there was no hash then we need to check it */ + if (hash.size() <= 1) + { + std::cerr << "Checking Cert Configuration for spam char" << std::endl; + for(i = 0, j = 0; i < rbytes; i++) + { + if (isxdigit(config_hex[i])) + { + config_hex[j++] = config_hex[i]; + } + else + { + std::cerr << "Stripped out:" << config_hex[i] << " or " + << (int) config_hex[i] << "@" << i + << " j:" << j << std::endl; + } + + } + if (j < size * 2) + { + std::cerr << "Error Cert Config wrong size" << std::endl; + return npc; + } + std::cerr << "Stripped out " << i - j << " spam chars" << std::endl; + } + + /* now convert the hex into binary */ + if (!configLoaded) + { + for(i = 0; i < size; i++) + { + ((unsigned char *) line)[i] = convertHexToChar( + config_hex[2 * i], config_hex[2 * i + 1]); + } + configLoaded = true; + } + + // Data arrangment. + // so far + // ------------ + // 4 - (int) status + // 8 - sockaddr + // 8 - sockaddr + // 8 - sockaddr + // 4 - lc_timestamp + // 4 - lr_timestamp + // 4 - nc_timestamp + // 4 - nc_timeintvl + // 20 - name. + // 1 - end + + // This will need to be made portable..... + + struct sockaddr_in *addr_inet; + struct sockaddr_in *addr_inet2; + struct sockaddr_in *addr_inet3; + + //int pos_status = 0; + int pos_addr = sizeof(int); + int pos_addr2 = pos_addr + sizeof(*addr_inet); + int pos_addr3 = pos_addr2 + sizeof(*addr_inet2); + int pos_lcts = pos_addr3 + sizeof(*addr_inet3); + + int pos_lrts = pos_lcts + sizeof(int); + int pos_ncts = pos_lrts + sizeof(int); + int pos_ncvl = pos_ncts + sizeof(int); + int pos_name = pos_ncvl + sizeof(int); + //int pos_end = pos_name + 20; // \n. for readability. + + int *status = (int *) line; + addr_inet = (struct sockaddr_in *) &(line[pos_addr]); + addr_inet2 = (struct sockaddr_in *) &(line[pos_addr2]); + addr_inet3 = (struct sockaddr_in *) &(line[pos_addr3]); + int *lcts = (int *) &(line[pos_lcts]); + int *lrts = (int *) &(line[pos_lrts]); + int *ncts = (int *) &(line[pos_ncts]); + int *ncvl = (int *) &(line[pos_ncvl]); + char *name = &(line[pos_name]); + //char *end = &(line[pos_end]); + + // end of data structures.... + + + + // fill in the data. + cert *c = npc; + c -> Status(*status); + + std::cerr << "Loaded Cert: " << c -> Name() << " Prev Status: "; + std::cerr << std::hex << (unsigned int) c->Status() << std::dec << std::endl; + + // but ensure that inUse is not set. + c -> InUse(false); + + c -> lastaddr = *addr_inet; + c -> localaddr = *addr_inet2; + c -> serveraddr = *addr_inet3; + + c -> lc_timestamp = *lcts; + c -> lr_timestamp = *lrts; + c -> nc_timestamp = *ncts; + c -> nc_timeintvl = *ncvl; + + + name[20 - 1] = '\0'; + c -> Name(std::string(name)); + + // save the hash. + c -> Hash(hash); + + fclose(pcertfp); + + // small hack - as the timestamps seem wrong..... + // could be a saving thing - or a bug.... + c -> lc_timestamp = 0; + c -> lr_timestamp = 0; + + // reset these. as well. + c -> nc_timestamp = 0; + c -> nc_timeintvl = 5; + + return c; +} + + // for sending stuff as text + // cert * loadCertFromString(std::string pem); + // std::string saveCertAsString(cert *c); + // + +std::string sslroot::saveCertAsString(cert *c) +{ + // save certificate to a string, + // must use a BIO. + std::string certstr; + BIO *bp = BIO_new(BIO_s_mem()); + + std::cerr << "saveCertAsString:" << c -> Name() << std::endl; + PEM_write_bio_XPGP(bp, c -> certificate); + + /* translate the bp data to a string */ + char *data; + int len = BIO_get_mem_data(bp, &data); + for(int i = 0; i < len; i++) + { + certstr += data[i]; + } + + BIO_free(bp); + + return certstr; +} + +cert *sslroot::loadCertFromString(std::string pem) +{ + /* Put the data into a mem BIO */ + char *certstr = strdup(pem.c_str()); + + BIO *bp = BIO_new_mem_buf(certstr, -1); + + XPGP *pc; + cert *npc = NULL; + + pc = PEM_read_bio_XPGP(bp, NULL, NULL, NULL); + + BIO_free(bp); + free(certstr); + + if (pc != NULL) + { + // read a certificate. + std::cerr << "loadCertFromString: "; + std::cerr << pc -> name << std::endl; + + npc = makeCertificateXPGP(pc); + if (npc == NULL) + { + std::cerr << "Failed to Create Cert!" << std::endl; + return NULL; + } + } + else // (pc == NULL) + { + unsigned long err = ERR_get_error(); + std::cerr << "Read Failed .... CODE(" << err << ")" << std::endl; + std::cerr << ERR_error_string(err, NULL) << std::endl; + return NULL; + } + + // small hack - as the timestamps seem wrong..... + // could be a saving thing - or a bug.... + npc -> lc_timestamp = 0; + npc -> lr_timestamp = 0; + + // reset these. as well. + npc -> nc_timestamp = 0; + npc -> nc_timeintvl = 5; + + return npc; +} + + +unsigned char convertHexToChar(unsigned char a, unsigned char b) +{ + int num1 = 0; + int num2 = 0; + if (('0' <= a) && ('9' >= a)) + { + num1 = a - '0'; + } + else if (('a' <= a) && ('f' >= a)) + { + num1 = 10 + a - 'a'; + } + else if (('A' <= a) && ('F' >= a)) + { + num1 = 10 + a - 'A'; + } + + if (('0' <= b) && ('9' >= b)) + { + num2 = b - '0'; + } + else if (('a' <= b) && ('f' >= b)) + { + num2 = 10 + b - 'a'; + } + else if (('A' <= b) && ('F' >= b)) + { + num2 = 10 + b - 'A'; + } + + num1 *= 16; + num1 += num2; + + return (unsigned char) num1; +} + +int sslroot::printCertificate(cert *c, std::ostream &out) +{ + out << "Cert Name:" << (c -> certificate) -> name << std::endl; + //X509_print_fp(stderr, c -> certificate); + return 1; +} + +// This function will clean up X509 *c if necessary. +// This fn will also collate the signatures.... +// that are received via p3disc. +// (connections -> registerCertificate, which does similar sign merging) +// +cert *sslroot::makeCertificateXPGP(XPGP *c) +{ + if (c == NULL) + { + return NULL; + } + + // At this point we check to see if there is a duplicate. + cert *dup = checkDuplicateXPGP(c); + cert *npc = NULL; + if (dup == NULL) + { + npc = new cert(); + npc -> certificate = c; + if (!addtosignmap(npc)) // only allow the cert if no dup + { + std::cerr << "sslroot::makeCertificate()"; + std::cerr << "Failed to Get Signature - Not Allowed!"; + std::cerr << std::endl; + + // failed!... cannot add it!. + delete npc; + return NULL; + } + + /* setup the defaults */ + npc -> Status(PERSON_STATUS_MANUAL); + npc -> trustLvl = -1; + // set Tag to be their X509CN. + npc -> Name(getX509CNString(npc->certificate-> + subject->subject)); + + allcerts.push_back(npc); + std::cerr << "sslroot::makeCertificate() For " << c -> name; + std::cerr << " A-Okay!" << std::endl; + // at this point we need to add to the signaturelist.... + + } + else if (c == dup -> certificate) + { + // identical - so okay. + npc = dup; + std::cerr << "sslroot::makeCertificate() For " << c -> name; + std::cerr << " Found Identical - A-Okay!" << std::endl; + } + else + { + std::cerr << "sslroot::makeCertificate() For " << c -> name; + std::cerr << " Cleaning up other XPGP!" << std::endl; + std::cerr << " Also moving new signatures ... " << std::endl; + // clean up c. + XPGP_copy_known_signatures(pgp_keyring, dup -> certificate, c); + XPGP_free(c); + npc = dup; + } + return npc; +} + + +cert *sslroot::checkDuplicateXPGP(XPGP *x) +{ + if (x == NULL) + return NULL; + + // loop through and print - then check. + std::list::iterator it; + for(it = allcerts.begin(); it != allcerts.end(); it++) + { + if (0 == XPGP_cmp((*it) -> certificate, x)) + { + return (*it); + } + } + return NULL; +} + + +cert *sslroot::checkPeerXPGP(XPGP *x) +{ + if (x == NULL) + return NULL; + + // loop through and print - then check. + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + if (0 == XPGP_cmp((*it) -> certificate, x)) + { + return (*it); + } + } + return NULL; +} + + + +cert *sslroot::findpeercert(const char *name) +{ + // loop through and print - then check. + //std::cerr << "Checking Certs for: " << name << std::endl; + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + char *certname = ((*it) -> certificate) -> name; + //std::cerr << "Cert Name:" << certname << std::endl; + if (strstr(certname, name) != NULL) + { + //std::cerr << "Matches!" << std::endl; + return (*it); + } + } + std::cerr << "sslroot::findpeercert() Failed!" << std::endl; + return NULL; +} + +// returns zero for the same. +int sslroot::compareCerts(cert *a, cert *b) +{ + // std::cerr << "Comparing Certificates:" << std::endl; + //printCertificate(a); + //printCertificate(b); + //X509_print_fp(stderr, a -> certificate); + //X509_print_fp(stderr, b -> certificate); + + int val = XPGP_cmp(a -> certificate, b -> certificate); + + std::cerr << "Certificate Comparison Returned: " << val << std::endl; + + return val; +} + +cert * sslroot::registerCertificateXPGP(XPGP *nc, struct sockaddr_in raddr, bool in) +{ + if (nc == NULL) + return NULL; + + // shoud check all certs. + cert *c = checkDuplicateXPGP(nc); + if (c != NULL) + { + if (c -> certificate == nc) + { + std::cerr << "sslroot::registerCertificate()"; + std::cerr << " Found Identical XPGP cert"; + std::cerr << std::endl; + } + else + { + std::cerr << "sslroot::registerCertificate()"; + std::cerr << " Found Same XPGP cert/diff mem - Clean"; + std::cerr << std::endl; + std::cerr << "sslroot::registerCertificate()"; + std::cerr << " Copying New Signatures before deleting"; + std::cerr << std::endl; + /* copy across the signatures -> if necessary */ + XPGP_copy_known_signatures(pgp_keyring, c->certificate, nc); + XPGP_free(nc); + } + + if (!c -> Connected()) + { + c -> lastaddr = raddr; + + if (in == true) + { + c -> lr_timestamp = time(NULL); + // likely to be server address + // (with default port) + // if null! + if (!isValidNet(&(c -> serveraddr.sin_addr))) + { + std::cerr << "Guessing Default Server Addr!"; + std::cerr << std::endl; + c -> serveraddr = raddr; + c -> serveraddr.sin_port = + htons(PQI_DEFAULT_PORT); + } + } + else + { + c -> lc_timestamp = time(NULL); + // also likely to be servera address, + // but we can check and see if its local. + // can flag local + if (0 == inaddr_cmp(c -> localaddr, raddr)) + { + c -> Local(true); + // don't set serveraddr -> just ignore + } + else + { + c -> serveraddr = raddr; + c -> Firewalled(false); + } + } + } + else + { + std::cerr << "WARNING: attempt to reg CONNECTED Cert!"; + std::cerr << std::endl; + } + return c; + } + + std::cerr << "sslroot::registerCertificate() Certificate Not Found!" << std::endl; + std::cerr << "Saving :" << nc -> name << std::endl; + std::cerr << std::endl; + + cert *npc = makeCertificateXPGP(nc); + if (npc == NULL) + { + std::cerr << "Failed to Make Certificate"; + std::cerr << std::endl; + return NULL; + } + + npc -> Name(nc -> name); + + npc -> lastaddr = raddr; + if (in == true) + { + npc -> lr_timestamp = time(NULL); + // likely to be server address (with default port) + std::cerr << "Guessing Default Server Addr!"; + std::cerr << std::endl; + npc -> serveraddr = raddr; + npc -> serveraddr.sin_port = htons(PQI_DEFAULT_PORT); + } + else + { + npc -> lc_timestamp = time(NULL); + + // as it is a new cert... all fields are + // null and the earlier tests must be + // delayed until the discovery packets. + + // also likely to be server. + npc -> serveraddr = raddr; + } + + // push back onto collected. + npc -> nc_timestamp = 0; + collectedcerts.push_back(npc); + + // return NULL to indicate that it dosen't yet exist in dbase. + return NULL; +} + +cert * sslroot::getCollectedCert() +{ + if (collectedcerts.size() < 1) + return NULL; + + cert *c = collectedcerts.front(); + collectedcerts.pop_front(); + return c; +} + +bool sslroot::collectedCerts() +{ + return (collectedcerts.size() > 0); +} + + +int sslroot::removeCertificate(cert *c) +{ + if (c -> InUse()) + { + std::cerr << "sslroot::removeCertificate() Failed" << std::endl; + std::cerr << "\t a cert is in use." << std::endl; + return -1; + } + + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + if (c == (*it)) + { + peercerts.erase(it); + + c -> InUse(false); + c -> Accepted(false); + + std::cerr << "sslroot::removeCertificate() "; + std::cerr << "Success!" << std::endl; + std::cerr << "\tMoved to Collected Certs" << std::endl; + + /* remove from the keyring */ + XPGP_remove_certificate(pgp_keyring, c->certificate); + + collectedcerts.push_back(c); + + certsChanged.IndicateChanged(); + certsMajorChanged.IndicateChanged(); + return 1; + } + } + std::cerr << "sslroot::removeCertificate() "; + std::cerr << "Failed to Match Cert!" << std::endl; + + return 0; +} + + +int sslroot::addCertificate(cert *c) +{ + std::cerr << "sslroot::addCertificate()" << std::endl; + c -> InUse(false); + // let most flags through. + //c -> Accepted(false); + //c -> WillConnect(false); + if (c -> certificate == NULL) + { + std::cerr << "sslroot::addCertificate() certificate==NULL" << std::endl; + std::cerr << "\tNot Adding Certificate!" << std::endl; + return 0; + } + + cert *dup = checkPeerXPGP(c -> certificate); + if (dup != NULL) + { + std::cerr << "sslroot::addCertificate() Not Adding"; + std::cerr << "Certificate with duplicate...." << std::endl; + std::cerr << "\t\tTry RegisterCertificate() " << std::endl; + + return -1; + } + + // else put in in the list. + peercerts.push_back(c); + + /* add to keyring */ + XPGP_add_certificate(pgp_keyring, c->certificate); + + /* if this should be a trusted cert... setup */ + if (c-> Trusted()) + { + if (XPGP_signer_trusted(pgp_keyring, c -> certificate)) + { + c -> Trusted(true); + } + else + { + c -> Trusted(false); + } + } + + c -> trustLvl = XPGP_auth_certificate(pgp_keyring, c->certificate); + + certsChanged.IndicateChanged(); + certsMajorChanged.IndicateChanged(); + + return 1; +} + + +int sslroot::addUntrustedCertificate(cert *c) +{ + // blank it all. + c -> Status(PERSON_STATUS_MANUAL); + // set Tag to be their X509CN. + c -> Name(getX509CNString(c -> certificate -> subject -> subject)); + + return addCertificate(c); +} + + +int sslroot::addCollectedCertificate(cert *c) +{ + // blank it all. + c -> Status(PERSON_STATUS_MANUAL); + // set Tag to be their X509CN. + c -> Name(getX509CNString(c -> certificate -> subject -> subject)); + + // put in the collected certs ... + collectedcerts.push_back(c); + return 1; +} + + + +int sslroot::validateCertificateXPGP(cert *c) +{ + std::cerr << "sslroot::validateCertificate() Why Not!" << std::endl; + if (XPGP_check_valid_certificate(c->certificate)) + { + c -> Valid(true); + } + else + { + c -> Valid(false); + } + std::cerr << "Cert Status: " << c -> Status() << std::endl; + return 1; +} + + +/* this redoes the trust calculations */ +int sslroot::checkAuthCertificate(cert *xpgp) +{ + std::cerr << "sslroot::checkAuthCertificate()" << std::endl; + if ((xpgp == NULL) || (xpgp -> certificate == NULL)) + { + return -1; + } + + /* reevaluate the auth of the xpgp */ + xpgp -> trustLvl = XPGP_auth_certificate(pgp_keyring, xpgp->certificate); + + /* this also merges the signature into the keyring */ + certsChanged.IndicateChanged(); + certsMajorChanged.IndicateChanged(); + return 1; +} + + +int sslroot::signCertificate(cert *xpgp) +{ + std::cerr << "sslroot::signCertificate()" << std::endl; + cert *own = getOwnCert(); + + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::signCertificate()"); + + /* check that cert is suitable */ + /* sign it */ + XPGP_sign_certificate(pgp_keyring, xpgp -> certificate, own -> certificate); + + /* reevaluate the auth of the xpgp */ + xpgp -> trustLvl = XPGP_auth_certificate(pgp_keyring, xpgp->certificate); + + /* this also merges the signature into the keyring */ + certsChanged.IndicateChanged(); + certsMajorChanged.IndicateChanged(); + return 1; +} + +int sslroot::trustCertificate(cert *c, bool totrust) +{ + std::cerr << "sslroot::trustCertificate()" << std::endl; + /* check auth status of certificate */ + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::trustCertificate()"); + + /* if trusted -> untrust */ + if (!totrust) + { + XPGP_signer_untrusted(pgp_keyring, c -> certificate); + c -> Trusted(false); + } + else + { + /* if auth then we can trust them */ + if (XPGP_signer_trusted(pgp_keyring, c -> certificate)) + { + c -> Trusted(true); + } + } + + /* reevaluate the auth of the xpgp */ + c -> trustLvl = XPGP_auth_certificate(pgp_keyring, c->certificate); + + certsChanged.IndicateChanged(); + certsMajorChanged.IndicateChanged(); + + return 1; +} + +int sslroot::superNodeMode() +{ +# +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS // UNIX only. + + XPGP_supernode(pgp_keyring); +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + return 1; +} + +/***** REMOVED!!! + * + * +std::list sslroot::listCertificates() +{ + std::list names; + std::list::iterator it; + for(it = peercerts.begin(); it != peercerts.end(); it++) + { + names.push_back(((*it) -> certificate) -> name); + } + return names; +} + * + * + * + */ + + +bool sslroot::CertsChanged() +{ + return certsChanged.Changed(0); +} + +bool sslroot::CertsMajorChanged() +{ + return certsMajorChanged.Changed(0); +} + +void sslroot::IndicateCertsChanged() +{ + certsChanged.IndicateChanged(); +} + + +std::list &sslroot::getCertList() +{ + return peercerts; +} + +std::string sslroot::getSetting(std::string opt) +{ + std::map::iterator it; + if (settings.end() != (it = settings.find(opt))) + { + // found setting. + std::cerr << "sslroot::getSetting(" << opt << ") = "; + std::cerr << it -> second << std::endl; + return it -> second; + } + // else return empty string. + std::cerr << "sslroot::getSetting(" << opt; + std::cerr << ") Not There!" << std::endl; + + std::string empty(""); + return empty; +} + +void sslroot::setSetting(std::string opt, std::string val) +{ + // check settings.. + std::cerr << "sslroot::saveSetting(" << opt << ", "; + std::cerr << val << ")" << std::endl; + + settings[opt] = val; + return; +} + +cert *sslroot::getOwnCert() +{ + return own_cert; +} + +int sslroot::checkNetAddress() +{ + std::list addrs = getLocalInterfaces(); + std::list::iterator it; + + bool found = false; + for(it = addrs.begin(); (!found) && (it != addrs.end()); it++) + { + if ((*it) == inet_ntoa(own_cert -> localaddr.sin_addr)) + { + found = true; + } + } + /* check that we didn't catch 0.0.0.0 - if so go for prefered */ + if ((found) && (own_cert -> localaddr.sin_addr.s_addr == 0)) + { + found = false; + } + + if (!found) + { + own_cert -> localaddr.sin_addr = getPreferredInterface(); + } + if ((isPrivateNet(&(own_cert -> localaddr.sin_addr))) || + (isLoopbackNet(&(own_cert -> localaddr.sin_addr)))) + { + own_cert -> Firewalled(true); + } + else + { + //own_cert -> Firewalled(false); + } + + int port = ntohs(own_cert -> localaddr.sin_port); + if ((port < PQI_MIN_PORT) || (port > PQI_MAX_PORT)) + { + own_cert -> localaddr.sin_port = htons(PQI_DEFAULT_PORT); + } + + /* if localaddr = serveraddr, then ensure that the ports + * are the same (modify server)... this mismatch can + * occur when the local port is changed.... + */ + + if (own_cert -> localaddr.sin_addr.s_addr == + own_cert -> serveraddr.sin_addr.s_addr) + { + own_cert -> serveraddr.sin_port = + own_cert -> localaddr.sin_port; + } + + // ensure that address family is set, otherwise windows Barfs. + own_cert -> localaddr.sin_family = AF_INET; + own_cert -> serveraddr.sin_family = AF_INET; + own_cert -> lastaddr.sin_family = AF_INET; + + return 1; +} + + + + +/********** SSL ERROR STUFF ******************************************/ + +int printSSLError(SSL *ssl, int retval, int err, unsigned long err2, + std::ostream &out) +{ + std::string reason; + + std::string mainreason = std::string("UNKNOWN ERROR CODE"); + if (err == SSL_ERROR_NONE) + { + mainreason = std::string("SSL_ERROR_NONE"); + } + else if (err == SSL_ERROR_ZERO_RETURN) + { + mainreason = std::string("SSL_ERROR_ZERO_RETURN"); + } + else if (err == SSL_ERROR_WANT_READ) + { + mainreason = std::string("SSL_ERROR_WANT_READ"); + } + else if (err == SSL_ERROR_WANT_WRITE) + { + mainreason = std::string("SSL_ERROR_WANT_WRITE"); + } + else if (err == SSL_ERROR_WANT_CONNECT) + { + mainreason = std::string("SSL_ERROR_WANT_CONNECT"); + } + else if (err == SSL_ERROR_WANT_ACCEPT) + { + mainreason = std::string("SSL_ERROR_WANT_ACCEPT"); + } + else if (err == SSL_ERROR_WANT_X509_LOOKUP) + { + mainreason = std::string("SSL_ERROR_WANT_X509_LOOKUP"); + } + else if (err == SSL_ERROR_SYSCALL) + { + mainreason = std::string("SSL_ERROR_SYSCALL"); + } + else if (err == SSL_ERROR_SSL) + { + mainreason = std::string("SSL_ERROR_SSL"); + } + out << "RetVal(" << retval; + out << ") -> SSL Error: " << mainreason << std::endl; + out << "\t + ERR Error: " << ERR_error_string(err2, NULL) << std::endl; + return 1; +} + +cert::cert() + :certificate(NULL), hash("") +{ + return; +} + +cert::~cert() +{ + return; +} + +std::string cert::Signature() +{ + if (certificate == NULL) + { + return Name(); + } + else + { + std::ostringstream out; + certsign cs; + getSSLRoot() -> getcertsign(this, cs); + + out << std::hex; + for(int i = 0; i < CERTSIGNLEN; i++) + { + + unsigned char n = cs.data[i]; + out << std::hex << std::setw(2) << std::setfill('0') + << std::setprecision(2) << (unsigned int) n; + } + return out.str(); + } +} + +std::string cert::Hash() +{ + return hash; +} + + +void cert::Hash(std::string h) +{ + hash = h; + return; +} + + + +/********************* signature stuff *********************/ + +bool certsign::operator<(const certsign &ref) const +{ + //compare the signature. + if (0 > memcmp(data, ref.data, CERTSIGNLEN)) + return true; + return false; +} + + +bool certsign::operator==(const certsign &ref) const +{ + //compare the signature. + return (0 == memcmp(data, ref.data, CERTSIGNLEN)); +} + + +/* Fns for relating cert signatures to structures */ +cert *sslroot::findPeerId(std::string id) +{ + certsign sign; + if (!convert_to_certsign(id, sign)) + { + pqioutput(PQL_WARNING, pqisslrootzone, + "sslroot::findPeerId() ERROR: Failed to Convert to certsign"); + return NULL; + } + + return findcertsign(sign); +} + +cert *sslroot::findcertsign(certsign &sign) +{ + std::map::iterator it; + + std::ostringstream out; + out << "sslroot::findcertsign()" << std::endl; + for (it = signmap.begin(); it != signmap.end(); it++) + { + out << "Checking Vs " << it -> second -> Name(); + if (sign == it -> first) + { + out << "Match!"; + } + out << std::endl; + } + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + + if (signmap.end() != (it = signmap.find(sign))) + { + return it -> second; + } + pqioutput(PQL_WARNING, pqisslrootzone, + "sslroot::findcertsign() ERROR: No Matching Entry"); + return NULL; +} + +int sslroot::getcertsign(cert *c, certsign &sign) +{ + // bug ... segv a couple of times here! + if ((c == NULL) || (c->certificate == NULL)) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::getcertsign() ERROR: NULL c || c->certificate"); + return 0; + } + + // a Bit of a hack here..... + // get the first signature.... + if (sk_XPGP_SIGNATURE_num(c->certificate->signs) < 1) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::getcertsign() ERROR: No Signatures"); + return 0; + } + XPGP_SIGNATURE *xpgpsign = sk_XPGP_SIGNATURE_value(c->certificate->signs, 0); + + // get the signature from the cert, and copy to the array. + ASN1_BIT_STRING *signature = xpgpsign->signature; + int signlen = ASN1_STRING_length(signature); + if (signlen < CERTSIGNLEN) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::getcertsign() ERROR: short Signature"); + return 0; + } + // else copy in the first CERTSIGNLEN. + unsigned char *signdata = ASN1_STRING_data(signature); + memcpy(sign.data, signdata, CERTSIGNLEN); + + return 1; +} + +int sslroot::addtosignmap(cert *c) +{ + certsign cs; + if (!getcertsign(c, cs)) + { + // error. + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::addsigntomap() ERROR: Fail to getcertsign()"); + return 0; + } + cert *c2 = findcertsign(cs); + if (c2 == NULL) + { + // add, and return okay. + signmap[cs] = c; + return 1; + } + if (c2 != c) + { + // error. + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::addsigntomap() ERROR: Duplicate Entry()"); + return 0; + } + + // else already exists. + return 1; +} + + + + +int sslroot::hashFile(std::string fname, unsigned char *hash, unsigned int hlen) +{ + // open the file. + // setup the hash. + + // pipe the file through. + + + return 1; +} + +int sslroot::hashDigest(char *data, unsigned int dlen, + unsigned char *hash, unsigned int hlen) +{ + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_DigestInit_ex(mdctx, EVP_sha1(), NULL)) + { + std::cerr << "EVP_DigestInit Failure!" << std::endl; + return -1; + } + + if (0 == EVP_DigestUpdate(mdctx, data, dlen)) + { + std::cerr << "EVP_DigestUpdate Failure!" << std::endl; + return -1; + } + + unsigned int signlen = hlen; + if (0 == EVP_DigestFinal_ex(mdctx, hash, &signlen)) + { + std::cerr << "EVP_DigestFinal Failure!" << std::endl; + return -1; + } + + EVP_MD_CTX_destroy(mdctx); + return signlen; +} + + + +int sslroot::signDigest(EVP_PKEY *key, char *data, unsigned int dlen, + unsigned char *sign, unsigned int slen) +{ + unsigned int signlen = EVP_PKEY_size(key); + + { + std::ostringstream out; + out << "sslroot::signDigest(" << (void *) key; + out << ", " << (void *) data << ", " << dlen << ", "; + out << (void *) sign << ", " << slen << ")" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + } + + if (signlen > slen) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::signDigest() Sign Length too short"); + return -1; + } + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::signDigest() EVP_SignInit Failure!"); + return -1; + } + + if (0 == EVP_SignUpdate(mdctx, data, dlen)) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::signDigest() EVP_SignUpdate Failure!"); + return -1; + } + + signlen = slen; + if (0 == EVP_SignFinal(mdctx, sign, &signlen, key)) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::signDigest() EVP_SignFinal Failure!"); + return -1; + } + + { + // display signed data + std::ostringstream out; + out << "sslroot::signDigest() Data Display" << std::endl; + out << "Data To Sign (" << dlen << "):::::::::::::" << std::hex; + for(unsigned int i = 0; i < dlen; i++) + { + if (i % 16 == 0) + { + out << std::endl; + out << std::setw(4) << i << " : "; + } + out << std::setw(2) << (unsigned int) ((unsigned char *) data)[i] << " "; + } + out << std::endl; + out << "Signature (" << std::dec << slen << "):::::::::::::" << std::hex; + for(unsigned int i = 0; i < slen; i++) + { + if (i % 16 == 0) + { + out << std::endl; + out << std::setw(4) << i << " : "; + } + out << std::setw(2) << (unsigned int) ((unsigned char *) sign)[i] << " "; + } + out << std::endl; + out << "::::::::::::::::::::::::::::::::::::::::::::::" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str()); + + } + + + EVP_MD_CTX_destroy(mdctx); + return signlen; +} + + +int sslroot::verifyDigest(EVP_PKEY *key, char *data, unsigned int dlen, + unsigned char *sign, unsigned int slen) +{ + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + + if (0 == EVP_VerifyInit_ex(mdctx, EVP_sha1(), NULL)) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::verifyDigest() EVP_VerifyInit Failure!"); + return -1; + } + + if (0 == EVP_VerifyUpdate(mdctx, data, dlen)) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::verifyDigest() EVP_VerifyUpdate Failure!"); + return -1; + } + + int vv; + if (0 > (vv = EVP_VerifyFinal(mdctx, sign, slen, key))) + { + pqioutput(PQL_ALERT, pqisslrootzone, + "sslroot::verifyDigest() EVP_VerifyFinale Failure!"); + return -1; + } + if (vv == 1) + { + pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, + "sslroot::verifyDigest() Verified Signature OKAY"); + } + else + { + std::ostringstream out; + out << "sslroot::verifyDigest() Failed Verification!" << std::endl; + out << "Data To Verify (" << dlen << "):::::::::::::" << std::hex; + for(unsigned int i = 0; i < dlen; i++) + { + if (i % 16 == 0) + { + out << std::endl; + out << std::setw(4) << i << " : "; + } + out << std::setw(2) << (unsigned int) ((unsigned char *) data)[i] << " "; + } + out << std::endl; + out << "Signature (" << std::dec << slen << "):::::::::::::" << std::hex; + for(unsigned int i = 0; i < slen; i++) + { + if (i % 16 == 0) + { + out << std::endl; + out << std::setw(4) << i << " : "; + } + out << std::setw(2) << (unsigned int) ((unsigned char *) sign)[i] << " "; + } + out << std::endl; + out << "::::::::::::::::::::::::::::::::::::::::::::::" << std::endl; + out << "sslroot::verifyDigest() Should Clear SSL Error!"; + + pqioutput(PQL_ALERT, pqisslrootzone, out.str()); + } + + EVP_MD_CTX_destroy(mdctx); + return vv; +} + +// Think both will fit in the one Structure. +int sslroot::generateKeyPair(EVP_PKEY *keypair, unsigned int keylen) +{ + RSA *rsa = RSA_generate_key(2048, 65537, NULL, NULL); + EVP_PKEY_assign_RSA(keypair, rsa); + std::cerr << "sslroot::generateKeyPair()" << std::endl; + return 1; +} + +// Extra features for XPGP..... (for login window) + + + +// This fn installs and signs a trusted peer. +// It is limited to only working just after certificate creation. +// this is done by checking the timestamps. +// +// It should be called before the pqi handler is initiated, +// otherwise the connection will not be automatically started. + +int sslroot::loadInitialTrustedPeer(std::string tp_file) +{ + /* we will only do this if various conditions are met. + * (1) check validity of certificate + * (2) check that we don't have any other certificates loaded. + * (3) check that our certificate has just been created (timestamp) and only has one signature. + */ + + bool canLoad = true; + + std::string userName; + + /* check (1) valid cert */ + if (!LoadCheckXPGPandGetName(tp_file.c_str(),userName)) + { + std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(1)"; + std::cerr << std::endl; + canLoad = false; + return 0; + } + + /* check (2) no other certificates loaded */ + if (peercerts.size() != 1) + { + /* too many certs loaded! */ + std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(2a)"; + std::cerr << std::endl; + canLoad = false; + return 0; + } + + /* that one must be our own */ + cert *ourcert = getOwnCert(); + if ((!ourcert) || (ourcert != *(peercerts.begin())) || (!ourcert->certificate)) + { + /* too many certs loaded! */ + std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(2b)"; + std::cerr << std::endl; + canLoad = false; + return 0; + } + + XPGP *xpgp = ourcert->certificate; + if (sk_XPGP_SIGNATURE_num(xpgp->signs) != 1) + { + /* too many certs loaded! */ + std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(3a)"; + std::cerr << std::endl; + canLoad = false; + return 0; + } + + /* check own certificate timestamps */ + time_t cts = time(NULL); + X509_VAL *certv = xpgp->key->validity; + XPGP_SIGNATURE *ownsign = sk_XPGP_SIGNATURE_value(xpgp->signs, 0); + ASN1_TIME *signts = ownsign->timestamp; + ASN1_TIME *createts = certv->notBefore; + + /* compare timestamps + * Certificate timestamp should have been generated + * within the last 5 seconds, */ + time_t max_initts = cts - 5; + if ((0 > X509_cmp_time(createts, &max_initts)) || (0 > X509_cmp_time(signts, &max_initts))) + { + std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(3b)"; + std::cerr << std::endl; + canLoad = false; + return 0; + } + + /* or if in the future! */ + if ((0 < X509_cmp_current_time(createts)) || (0 < X509_cmp_current_time(signts))) + { + std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(3c)"; + std::cerr << std::endl; + canLoad = false; + return 0; + } + + /* if we get here - it has passed the tests, and we can sign it, and install it */ + cert *trusted_cert = loadcertificate(tp_file.c_str(), ""); /* no Hash! */ + if (!trusted_cert) + { + std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(4a)"; + std::cerr << std::endl; + canLoad = false; + return 0; + } + + /* now add it */ + if (1 != addCertificate(trusted_cert)) + { + std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(4b)"; + std::cerr << std::endl; + canLoad = false; + return 0; + } + + /* must set these flags completely - as they aren't changed */ + trusted_cert->Accepted(true); + trusted_cert->Manual(false); + trusted_cert->WillConnect(true); + trusted_cert->WillListen(true); + /* use existing firewall/forwarded flags */ + + /* sign it! (must be after add) */ + if (!signCertificate(trusted_cert)) + { + std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(4c)"; + std::cerr << std::endl; + canLoad = false; + return 0; + } + + if (canLoad) + { + std::cerr << "sslroot::loadInitialTrustedPeer() Loaded: " << userName; + std::cerr << std::endl; + return 1; + } + return 0; +} + + + + + +// Not dependent on sslroot. load, and detroys the XPGP memory. + +int LoadCheckXPGPandGetName(const char *cert_file, std::string &userName) +{ + /* This function loads the XPGP certificate from the file, + * and checks the certificate + */ + + FILE *tmpfp = fopen(cert_file, "r"); + if (tmpfp == NULL) + { + std::cerr << "sslroot::LoadCheckAndGetXPGPName()"; + std::cerr << " Failed to open Certificate File:" << cert_file; + std::cerr << std::endl; + return 0; + } + + // get xPGP certificate. + XPGP *xpgp = PEM_read_XPGP(tmpfp, NULL, NULL, NULL); + fclose(tmpfp); + + // check the certificate. + bool valid = false; + if (xpgp) + { + valid = XPGP_check_valid_certificate(xpgp); + } + + if (valid) + { + // extract the name. + userName = getX509CNString(xpgp->subject->subject); + } + + // clean up. + XPGP_free(xpgp); + + if (valid) + { + // happy! + return 1; + } + else + { + // something went wrong! + return 0; + } +} + +std::string getX509NameString(X509_NAME *name) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + namestr += "\t"; + namestr += OBJ_nid2ln(OBJ_obj2nid(entry_obj)); + namestr += " : "; + + //namestr += entry_obj -> flags; + //namestr += entry_data -> length; + //namestr += entry_data -> type; + + //namestr += entry_data -> flags; + //entry -> set; + + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "NULL"; + } + + if (i + 1 < X509_NAME_entry_count(name)) + { + namestr += "\n"; + } + + } + return namestr; +} + + +std::string getX509CNString(X509_NAME *name) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + if (0 == strncmp("CN", OBJ_nid2sn(OBJ_obj2nid(entry_obj)), 2)) + { + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "Unknown"; + } + return namestr; + } + } + return namestr; +} + + +std::string getX509TypeString(X509_NAME *name, char *type, int len) +{ + std::string namestr; + for(int i = 0; i < X509_NAME_entry_count(name); i++) + { + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); + ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); + ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); + + if (0 == strncmp(type, OBJ_nid2sn(OBJ_obj2nid(entry_obj)), len)) + { + if (entry_data -> data != NULL) + { + namestr += (char *) entry_data -> data; + } + else + { + namestr += "Unknown"; + } + return namestr; + } + } + return namestr; +} + + +std::string getX509LocString(X509_NAME *name) +{ + return getX509TypeString(name, "L", 2); +} + +std::string getX509OrgString(X509_NAME *name) +{ + return getX509TypeString(name, "O", 2); +} + + +std::string getX509CountryString(X509_NAME *name) +{ + return getX509TypeString(name, "C", 2); +} + + +std::string convert_to_str(certsign &sign) +{ + std::ostringstream id; + for(int i = 0; i < CERTSIGNLEN; i++) + { + id << std::hex << std::setw(2) << std::setfill('0') << (uint16_t) (((uint8_t *) (sign.data))[i]); + } + return id.str(); +} + +bool convert_to_certsign(std::string id, certsign &sign) +{ + char num[3]; + if (id.length() < CERTSIGNLEN * 2) + { + return false; + } + + for(int i = 0; i < CERTSIGNLEN; i++) + { + num[0] = id[i * 2]; + num[1] = id[i * 2 + 1]; + num[2] = '\0'; + int32_t val; + if (1 != sscanf(num, "%x", &val)) + { + return false; + } + sign.data[i] = (uint8_t) val; + } + return true; +} + + diff --git a/libretroshare/src/_pqi/xpgpcert.h b/libretroshare/src/_pqi/xpgpcert.h new file mode 100644 index 000000000..88975c721 --- /dev/null +++ b/libretroshare/src/_pqi/xpgpcert.h @@ -0,0 +1,221 @@ +/* + * "$Id: xpgpcert.h,v 1.9 2007-04-15 18:45:18 rmf24 Exp $" + * + * 3P/PQI network interface for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef XPGPCERT_H +#define XPGPCERT_H + +/* This is the trial XPGP version + * + * It has to be compiled against XPGP ssl version. + * this is only a hacked up version, merging + * (so both can operate in parallel will happen later) + * + */ + +#include +#include + +#include +#include + +#include "pqi_base.h" +#include "pqinetwork.h" + +#include "pqiindic.h" + + +// helper fns. +int printSSLError(SSL *ssl, int retval, int err, unsigned long err2, std::ostream &out); +std::string getX509NameString(X509_NAME *name); +std::string getX509CNString(X509_NAME *name); + +std::string getX509OrgString(X509_NAME *name); +std::string getX509LocString(X509_NAME *name); +std::string getX509CountryString(X509_NAME *name); + +int LoadCheckXPGPandGetName(const char *cert_file, std::string &userName); + +std::string convert_to_str(certsign &sign); +bool convert_to_certsign(std::string id, certsign &sign); + +class sslroot; + +class cert: public Person +{ + public: + cert(); +virtual ~cert(); + +virtual std::string Signature(); +std::string Hash(); +void Hash(std::string); +std::string PeerId() { return Signature(); } + + XPGP *certificate; + std::string hash; + std::string peerid; +}; + + +// returns pointer to static variable. +// which must be inited.. +sslroot *getSSLRoot(); + +class sslroot +{ +public: + sslroot(); + int active(); + int setcertdir(char *path); + int initssl(const char *srvr_cert, const char *priv_key, + const char *passwd); + int closessl(); + + /* Context handling */ + SSL_CTX *getCTX(); + + /* Certificate handling */ + int compareCerts(cert *a, cert *b); + + // network interface. + + // program interface. + int addCertificate(cert *c); + int addUntrustedCertificate(cert *c); + int addCollectedCertificate(cert *c); + + int removeCertificate(cert *); + + // Creation of Certificates.... (From X509) + // Core functions.... + cert *checkDuplicateXPGP(XPGP *x); + cert *checkPeerXPGP(XPGP *x); + cert *makeCertificateXPGP(XPGP *c); + cert *registerCertificateXPGP(XPGP *nc, struct sockaddr_in, bool in); + + int validateCertificateXPGP(cert *c); + + /* Fns specific to XPGP */ + int checkAuthCertificate(cert *xpgp); + int signCertificate(cert *); + int trustCertificate(cert *, bool totrust); + int superNodeMode(); + int loadInitialTrustedPeer(std::string tp_file); + + // depreciated... + cert *findpeercert(const char *name); + //int loadpeercert(const char *fname); + //int savepeercert(const char *fname); + + // Configuration Handling... + int setConfigDirs(const char *cdir, const char *ndir); + + // these save both the certificates + the settings. + int saveCertificates(const char *fname); + int saveCertificates(); + int loadCertificates(const char *fname); + + // with a hash check/recalc in there for good measure. + cert * loadcertificate(const char* fname, std::string hash); + int savecertificate(cert *c, const char* fname); + + // for sending stuff as text + cert * loadCertFromString(std::string pem); + std::string saveCertAsString(cert *c); + + // digest hashing /signing or encrypting interface. + int hashFile(std::string fname, unsigned char *hash, unsigned int hlen); + int hashDigest(char *data, unsigned int dlen, unsigned char *hash, unsigned int hlen); + int signDigest(EVP_PKEY *key, char *data, unsigned int dlen, unsigned char *hash, unsigned int hlen); + int verifyDigest(EVP_PKEY *key, char *data, unsigned int dlen, unsigned char *enc, unsigned int elen); + int generateKeyPair(EVP_PKEY *keypair, unsigned int keylen); + + + + int printCertificate(cert *, std::ostream &out); + /* removing the list of certificate names - ambiguity! + * + std::list listCertificates(); + * + */ + + std::list &getCertList(); + + cert * getOwnCert(); + int checkNetAddress(); + + // extra list for certs that aren't in main list. + cert * getCollectedCert(); + bool collectedCerts(); + + bool CertsChanged(); + bool CertsMajorChanged(); + void IndicateCertsChanged(); + + std::string getSetting(std::string opt); + void setSetting(std::string opt, std::string val); + + + /* Fns for relating cert signatures to structures */ + cert *findPeerId(std::string id); + cert *findcertsign(certsign &sign); + int getcertsign(cert *c, certsign &sign); + int addtosignmap(cert *); + +private: /* data */ + std::list peercerts; + std::list allcerts; + std::list collectedcerts; + + // whenever a cert is added, it should also be put in the map. + std::map signmap; + + + + // General Configuration System + // easy it put it here - so it can be signed easily. + std::map settings; + + std::string certdir; + std::string neighbourdir; + std::string certfile; + + SSL_CTX *sslctx; + int init; + + Indicator certsChanged; + Indicator certsMajorChanged; + + EVP_PKEY *pkey; + + cert *own_cert; + + XPGP_KEYRING *pgp_keyring; + +}; + +#endif // XPGPCERT_H diff --git a/libretroshare/src/_server/filedexserver.cc b/libretroshare/src/_server/filedexserver.cc new file mode 100644 index 000000000..c44d4ced0 --- /dev/null +++ b/libretroshare/src/_server/filedexserver.cc @@ -0,0 +1,876 @@ +/* + * "$Id: filedexserver.cc,v 1.24 2007-05-05 16:10:06 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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 "_server/filedexserver.h" +#include +#include + +#include "_pqi/pqibin.h" +#include "_pqi/pqiarchive.h" + +#include "_util/rsdebug.h" +#include "_util/rsdir.h" + +#include +#include + + +/* New FileCache Stuff */ +#include "_server/ftfiler.h" +#include "_dbase/cachestrapper.h" +#include "_dbase/fimonitor.h" +#include "_dbase/fistore.h" + +#include "_pqi/p3connmgr.h" +#include "_pqi/p3authmgr.h" + +#include "_serialiser/rsserviceids.h" +#include "_serialiser/rsconfigitems.h" + + +#include + +const int fldxsrvrzone = 47659; + +/**** +#define SERVER_DEBUG 1 +#define DEBUG_TICK 1 +****/ + +filedexserver::filedexserver() + :p3Config(CONFIG_TYPE_FSERVER), + pqisi(NULL), mAuthMgr(NULL), mConnMgr(NULL), + save_dir("."), + mCacheStrapper(NULL), ftFiler(NULL), fiStore(NULL), fimon(NULL) +{ + initialiseFileStore(); +} + +int filedexserver::setSearchInterface(P3Interface *si, p3AuthMgr *am, p3ConnectMgr *cm) +{ + pqisi = si; + mAuthMgr = am; + mConnMgr = cm; + return 1; +} + +std::list filedexserver::getTransfers() +{ + return ftFiler->getStatus(); +} + + +int filedexserver::tick() +{ + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, + "filedexserver::tick()"); + + /* the new Cache Hack() */ + FileStoreTick(); + + if (pqisi == NULL) + { + std::ostringstream out; + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, + "filedexserver::tick() Invalid Interface()"); + + return 1; + } + + int moreToTick = 0; + + if (0 < pqisi -> tick()) + { + moreToTick = 1; +#ifdef DEBUG_TICK + std::cerr << "filedexserver::tick() moreToTick from pqisi" << std::endl; +#endif + } + + if (0 < handleInputQueues()) + { + moreToTick = 1; +#ifdef DEBUG_TICK + std::cerr << "filedexserver::tick() moreToTick from InputQueues" << std::endl; +#endif + } + + if (0 < handleOutputQueues()) + { + moreToTick = 1; +#ifdef DEBUG_TICK + std::cerr << "filedexserver::tick() moreToTick from OutputQueues" << std::endl; +#endif + } + return moreToTick; +} + + +int filedexserver::status() +{ + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, + "filedexserver::status()"); + + if (pqisi == NULL) + { + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, + "filedexserver::status() Invalid Interface()"); + return 1; + } + + pqisi -> status(); + + return 1; +} + +std::string filedexserver::getSaveDir() +{ + return save_dir; +} + +void filedexserver::setSaveDir(std::string d) +{ + save_dir = d; + if (ftFiler) + ftFiler -> setSaveBasePath(save_dir); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ +} + +void filedexserver::setEmergencySaveDir(std::string s) +{ + if (ftFiler) + { + ftFiler -> setEmergencyBasePath(s); + } +} + +bool filedexserver::getSaveIncSearch() +{ + return save_inc; +} + +void filedexserver::setSaveIncSearch(bool v) +{ + save_inc = v; + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ +} + +int filedexserver::addSearchDirectory(std::string dir) +{ + dbase_dirs.push_back(dir); + reScanDirs(); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + return 1; +} + +int filedexserver::removeSearchDirectory(std::string dir) +{ + std::list::iterator it; + for(it = dbase_dirs.begin(); (it != dbase_dirs.end()) + && (dir != *it); it++); + if (it != dbase_dirs.end()) + { + dbase_dirs.erase(it); + } + + reScanDirs(); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + return 1; +} + +std::list &filedexserver::getSearchDirectories() +{ + return dbase_dirs; +} + + +int filedexserver::reScanDirs() +{ + if (fimon) + fimon->setSharedDirectories(dbase_dirs); + + return 1; +} + +bool filedexserver::ConvertSharedFilePath(std::string path, std::string &fullpath) +{ + if (fimon) + return fimon->convertSharedFilePath(path, fullpath); + + return false; +} + +void filedexserver::ForceDirectoryCheck() +{ + if (fimon) + fimon->forceDirectoryCheck(); + return; +} + +bool filedexserver::InDirectoryCheck() +{ + if (fimon) + return fimon->inDirectoryCheck(); + return false; +} + + +/*************************************** NEW File Cache Stuff ****************************/ + +void filedexserver::initialiseFileStore() +{ + +} + +const std::string LOCAL_CACHE_FILE_KEY = "LCF_NAME"; +const std::string LOCAL_CACHE_HASH_KEY = "LCF_HASH"; +const std::string LOCAL_CACHE_SIZE_KEY = "LCF_SIZE"; + +void filedexserver::setFileCallback(std::string ownId, CacheStrapper *strapper, ftfiler *ft, NotifyBase *cb) +{ + mCacheStrapper = strapper; + ftFiler = ft; + + /* setup FiStore/Monitor */ + std::string localcachedir = config_dir + "/cache/local"; + std::string remotecachedir = config_dir + "/cache/remote"; + fiStore = new FileIndexStore(strapper, ftFiler, cb, ownId, remotecachedir); + + /* now setup the FiMon */ + fimon = new FileIndexMonitor(strapper, localcachedir, ownId); + + /* setup ftFiler + * to find peer info / savedir + */ + FileHashSearch *fhs = new FileHashSearch(fiStore, fimon); + ftFiler -> setFileHashSearch(fhs); + ftFiler -> setSaveBasePath(save_dir); + + /* now add the set to the cachestrapper */ + + CachePair cp(fimon, fiStore, CacheId(RS_SERVICE_TYPE_FILE_INDEX, 0)); + mCacheStrapper -> addCachePair(cp); + + return; +} + + +void filedexserver::StartupMonitor() +{ + /* startup the FileMonitor (after cache load) */ + fimon->setPeriod(600); /* 10 minutes */ + /* start it up */ + fimon->setSharedDirectories(dbase_dirs); + fimon->start(); + + + std::list::iterator tit; + for(tit = mResumeTransferList.begin(); + tit != mResumeTransferList.end(); tit++) + { + RsFileTransfer *rsft = (*tit); + + /* only add in ones which have a hash (filters old versions) */ + if (rsft->file.hash != "") + { + ftFiler -> getFile( + rsft->file.name, + rsft->file.hash, + rsft->file.filesize, ""); + } + delete rsft; + } + mResumeTransferList.clear(); +} + + + +int filedexserver::FileCacheSave() +{ + /************ TMP HACK SAVE until new serialiser is finished */ + + RsPeerId pid; + std::map ids; + std::map::iterator it; + +#ifdef SERVER_DEBUG + std::cerr << "filedexserver::FileCacheSave() listCaches:" << std::endl; + fimon->listCaches(std::cerr); + fimon->cachesAvailable(pid, ids); +#endif + + std::string localCacheFile; + std::string localCacheHash; + std::string localCacheSize; + + if (ids.size() == 1) + { + it = ids.begin(); + localCacheFile = (it->second).name; + localCacheHash = (it->second).hash; + std::ostringstream out; + out << (it->second).size; + localCacheSize = out.str(); + } + + /* extract the details of the local cache */ + //getSSLRoot()->setSetting(LOCAL_CACHE_FILE_KEY, localCacheFile); + //getSSLRoot()->setSetting(LOCAL_CACHE_HASH_KEY, localCacheHash); + //getSSLRoot()->setSetting(LOCAL_CACHE_SIZE_KEY, localCacheSize); + + /************ TMP HACK SAVE until new serialiser is finished */ + return 1; +} + +// Transfer control. +void filedexserver::saveFileTransferStatus() +{ + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ +} + + +// Transfer control. +int filedexserver::getFile(std::string fname, std::string hash, + uint32_t size, std::string dest) + +{ + int ret = ftFiler -> getFile(fname, hash, size, dest); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + return ret; +} + +void filedexserver::clear_old_transfers() +{ + ftFiler -> clearFailedTransfers(); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ +} + +void filedexserver::cancelTransfer(std::string fname, std::string hash, uint32_t size) +{ + ftFiler -> cancelFile(hash); + + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ +} + + +int filedexserver::RequestDirDetails(std::string uid, std::string path, + DirDetails &details) +{ + return fiStore->RequestDirDetails(uid, path, details); +} + +int filedexserver::RequestDirDetails(void *ref, DirDetails &details, uint32_t flags) +{ + return fiStore->RequestDirDetails(ref, details, flags); +} + +int filedexserver::SearchKeywords(std::list keywords,std::list &results,uint32_t flags) +{ + return fiStore->SearchKeywords(keywords, results,flags); +} + +int filedexserver::SearchBoolExp(Expression * exp, std::list &results) +{ + return fiStore->searchBoolExp(exp, results); +} + + +int filedexserver::FileStoreTick() +{ + ftFiler -> tick(); + return 1; +} + + +// This function needs to be divided up. +int filedexserver::handleInputQueues() +{ + // get all the incoming results.. and print to the screen. + RsCacheRequest *cr; + RsCacheItem *ci; + RsFileRequest *fr; + RsFileData *fd; + + // Loop through Search Results. + int i = 0; + int i_init = 0; + + //std::cerr << "filedexserver::handleInputQueues()" << std::endl; + while((ci = pqisi -> GetSearchResult()) != NULL) + { + //std::cerr << "filedexserver::handleInputQueues() Recvd SearchResult (CacheResponse!)" << std::endl; + std::ostringstream out; + if (i++ == i_init) + { + out << "Recieved Search Results:" << std::endl; + } + ci -> print(out); + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, out.str()); + + /* these go to the CacheStrapper! */ + CacheData data; + data.cid = CacheId(ci->cacheType, ci->cacheSubId); + data.hash = ci->file.hash; + data.size = ci->file.filesize; + data.name = ci->file.name; + data.path = ci->file.path; + data.pid = ci->PeerId(); + data.pname = mAuthMgr->getName(ci->PeerId()); + mCacheStrapper->recvCacheResponse(data, time(NULL)); + + delete ci; + } + + // now requested Searches. + i_init = i; + while((cr = pqisi -> RequestedSearch()) != NULL) + { + /* just delete these */ + std::ostringstream out; + out << "Requested Search:" << std::endl; + cr -> print(out); + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, out.str()); + delete cr; + } + + + // Now handle it replacement (pushed cache results) + { + std::list > cacheUpdates; + std::list >::iterator it; + + mCacheStrapper->getCacheUpdates(cacheUpdates); + for(it = cacheUpdates.begin(); it != cacheUpdates.end(); it++) + { + /* construct reply */ + RsCacheItem *ci = new RsCacheItem(); + + /* id from incoming */ + ci -> PeerId(it->first); + + ci -> file.hash = (it->second).hash; + ci -> file.name = (it->second).name; + ci -> file.path = ""; // (it->second).path; + ci -> file.filesize = (it->second).size; + ci -> cacheType = (it->second).cid.type; + ci -> cacheSubId = (it->second).cid.subid; + +#ifdef SERVER_DEBUG + std::ostringstream out2; + out2 << "Outgoing CacheStrapper Update -> RsCacheItem:" << std::endl; + ci -> print(out2); + std::cerr << out2.str() << std::endl; +#endif + + //pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, out2.str()); + pqisi -> SendSearchResult(ci); + } + } + + // now File Input. + i_init = i; + while((fr = pqisi -> GetFileRequest()) != NULL ) + { +#ifdef SERVER_DEBUG + std::cerr << "filedexserver::handleInputQueues() Recvd ftFiler Request" << std::endl; + std::ostringstream out; + if (i == i_init) + { + out << "Incoming(Net) File Item:" << std::endl; + } + fr -> print(out); + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, out.str()); +#endif + i++; /* count */ + + /* This bit is for debugging only! (not really needed) */ + + /* request */ + ftFileRequest *ffr = new ftFileRequest(fr->PeerId(), + fr->file.hash, fr->file.filesize, + fr->fileoffset, fr->chunksize); + ftFiler->recvFileInfo(ffr); + + delete fr; + } + + // now File Data. + i_init = i; + while((fd = pqisi -> GetFileData()) != NULL ) + { +#ifdef SERVER_DEBUG + //std::cerr << "filedexserver::handleInputQueues() Recvd ftFiler Data" << std::endl; + std::ostringstream out; + if (i == i_init) + { + out << "Incoming(Net) File Data:" << std::endl; + } + fd -> print(out); + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, out.str()); +#endif + i++; /* count */ + + /* incoming data */ + ftFileData *ffd = new ftFileData(fd->PeerId(), + fd->fd.file.hash, fd->fd.file.filesize, + fd->fd.file_offset, + fd->fd.binData.bin_len, + fd->fd.binData.bin_data, FT_FILEDATA_FLAG_NOFREE); + + ftFiler->recvFileInfo(ffd); + delete fd; + } + + if (i > 0) + { + return 1; + } + return 0; +} + + +// This function needs to be divided up. +int filedexserver::handleOutputQueues() +{ + // get all the incoming results.. and print to the screen. + //std::cerr << "filedexserver::handleOutputQueues()" << std::endl; + int i = 0; + + /* now see if the filer has any data */ + ftFileRequest *ftr; + while((ftr = ftFiler -> sendFileInfo()) != NULL) + { + //std::cerr << "filedexserver::handleOutputQueues() ftFiler Data for: " << ftr->id << std::endl; + + /* decide if its data or request */ + ftFileData *ftd = dynamic_cast(ftr); + if (ftd) + { + SendFileData(ftd, ftr->id); + } + else + { + SendFileRequest(ftr, ftr->id); + } + + std::ostringstream out; + if (i++ == 0) + { + out << "Outgoing filer -> PQFileItem:" << std::endl; + } + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, out.str()); + + /* clean up */ + delete ftr; + } + + + + if (i > 0) + { + return 1; + } + return 0; +} + +void filedexserver::SendFileRequest(ftFileRequest *ftr, std::string pid) +{ + RsFileRequest *rfi = new RsFileRequest(); + + /* id */ + rfi->PeerId(pid); + + /* file info */ + rfi->file.filesize = ftr->size; + rfi->file.hash = ftr->hash; + + /* offsets */ + rfi->fileoffset = ftr->offset; + rfi->chunksize = ftr->chunk; + + pqisi -> SendFileRequest(rfi); +} + +#define MAX_FT_CHUNK 4096 + +void filedexserver::SendFileData(ftFileData *ftd, std::string pid) +{ + uint32_t tosend = ftd->chunk; + uint32_t baseoffset = ftd->offset; + uint32_t offset = 0; + uint32_t chunk; + + + while(tosend > 0) + { + /* workout size */ + chunk = MAX_FT_CHUNK; + if (chunk > tosend) + { + chunk = tosend; + } + + /******** New Serialiser Type *******/ + + RsFileData *rfd = new RsFileData(); + + /* set id */ + rfd->PeerId(pid); + + /* file info */ + rfd->fd.file.filesize = ftd->size; + rfd->fd.file.hash = ftd->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 *) ftd->data)[offset]), chunk); + + pqisi -> SendFileData(rfd); + + offset += chunk; + tosend -= chunk; + } +} + + +/***************************************************************************/ +/****************************** CONFIGURATION HANDLING *********************/ +/***************************************************************************/ + + +/**** OVERLOADED FROM p3Config ****/ + + + +static const std::string fdex_dir("FDEX_DIR"); +static const std::string save_dir_ss("SAVE_DIR"); +static const std::string save_inc_ss("SAVE_INC"); + +RsSerialiser *filedexserver::setupSerialiser() +{ + RsSerialiser *rss = new RsSerialiser(); + + /* add in the types we need! */ + rss->addSerialType(new RsFileConfigSerialiser()); + rss->addSerialType(new RsGeneralConfigSerialiser()); + + return rss; +} + + +std::list filedexserver::saveList(bool &cleanup) +{ + std::list saveData; + + /* it can delete them! */ + cleanup = true; + + /* create a key/value set for most of the parameters */ + std::map configMap; + std::map::iterator mit; + std::list::iterator it; + + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, + "fildexserver::save_config()"); + + /* basic control parameters */ + configMap[save_dir_ss] = getSaveDir(); + if (getSaveIncSearch()) + { + configMap[save_inc_ss] = "true"; + } + else + { + configMap[save_inc_ss] = "false"; + } + + int i; + for(it = dbase_dirs.begin(), i = 0; (it != dbase_dirs.end()) + && (i < 1000); it++, i++) + { + std::string name = fdex_dir; + int d1, d2, d3; + d1 = i / 100; + d2 = (i - d1 * 100) / 10; + d3 = i - d1 * 100 - d2 * 10; + + name += '0'+d1; + name += '0'+d2; + name += '0'+d3; + + configMap[name] = (*it); + } + + RsConfigKeyValueSet *rskv = new RsConfigKeyValueSet(); + + /* Convert to TLV */ + for(mit = configMap.begin(); mit != configMap.end(); mit++) + { + RsTlvKeyValue kv; + kv.key = mit->first; + kv.value = mit->second; + + rskv->tlvkvs.pairs.push_back(kv); + } + + /* Add KeyValue to saveList */ + saveData.push_back(rskv); + + std::list::iterator fit; + std::list ftlist = ftFiler -> getStatus(); + for(fit = ftlist.begin(); fit != ftlist.end(); fit++) + { + /* only write out the okay/uncompleted (with hash) files */ + if (((*fit)->state == FT_STATE_FAILED) || + ((*fit)->state == FT_STATE_COMPLETE) || + ((*fit)->in == false) || + ((*fit)->file.hash == "")) + { + /* ignore */ + /* cleanup */ + delete(*fit); + } + else + { + saveData.push_back(*fit); + } + } + + /* list completed! */ + return saveData; +} + + +bool filedexserver::loadList(std::list load) +{ + std::list::iterator it; + std::list::iterator kit; + RsConfigKeyValueSet *rskv; + RsFileTransfer *rsft; + +#ifdef SERVER_DEBUG + std::cerr << "filedexserver::loadList() Item Count: " << load.size(); + std::cerr << std::endl; +#endif + + for(it = load.begin(); it != load.end(); it++) + { + /* switch on type */ + if (NULL != (rskv = dynamic_cast(*it))) + { + /* make into map */ + std::map configMap; + for(kit = rskv->tlvkvs.pairs.begin(); + kit != rskv->tlvkvs.pairs.end(); kit++) + { + configMap[kit->key] = kit->value; + } + + loadConfigMap(configMap); + /* cleanup */ + delete (*it); + + } + else if (NULL != (rsft = dynamic_cast(*it))) + { + /* save to the preLoad list */ + mResumeTransferList.push_back(rsft); + } + else + { + /* cleanup */ + delete (*it); + } + } + + return true; + +} + +bool filedexserver::loadConfigMap(std::map &configMap) +{ + std::map::iterator mit; + + int i; + std::string str_true("true"); + std::string empty(""); + std::string dir = "notempty"; + + if (configMap.end() != (mit = configMap.find(save_dir_ss))) + { + setSaveDir(mit->second); + } + + if (configMap.end() != (mit = configMap.find(save_inc_ss))) + { + setSaveIncSearch(mit->second == str_true); + } + + dbase_dirs.clear(); + + for(i = 0; (i < 1000) && (dir != empty); i++) + { + std::string name = fdex_dir; + int d1, d2, d3; + d1 = i / 100; + d2 = (i - d1 * 100) / 10; + d3 = i - d1 * 100 - d2 * 10; + + name += '0'+d1; + name += '0'+d2; + name += '0'+d3; + + if (configMap.end() != (mit = configMap.find(name))) + { + dir = mit->second; + dbase_dirs.push_back(mit->second); + } + } + if (dbase_dirs.size() > 0) + { + std::ostringstream out; + out << "Loading " << dbase_dirs.size(); + out << " Directories" << std::endl; + pqioutput(PQL_DEBUG_BASIC, fldxsrvrzone, out.str()); + + reScanDirs(); + } + + return true; +} + diff --git a/libretroshare/src/_server/filedexserver.h b/libretroshare/src/_server/filedexserver.h new file mode 100644 index 000000000..5d970e613 --- /dev/null +++ b/libretroshare/src/_server/filedexserver.h @@ -0,0 +1,177 @@ +/* + * "$Id: filedexserver.h,v 1.18 2007-05-05 16:10:06 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef FILEDEXSERVER_H +#define FILEDEXSERVER_H + +/* + * Slightly more complete server.... + * has a filedex pointer, which manages the local indexing/searching. + * + */ + +#include "_pqi/pqi.h" +#include "_pqi/pqiindic.h" +#include "_serialiser/rsconfigitems.h" +#include +#include +#include +#include +#include + +#include "_rsiface/rsiface.h" + +#include "_pqi/p3cfgmgr.h" + +class p3ConnectMgr; +class p3AuthMgr; + +class CacheStrapper; +class ftfiler; +class FileIndexStore; +class FileIndexMonitor; + +class ftFileRequest; +class ftFileData; + +class Expression; + +#define MAX_RESULTS 100 // nice balance between results and traffic. + +class filedexserver: public p3Config +{ +public: + filedexserver(); + + void loadWelcomeMsg(); /* startup message */ + + int setSearchInterface(P3Interface *si, p3AuthMgr *am, p3ConnectMgr *cm); + + std::list getTransfers(); + + void saveFileTransferStatus(); + int getFile(std::string fname, std::string hash, + uint32_t size, std::string dest); + void clear_old_transfers(); + void cancelTransfer(std::string fname, std::string hash, uint32_t size); + + + // access to search info is also required. + + bool ConvertSharedFilePath(std::string path, std::string &fullpath); + void ForceDirectoryCheck(); + bool InDirectoryCheck(); + + std::list &getSearchDirectories(); + int addSearchDirectory(std::string dir); + int removeSearchDirectory(std::string dir); + int reScanDirs(); + int check_dBUpdate(); + + std::string getSaveDir(); + void setSaveDir(std::string d); + void setEmergencySaveDir(std::string s); + + void setConfigDir(std::string d) { config_dir = d; } + bool getSaveIncSearch(); + void setSaveIncSearch(bool v); + + int tick(); + int status(); + + +private: + + int handleInputQueues(); + int handleOutputQueues(); + + std::list dbase_dirs; + + P3Interface *pqisi;MRK_PQI_FILEDEX_SERVER_HEADER + p3AuthMgr *mAuthMgr; + p3ConnectMgr *mConnMgr; + + std::string config_dir; + std::string save_dir; + bool save_inc; // is savedir include in share list. + +public: + /* some more switches (here for uniform saving) */ + int getDHTEnabled() const { return DHTState; } + int getUPnPEnabled() const { return UPnPState; } + void setDHTEnabled(int i) { DHTState = i; } + void setUPnPEnabled(int i) { UPnPState = i; } + +private: + int DHTState; + int UPnPState; + + /*************************** p3 Config Overload ********************/ +protected: + /* Key Functions to be overloaded for Full Configuration */ + virtual RsSerialiser *setupSerialiser(); + virtual std::list saveList(bool &cleanup); + virtual bool loadList(std::list load); + +private: + bool loadConfigMap(std::map &configMap); + + /*************************** p3 Config Overload ********************/ + + /* new FileCache stuff */ + public: + + int FileStoreTick(); + int FileCacheSave(); + + /* Setup */ + void initialiseFileStore(); + void setFileCallback(std::string ownId, CacheStrapper *strapper, + ftfiler *ft, NotifyBase *cb); + void StartupMonitor(); + + /* Controls */ + int RequestDirDetails(std::string uid, std::string path, DirDetails &details); + int RequestDirDetails(void *ref, DirDetails &details, uint32_t flags); + + int SearchKeywords(std::list keywords, std::list &results); + int SearchBoolExp(Expression * exp, std::list &results); + +private: + void SendFileRequest(ftFileRequest *ftr, std::string pid); + void SendFileData(ftFileData *ftd, std::string pid); + + CacheStrapper *mCacheStrapper; + ftfiler *ftFiler; + FileIndexStore *fiStore; + FileIndexMonitor *fimon; + + /* Temp Transfer List (for loading config) */ + std::list mResumeTransferList; +}; + +#endif // FILEDEXSERVER_H diff --git a/libretroshare/src/_server/ft.cc b/libretroshare/src/_server/ft.cc new file mode 100644 index 000000000..7d6698854 --- /dev/null +++ b/libretroshare/src/_server/ft.cc @@ -0,0 +1,162 @@ +/* + * "$Id: pqifiler.cc,v 1.13 2007-02-19 20:08:30 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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 "_server/ft.h" + +/**** + * #define FT_DEBUG 1 + ***/ + + +ftFileRequest::ftFileRequest(std::string id_in, std::string hash_in, + uint64_t size_in, uint64_t offset_in, + uint32_t chunk_in) : + id(id_in), + hash(hash_in), + size(size_in), + offset(offset_in), + chunk(chunk_in) +{ +} + +ftFileData::ftFileData(std::string id_in, std::string hash_in, uint64_t size_in, + uint64_t offset_in, uint32_t chunk_in, void *data_in, + uint32_t flags): + ftFileRequest(id_in, hash_in, + size_in, offset_in, + chunk_in), + data(data_in), + ftFlags(flags) +{ +} + +ftFileData::~ftFileData() +{ + // CHANGED : CODE_SIMPLIFIED + if( !(ftFlags & FT_FILEDATA_FLAG_NOFREE) && data) + free(data); + data = NULL; + +// if (ftFlags & FT_FILEDATA_FLAG_NOFREE) +// { +// /* don't free */ +// } +// else +// { +// if (data) +// { +// free(data); +// } +// } +// data = NULL; + +} + +bool ftManager::lookupLocalHash(std::string hash, std::string &path, uint64_t &size) +{ + std::list details; + +#ifdef FT_DEBUG + std::cerr << "ftManager::lookupLocalHash() hash: " << hash << std::endl; +#endif + + if (FindCacheFile(hash, path, size)) + { + /* got it from the CacheTransfer() */ +#ifdef FT_DEBUG + std::cerr << "ftManager::lookupLocalHash() Found in CacheStrapper:"; + std::cerr << path << " size: " << size << std::endl; +#endif + + return true; + } + + bool ok = false; + if (fhs) + { + ok = (0 != fhs -> searchLocalHash(hash, path, size)); + } + else + { +#ifdef FT_DEBUG + std::cerr << "Warning FileHashSearch is Invalid" << std::endl; +#endif + } + + if (ok) + { +#ifdef FT_DEBUG + std::cerr << "ftManager::lookupLocalHash() Found in FileHashSearch:"; + std::cerr << path << " size: " << size << std::endl; +#endif + return true; + } + return ok; + +} + + + +bool ftManager::lookupRemoteHash(std::string hash, std::list &ids) +{ + std::list details; + std::list::iterator it; + +#ifdef FT_DEBUG + std::cerr << "ftManager::lookupRemoteHash() hash: " << hash << std::endl; +#endif + + if (fhs) + { + fhs -> searchRemoteHash(hash, details); + } + else + { +#ifdef FT_DEBUG + std::cerr << "Warning FileHashSearch is Invalid" << std::endl; +#endif + } + + if (details.size() == 0) + { +#ifdef FT_DEBUG + std::cerr << "ftManager::lookupRemoteHash() Not Found!" << std::endl; +#endif + return false; + } + + for(it = details.begin(); it != details.end(); it++) + { +#ifdef FT_DEBUG + std::cerr << "ftManager::lookupRemoteHash() Found in FileHashSearch:"; + std::cerr << " id: " << it->id << std::endl; +#endif + ids.push_back(it->id); + } + return true; +} + + diff --git a/libretroshare/src/_server/ft.h b/libretroshare/src/_server/ft.h new file mode 100644 index 000000000..d43c205af --- /dev/null +++ b/libretroshare/src/_server/ft.h @@ -0,0 +1,140 @@ +/* + * "$Id: ftManager.h,v 1.13 2007-02-19 20:08:30 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + +#ifndef FT_H +#define FT_H + +/* + * ftManager - virtual base class for FileTransfer + */ + +#include +#include +#include + +#include "_pqi/pqi.h" +#include "_serialiser/rsconfigitems.h" + +#include "_dbase/cachestrapper.h" +#include "_server/hashsearch.h" + +class ftFileManager; /* stores files */ + + +class ftFileRequest +{ +public: + ftFileRequest(std::string id_in, std::string hash_in, + uint64_t size_in, uint64_t offset_in, + uint32_t chunk_in); + + virtual ~ftFileRequest() { } + + std::string id; + std::string hash; + uint64_t size; + uint64_t offset; + uint32_t chunk; +}; + +const uint32_t FT_FILEDATA_FLAG_NOFREE = 0x01; + +class ftFileData: public ftFileRequest +{ +public: + ftFileData(std::string id_in, std::string hash_in, + uint64_t size_in, uint64_t offset_in, + uint32_t chunk_in, void *data_in, uint32_t flags); + virtual ~ftFileData(); + + void *data; + uint32_t ftFlags; +}; + + +class ftManager: public CacheTransfer +{ +public: + ftManager(CacheStrapper *cs) + :CacheTransfer(cs), fhs(NULL) { return; } + virtual ~ftManager() { return; } + + void setFileHashSearch(FileHashSearch *hs) { fhs = hs; } + + /****************** PART to be IMPLEMENTE******************/ + /* Functions to implement */ + + /*********** overloaded from CacheTransfer ***************/ + /* Must callback after this fn - using utility functions */ + //virtual bool RequestCacheFile(RsPeerId id, std::string path, + // std::string hash, uint64_t size); + /******************* GUI Interface ************************/ + virtual int getFile(std::string name, std::string hash, + uint64_t size, std::string destpath) = 0; + + virtual int cancelFile(std::string hash) = 0; + virtual int clearFailedTransfers() = 0; + + virtual int tick() = 0; + virtual std::list getStatus() = 0; + + /************* Network Interface****************************/ + + public: + virtual void setSaveBasePath(std::string s) = 0; + virtual void setEmergencyBasePath(std::string s) = 0; + virtual int recvFileInfo(ftFileRequest *in) = 0; + virtual ftFileRequest * sendFileInfo() = 0; + +protected: + + /****************** UTILITY FUNCTIONS ********************/ + + /* combines two lookup functions */ + bool lookupLocalHash(std::string hash, std::string &path, uint64_t &size); + bool lookupRemoteHash(std::string hash, std::list &ids); + + /*********** callback from CacheTransfer ***************/ + //bool CompletedCache(std::string hash); /* internal completion -> does cb */ + //bool FailedCache(std::string hash); /* internal completion -> does cb */ + /*********** available from CacheTransfer ***************/ + /* upload side of things .... searches through CacheStrapper(Sources) for a cache. */ + //bool FindCacheFile(std::string id, std::string hash, std::string &path); + /*********** available from CacheTransfer ***************/ + +private: + FileHashSearch *fhs; +}; + +#endif // FT_H + + + + + + + + + diff --git a/libretroshare/src/_server/ftfiler.cc b/libretroshare/src/_server/ftfiler.cc new file mode 100644 index 000000000..0721871f9 --- /dev/null +++ b/libretroshare/src/_server/ftfiler.cc @@ -0,0 +1,1767 @@ +/* + * "$Id: ftfiler.cc,v 1.13 2007-02-19 20:08:30 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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 "_server/ftfiler.h" +#include "_util/rsdir.h" +#include "_util/rsdebug.h" + +#include "_pqi/pqinotify.h" + +#include +#include + +const int ftfilerzone = 86539; + +/**** + * #define FT_DEBUG 1 + ***/ + +/* + * PQI Filer + * + * This managers the file transfers. + * + * + * TODO: add trickle transfers. + * + */ + +const uint32_t PQIFILE_OFFLINE_CHECK = 120; /* check every 2 minutes */ +const uint32_t PQIFILE_DOWNLOAD_TIMEOUT = 60; /* time it out, -> offline after 60 secs */ +const uint32_t PQIFILE_DOWNLOAD_CHECK = 10; /* desired delta = 10 secs */ +const uint32_t PQIFILE_DOWNLOAD_TOO_FAST = 8; /* 8 secs */ +const uint32_t PQIFILE_DOWNLOAD_TOO_SLOW = 12; /* 12 secs */ +const uint32_t PQIFILE_DOWNLOAD_MIN_DELTA = 5; /* 5 secs */ + +const float TRANSFER_MODE_TRICKLE_RATE = 1000; /* 1 kbyte limit */ +const float TRANSFER_MODE_NORMAL_RATE = 500000; /* 500 kbyte limit - everyone uses this one for now */ +const float TRANSFER_MODE_FAST_RATE = 500000; /* 500 kbyte limit */ + +const uint32_t TRANSFER_START_MIN = 500; /* 500 byte min limit */ +const uint32_t TRANSFER_START_MAX = 10000; /* 10000 byte max limit */ + +const uint32_t CACHE_FILE_TIMEOUT = 30; /* 30 seconds */ + +void printFtFileStatus(ftFileStatus *s, std::ostream &out); + +/************* Local File Interface **************************** + * + * virtual bool getCacheFile(std::string id, std::string path, std::string hash) = 0; + * virtual int getFile(std::string name, std::string hash, + * int size, std::string destpath) = 0; + * + * virtual int cancelFile(std::string hash) = 0; + * + * int ftfiler::clearFailedTransfers(); + * + * * Worker Fns. + * ftFileStatus *ftfiler::findRecvFileItem(PQFileItem *in); + */ + + +int ftfiler::getFile(std::string name, std::string hash, + uint64_t size, std::string destpath) +{ + /* add to local queue */ +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::getFile(): "; + out << " name: " << name << " hash: " << hash; + out << " path: " << destpath << " size: " << size; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + + /* check for duplicates */ + ftFileStatus *state = findRecvFileItem(hash); + if (state) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::getFile() - duplicate, giving push!"; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + /* if duplicate - give download a push! */ + /* do this by flagging last transfer at 0. + */ + /* and also set the request stuff, so it'll + * generate a new request + * - we might lose the current transfer - but + * that's the idiots fault for redownloading. + */ + + resetFileTransfer(state); + return 1; + } + + // HANDLE destpath - TODO! + // state = new ftFileStatus(name, hash, size, destpath, FT_MODE_STD); + state = new ftFileStatus(name, hash, size, "", FT_MODE_STD); + if (initiateFileTransfer(state)) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::getFile() "; + out << "adding to recvFiles queue"; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + +#endif + recvFiles.push_back(state); + } + return 1; +} + +bool ftfiler::RequestCacheFile(std::string id, std::string destpath, std::string hash, uint64_t size) +{ + /* add to local queue */ +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::getCacheFile(): "; + out << " id: " << id << " hash: " << hash; + out << " path: " << destpath; + out << " size: " << size; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + + /* check for duplicates */ + ftFileStatus *state = findRecvFileItem(hash); + if (state) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::getFile() - duplicate, giving push!"; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + /* if duplicate - give download a push! */ + /* do this by flagging last transfer at 0. + */ + /* and also set the request stuff, so it'll + * generate a new request + * - we might lose the current transfer - but + * that's the idiots fault for redownloading. + */ + + resetFileTransfer(state); + return 1; + } + + state = new ftFileStatus(id, hash, hash, size, destpath, FT_MODE_CACHE); + if (initiateFileTransfer(state)) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::getFile() "; + out << "adding to recvFiles queue"; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + recvFiles.push_back(state); + } + return 1; +} + +bool ftfiler::CancelCacheFile(RsPeerId id, std::string path, + std::string hash, uint64_t size) +{ + /* clean up old transfer - just remove it (no callback) */ +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::CancelCacheFile() Looking for: " << hash; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + + /* iterate through fileItems and check for this one */ + std::list::iterator it; + for(it = recvFiles.begin(); it != recvFiles.end(); it++) + { + if ((hash==(*it)->hash) && + (size==(*it)->size) && + ((*it)->ftMode == FT_MODE_CACHE)) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::CancelCacheFile() "; + out << "Match ftFileStatus: " << hash; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + /* same */ + + std::cerr << "Clearing Failed Cache Transfer: " << (*it)->name; + std::cerr << std::endl; +#endif + + delete (*it); + it = recvFiles.erase(it); + + return true; + } + } + +#ifdef FT_DEBUG + std::cerr << "************* ERROR *****************"; + std::cerr << std::endl; + std::cerr << "ftfiler::CancelCacheFile() Failed to Find: " << hash; + std::cerr << std::endl; +#endif + return false; +} + + + +ftFileStatus *ftfiler::findRecvFileItem(std::string hash) +{ +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::findRecvFileItem() Looking for: " << hash; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + + /* iterate through fileItems and check for this one */ + std::list::iterator it; + for(it = recvFiles.begin(); it != recvFiles.end(); it++) + { + if (hash==(*it)->hash) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::findRecvFileItem() "; + out << "Match ftFileStatus: " << hash; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + /* same */ + return (*it); + } + } + return NULL; +} + + + + +int ftfiler::cancelFile(std::string hash) +{ + /* flag as cancelled */ + /* iterate through fileItems and check for this one */ +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::cancelFile() hash: " << hash << std::endl; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + + std::list::iterator it; + for(it = recvFiles.begin(); it != recvFiles.end(); it++) + { + if (hash==(*it)->hash) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::cancelFile() "; + out << "Found file: " << hash; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + /* same */ + (*it)->status = (PQIFILE_FAIL | PQIFILE_FAIL_CANCEL); + return 1; + } + } + +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::cancelFile() "; + out << "couldn't match ftFileStatus!"; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + return 0; +} + +int ftfiler::clearFailedTransfers() +{ + /* remove all the failed items */ + /* iterate through fileItems and check for this one */ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::clearFailedTransfers()"); +#endif + + std::list::iterator it; + int cleared = 0; + for(it = recvFiles.begin(); it != recvFiles.end(); /* done in loop */) + { + if ((*it)->status & PQIFILE_FAIL) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::clearFailedTransfers() "; + out << "removing item: " << (*it) -> name; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + /* same */ + ftFileStatus *cfile = (*it); + it = recvFiles.erase(it); + delete cfile; + cleared++; + } + else if ((*it)->status & PQIFILE_COMPLETE) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::clearFailedTransfers() "; + out << "removing Completed item: "; + out << (*it) -> name; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + /* same */ + ftFileStatus *cfile = (*it); + it = recvFiles.erase(it); + delete cfile; + cleared++; + } + else + { + it++; + } + } + +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::clearFailedTransfers() cleared: "; + out << cleared; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + + return 1; +} + +std::list ftfiler::getStatus() +{ + +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::getTransferStatus()"); +#endif + + std::list stateList; + + /* iterate through all files to recv */ + std::list::iterator it; + for(it = recvFiles.begin(); it != recvFiles.end(); it++) + { + RsFileTransfer *rft = new RsFileTransfer(); + rft -> in = true; + + /* Ids: current and all */ + std::list::iterator pit; + for(pit = (*it)->sources.begin(); + pit != (*it)->sources.end(); pit++) + { + rft->allPeerIds.ids.push_back(*pit); + } + rft -> cPeerId = (*it)->id; + + /* file details */ + rft -> file.name = (*it)->name; + rft -> file.filesize = (*it)->size; + rft -> file.path = ""; + rft -> file.pop = 0; + rft -> file.age = 0; + + /* hack to store 'Real' Transfers (Cache have blank hash)*/ + if ((*it)->ftMode != FT_MODE_CACHE) + rft -> file.hash = (*it)->hash; + else + rft -> file.hash = ""; + + /* Fill in rate and State */ + rft -> transferred = (*it)->recv_size; + rft -> crate = (*it)->rate; // bytes. + rft -> trate = (*it)->rate; // bytes. + rft -> lrate = (*it)->rate; // bytes. + rft -> ltransfer = (*it)->req_size; + + /* get inactive period */ + if ((*it) -> status == PQIFILE_NOT_ONLINE) + { + rft -> crate = 0; + rft -> trate = 0; + rft -> lrate = 0; + rft -> ltransfer = 0; + rft -> state = FT_STATE_WAITING; + } + else if ((*it) -> status & PQIFILE_FAIL) + { + rft -> crate = 0; + rft -> trate = 0; + rft -> lrate = 0; + rft -> ltransfer = 0; + rft -> state = FT_STATE_FAILED; + + } + else if ((*it) -> status == PQIFILE_COMPLETE) + { + rft -> state = FT_STATE_COMPLETE; + } + else if ((*it) -> status == PQIFILE_DOWNLOADING) + { + rft -> state = FT_STATE_DOWNLOADING; + } + else + { + rft -> state = FT_STATE_FAILED; + } + stateList.push_back(rft); + } + + /* outgoing files */ + for(it = fileCache.begin(); it != fileCache.end(); it++) + { + RsFileTransfer *rft = new RsFileTransfer(); + rft -> in = false; + + /* Only set the most recent */ + rft -> cPeerId = (*it)->id; + + /* file details */ + rft -> file.name = (*it)->name; + rft -> file.filesize = (*it)->size; + rft -> file.path = ""; + rft -> file.pop = 0; + rft -> file.age = 0; + + /* hack to store 'Real' Transfers (Cache have blank hash)*/ + if ((*it)->ftMode != FT_MODE_CACHE) + rft -> file.hash = (*it)->hash; + else + rft -> file.hash = ""; + + /* Fill in rate and State */ + rft -> transferred = (*it)->req_loc; + rft -> crate = (*it)->req_size / 10.0; /* very approx */ + rft -> trate = (*it)->req_size / 10.0; + rft -> lrate = (*it)->req_size / 10.0; + rft -> ltransfer = (*it)->req_size; + + /* get inactive period */ + if ((*it) -> status == PQIFILE_NOT_ONLINE) + { + rft -> crate = 0; + rft -> trate = 0; + rft -> lrate = 0; + rft -> ltransfer = 0; + rft -> state = FT_STATE_WAITING; + } + else if ((*it) -> status & PQIFILE_FAIL) + { + rft -> crate = 0; + rft -> trate = 0; + rft -> lrate = 0; + rft -> ltransfer = 0; + rft -> state = FT_STATE_FAILED; + + } + else if ((*it) -> status == PQIFILE_COMPLETE) + { + // uploads are going while they are here.. + //rft -> state = FT_STATE_COMPLETE; + rft -> state = FT_STATE_DOWNLOADING; + } + else if ((*it) -> status == PQIFILE_DOWNLOADING) + { + rft -> state = FT_STATE_DOWNLOADING; + } + else + { + rft -> state = FT_STATE_FAILED; + } + stateList.push_back(rft); + } + return stateList; +} + + +/************* Incoming FileItems ****************************** + * + * PQFileItem *ftfiler::sendPQFileItem() + * int ftfiler::recvPQFileItem(PQFileItem *in) + * + * * Worker Fns. + * int ftfiler::handleFileNotOnline(PQFileItem *in) + * int ftfiler::handleFileNotOnline(PQFileItem *in) + * int ftfiler::handleFileNotAvailable(PQFileItem *in) + * int ftfiler::handleFileData(PQFileItem *in) + * int ftfiler::handleFileRequest(PQFileItem *in) + * int ftfiler::handleFileCacheRequest(PQFileItem *req) + * + */ + +ftFileRequest *ftfiler::sendFileInfo() +{ + if (out_queue.size() < 1) + { + return NULL; + } + ftFileRequest *i = out_queue.front(); + out_queue.pop_front(); + return i; +} + +int ftfiler::recvFileInfo(ftFileRequest *in) +{ + + /* decide if it is a fileData or Request */ + ftFileData *dta = dynamic_cast(in); + if (dta) + { + handleFileData(dta->hash, dta->offset, dta->data, dta->chunk); + } + else + { + handleFileRequest(in->id, in->hash, in->offset, in->chunk); + } + /* cleanup */ + delete in; + + return 1; +} + + +int ftfiler::handleFileError(std::string hash, uint32_t err) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::handleFileError()"); +#endif + + /* get out the error */ + if (err & PQIFILE_NOT_ONLINE) + { + return handleFileNotOnline(hash); + } + if (err & PQIFILE_FAIL) + { + return handleFileNotAvailable(hash); + } + return 0; +} + +int ftfiler::handleFileNotOnline(std::string hash) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::handleFileNotOnline()"); +#endif + + /* flag recvFile item as not Online */ + ftFileStatus *s = findRecvFileItem(hash); + if ((!s) || (s -> status & PQIFILE_FAIL)) + { + return 0; + } + + s -> status = PQIFILE_NOT_ONLINE; + + return 1; +} + + +int ftfiler::handleFileNotAvailable(std::string hash) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::handleFileNotAvailable()"); +#endif + + /* error - flag recvFile item with FAILED */ + ftFileStatus *s = findRecvFileItem(hash); + if (!s) + { + return 0; + } + + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_AVAIL); + return 1; +} + + +int ftfiler::handleFileData(std::string hash, uint64_t offset, + void *data, uint32_t datalen) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::handleFileData()"); +#endif + + /* find the right ftFileStatus */ + ftFileStatus *recv = findRecvFileItem(hash); + if (!recv) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::handleFileData() no matching ftFileStatus (current download)"); +#endif + return 0; + } + + if(recv->status & PQIFILE_FAIL) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::handleFileData() marked as FAIL"); +#endif + return 0; + } + + /* add to file */ + addFileData(recv, offset, data, datalen); + + if (recv->status == PQIFILE_NOT_ONLINE) + { + /* switch to active */ + recv->status = PQIFILE_DOWNLOADING; + } + + /* if we have recieved all data - request some more */ + if ((recv->recv_size == recv->req_loc + recv->req_size) && + (recv->status != PQIFILE_COMPLETE)) + { + requestData(recv); + } + return 1; +} + +int ftfiler::handleFileRequest(std::string id, std::string hash, uint64_t offset, uint32_t chunk) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::handleFileRequest()"); +#endif + /* see if in cache */ + /* if yes send out chunk */ + if (handleFileCacheRequest(id, hash, offset, chunk)) + { + return 1; + } + + /* if not in cache - find file */ + ftFileStatus *new_file = createFileCache(hash); + if (!new_file) + { + /* bad file */ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::handleFileRequest() Failed to Load File-sendNotAvail"); +#endif + return 0; + //sendFileNotAvail(in); + } + + fileCache.push_back(new_file); + + return handleFileCacheRequest(id, hash, offset, chunk); +} + +int ftfiler::handleFileCacheRequest(std::string id, std::string hash, uint64_t offset, uint32_t chunk) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::handleFileCacheRequest()"); +#endif + /* check if in cache */ + bool found = false; + ftFileStatus *s; + std::list::iterator it; + for(it = fileCache.begin(); it != fileCache.end(); it++) + { + if (hash==(*it)->hash) + { + found = true; + s = (*it); + break; + } + } + if (!found) + return 0; + + /* push to out queue */ + return generateFileData(s, id, offset, chunk); +} + +/************* Outgoing FileItems ****************************** + * + * PQFileItem *ftfiler::sendPQFileItem() + * + * * Worker Fns. + * int ftfiler::tick(); + * void ftfiler::queryInactive() + * + */ + + +int ftfiler::tick() +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::tick()"); +#endif + /* check the status of recved files */ + queryInactive(); + + /* this doesn't matter much how often it's ticked... + * if it generates Items, they will be detected other places. + * so we can return 0 (waiting) + */ + return 0; +} + + +void ftfiler::queryInactive() +{ +#ifdef FT_DEBUG + std::ostringstream out; + + out << "ftfiler::queryInactive()"; + out << std::endl; +#endif + + + /* iterate through all files to recv */ + int ts = time(NULL); + std::list::iterator it; + for(it = recvFiles.begin(); it != recvFiles.end();) /* increment at end of loop */ + { + /* get inactive period */ + switch((*it) -> status) + { + case PQIFILE_NOT_ONLINE: +#ifdef FT_DEBUG + out << "File: " << (*it)->name << " Not Online: "; + out << "Delta: " << (ts - (*it)->lastTS) << std::endl; + out << " LDelta: " << (*it)->lastDelta; + out << " Recved: " << (*it)->recv_size; + out << " Total: " << (*it)->total_size; + out << " LChunk: " << (*it)->req_size; + out << std::endl; +#endif + + if (ts - ((*it)->lastTS) > PQIFILE_OFFLINE_CHECK) + { + resetFileTransfer(*it); + requestData(*it); + } + break; + case PQIFILE_DOWNLOADING: +#ifdef FT_DEBUG + out << "File: " << (*it)->name << " Downloading: "; + out << " Delta: " << (ts - (*it)->lastTS) << std::endl; + out << " LDelta: " << (*it)->lastDelta; + out << " Recved: " << (*it)->recv_size; + out << " Total: " << (*it)->total_size; + out << " LChunk: " << (*it)->req_size; + out << std::endl; +#endif + + if (ts - ((*it)->lastTS) > PQIFILE_DOWNLOAD_CHECK) + { + requestData(*it); /* give it a push */ + } + break; + default: +#ifdef FT_DEBUG + out << "File: " << (*it)->name << " Other mode: " << (*it)->status; + out << " Delta: " << (ts - (*it)->lastTS) << std::endl; + out << " LDelta: " << (*it)->lastDelta; + out << " Recved: " << (*it)->recv_size; + out << " Total: " << (*it)->total_size; + out << " LChunk: " << (*it)->req_size; + out << std::endl; +#endif + /* nothing */ + break; + } + + /* remove/increment */ + if (((*it) -> status == PQIFILE_COMPLETE) && ((*it)->ftMode == FT_MODE_CACHE)) + { +#ifdef FT_DEBUG + std::cerr << "Clearing Completed Cache File: " << (*it)->name; + std::cerr << std::endl; +#endif + delete (*it); + it = recvFiles.erase(it); + } + else + { + it++; + } + + } + + /* check the cached upload files too */ + time_t now = time(NULL); + for(it = fileCache.begin(); it != fileCache.end();) /* increment at end of loop */ + { + if (now - (*it)->lastTS > CACHE_FILE_TIMEOUT) + { +#ifdef FT_DEBUG + std::cerr << "Clearing Timed-out Cache File: " << (*it)->name; + std::cerr << std::endl; +#endif + delete (*it); + it = fileCache.erase(it); + } + else + { + it++; + } + } + +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif +} + + +int ftfiler::requestData(ftFileStatus *item) +{ + + /* formulate a request for the next desired data chunk */ + /* this handles the key throttling. This algorithm can + * be quite crude, as the tcp / and pqistreamer throttle as + * well. + */ + +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::requestData()" << std::endl; +#endif + + /* get the time since last request */ + int tm = time(NULL); + float delta = tm - item -> lastTS; + + if (item->id == "") /* no possible source */ + { + /* flag as handled for now so it doesn't repeat to fast */ + item->lastTS = tm; + return 0; + } + + /* decide on transfer mode */ + float max_rate = TRANSFER_MODE_NORMAL_RATE; + switch(item->mode) + { + case TRANSFER_MODE_TRICKLE: + max_rate = TRANSFER_MODE_TRICKLE_RATE; + break; + case TRANSFER_MODE_NORMAL: + max_rate = TRANSFER_MODE_NORMAL_RATE; + break; + case TRANSFER_MODE_FAST: + max_rate = TRANSFER_MODE_FAST_RATE; + break; + default: + break; + } +#ifdef FT_DEBUG + out << "max rate: " << max_rate; + out << std::endl; +#endif + + /* not finished */ + if (item->recv_size < item->req_loc + item->req_size) + { + if (delta > PQIFILE_DOWNLOAD_TIMEOUT) + { + /* we have timed out ... switch to + * offline + */ + /* start again slowly */ + item->req_size = (int) (0.1 * max_rate); +#ifdef FT_DEBUG + out << "Timeout: switching to Offline."; + out << std::endl; +#endif + item->status = PQIFILE_NOT_ONLINE; + } + else + { +#ifdef FT_DEBUG + out << "Pause: Not Finished"; + out << std::endl; +#endif + /* pause */ + } +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + return 0; + } + + + if (delta <= PQIFILE_DOWNLOAD_MIN_DELTA) + { + /* pause */ +#ifdef FT_DEBUG + out << "Small Delta -> Pause"; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + return 0; + } + + /* From this point - we will continue ... so handle rate now! */ + /* calc rate */ + float bytes_psec = item -> req_size / delta; + item -> rate = 0.7 * item -> rate + 0.3 * bytes_psec; +#ifdef FT_DEBUG + out << "delta: " << delta << " bytes: " << bytes_psec << " rate: " << item -> rate; + out << std::endl; +#endif + + if (item->lastDelta <= PQIFILE_DOWNLOAD_TOO_FAST) + { + /* increase 0.75 of the calculated extra that could be transmitted + * in the timeframe + */ + + float data_tf = item -> req_size; + float ldelta_f = item->lastDelta + 0.5; // 0.5 for extra space (+ dont / 0.0) + float tf_p_sec = data_tf / ldelta_f; + float extra_tf = tf_p_sec * (PQIFILE_DOWNLOAD_CHECK - ldelta_f); + + item -> req_size = item->req_size + (int) (0.75 * extra_tf); + + if (item->req_size > max_rate * PQIFILE_DOWNLOAD_CHECK) + item->req_size = (int) (max_rate * PQIFILE_DOWNLOAD_CHECK); + +#ifdef FT_DEBUG + out << "Small Delta: " << ldelta_f << " (sec), rate: " << tf_p_sec; + out << std::endl; + out << "Small Delta Incrementing req_size from: " << data_tf; + out << " to :" << item->req_size; + out << std::endl; +#endif + + } + else if (item->lastDelta > PQIFILE_DOWNLOAD_TOO_SLOW) + { + /* similarly decrease rate by 1.5 of extra time */ + + float data_tf = item -> req_size; + float ldelta_f = item->lastDelta + 0.5; // 0.5 for extra space (+ dont / 0.0) + float tf_p_sec = data_tf / ldelta_f; + float extra_tf = tf_p_sec * (ldelta_f - PQIFILE_DOWNLOAD_CHECK); + + item -> req_size -= (int) (1.25 * extra_tf); + +#ifdef FT_DEBUG + out << "Long Delta: " << ldelta_f << " (sec), rate: " << tf_p_sec; + out << std::endl; + out << "Long Delta Decrementing req_size from: " << data_tf; + out << " to :" << item->req_size; + out << std::endl; +#endif + } + + /* make the packet */ + + item->req_loc = item->recv_size; + /* req_size already calculated (unless NULL) */ + if (item->req_size < TRANSFER_START_MIN) + { + /* start again slowly + * added an extra limiter. + * - make this dependent on number of transfers ... */ + item->req_size = (int) (max_rate * (0.01 + 0.10 / recvFiles.size())); + if (item->req_size < TRANSFER_START_MIN) + { + item->req_size = TRANSFER_START_MIN; + } + else if (item->req_size > TRANSFER_START_MAX) + { + item->req_size = TRANSFER_START_MAX; + } + } + +#ifdef FT_DEBUG + out << "Making Packet: offset: " << item->req_loc << " size: " << item->req_size; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + ftFileRequest *req = generateFileRequest(item); + out_queue.push_back(req); + + return 1; +} + +/************* PQIFILEITEM Generator *************************** + * + * PQFileItem *ftfiler::generatePQFileRequest(ftFileStatus *s); + * int ftfiler::generateFileData(ftFileStatus *s, PQFileItem *req); + * int ftfiler::sendFileNotAvail(PQFileItem *req) + * + */ + + +int ftfiler::generateFileData(ftFileStatus *s, std::string id, uint64_t offset, uint32_t chunk) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::generateFileData()"); +#endif + + if ((!s) || (!s->fd) || (s->status & PQIFILE_FAIL)) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::generateFileData() Bad Status"); +#endif + if (!s) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::generateFileData() Bad Status (!s)"); +#endif + } + if (!s->fd) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::generateFileData() Bad Status (!s->fd)"); +#endif + } + if (s->status & PQIFILE_FAIL) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::generateFileData() Bad Status (s->status): " << s->status; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone,out.str()); +#endif + } + + /* return an error */ + return 0; + //sendFileNotAvail(req); + } + + /* make the packets */ + int tosend = chunk; + long base_loc = offset; + + if (base_loc + tosend > s -> total_size) + { + tosend = s -> total_size - base_loc; + } + +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::generateFileData() Sending " << tosend; + out << " bytes from offset: " << base_loc << std::endl; + out << "\tFrom File:" << s -> name; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + + if (tosend > 0) + { + /* seek for base_loc */ + fseek(s->fd, base_loc, SEEK_SET); + + void *data = malloc(tosend); + /* read the data */ + if (1 != fread(data, tosend, 1, s->fd)) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::generateFileData() Failed to get data!"; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + free(data); + return 0; + } + + // make a FileData type. + ftFileData *fd = new ftFileData(id, s->hash, s->size, offset, tosend, data, 0); + + /* send off the packet */ + out_queue.push_back(fd); + + /* Update status of ftFileStatus to reflect last usage (for GUI display) + * We need to store. + * (a) Id, + * (b) Offset, + * (c) Size, + * (d) timestamp + */ + + time_t now = time(NULL); + s->id = id; + s->req_loc = offset; + s->req_size = tosend; + s->lastTS = now; + } + return 1; +} + + +ftFileRequest *ftfiler::generateFileRequest(ftFileStatus *s) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::generatePQFileRequest()"); +#endif + + ftFileRequest *fr = new ftFileRequest(s->id, s->hash, + s->size, s->req_loc, s->req_size); + +#ifdef FT_DEBUG + std::ostringstream out; + + out << "ftfiler::generateFileRequest() for: " << s->name << std::endl; + out << "ftfiler::generateFileRequest() offset: " << fr->offset << " chunksize: "; + out << fr->chunk << std::endl; + + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + // timestamp request. + s->lastTS = time(NULL); + + return fr; +} + +/************* FILE DATA HANDLING ****************************** + * + * std::string ftfiler::determineTmpFilePath(ftFileStatus *s); + * std::string ftfiler::determineDestFilePath(ftFileStatus *s) + * int ftfiler::initiateFileTransfer(ftFileStatus *s); + * int ftfiler::resetFileTransfer(ftFileStatus *s); + * int ftfiler::addFileData(ftFileStatus *s, long idx, void *data, int size); + * + */ + +const std::string PARTIAL_DIR = "partials"; + +std::string ftfiler::determineTmpFilePath(ftFileStatus *s) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::determineTmpFilePath()"); +#endif + + /* get the download path */ + // savePath = "."; + std::string filePath = saveBasePath; + filePath += "/"; + filePath += PARTIAL_DIR; + filePath += "/"; + filePath += s->hash; + + return filePath; + + +} + +std::string ftfiler::determineDestFilePath(ftFileStatus *s) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::determineDestFilePath()"); +#endif + + /* should be three different options here: + * (1) relative to baseSavePath (default) + * (2) Abs (for Cache Files) + * (3) relative to shared dirs (TODO) + * + * XXX TODO. + */ + + std::string filePath; + + if (s->destpath == "") + { + filePath = saveBasePath; + } + else + { + filePath = s->destpath; + } + + /* get the download path */ + filePath += "/"; + filePath += s->name; + + return filePath; + + +} + +/****** + * NOTES: + * + * This is called to start the Transfer - from GetFile() or GetCacheFile() + * + * we need to determine the destination. + * + * + * + */ + +int ftfiler::initiateFileTransfer(ftFileStatus *s) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::initiateFileTransfer()"); +#endif + + + std::string partialpath = saveBasePath + "/"; + partialpath += PARTIAL_DIR; + if (!RsDirUtil::checkCreateDirectory(partialpath)) + { +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::initiateFileTransfer() Cannot create partial directory: " << partialpath; + pqioutput(PQL_ALERT, ftfilerzone, out.str()); + } +#endif + + std::string tmppath = mEmergencyIncomingDir; + if (!RsDirUtil::checkCreateDirectory(tmppath)) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::initiateFileTransfer() Cannot create EmergencyIncomingDir: "; + out << tmppath; + pqioutput(PQL_ALERT, ftfilerzone, out.str()); +#endif + exit(1); + } + + /* Store new temp path */ + saveBasePath = tmppath; + + tmppath += "/"; + tmppath += PARTIAL_DIR; + + if (!RsDirUtil::checkCreateDirectory(tmppath)) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::initiateFileTransfer() Cannot create EmergencyIncomingPartialsDir: "; + out << tmppath; + pqioutput(PQL_ALERT, ftfilerzone, out.str()); +#endif + exit(1); + } + +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::initiateFileTransfer() Using Emergency Download Directory: " << saveBasePath; + pqioutput(PQL_ALERT, ftfilerzone, out.str()); + } +#endif + + pqiNotify *notify = getPqiNotify(); + if (notify) + { + std::string title = + "Warning: Bad Incoming Directory"; + + std::string msg; + msg += " **** WARNING **** \n"; + msg += "Retroshare cannot create Incoming Partials Directory: "; + msg += "\n"; + msg += partialpath; + msg += "\n"; + msg += "\n"; + msg += "This is needed for normal operation."; + msg += "\n"; + msg += "\n"; + msg += "The incoming directory has been temporarily changed to:"; + msg += "\n"; + msg += saveBasePath; + msg += "\n"; + msg += "\n"; + msg += "Please select a new Downloads Directory ASAP Using:"; + msg += "\n"; + msg += "SideBar->Options->Directories"; + msg += "\n"; + + notify->AddSysMessage(0, RS_SYS_WARNING, title, msg); + } + else + { +#ifdef FT_DEBUG + std::cerr << "ftfiler::initiateFileTransfer() Notify not exist!"; + std::cerr << std::endl; +#endif + exit(1); + } + } + + /* check if the file exists */ + s->file_name = determineTmpFilePath(s); + +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::initiateFileTransfer() Filename: "; + out << s->file_name; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + + /* attempt to open file */ + FILE *fd = fopen(s->file_name.c_str(), "r+b"); + if (!fd) + { +#ifdef FT_DEBUG + { + std::ostringstream out; + out << "ftfiler::initiateFileTransfer() Failed to open (r+b): "; + out << s->file_name << " Error: " << errno; + out << " Will try to create file"; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); + } +#endif + + /* open in writing mode */ + fd = fopen(s->file_name.c_str(), "w+b"); + if (!fd) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::initiateFileTransfer() Failed to open (w+b): "; + out << s->file_name << " Error:" << errno; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + + /* failed to open the file */ + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_OPEN); + return 0; + } + + } + + + /* if it opened, find it's length */ + /* move to the end */ + if (0 != fseek(fd, 0L, SEEK_END)) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::initiateFileTransfer() Seek Failed"); +#endif + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_SEEK); + return 0; + } + + s->recv_size = ftell(fd); /* get the file length */ + s->total_size = s->size; /* save the total length */ + s->fd = fd; + + /* now determine the sources */ + if (s->ftMode != FT_MODE_CACHE) + { + } + + resetFileTransfer(s); + return 1; +} + +int ftfiler::resetFileTransfer(ftFileStatus *state) +{ + // reset all the basic items ... so the transfer will continue. + state->req_loc = 0; + state->req_size = 0; + state->lastTS = 0; + state->lastDelta = 0; + state->status = PQIFILE_NOT_ONLINE; + state->mode = TRANSFER_MODE_NORMAL; + state->rate = 0; + if (state->recv_size == state->total_size) + { + state->status = PQIFILE_COMPLETE; + /* if we're kicking it again for some reason? */ + completeFileTransfer(state); + } + else if (state->ftMode != FT_MODE_CACHE) + { + /* lookup options */ + state->sources.clear(); + if (!lookupRemoteHash(state->hash, state->sources)) + { +#ifdef FT_DEBUG + pqioutput(PQL_WARNING, ftfilerzone, + "ftfiler::resetFileTransfer() Failed to locate Peers"); +#endif + } + if (state->sources.size() == 0) + { + state->id = ""; + return 0; + } + + /* select a new source if possible */ + int idno = state->resetCount % state->sources.size(); + int i = 0; + std::list::const_iterator it; + for(it = state->sources.begin(); (it != state->sources.end()) + && (i < idno); it++, i++); + + if (it != state->sources.end()) + { + state->id = (*it); + } + } + state->resetCount++; + return 1; +} + + + +int ftfiler::addFileData(ftFileStatus *s, uint64_t idx, void *data, uint32_t size) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::addFileData()"); +#endif + + //std::cerr << "ftfiler::addFileData() PreStatus" << std::endl; + //printFtFileStatus(s, std::cerr); + + /* check the status */ + if ((!s) || (!s->fd) || (s->status & PQIFILE_FAIL)) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::addFileData() Bad Status"); +#endif + return 0; + } + + /* check its at the correct location */ + if ((idx != s->recv_size) || (s->recv_size + size > s->total_size)) + { +#ifdef FT_DEBUG + std::ostringstream out; + out << "ftfiler::addFileData() Bad Data Location" << std::endl; + out << " recv_size: " << s->recv_size << " offset: " << idx; + out << " total_size: " << s->total_size << " size: " << size; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + return 0; + } + + /* go to the end of the file */ + if (0 != fseek(s->fd, 0L, SEEK_END)) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::addFileData() Bad fseek"); +#endif + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_SEEK); + return 0; + } + + /* add the data */ + if (1 != fwrite(data, size, 1, s->fd)) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::addFileData() Bad fwrite"); +#endif + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_WRITE); + return 0; + } + + s->recv_size += size; + + + /* if we've completed the request this time */ + if (s->req_loc + s->req_size == s->recv_size) + { + s->lastDelta = time(NULL) - s->lastTS; + } + + if (s->recv_size == s->total_size) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::addFileData() File Complete!"); +#endif + s->status = PQIFILE_COMPLETE; + + /* HANDLE COMPLETION HERE */ + completeFileTransfer(s); + } + + return 1; +} + + +int ftfiler::completeFileTransfer(ftFileStatus *s) +{ + /* cleanup transfer */ + if (s->fd) + { + fclose(s->fd); + s->fd = 0; + + // re-open in read mode (for transfers?) + // don't bother .... + // s->fd = fopen(s->file_name.c_str(), "r+b"); + + } + + /* so now we move it to the expected destination */ + /* determine where it should go! */ + bool ok = true; + + std::string dest = determineDestFilePath(s); + if (0 == rename(s->file_name.c_str(), dest.c_str())) + { + /* correct the file_name */ + s->file_name = dest; + } + else + { + ok = false; + } + + /* do callback if CACHE */ + if (s->ftMode == FT_MODE_CACHE) + { + if (ok) + { + CompletedCache(s->hash); + } + else + { + FailedCache(s->hash); + } + } + return 1; +} + + +/*********************** + * Notes + * + * createFileCache is called: int ftfiler::handleFileRequest(PQFileItem *in) only. + * + * it should + * (1) create a ftFileStatus + * (2) find it in the indices. + * (3) load up the details. + */ + + +ftFileStatus *ftfiler::createFileCache(std::string hash) +{ +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::createFileCache()"); +#endif + + ftFileStatus *s = new ftFileStatus(hash, hash, 0, "", FT_MODE_UPLOAD); + + /* request from fileindex */ + bool found = false; + + /* so look it up! */ + std::string srcpath; + uint64_t size; + if (lookupLocalHash(s->hash, srcpath, size)) + { + found = true; + s->file_name = srcpath; + s->size = size; + } + + if ((!found) || (s->file_name.length() < 1)) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::createFileCache() Failed to Find File"); +#endif + /* failed to open the file */ + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_BAD_PATH); + delete s; + return NULL; + } + + + /* attempt to open file (readonly) */ + FILE *fd = fopen(s->file_name.c_str(), "rb"); + if (!fd) + { +#ifdef FT_DEBUG + std::stringstream out; + out << "ftfiler::createFileCache() Failed to Open the File" << std::endl; + out << "\tFull Path:" << s->file_name.c_str() << std::endl; + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, out.str()); +#endif + /* failed to open the file */ + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_OPEN); + delete s; + return NULL; + } + + /* if it opened, find it's length */ + /* move to the end */ + if (0 != fseek(fd, 0L, SEEK_END)) + { +#ifdef FT_DEBUG + pqioutput(PQL_DEBUG_BASIC, ftfilerzone, + "ftfiler::createFileCache() Fseek Failed"); +#endif + + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_OPEN); + delete s; + return NULL; + } + + s->recv_size = ftell(fd); /* get the file length */ + s->total_size = s->size; /* save the total length */ + s->req_loc = 0; /* no request */ + s->req_size = 0; + + s->name = RsDirUtil::getTopDir(s->file_name); + + /* we are now ready for transfers */ + s->fd = fd; + s->lastTS = 0; + s->status = PQIFILE_DOWNLOADING; + return s; +} + + + + +/**** + * NOTE this should move all temporary and cache files. + * TODO! + */ + +void ftfiler::setSaveBasePath(std::string s) +{ + saveBasePath = s; + return; +} + + +void ftfiler::setEmergencyBasePath(std::string s) +{ + mEmergencyIncomingDir = s; + return; +} + + + +/*********************** + * Notes + * + * debugging functions. + * + */ + + +void printFtFileStatus(ftFileStatus *s, std::ostream &out) +{ + /* main details */ + out << "FtFileStatus::Internals:" << std::endl; + out << "name: " << s->name << std::endl; + out << "hash: " << s->hash << std::endl; + out << "destpath " << s->destpath << std::endl; + // + + out << "Source: " << s->id << std::endl; + out << "Alt Srcs: "; + std::list::iterator it; + for(it = s->sources.begin(); it != s->sources.end(); it++) + { + out << " " << (*it); + } + + out << std::endl; + out << " mode: " << s->mode; + out << " ftMode: " << s->ftMode; + out << " status: " << s->status; + out << " resetCount: " << s->resetCount; + out << std::endl; + + if (s->fd) + { + out << "FD Valid: "; + } + else + { + out << "FD Invalid: "; + } + out << "file_name " << s->file_name << std::endl; + + out << " size " << s->size; + out << " total_size " << s->total_size; + out << " recv_size " << s->recv_size; + out << " rate: " << s->rate; + out << std::endl; + out << " Req loc: " << s->req_loc; + out << " Req size: " << s->req_size; + out << std::endl; + out << " last Delta: " << s->lastDelta; + out << " last TS: " << s->lastTS; + out << std::endl; +} + + diff --git a/libretroshare/src/_server/ftfiler.h b/libretroshare/src/_server/ftfiler.h new file mode 100644 index 000000000..982d080ea --- /dev/null +++ b/libretroshare/src/_server/ftfiler.h @@ -0,0 +1,232 @@ +/* + * "$Id: ftfiler.h,v 1.5 2007-02-19 20:08:30 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef FTFILER_H +#define FTFILER_H + +/* + * PQI Filer + * + * This managers the file transfers. + * + */ + +#include "_server/ft.h" +#include "_pqi/pqi.h" +#include +#include +#include + +const int PQIFILE_INIT = 0x0000; +const int PQIFILE_NOT_ONLINE = 0x0001; +const int PQIFILE_DOWNLOADING = 0x0002; +const int PQIFILE_COMPLETE = 0x0004; +const int PQIFILE_FAIL = 0x0010; +/* reasons for DOWNLOAD FAILURE (2nd byte) */ +const int PQIFILE_FAIL_CANCEL = 0x0100; +const int PQIFILE_FAIL_NOT_AVAIL = 0x0200; +const int PQIFILE_FAIL_NOT_OPEN = 0x0400; +const int PQIFILE_FAIL_NOT_SEEK = 0x0800; +const int PQIFILE_FAIL_NOT_WRITE = 0x1000; +const int PQIFILE_FAIL_NOT_READ = 0x2000; +const int PQIFILE_FAIL_BAD_PATH = 0x4000; + + +const int TRANSFER_MODE_TRICKLE = 1; +const int TRANSFER_MODE_NORMAL = 2; +const int TRANSFER_MODE_FAST = 3; + + +const uint32_t FT_MODE_STD = 1; +const uint32_t FT_MODE_CACHE = 2; +const uint32_t FT_MODE_UPLOAD = 4; + +class ftFileStatus +{ +public: +/**** + ftFileStatus(PQFileItem *in) + :fileItem(in), status(PQIFILE_INIT), fd(NULL), + total_size(0), recv_size(0), + req_loc(0), req_size(0), lastTS(0) + { + return; + } +****/ + + ftFileStatus(const std::string& name_in, const std::string& hash_in, uint64_t size_in, + const std::string& destpath_in, uint32_t mode_in) + :name(name_in), hash(hash_in), destpath(destpath_in), size(size_in), ftMode(mode_in), + status(PQIFILE_INIT), mode(0), rate(0), fd(NULL), total_size(0), recv_size(0), + req_loc(0), req_size(0), lastTS(0), lastDelta(0),file_name(""),id("") + { + /* not set ... + * id, + * filename, + * sources + */ + return; + } + + ftFileStatus(const std::string& id_in, const std::string& name_in, const std::string& hash_in, uint64_t size_in, + const std::string& destpath_in, uint32_t mode_in) + :id(id_in), name(name_in), hash(hash_in), destpath(destpath_in), size(size_in), ftMode(mode_in), + status(PQIFILE_INIT), mode(0), rate(0), fd(NULL), total_size(0), recv_size(0), + req_loc(0), req_size(0), lastTS(0), lastDelta(0),file_name("") + { + /* not set ... + * id, + * filename, + * sources + */ + return; + } + + + ~ftFileStatus() + { + if (fd) + fclose(fd); + } + + + /* data */ + std::string id; /* current source / most recent destination */ + std::string name; + std::string hash; + std::string destpath; + uint64_t size; + + /* new stuff */ + uint32_t ftMode; + std::list sources; + uint32_t resetCount; + + /* transfer inprogress or not */ + int status; + int mode; + float rate; + + std::string file_name; + FILE *fd; + uint64_t total_size; + /* this is the simplistic case where only inorder data + * otherwise - need much more status info */ + uint64_t recv_size; + /* current file data request / most recent request answered (upload) */ + uint64_t req_loc; + uint32_t req_size; + + /* timestamp */ + time_t lastTS; /* last request / request answered (upload) */ + uint32_t lastDelta; /* send til all recved */ +}; + + +class ftfiler: public ftManager +{ +public: + + ftfiler(CacheStrapper *cs) + :ftManager(cs) { return; } + + virtual ~ftfiler() { return; } + + virtual bool RequestCacheFile(std::string id, std::string path, + std::string hash, uint64_t size); + virtual bool CancelCacheFile(RsPeerId id, std::string path, + std::string hash, uint64_t size); + + virtual int getFile(std::string name, std::string hash, + uint64_t size, std::string destpath); + + virtual int cancelFile(std::string hash); + virtual int clearFailedTransfers(); + + int tick(); + std::list getStatus(); + + virtual void setSaveBasePath(std::string s); + virtual void setEmergencyBasePath(std::string s); + + /************* Network Interface****************************/ + virtual int recvFileInfo(ftFileRequest *in); + virtual ftFileRequest *sendFileInfo(); + +private: + + virtual int handleFileError(std::string hash, uint32_t err); + virtual int handleFileNotOnline(std::string hash); + virtual int handleFileNotAvailable(std::string hash); + virtual int handleFileData(std::string hash, uint64_t offset, + void *data, uint32_t size); + + virtual int handleFileRequest( std::string id, std::string hash, + uint64_t offset, uint32_t chunk); + virtual int handleFileCacheRequest(std::string id, std::string hash, + uint64_t offset, uint32_t chunk); + + ftFileStatus *findRecvFileItem(std::string hash); + void queryInactive(); + + + /**************** End of Interface *************************/ + int requestData(ftFileStatus *item); + + /************* PQIFILEITEM Generator *************************** + */ + + ftFileStatus *createFileCache(std::string hash); + ftFileRequest *generateFileRequest(ftFileStatus *); + int generateFileData(ftFileStatus *s, std::string id, uint64_t offset, uint32_t size); + //int sendFileNotAvail(PQFileItem *req); + + /************* FILE DATA HANDLING ****************************** + */ + + std::string determineTmpFilePath(ftFileStatus *s); + std::string determineDestFilePath(ftFileStatus *s); + + int initiateFileTransfer(ftFileStatus *s); + int resetFileTransfer(ftFileStatus *s); + int addFileData(ftFileStatus *s, uint64_t idx, void *data, uint32_t size); + int completeFileTransfer(ftFileStatus *s); + + + // Data. +private: + std::list recvFiles; + std::list fileCache; + std::list out_queue; + + std::string saveBasePath; + std::string mEmergencyIncomingDir; +}; + + + +#endif // FTFILER_H diff --git a/libretroshare/src/_server/hashsearch.cc b/libretroshare/src/_server/hashsearch.cc new file mode 100644 index 000000000..aa9862b5d --- /dev/null +++ b/libretroshare/src/_server/hashsearch.cc @@ -0,0 +1,51 @@ +/* + * "$Id: hashsearch.cc,v 1.5 2007-02-19 20:08:30 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + +/********** + * SearchInterface for the FileTransfer + */ + +#include "_server/hashsearch.h" +#include "_dbase/fistore.h" +#include "_dbase/fimonitor.h" + + /* Search Interface - For FileTransfer Lookup */ +int FileHashSearch::searchLocalHash(std::string hash, std::string &path, uint64_t &size) +{ + if (monitor) + { + return monitor->findLocalFile(hash, path, size); + } + return 0; +} + +int FileHashSearch::searchRemoteHash(std::string hash, std::list &results) +{ + if (store) + store->SearchHash(hash, results); + return results.size(); +} + + diff --git a/libretroshare/src/_server/hashsearch.h b/libretroshare/src/_server/hashsearch.h new file mode 100644 index 000000000..07c261f13 --- /dev/null +++ b/libretroshare/src/_server/hashsearch.h @@ -0,0 +1,61 @@ +/* + * "$Id: hashsearch.h,v 1.5 2007-02-19 20:08:30 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + + +#ifndef HASHSEARCH_H +#define HASHSEARCH_H + +/********** + * SearchInterface for the FileTransfer + */ + +#include "rsiface/rstypes.h" +class FileIndexStore; +class FileIndexMonitor; +#include "dbase/fistore.h" +#include "dbase/fimonitor.h" + +class FileHashSearch +{ +public: + FileHashSearch(FileIndexStore *s, FileIndexMonitor *m) + :store(s), monitor(m) { return; } + + ~FileHashSearch() { return; } + + /* Search Interface - For FileTransfer Lookup */ + int searchLocalHash(std::string hash, std::string &path, uint64_t &size); + + int searchRemoteHash(std::string hash, std::list &results); + +private: + + FileIndexStore *store; + FileIndexMonitor *monitor; +}; + +#endif // HASHSEARCH_H diff --git a/libretroshare/src/_server/pqifiler.cc b/libretroshare/src/_server/pqifiler.cc new file mode 100644 index 000000000..1319e95e2 --- /dev/null +++ b/libretroshare/src/_server/pqifiler.cc @@ -0,0 +1,1220 @@ +/* + * "$Id: pqifiler.cc,v 1.13 2007-02-19 20:08:30 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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 "_server/pqifiler.h" + +#include "_pqi/pqidebug.h" +#include + +#include + +const int pqifilerzone = 86539; + +/* + * PQI Filer + * + * This managers the file transfers. + * + * + * TODO: add trickle transfers. + * + */ + +const int PQIFILE_OFFLINE_CHECK = 120; /* check every 2 minutes */ +const int PQIFILE_DOWNLOAD_TIMEOUT = 60; /* time it out, -> offline after 60 secs */ +const int PQIFILE_DOWNLOAD_CHECK = 10; /* desired delta = 10 secs */ +const int PQIFILE_DOWNLOAD_TOO_FAST = 8; /* 8 secs */ +const int PQIFILE_DOWNLOAD_TOO_SLOW = 12; /* 12 secs */ +const int PQIFILE_DOWNLOAD_MIN_DELTA = 5; /* 5 secs */ + +const float TRANSFER_MODE_TRICKLE_RATE = 1000; /* 1 kbyte limit */ +const float TRANSFER_MODE_NORMAL_RATE = 500000; /* 500 kbyte limit - everyone uses this one for now */ +const float TRANSFER_MODE_FAST_RATE = 500000; /* 500 kbyte limit */ + +const int TRANSFER_START_MIN = 500; /* 500 byte min limit */ +const int TRANSFER_START_MAX = 2000; /* 2000 byte max limit */ + +#ifdef USE_FILELOOK +pqifiler::pqifiler(fileLook *fd) + :fileIndex(fd) { return; } +#else +pqifiler::pqifiler(filedex *fd) + :fileIndex(fd) { return; } +#endif + + +/************* Local File Interface **************************** + * + * int pqifiler::getFile(PQFileItem *in); + * int pqifiler::cancelFile(PQFileItem *i); + * int pqifiler::clearFailedTransfers(); + * + * * Worker Fns. + * PQFileStatus *pqifiler::findRecvFileItem(PQFileItem *in); + */ + +int pqifiler::getFile(PQFileItem *in) +{ + /* add to local queue */ + { + std::ostringstream out; + out << "pqifiler::getFile(): " << std::endl; + in -> print(out); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + } + + + + /* check for duplicates */ + PQFileStatus *state = findRecvFileItem(in); + if (state) + { + std::ostringstream out; + out << "pqifiler::getFile() - duplicate, giving push!"; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + + /* if duplicate - give download a push! */ + /* do this by flagging last transfer at 0. + */ + /* and also set the request stuff, so it'll + * generate a new request + * - we might lose the current transfer - but + * that's the idiots fault for redownloading. + */ + + resetFileTransfer(state); + return 1; + } + + state = new PQFileStatus(in); + if (initiateFileTransfer(state)) + { + std::ostringstream out; + out << "pqifiler::getFile() "; + out << "adding to recvFiles queue"; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + + recvFiles.push_back(state); + } + return 1; +} + +PQFileStatus *pqifiler::findRecvFileItem(PQFileItem *in) +{ + { + std::ostringstream out; + out << "pqifiler::findRecvFileItem() Looking for: " << in->name; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + } + + /* iterate through fileItems and check for this one */ + std::list::iterator it; + for(it = recvFiles.begin(); it != recvFiles.end(); it++) + { + if ((in->name==(*it)->fileItem->name) && + (in->hash==(*it)->fileItem->hash)) + { + std::ostringstream out; + out << "pqifiler::findRecvFileItem() "; + out << "Match PQFileStatus: " << in -> name; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + /* same */ + return (*it); + } + } + return NULL; +} + + + + +int pqifiler::cancelFile(PQFileItem *i) +{ + /* flag as cancelled */ + /* iterate through fileItems and check for this one */ + { + std::ostringstream out; + out << "pqifiler::cancelFile(): " << std::endl; + i -> print(out); + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + } + + std::list::iterator it; + for(it = recvFiles.begin(); it != recvFiles.end(); it++) + { + if ((i->name==(*it)->fileItem->name) && + (i->hash==(*it)->fileItem->hash)) + { + std::ostringstream out; + out << "pqifiler::cancelFile() "; + out << "Found file: " << i -> name; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + + /* same */ + (*it)->status = (PQIFILE_FAIL | PQIFILE_FAIL_CANCEL); + return 1; + } + } + + { + std::ostringstream out; + out << "pqifiler::cancelFile() "; + out << "couldn't match PQFileStatus!"; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + } + return 0; +} + +int pqifiler::clearFailedTransfers() +{ + /* remove all the failed items */ + /* iterate through fileItems and check for this one */ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::clearFailedTransfers()"); + + std::list::iterator it; + int cleared = 0; + for(it = recvFiles.begin(); it != recvFiles.end(); /* done in loop */) + { + if ((*it)->status & PQIFILE_FAIL) + { + std::ostringstream out; + out << "pqifiler::clearFailedTransfers() "; + out << "removing item: " << (*it) -> fileItem -> name; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + + /* same */ + PQFileStatus *cfile = (*it); + it = recvFiles.erase(it); + delete cfile; + cleared++; + } + else if ((*it)->status & PQIFILE_COMPLETE) + { + std::ostringstream out; + out << "pqifiler::clearFailedTransfers() "; + out << "removing Completed item: "; + out << (*it) -> fileItem -> name; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + + /* same */ + PQFileStatus *cfile = (*it); + it = recvFiles.erase(it); + delete cfile; + cleared++; + } + else + { + it++; + } + } + + { + std::ostringstream out; + out << "pqifiler::clearFailedTransfers() cleared: "; + out << cleared; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + } + + return 1; +} + + +std::list pqifiler::getStatus() +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::getTransferStatus()"); + std::list stateList; + + /* iterate through all files to recv */ + std::list::iterator it; + for(it = recvFiles.begin(); it != recvFiles.end(); it++) + { + FileTransferItem *fti = new FileTransferItem(); + fti -> PQFileItem::copy((*it)->fileItem); + + /* Fill in rate and State */ + fti -> transferred = (*it)->recv_size; + fti -> crate = (*it)->rate / 1000.0; // kbytes. + fti -> trate = (*it)->rate / 1000.0; // kbytes. + fti -> lrate = (*it)->rate / 1000.0; // kbytes. + fti -> ltransfer = (*it)->req_size; + fti -> in = true; + + /* get inactive period */ + if ((*it) -> status == PQIFILE_NOT_ONLINE) + { + fti -> crate = 0; + fti -> trate = 0; + fti -> lrate = 0; + fti -> ltransfer = 0; + fti -> state = FT_STATE_OKAY; + } + else if ((*it) -> status & PQIFILE_FAIL) + { + fti -> crate = 0; + fti -> trate = 0; + fti -> lrate = 0; + fti -> ltransfer = 0; + fti -> state = FT_STATE_FAILED; + + } + else if ((*it) -> status == PQIFILE_COMPLETE) + { + fti -> state = FT_STATE_COMPLETE; + } + else if ((*it) -> status == PQIFILE_DOWNLOADING) + { + fti -> state = FT_STATE_OKAY; + } + else + { + fti -> state = FT_STATE_FAILED; + } + stateList.push_back(fti); + } + return stateList; +} + + +/************* Incoming FileItems ****************************** + * + * PQFileItem *pqifiler::sendPQFileItem() + * int pqifiler::recvPQFileItem(PQFileItem *in) + * + * * Worker Fns. + * int pqifiler::handleFileNotOnline(PQFileItem *in) + * int pqifiler::handleFileNotOnline(PQFileItem *in) + * int pqifiler::handleFileNotAvailable(PQFileItem *in) + * int pqifiler::handleFileData(PQFileItem *in) + * int pqifiler::handleFileRequest(PQFileItem *in) + * int pqifiler::handleFileCacheRequest(PQFileItem *req) + * + */ + +PQItem *pqifiler::sendPQFileItem() +{ + if (out_queue.size() < 1) + { + return NULL; + } + PQItem *i = out_queue.front(); + out_queue.pop_front(); + { + std::ostringstream out; + out << "pqifiler::sendPQFileItem() "; + out << "returning: " << std::endl; + i -> print(out); + pqioutput(PQL_DEBUG_ALL, pqifilerzone, out.str()); + } + return i; +} + +int pqifiler::recvPQFileItem(PQItem *item) +{ + /* check what type */ + PQFileItem *in; + + { + std::ostringstream out; + out << "pqifiler::recvPQFileItem() "; + out << "input: " << std::endl; + item -> print(out); + pqioutput(PQL_DEBUG_ALL, pqifilerzone, out.str()); + } + + if (NULL == (in = dynamic_cast(item))) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::recvPQFileItem() Error Not PQFileItem"); + delete item; + return 0; + } + + + switch(in -> subtype) + { + case PQI_FI_SUBTYPE_ERROR: /* not currently connected */ + handleFileError(in); + break; + case PQI_FI_SUBTYPE_DATA: /* received some data */ + handleFileData(in); + break; + case PQI_FI_SUBTYPE_REQUEST: + handleFileRequest(in); + break; + default: + /* what ? */ + break; + } + + /* clean up */ + delete in; + return 1; +} + + + +int pqifiler::handleFileError(PQFileItem *in) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileError()"); + /* get out the error */ + if (in->fileoffset | PQIFILE_NOT_ONLINE) + { + return handleFileNotOnline(in); + } + if (in->fileoffset & PQIFILE_FAIL) + { + return handleFileNotAvailable(in); + } + return 0; +} + +int pqifiler::handleFileNotOnline(PQFileItem *in) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileNotOnline()"); + /* flag recvFile item as not Online */ + PQFileStatus *s = findRecvFileItem(in); + if ((!s) || (s -> status & PQIFILE_FAIL)) + { + return 0; + } + + s -> status = PQIFILE_NOT_ONLINE; + + return 1; +} + + +int pqifiler::handleFileNotAvailable(PQFileItem *in) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileNotAvailable()"); + /* error - flag recvFile item with FAILED */ + PQFileStatus *s = findRecvFileItem(in); + if (!s) + { + return 0; + } + + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_AVAIL); + return 1; +} + + +int pqifiler::handleFileData(PQFileItem *in) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileData()"); + /* find the right PQFileStatus */ + PQFileStatus *recv = findRecvFileItem(in); + if (!recv) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileData() no matching PQFileStatus (current download)"); + return 0; + } + + if(recv->status & PQIFILE_FAIL) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileData() marked as FAIL"); + return 0; + } + + PQFileData *dta; + if (NULL == (dta = dynamic_cast(in))) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileData() not PQFileData"); + return 0; + } + + /* first check the cid matches, so we can get it right for later */ + if (0 != pqicid_cmp(&(in->cid), &(recv->fileItem->cid))) + { + /* not matched */ + pqioutput(PQL_WARNING, pqifilerzone, + "pqifiler::handleFileData() correcting fileItem->cid"); + pqicid_copy(&(in->cid), &(recv->fileItem->cid)); + + std::ostringstream out; + out << "pqifiler::handleFileData() in->cid != recv->fileItem->cid"; + out << std::endl; + out << "in -> CID [" << in->cid.route[0]; + for(int i = 0; i < 10; i++) + { + out << ":" << in->cid.route[i]; + } + out << "]" << std::endl; + + out << "recv -> CID [" << recv->fileItem->cid.route[0]; + for(int i = 0; i < 10; i++) + { + out << ":" << recv->fileItem->cid.route[i]; + } + out << "]" << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone,out.str()); + } + + + /* add to file */ + addFileData(recv, dta->fileoffset, dta->data, dta->datalen); + + if (recv->status == PQIFILE_NOT_ONLINE) + { + /* switch to active */ + recv->status = PQIFILE_DOWNLOADING; + } + + /* if we have recieved all data - request some more */ + if ((recv->recv_size == recv->req_loc + recv->req_size) && + (recv->status != PQIFILE_COMPLETE)) + { + requestData(recv); + } + return 1; +} + +int pqifiler::handleFileRequest(PQFileItem *in) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileRequest()"); + /* see if in cache */ + /* if yes send out chunk */ + if (handleFileCacheRequest(in)) + { + return 1; + } + + /* if not in cache - find file */ + PQFileStatus *new_file = createFileCache(in); + if (!new_file) + { + /* bad file */ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileRequest() Failed to Load File-sendNotAvail"); + return sendFileNotAvail(in); + } + + fileCache.push_back(new_file); + + return handleFileCacheRequest(in); +} + +int pqifiler::handleFileCacheRequest(PQFileItem *req) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::handleFileCacheRequest()"); + /* check if in cache */ + bool found = false; + PQFileStatus *s; + std::list::iterator it; + for(it = fileCache.begin(); (!found) && (it != fileCache.end()); it++) + { + if ((req->name==(*it)->fileItem->name) && + (req->hash==(*it)->fileItem->hash)) + { + found = true; + s = (*it); + } + } + if (!found) + return 0; + + /* push to out queue */ + return generateFileData(s, req); +} + +/************* Outgoing FileItems ****************************** + * + * PQFileItem *pqifiler::sendPQFileItem() + * + * * Worker Fns. + * int pqifiler::tick(); + * void pqifiler::queryInactive() + * + */ + + +int pqifiler::tick() +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::tick()"); + /* check the status of recved files */ + queryInactive(); + + /* this doesn't matter much how often it's ticked... + * if it generates Items, they will be detected other places. + * so we can return 0 (waiting) + */ + return 0; +} + + +void pqifiler::queryInactive() +{ + std::ostringstream out; + + out << "pqifiler::queryInactive()"; + out << std::endl; + + + + /* iterate through all files to recv */ + int ts = time(NULL); + std::list::iterator it; + for(it = recvFiles.begin(); it != recvFiles.end(); it++) + { + /* get inactive period */ + switch((*it) -> status) + { + case PQIFILE_NOT_ONLINE: + out << "File: " << (*it)->fileItem->name << " Not Online: "; + out << "Delta: " << (ts - (*it)->lastTS) << std::endl; + out << " LDelta: " << (*it)->lastDelta; + out << " Recved: " << (*it)->recv_size; + out << " Total: " << (*it)->total_size; + out << " LChunk: " << (*it)->req_size; + out << std::endl; + + if (ts - ((*it)->lastTS) > PQIFILE_OFFLINE_CHECK) + { + resetFileTransfer(*it); + requestData(*it); + } + break; + case PQIFILE_DOWNLOADING: + out << "File: " << (*it)->fileItem->name << " Downloading: "; + out << " Delta: " << (ts - (*it)->lastTS) << std::endl; + out << " LDelta: " << (*it)->lastDelta; + out << " Recved: " << (*it)->recv_size; + out << " Total: " << (*it)->total_size; + out << " LChunk: " << (*it)->req_size; + out << std::endl; + + if (ts - ((*it)->lastTS) > PQIFILE_DOWNLOAD_CHECK) + { + requestData(*it); /* give it a push */ + } + break; + default: + out << "File: " << (*it)->fileItem->name << " Other mode: " << (*it)->status; + out << " Delta: " << (ts - (*it)->lastTS) << std::endl; + out << " LDelta: " << (*it)->lastDelta; + out << " Recved: " << (*it)->recv_size; + out << " Total: " << (*it)->total_size; + out << " LChunk: " << (*it)->req_size; + out << std::endl; + /* nothing */ + break; + } + } + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); +} + + +int pqifiler::requestData(PQFileStatus *item) +{ + + /* formulate a request for the next desired data chunk */ + /* this handles the key throttling. This algorithm can + * be quite crude, as the tcp / and pqistreamer throttle as + * well. + */ + + std::ostringstream out; + out << "pqifiler::requestData()" << std::endl; + + /* get the time since last request */ + int tm = time(NULL); + float delta = tm - item -> lastTS; + + + + + /* decide on transfer mode */ + float max_rate; + switch(item->mode) + { + case TRANSFER_MODE_TRICKLE: + max_rate = TRANSFER_MODE_TRICKLE_RATE; + break; + case TRANSFER_MODE_NORMAL: + max_rate = TRANSFER_MODE_NORMAL_RATE; + break; + case TRANSFER_MODE_FAST: + max_rate = TRANSFER_MODE_FAST_RATE; + break; + default: + max_rate = TRANSFER_MODE_NORMAL_RATE; + break; + } + out << "max rate: " << max_rate; + out << std::endl; + + /* not finished */ + if (item->recv_size < item->req_loc + item->req_size) + { + if (delta > PQIFILE_DOWNLOAD_TIMEOUT) + { + /* we have timed out ... switch to + * offline + */ + /* start again slowly */ + item->req_size = (int) (0.1 * max_rate); + out << "Timeout: switching to Offline."; + out << std::endl; + item->status = PQIFILE_NOT_ONLINE; + } + else + { + out << "Pause: Not Finished"; + out << std::endl; + /* pause */ + } + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + return 0; + } + + + if (delta <= PQIFILE_DOWNLOAD_MIN_DELTA) + { + /* pause */ + out << "Small Delta -> Pause"; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + return 0; + } + + /* From this point - we will continue ... so handle rate now! */ + /* calc rate */ + float bytes_psec = item -> req_size / delta; + item -> rate = 0.9 * item -> rate + 0.1 * bytes_psec; + out << "delta: " << delta << " bytes: " << bytes_psec << " rate: " << item -> rate; + out << std::endl; + + if (item->lastDelta <= PQIFILE_DOWNLOAD_TOO_FAST) + { + /* increase 0.75 of the calculated extra that could be transmitted + * in the timeframe + */ + + float data_tf = item -> req_size; + float ldelta_f = item->lastDelta + 0.5; // 0.5 for extra space (+ dont / 0.0) + float tf_p_sec = data_tf / ldelta_f; + float extra_tf = tf_p_sec * (PQIFILE_DOWNLOAD_CHECK - ldelta_f); + + item -> req_size = item->req_size + (int) (0.75 * extra_tf); + + if (item->req_size > max_rate * PQIFILE_DOWNLOAD_CHECK) + item->req_size = (int) (max_rate * PQIFILE_DOWNLOAD_CHECK); + + out << "Small Delta: " << ldelta_f << " (sec), rate: " << tf_p_sec; + out << std::endl; + out << "Small Delta Incrementing req_size from: " << data_tf; + out << " to :" << item->req_size; + out << std::endl; + + } + else if (item->lastDelta > PQIFILE_DOWNLOAD_TOO_SLOW) + { + /* similarly decrease rate by 1.5 of extra time */ + + float data_tf = item -> req_size; + float ldelta_f = item->lastDelta + 0.5; // 0.5 for extra space (+ dont / 0.0) + float tf_p_sec = data_tf / ldelta_f; + float extra_tf = tf_p_sec * (ldelta_f - PQIFILE_DOWNLOAD_CHECK); + + item -> req_size -= (int) (1.25 * extra_tf); + + out << "Long Delta: " << ldelta_f << " (sec), rate: " << tf_p_sec; + out << std::endl; + out << "Long Delta Decrementing req_size from: " << data_tf; + out << " to :" << item->req_size; + out << std::endl; + } + + /* make the packet */ + + item->req_loc = item->recv_size; + /* req_size already calculated (unless NULL) */ + if (item->req_size < TRANSFER_START_MIN) + { + /* start again slowly + * added an extra limiter. */ + item->req_size = (int) (0.01 * max_rate); + if (item->req_size < TRANSFER_START_MIN) + { + item->req_size = TRANSFER_START_MIN; + } + else if (item->req_size > TRANSFER_START_MAX) + { + item->req_size = TRANSFER_START_MAX; + } + } + + out << "Making Packet: offset: " << item->req_loc << " size: " << item->req_size; + out << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + + PQFileItem *req = generatePQFileRequest(item); + out_queue.push_back(req); + + return 1; +} + +/************* PQIFILEITEM Generator *************************** + * + * PQFileItem *pqifiler::generatePQFileRequest(PQFileStatus *s); + * int pqifiler::generateFileData(PQFileStatus *s, PQFileItem *req); + * int pqifiler::sendFileNotAvail(PQFileItem *req) + * + */ + + +int pqifiler::generateFileData(PQFileStatus *s, PQFileItem *req) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::generateFileData()"); + + if ((!s) || (!s->fd) || (s->status & PQIFILE_FAIL)) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::generateFileData() Bad Status"); + if (!s) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::generateFileData() Bad Status (!s)"); + } + if (!s->fd) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::generateFileData() Bad Status (!s->fd)"); + } + if (s->status & PQIFILE_FAIL) + { + std::ostringstream out; + out << "pqifiler::generateFileData() Bad Status (s->status): " << s->status; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone,out.str()); + } + + /* return an error */ + return sendFileNotAvail(req); + } + + /* make the packets */ + int tosend = req -> chunksize; + long base_loc = req -> fileoffset; + + if (base_loc + tosend > s -> total_size) + { + tosend = s -> total_size - base_loc; + } + + { + std::ostringstream out; + out << "pqifiler::generateFileData() Sending " << tosend; + out << " bytes from offset: " << base_loc << std::endl; + out << "\tFrom File:" << req -> name; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + } + + + while(tosend > 0) + { + int pktsize = 5 * 1024; + if (pktsize > tosend) + pktsize = tosend; + + /* seek for base_loc */ + fseek(s->fd, base_loc, SEEK_SET); + + // make a FileData type. + PQFileData *fd = new PQFileData(); + + // Copy details from the Request. + fd -> PQFileItem::copy(req); + + // PQItem + fd -> sid = req -> sid; + fd -> type = PQI_ITEM_TYPE_FILEITEM; + fd -> subtype = PQI_FI_SUBTYPE_DATA; + + // PQFileITem + fd -> size = s->fileItem->size; // total size of file. + fd -> fileoffset = base_loc; + fd -> chunksize = pktsize; + + // data + fd -> datalen = pktsize; + fd -> data = malloc(fd -> datalen); + fd -> fileflags = 0; + { + std::ostringstream out; + out << "pqifiler::generateFileData() pkt:" << std::endl; + //fd -> print(out); + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + } + + /* read the data */ + if (1 != fread(fd -> data, fd -> datalen, 1, s->fd)) + { + std::ostringstream out; + + out << "pqifiler::generateFileData() Read only: "; + out << fd->datalen << "/" << pktsize << " bytes of data - Discarding"; + + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + + //free(fd->data); + //fd->data = NULL; + delete fd; + return 0; + } + + /* decrement sizes */ + base_loc += pktsize; + tosend -= pktsize; + + /* send off the packet */ + out_queue.push_back(fd); + } + return 1; +} + + +PQFileItem *pqifiler::generatePQFileRequest(PQFileStatus *s) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::generatePQFileRequest()"); + + PQFileItem *fi = s->fileItem->clone(); + + /* set req_loc, and req_size */ + + // PQItem + fi -> sid = getPQIsearchId(); + fi -> type = PQI_ITEM_TYPE_FILEITEM; + fi -> subtype = PQI_FI_SUBTYPE_REQUEST; + + // PQFileITem + fi -> size = s->fileItem->size; // total size of file. + fi -> fileoffset = s->req_loc; + fi -> chunksize = s->req_size; + + std::ostringstream out; + + out << "pqifiler::generatePQFileRequest() for: " << s->fileItem->name << std::endl; + out << "pqifiler::generatePQFileRequest() offset: " << fi->fileoffset << " chunksize: "; + out << fi->chunksize << std::endl; + + //out << "s->fileItem:" << std::endl; + //s->fileItem->print(out); + //out << "DataRequest:" << std::endl; + //fi->print(out); + + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + + // timestamp request. + s->lastTS = time(NULL); + + return fi; +} + +int pqifiler::sendFileNotAvail(PQFileItem *req) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::sendFileNotAvail()"); + PQFileItem *fi = req -> clone(); + + /* set error code */ + fi -> subtype = PQI_FI_SUBTYPE_ERROR; + fi -> fileoffset = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_AVAIL); + + /* send out */ + out_queue.push_back(fi); + return 1; +} + + +/************* FILE DATA HANDLING ****************************** + * + * std::string pqifiler::determineFilePath(PQFileItem *item); + * int pqifiler::initiateFileTransfer(PQFileStatus *s); + * int pqifiler::resetFileTransfer(PQFileStatus *s); + * int pqifiler::addFileData(PQFileStatus *s, long idx, void *data, int size); + * + */ + +std::string pqifiler::determineFilePath(PQFileItem *item) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::determineFilePath()"); + + /* get the download path */ + // savePath = "."; + std::string filePath = savePath; + filePath += "/"; + filePath += item->name; + return filePath; +} + +int pqifiler::initiateFileTransfer(PQFileStatus *s) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::initiateFileTransfer()"); + + /* check if the file exists */ + if (s->file_name.length() < 1) + { + s->file_name = determineFilePath(s->fileItem); + } + + { + std::ostringstream out; + out << "pqifiler::initiateFileTransfer() Filename: "; + out << s->file_name; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + } + + /* attempt to open file */ + FILE *fd = fopen(s->file_name.c_str(), "r+b"); + if (!fd) + { + { + std::ostringstream out; + out << "pqifiler::initiateFileTransfer() Failed to open: "; + out << s->file_name << " Error:" << errno; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + } + + /* open in writing mode */ + fd = fopen(s->file_name.c_str(), "w+b"); + if (!fd) + { + std::ostringstream out; + out << "pqifiler::initiateFileTransfer() Failed to open: "; + out << s->file_name << " Error:" << errno; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + + /* failed to open the file */ + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_OPEN); + return 0; + } + + } + + + /* if it opened, find it's length */ + /* move to the end */ + if (0 != fseek(fd, 0L, SEEK_END)) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::initiateFileTransfer() Seek Failed"); + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_SEEK); + return 0; + } + + s->recv_size = ftell(fd); /* get the file length */ + s->total_size = s->fileItem->size; /* save the total length */ + s->fd = fd; + + resetFileTransfer(s); + return 1; +} + + +int pqifiler::resetFileTransfer(PQFileStatus *state) +{ + // reset all the basic items ... so the transfer will continue. + state->req_loc = 0; + state->req_size = 0; + state->lastTS = 0; + state->lastDelta = 0; + state->status = PQIFILE_NOT_ONLINE; + state->mode = TRANSFER_MODE_NORMAL; + state->rate = 0; + if (state->recv_size == state->total_size) + { + state->status = PQIFILE_COMPLETE; + } + + return 1; +} + + + +int pqifiler::addFileData(PQFileStatus *s, long idx, void *data, int size) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::addFileData()"); + + /* check the status */ + if ((!s) || (!s->fd) || (s->status & PQIFILE_FAIL)) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::addFileData() Bad Status"); + return 0; + } + + /* check its at the correct location */ + if ((idx != s->recv_size) || (s->recv_size + size > s->total_size)) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::addFileData() Bad Data Location"); + return 0; + } + + /* go to the end of the file */ + if (0 != fseek(s->fd, 0L, SEEK_END)) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::addFileData() Bad fseek"); + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_SEEK); + return 0; + } + + /* add the data */ + if (1 != fwrite(data, size, 1, s->fd)) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::addFileData() Bad fwrite"); + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_WRITE); + return 0; + } + + s->recv_size += size; + + + /* if we've completed the request this time */ + if (s->req_loc + s->req_size == s->recv_size) + { + s->lastDelta = time(NULL) - s->lastTS; + } + + if (s->recv_size == s->total_size) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::addFileData() File Complete!"); + s->status = PQIFILE_COMPLETE; + fclose(s->fd); + // re-open in read mode (for transfers?) + s->fd = fopen(s->file_name.c_str(), "r+b"); + } + + return 1; +} + + +PQFileStatus *pqifiler::createFileCache(PQFileItem *in) +{ + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::createFileCache()"); + + PQFileStatus *s = new PQFileStatus(in->clone()); + + /* request from fileindex */ + bool found = false; + + /* so here we will work with + */ + +#ifdef USE_FILELOOK + PQFileItem *real = fileIndex -> findFileEntry(in); + if (real) + { + s->file_name = real -> path + "/" + real -> name; + found = true; + } + +#else /*************************************************************************/ + std::list flist = fileIndex -> findfilename(in->name); + std::list::iterator it; + for(it = flist.begin(); (!found) && (it != flist.end()); it++) + { + if (in -> size == (*it) -> len) + { + found = true; + s->file_name = (*it) -> path; + } + } +#endif + + if ((!found) || (s->file_name.length() < 1)) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::createFileCache() Failed to Find File"); + /* failed to open the file */ + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_BAD_PATH); + delete s; + return NULL; + } + + + /* attempt to open file (readonly) */ + FILE *fd = fopen(s->file_name.c_str(), "rb"); + if (!fd) + { + std::stringstream out; + out << "pqifiler::createFileCache() Failed to Open the File" << std::endl; + out << "\tFull Path:" << s->file_name.c_str() << std::endl; + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str()); + /* failed to open the file */ + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_OPEN); + delete s; + return NULL; + } + + /* if it opened, find it's length */ + /* move to the end */ + if (0 != fseek(fd, 0L, SEEK_END)) + { + pqioutput(PQL_DEBUG_BASIC, pqifilerzone, + "pqifiler::createFileCache() Fseek Failed"); + + s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_OPEN); + delete s; + return NULL; + } + + s->recv_size = ftell(fd); /* get the file length */ + s->total_size = s->fileItem->size; /* save the total length */ + s->req_loc = 0; /* no request */ + s->req_size = 0; + + /* we are now ready for transfers */ + s->fd = fd; + s->lastTS = 0; + s->status = PQIFILE_DOWNLOADING; + return s; +} diff --git a/libretroshare/src/_server/pqifiler.h b/libretroshare/src/_server/pqifiler.h new file mode 100644 index 000000000..b932924bc --- /dev/null +++ b/libretroshare/src/_server/pqifiler.h @@ -0,0 +1,183 @@ +/* + * "$Id: pqifiler.h,v 1.5 2007-02-19 20:08:30 rmf24 Exp $" + * + * Other Bits for RetroShare. + * + * Copyright 2004-2006 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". + * + */ + + + +#ifndef PQIFILER_H +#define PQIFILER_H + +/* + * PQI Filer + * + * This managers the file transfers. + * + */ + +#include "_pqi/pqi.h" +#include "_dbase/filelook.h" +#include "_dbase/filedex.h" +#include +#include +#include + +const int PQIFILE_INIT = 0x0000; +const int PQIFILE_NOT_ONLINE = 0x0001; +const int PQIFILE_DOWNLOADING = 0x0002; +const int PQIFILE_COMPLETE = 0x0004; +const int PQIFILE_FAIL = 0x0010; +/* reasons for DOWNLOAD FAILURE (2nd byte) */ +const int PQIFILE_FAIL_CANCEL = 0x0100; +const int PQIFILE_FAIL_NOT_AVAIL = 0x0200; +const int PQIFILE_FAIL_NOT_OPEN = 0x0400; +const int PQIFILE_FAIL_NOT_SEEK = 0x0800; +const int PQIFILE_FAIL_NOT_WRITE = 0x1000; +const int PQIFILE_FAIL_NOT_READ = 0x2000; +const int PQIFILE_FAIL_BAD_PATH = 0x4000; + + +const int TRANSFER_MODE_TRICKLE = 1; +const int TRANSFER_MODE_NORMAL = 2; +const int TRANSFER_MODE_FAST = 3; + +class PQFileStatus +{ +public: + PQFileStatus(PQFileItem *in): + fileItem(in), status(PQIFILE_INIT), fd(NULL), + total_size(0), recv_size(0), + req_loc(0), req_size(0), lastTS(0) {} + + ~PQFileStatus() + { + if (fileItem) + delete fileItem; + if (fd) + fclose(fd); + } + + +/* data */ + PQFileItem *fileItem; + /* transfer inprogress or not */ + int status; + int mode; + float rate; + + std::string file_name; + FILE *fd; + long total_size; + /* this is the simplistic case where only inorder data + * otherwise - need much more status info */ + long recv_size; + + /* current file data request */ + long req_loc; + int req_size; + + /* timestamp */ + long lastTS; + int lastDelta; /* send til all recved */ +}; + + +class pqifiler +{ +public: + +#ifdef USE_FILELOOK + pqifiler(fileLook*); +#else + pqifiler(filedex*); +#endif + + virtual ~pqifiler() { return; } + +/******************* GUI Interface ************************* + */ + + int getFile(PQFileItem *in); + int cancelFile(PQFileItem *i); + int clearFailedTransfers(); + + + int tick(); + std::list getStatus(); + +/************* Network Interface**************************** + */ + + PQItem * sendPQFileItem(); + int recvPQFileItem(PQItem *in); + + void setSavePath(std::string s) { savePath = s;} + +private: + + PQFileStatus *findRecvFileItem(PQFileItem *in); + void queryInactive(); + + int handleFileError(PQFileItem *in); + int handleFileNotOnline(PQFileItem *in); + int handleFileNotAvailable(PQFileItem *in); + int handleFileData(PQFileItem *in); + int handleFileRequest(PQFileItem *in); + int handleFileCacheRequest(PQFileItem *req); + + + int requestData(PQFileStatus *item); + +/************* PQIFILEITEM Generator *************************** + */ + + PQFileStatus * createFileCache(PQFileItem *in); + PQFileItem * generatePQFileRequest(PQFileStatus *); + int generateFileData(PQFileStatus *s, PQFileItem *req); + int sendFileNotAvail(PQFileItem *req); + +/************* FILE DATA HANDLING ****************************** + */ + + std::string determineFilePath(PQFileItem *item); + int initiateFileTransfer(PQFileStatus *s); + int resetFileTransfer(PQFileStatus *s); + int addFileData(PQFileStatus *s, long idx, void *data, int size); + + // Data. +private: + std::list recvFiles; + std::list fileCache; + std::list out_queue; + +#ifdef USE_FILELOOK + fileLook *fileIndex; +#else + filedex *fileIndex; +#endif + + std::string savePath; +}; + + +#endif // PQIFILER_H diff --git a/libretroshare/src/_server/server.pri b/libretroshare/src/_server/server.pri new file mode 100644 index 000000000..6210f5eb2 --- /dev/null +++ b/libretroshare/src/_server/server.pri @@ -0,0 +1,14 @@ +INCLUDEPATH += $$PWD \ + ../$$PWP +DEPENDPATH += $$PWD + +HEADERS = filedexserver.h \ + ft.h \ + ftfiler.h \ + hashsearch.h \ + pqifiler.h +SOURCES = filedexserver.cc \ + ft.cc \ + ftfiler.cc \ + hashsearch.cc \ + pqifiler.cc diff --git a/libretroshare/src/_tests/conn_test.cc b/libretroshare/src/_tests/conn_test.cc new file mode 100644 index 000000000..7ca8043a4 --- /dev/null +++ b/libretroshare/src/_tests/conn_test.cc @@ -0,0 +1,362 @@ + + +#include "pqi/p3connmgr.h" + + +/***** Test for the new DHT system *****/ + + +#include "util/rsnet.h" +#include "util/rsthreads.h" +#include "util/rsprint.h" +#include "pqi/p3dhtmgr.h" +#include "pqi/p3connmgr.h" +#include "pqi/pqisecurity.h" +#include "pqi/pqipersongrp.h" + +#include +#include + +#include "tcponudp/udpsorter.h" + +/***** Test Framework *****/ + +const int NumOfPeers = 10; +std::string peerIds[NumOfPeers] = + {"PEER01", + "PEER02", /* Always online, no notify */ + "PEER03", /* notify/online at 20sec */ + "PEER04", /* Always online, notify at 30 sec */ + "PEER05", + "PEER06", /* notify/online at 50sec */ + "PEER07", + "PEER08", + "PEER09", /* notify/online at 80sec */ + "PEER10"}; + +#define STUN_PORT 7777 + +std::string ownId = "OWNID-AAAA"; +time_t ownPublishTs; + +RsMutex frmMtx; +std::list searchIds; +std::list searchModes; + +std::map onlineMap; +std::map notifyMap; + +void initTestData() +{ + ownPublishTs = 0; + /* setup Peers that are online always */ + bool online; + uint32_t ts; + for(int i = 0; i < NumOfPeers; i++) + { + online = false; + if ((i == 1) || (i == 3)) + { + online = true; + } + onlineMap[peerIds[i]] = online; + + if ((i == 2) || (i == 3) || + (i == 5) || (i == 8)) + { + ts = i * 10; + notifyMap[ts] = peerIds[i]; + } + } +} + +void respondPublish() +{ + frmMtx.lock(); /* LOCK TEST FRAMEWORK MUTEX */ + if (!ownPublishTs) + { + std::cerr << "Own ID first published!" << std::endl; + ownPublishTs = time(NULL); + } + frmMtx.unlock(); /* UNLOCK TEST FRAMEWORK MUTEX */ +} + +void respondSearch(p3DhtMgr *mgr, std::string id, uint32_t mode) +{ + std::cerr << "Checking for Search Results" << std::endl; + time_t now = time(NULL); + bool doNotify = false; + bool doOnline = false; + std::string notifyId; + + frmMtx.lock(); /* LOCK TEST FRAMEWORK MUTEX */ + if ((mode == DHT_MODE_NOTIFY) && (ownPublishTs)) + { + /* */ + std::map::iterator it; + uint32_t delta_t = now - ownPublishTs; + it = notifyMap.begin(); + if (it != notifyMap.end()) + { + if (it->first <= delta_t) + { + notifyId = it->second; + onlineMap[notifyId] = true; + notifyMap.erase(it); + doNotify = true; + } + } + } + else if (mode == DHT_MODE_SEARCH) + { + + /* translate */ + std::map::iterator mit; + for(mit = onlineMap.begin(); (mit != onlineMap.end()) && + (RsUtil::HashId(mit->first, false) != id); mit++); + + if (mit != onlineMap.end()) + { + doOnline = mit->second; + } + } + + frmMtx.unlock(); /* UNLOCK TEST FRAMEWORK MUTEX */ + + uint32_t type = 0; + + struct sockaddr_in laddr; + inet_aton("10.0.0.129", &(laddr.sin_addr)); + laddr.sin_port = htons(7812); + laddr.sin_family = AF_INET; + + struct sockaddr_in raddr; + inet_aton("127.0.0.1", &(raddr.sin_addr)); + raddr.sin_port = htons(STUN_PORT); + raddr.sin_family = AF_INET; + + if (doNotify) + { + std::cerr << "Responding to Notify: id:" << notifyId << std::endl; + mgr->dhtResultNotify(RsUtil::HashId(notifyId, true)); + } + + if (doOnline) + { + std::cerr << "Responding to Search" << std::endl; + mgr->dhtResultSearch(id, laddr, raddr, type, ""); + } + +} + + +/***** Test Framework *****/ + +class DhtMgrTester: public p3DhtMgr +{ + + /* Implementation */ + public: + + DhtMgrTester(std::string id, pqiConnectCb *cb) + :p3DhtMgr(id, cb) + { + return; + } + + + + + + /* Blocking calls (only from thread) */ +virtual bool dhtPublish(std::string id, + struct sockaddr_in &laddr, struct sockaddr_in &raddr, + uint32_t type, std::string sign) +{ + std::cerr << "DhtMgrTester::dhtPublish() id: " << RsUtil::BinToHex(id); + std::cerr << " laddr: " << inet_ntoa(laddr.sin_addr) << " lport: " << ntohs(laddr.sin_port); + std::cerr << " raddr: " << inet_ntoa(raddr.sin_addr) << " rport: " << ntohs(raddr.sin_port); + std::cerr << " type: " << type << " sign: " << sign; + std::cerr << std::endl; + + respondPublish(); + + return true; +} + +virtual bool dhtNotify(std::string peerid, std::string ownid, std::string sign) +{ + std::cerr << "DhtMgrTester::dhtNotify() id: " << RsUtil::BinToHex(peerid) << ", ownId: " << RsUtil::BinToHex(ownId); + std::cerr << " sign: " << sign; + std::cerr << std::endl; + + return true; +} + +virtual bool dhtSearch(std::string id, uint32_t mode) +{ + std::cerr << "DhtMgrTester::dhtSearch(id: " << RsUtil::BinToHex(id) << ", mode: " << mode << ")" << std::endl; + + frmMtx.lock(); /* LOCK TEST FRAMEWORK MUTEX */ + searchIds.push_back(id); + searchModes.push_back(mode); + frmMtx.unlock(); /* LOCK TEST FRAMEWORK MUTEX */ + + return true; +} + +}; + + +/* OVERLOAD THE ConnMgr - to insert peers */ +class p3TestConnMgr: public p3ConnectMgr +{ + public: + p3TestConnMgr(int mode) + :p3ConnectMgr(new p3DummyAuthMgr()), mTestMode(mode) { return; } + + protected: + /* must be virtual for testing */ +virtual void loadConfiguration() +{ + + /* setup own address */ + ownState.id = ownId; + ownState.name = "SELF NAME"; + ownState.localaddr.sin_family = AF_INET; + inet_aton("127.0.0.1", &(ownState.localaddr.sin_addr)); + ownState.localaddr.sin_port = htons(7812); + ownState.netMode = RS_NET_MODE_UDP; + ownState.visState = RS_VIS_STATE_STD; + + /* others not important */ + //ownState.state = 0; + //ownState.actions = 0; + + + if (mTestMode == 1) /* Add to Stun List */ + { + for(int i = 0; i < NumOfPeers; i++) + { + mStunList.push_back(peerIds[i]); + } + } + else if (mTestMode == 2) /* add to peers */ + { + /* add in as peers */ + //addPeer(); + for(int i = 0; i < NumOfPeers; i++) + { + if (i < 5) + { + mStunList.push_back(RsUtil::HashId(peerIds[i])); + } + else + { + addFriend(peerIds[i]); + } + } + } +} + + protected: + + uint32_t mTestMode; +}; + + +int main() +{ + time_t startTime = time(NULL); + /* setup system */ + initTestData(); + + /* setup a Stunner to respond to ConnMgr */ + + struct sockaddr_in saddr; + saddr.sin_family = AF_INET; + inet_aton("127.0.0.1", &(saddr.sin_addr)); + saddr.sin_port = htons(STUN_PORT); + UdpSorter stunner(saddr); /* starts a receiving thread */ + + p3TestConnMgr connMgr(2); + DhtMgrTester dhtTester(ownId, &connMgr); + + /* now add in some peers */ + connMgr.setDhtMgr(&dhtTester); + connMgr.setUpnpMgr(NULL); + + /************ ADD pqipersongrp as pqimonitor *****************/ + + SecurityPolicy *pol = secpolicy_create(); + unsigned long flags = 0; + pqipersongrp *pqipg = new pqipersongrpDummy(pol, flags); + + connMgr.addMonitor(pqipg); + + /************ ADD pqipersongrp as pqimonitor *****************/ + + + /* startup dht */ + std::cerr << "Starting up DhtTester()" << std::endl; + dhtTester.start(); + + /* wait for a little before switching on */ +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS + sleep(1); +#else + Sleep(1000); +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + + std::cerr << "Switching on DhtTester()" << std::endl; + dhtTester.setDhtOn(true); + + /* wait loop */ + while(1) + { +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS + sleep(1); +#else + Sleep(1000); +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + + connMgr.tick(); + pqipg->tick(); + + /* handle async search */ + frmMtx.lock(); /* LOCK TEST FRAMEWORK MUTEX */ + + std::string id; + uint32_t mode; + bool doRespond = false; + if (searchIds.size() > 0) + { + id = searchIds.front(); + mode = searchModes.front(); + doRespond = true; + searchIds.pop_front(); + searchModes.pop_front(); + } + + frmMtx.unlock(); /* UNLOCK TEST FRAMEWORK MUTEX */ + + if (doRespond) + { + respondSearch(&dhtTester, id, mode); + } + } +}; + + + + + + + + + + diff --git a/libretroshare/src/_tests/ftcachetest.cc b/libretroshare/src/_tests/ftcachetest.cc new file mode 100644 index 000000000..bf709c9cc --- /dev/null +++ b/libretroshare/src/_tests/ftcachetest.cc @@ -0,0 +1,346 @@ +/* + * RetroShare FileCache Module: ficachetest.cc + * + * Copyright 2004-2007 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 "dbase/cachestrapper.h" +#include "dbase/cachetest.h" +#include "server/ftfiler.h" +#include "util/rsdir.h" + +#include "pqi/pqidebug.h" + +#include +#include + +void handleQuery(CacheStrapper *csp, RsPeerId pid, + std::map &strappers); + +/* A simple test of the CacheStrapper Code. + * + * create 3 different CacheStrappers, each with a Source/Store Pair and Transfer Class. + * pass queries and responses between the CacheStrappers, + * and ensure that the hashes in the Caches are updated. + * + */ + +int main(int argc, char **argv) +{ + /* setup test */ + std::string tmppath1 = "/tmp/ct1"; + std::string tmppath2 = "/tmp/ct2"; + std::string tmppath3 = "/tmp/ct3"; + std::string tmppathpart1 = tmppath1 + "/partials"; + std::string tmppathpart2 = tmppath2 + "/partials"; + std::string tmppathpart3 = tmppath3 + "/partials"; + std::string tmppathcompleted1 = tmppath1 + "/completed"; + std::string tmppathcompleted2 = tmppath2 + "/completed"; + std::string tmppathcompleted3 = tmppath3 + "/completed"; + + std::string tmppathcache2 = tmppath2 + "/cache"; + std::string cachefile = "cachefile.txt"; + std::string tmppathcachefile2 = tmppathcache2 + "/" + cachefile; + + RsDirUtil::checkCreateDirectory(tmppath1.c_str()); + RsDirUtil::checkCreateDirectory(tmppath2.c_str()); + RsDirUtil::checkCreateDirectory(tmppath3.c_str()); + RsDirUtil::checkCreateDirectory(tmppathpart1.c_str()); + RsDirUtil::checkCreateDirectory(tmppathpart2.c_str()); + RsDirUtil::checkCreateDirectory(tmppathpart3.c_str()); + RsDirUtil::checkCreateDirectory(tmppathcompleted1.c_str()); + RsDirUtil::checkCreateDirectory(tmppathcompleted2.c_str()); + RsDirUtil::checkCreateDirectory(tmppathcompleted3.c_str()); + + RsDirUtil::checkCreateDirectory(tmppathcache2.c_str()); + + + /* now create a file */ + std::ofstream out(tmppathcachefile2.c_str()); + out << "Hello this is a cache file!" << std::endl; + out.close(); + + + setOutputLevel(10); + time_t period = 11; + RsPeerId pid1("0x0101"); + RsPeerId pid2("0x0102"); + RsPeerId pid3("0x0103"); + + CacheStrapper sc1(pid1, period); + CacheStrapper sc2(pid2, period); + CacheStrapper sc3(pid3, period); + + //CacheTransfer ctt1(&sc1); + //CacheTransfer ctt2(&sc2); + //CacheTransfer ctt3(&sc3); + + /* setup of the FileTransfer should wait until + * the CacheSource + CacheStrapper are created + */ + + FileHashSearch *fhs1 = NULL; + FileHashSearch *fhs2 = NULL; + FileHashSearch *fhs3 = NULL; + ftfiler ff1(&sc1); + ftfiler ff2(&sc2); + ftfiler ff3(&sc3); + + ff1.setSaveBasePath(tmppath1); + ff2.setSaveBasePath(tmppath2); + ff3.setSaveBasePath(tmppath3); + + ff1.setFileHashSearch(fhs1); + ff2.setFileHashSearch(fhs2); + ff3.setFileHashSearch(fhs3); + + std::map strappers; + strappers[pid1] = &sc1; + strappers[pid2] = &sc2; + strappers[pid3] = &sc3; + + + std::string nulldir = ""; + + CacheSource *csrc1 = new CacheTestSource(nulldir); + //CacheStore *cstore1 = new CacheTestStore(&ctt1, nulldir); + CacheStore *cstore1 = new CacheTestStore(&ff1, nulldir); + CacheId cid1(TESTID, 0); + + CacheSource *csrc2 = new CacheTestSource(nulldir); + //CacheStore *cstore2 = new CacheTestStore(&ctt2, nulldir); + CacheStore *cstore2 = new CacheTestStore(&ff2, nulldir); + CacheId cid2(TESTID, 0); + + CacheSource *csrc3 = new CacheTestSource(nulldir); + //CacheStore *cstore3 = new CacheTestStore(&ctt3, nulldir); + CacheStore *cstore3 = new CacheTestStore(&ff3, nulldir); + CacheId cid3(TESTID, 0); + + CachePair cp1(csrc1, cstore1, cid1); + CachePair cp2(csrc2, cstore2, cid2); + CachePair cp3(csrc3, cstore3, cid3); + + sc1.addCachePair(cp1); + sc2.addCachePair(cp2); + sc3.addCachePair(cp3); + + + sc1.addPeerId(pid2); + sc2.addPeerId(pid1); + sc2.addPeerId(pid3); + sc3.addPeerId(pid2); + + /* add in a cache to sc2 */ + CacheData cdata; + + cdata.pid = pid1; + cdata.cid = cid1; + cdata.name = cachefile; //"Perm Cache"; + cdata.path = tmppathcache2; //"./"; + cdata.hash = "GHJKI"; + cdata.size = 28; + + csrc1->refreshCache(cdata); + + /* The file we created */ + cdata.pid = pid2; + cdata.cid = cid2; + cdata.name = "Funny Cache"; + cdata.path = "./"; + cdata.size = 1023; + cdata.hash = "ABCDEF"; + + csrc2->refreshCache(cdata); + + /* now exercise it */ + + for(int i = 0; 1 ; i++) + { + RsPeerId src(""); + CacheStrapper *csp = NULL; + + if (i % 5 == 1) + { + src = pid1; + csp = &sc1; + } + else if (i % 5 == 2) + { + src = pid2; + csp = &sc2; + } + else if (i % 5 == 3) + { + src = pid3; + csp = &sc3; + } + std::cerr << std::endl; + std::cerr << "Cache Iteraton: " << time(NULL) << std::endl; + std::cerr << std::endl; + + if (src != "") + { + handleQuery(csp, src, strappers); + } + + + if (i % 21 == 0) + { + /* print out the resources */ + sc1.listCaches(std::cerr); + sc2.listCaches(std::cerr); + sc3.listCaches(std::cerr); + } + + /* every once in a while change the cache on 2 */ + if (i % 31 == 25) + { + cdata.hash += "X"; + csrc2->refreshCache(cdata); + } +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS + sleep(1); +#else + Sleep(1000); +#endif +/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ + + /* tick the systems */ + ff1.tick(); + ff2.tick(); + ff3.tick(); + + /* exchange packets! */ + ftFileRequest *ftPkt = NULL; + while(NULL != (ftPkt = ff1.sendFileInfo())) + { + std::cerr << "Outgoing ftPkt from ff1"; + std::cerr << std::endl; + + if (ftPkt->id == pid2) + { + std::cerr << "ftPkt for ff2" << std::endl; + ftPkt->id = pid1; /* set source correctly */ + ff2.recvFileInfo(ftPkt); + } + else if (ftPkt->id == pid3) + { + std::cerr << "ftPkt for ff3" << std::endl; + ftPkt->id = pid1; /* set source correctly */ + ff3.recvFileInfo(ftPkt); + } + else + { + std::cerr << "ERROR unknown ftPkt destination!: " << ftPkt->id; + std::cerr << std::endl; + delete ftPkt; + } + } + + while(NULL != (ftPkt = ff2.sendFileInfo())) + { + std::cerr << "Outgoing ftPkt from ff2"; + std::cerr << std::endl; + + if (ftPkt->id == pid1) + { + std::cerr << "ftPkt for ff1" << std::endl; + ftPkt->id = pid2; /* set source correctly */ + ff1.recvFileInfo(ftPkt); + } + else if (ftPkt->id == pid3) + { + std::cerr << "ftPkt for ff3" << std::endl; + ftPkt->id = pid2; /* set source correctly */ + ff3.recvFileInfo(ftPkt); + } + else + { + std::cerr << "ERROR unknown ftPkt destination!: " << ftPkt->id; + std::cerr << std::endl; + delete ftPkt; + } + } + + + while(NULL != (ftPkt = ff3.sendFileInfo())) + { + std::cerr << "Outgoing ftPkt from ff3"; + std::cerr << std::endl; + + if (ftPkt->id == pid1) + { + std::cerr << "ftPkt for ff1" << std::endl; + ftPkt->id = pid3; /* set source correctly */ + ff1.recvFileInfo(ftPkt); + } + else if (ftPkt->id == pid2) + { + std::cerr << "ftPkt for ff2" << std::endl; + ftPkt->id = pid3; /* set source correctly */ + ff2.recvFileInfo(ftPkt); + } + else + { + std::cerr << "ERROR unknown ftPkt destination!: " << ftPkt->id; + std::cerr << std::endl; + delete ftPkt; + } + } + } + + /* Cleanup - TODO */ + + return 1; +} + +void handleQuery(CacheStrapper *csp, RsPeerId pid, + std::map &strappers) +{ + /* query */ + std::list ids; + std::list::iterator pit; + + std::cerr << "Cache Query from: " << pid << std::endl; + + csp -> sendCacheQuery(ids, time(NULL)); + for(pit = ids.begin(); pit != ids.end(); pit++) + { + std::cerr << "Cache Query for: " << (*pit) << std::endl; + std::map::iterator sit; + if (strappers.end() != (sit = strappers.find(*pit))) + { + std::map hashs; + std::map::iterator hit; + (sit -> second) -> handleCacheQuery(pid, hashs); + for(hit = hashs.begin(); hit != hashs.end(); hit++) + { + csp -> recvCacheResponse(hit->second, time(NULL)); + } + } + else + { + std::cerr << "Unknown Query Destination!" << std::endl; + } + } +} + diff --git a/libretroshare/src/_tests/xpgp_id.cc b/libretroshare/src/_tests/xpgp_id.cc new file mode 100644 index 000000000..c122d401f --- /dev/null +++ b/libretroshare/src/_tests/xpgp_id.cc @@ -0,0 +1,44 @@ + + +/***** Extract XPGP Id *****/ + +#include "pqi/authxpgp.h" + +#include +#include + +int main(int argc, char **argv) +{ + if (argc < 2) + { + std::cerr << "Usage: " << argv[0] << " "; + std::cerr << std::endl; + exit(1); + } + + std::string userName, userId; + + if (LoadCheckXPGPandGetName(argv[1], userName, userId)) + { + std::cerr << "Cert Ok: name: " << userName; + std::cerr << std::endl; + std::cerr << "id = \"" << userId << "\""; + std::cerr << std::endl; + } + else + { + std::cerr << "Cert Check Failed"; + std::cerr << std::endl; + } +} + + + + + + + + + + + diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index fecbfa7a8..956b41b29 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -3,6 +3,8 @@ include(_rsserver/rsserver.pri) include(_commonfuncs/commonfuncs.pri) include(_dht/dht.pri) include(_pqi/pqi.pri) +include(_server/server.pri) +include(_dbase/dbase.pri) TEMPLATE = lib CONFIG += static diff --git a/libretroshare/src/pqi/authxpgp.h b/libretroshare/src/pqi/authxpgp.h index cf086fdb9..15ba11111 100644 --- a/libretroshare/src/pqi/authxpgp.h +++ b/libretroshare/src/pqi/authxpgp.h @@ -51,7 +51,7 @@ class AuthXPGP; class xpgpcert { - public: +public: xpgpcert(XPGP *xpgp, std::string id); /* certificate parameters */ diff --git a/libretroshare/src/pqi/p3cfgmgr.cc b/libretroshare/src/pqi/p3cfgmgr.cc index 6985f1a8c..aa7d7fbf0 100644 --- a/libretroshare/src/pqi/p3cfgmgr.cc +++ b/libretroshare/src/pqi/p3cfgmgr.cc @@ -23,14 +23,10 @@ * */ -#include "util/rsdir.h" -#include "rsiface/rspeers.h" -#include "pqi/p3cfgmgr.h" -#include "pqi/p3authmgr.h" -#include "pqi/pqibin.h" -#include "pqi/pqistore.h" -#include "pqi/pqinotify.h" -#include + +#include "_pqi/p3cfgmgr.h" + + #include "serialiser/rsconfigitems.h"