2020-05-22 13:18:41 +00:00
/*
* Copyright ( c ) 2013 - 2020 , The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
2014-04-01 19:18:14 +00:00
# include <string.h>
2014-11-26 21:19:36 +00:00
# include <inttypes.h>
2014-04-01 19:18:14 +00:00
# include <string>
# include <map>
2014-11-26 21:19:36 +00:00
# include <fstream>
2014-12-19 19:40:02 +00:00
# include <chrono>
# include <condition_variable>
2016-03-26 14:31:47 +00:00
# include <openssl/rand.h>
2016-10-12 10:23:43 +00:00
# include <boost/algorithm/string.hpp>
2019-03-04 18:29:29 +00:00
# include <boost/filesystem.hpp>
2015-11-03 14:15:49 +00:00
# include "Base.h"
2014-04-01 19:18:14 +00:00
# include "util.h"
# include "Identity.h"
2016-02-11 00:00:00 +00:00
# include "FS.h"
2014-04-01 19:18:14 +00:00
# include "Log.h"
2016-07-16 00:00:00 +00:00
# include "HTTP.h"
2017-04-22 00:04:16 +00:00
# include "NetDb.hpp"
2014-12-19 19:40:02 +00:00
# include "ClientContext.h"
2014-04-01 19:18:14 +00:00
# include "AddressBook.h"
2016-10-12 10:23:43 +00:00
# include "Config.h"
2014-04-01 19:18:14 +00:00
namespace i2p
{
2014-10-24 19:22:36 +00:00
namespace client
2014-04-01 19:18:14 +00:00
{
2016-02-11 00:00:00 +00:00
// TODO: this is actually proxy class
2014-11-26 21:19:36 +00:00
class AddressBookFilesystemStorage : public AddressBookStorage
{
public :
2021-01-21 00:19:34 +00:00
2020-03-01 10:25:50 +00:00
AddressBookFilesystemStorage ( ) : storage ( " addressbook " , " b " , " " , " b32 " )
2019-03-01 19:42:20 +00:00
{
i2p : : config : : GetOption ( " persist.addressbook " , m_IsPersist ) ;
2021-01-21 00:19:34 +00:00
if ( m_IsPersist )
i2p : : config : : GetOption ( " addressbook.hostsfile " , m_HostsFile ) ;
2019-03-01 19:42:20 +00:00
}
2015-11-03 14:15:49 +00:00
std : : shared_ptr < const i2p : : data : : IdentityEx > GetAddress ( const i2p : : data : : IdentHash & ident ) const ;
void AddAddress ( std : : shared_ptr < const i2p : : data : : IdentityEx > address ) ;
2014-11-26 21:19:36 +00:00
void RemoveAddress ( const i2p : : data : : IdentHash & ident ) ;
2016-02-18 00:00:00 +00:00
bool Init ( ) ;
2019-03-27 19:19:10 +00:00
int Load ( std : : map < std : : string , std : : shared_ptr < Address > > & addresses ) ;
int LoadLocal ( std : : map < std : : string , std : : shared_ptr < Address > > & addresses ) ;
int Save ( const std : : map < std : : string , std : : shared_ptr < Address > > & addresses ) ;
2016-03-14 20:05:57 +00:00
void SaveEtag ( const i2p : : data : : IdentHash & subsciption , const std : : string & etag , const std : : string & lastModified ) ;
2016-03-15 18:37:07 +00:00
bool GetEtag ( const i2p : : data : : IdentHash & subscription , std : : string & etag , std : : string & lastModified ) ;
2019-03-04 18:29:29 +00:00
void ResetEtags ( ) ;
2016-03-15 18:37:07 +00:00
2016-03-16 19:40:29 +00:00
private :
2019-03-27 19:19:10 +00:00
int LoadFromFile ( const std : : string & filename , std : : map < std : : string , std : : shared_ptr < Address > > & addresses ) ; // returns -1 if can't open file, otherwise number of records
2016-03-16 19:40:29 +00:00
2019-03-01 19:42:20 +00:00
private :
2021-01-21 00:19:34 +00:00
i2p : : fs : : HashedStorage storage ;
std : : string etagsPath , indexPath , localPath ;
2019-03-01 19:42:20 +00:00
bool m_IsPersist ;
2021-01-21 00:19:34 +00:00
std : : string m_HostsFile ; // file to dump hosts.txt, empty if not used
2014-11-26 21:19:36 +00:00
} ;
2016-02-18 00:00:00 +00:00
bool AddressBookFilesystemStorage : : Init ( )
2017-05-22 14:34:29 +00:00
{
2016-02-18 00:00:00 +00:00
storage . SetPlace ( i2p : : fs : : GetDataDir ( ) ) ;
2016-03-14 20:05:57 +00:00
// init storage
2016-03-19 12:07:09 +00:00
if ( storage . Init ( i2p : : data : : GetBase32SubstitutionTable ( ) , 32 ) )
2017-05-22 14:34:29 +00:00
{
2016-03-19 12:07:09 +00:00
// init ETags
etagsPath = i2p : : fs : : StorageRootPath ( storage , " etags " ) ;
if ( ! i2p : : fs : : Exists ( etagsPath ) )
i2p : : fs : : CreateDirectory ( etagsPath ) ;
// init address files
indexPath = i2p : : fs : : StorageRootPath ( storage , " addresses.csv " ) ;
localPath = i2p : : fs : : StorageRootPath ( storage , " local.csv " ) ;
return true ;
2017-05-22 14:34:29 +00:00
}
2016-03-19 12:07:09 +00:00
return false ;
2016-02-18 00:00:00 +00:00
}
2015-11-03 14:15:49 +00:00
std : : shared_ptr < const i2p : : data : : IdentityEx > AddressBookFilesystemStorage : : GetAddress ( const i2p : : data : : IdentHash & ident ) const
2014-11-26 21:19:36 +00:00
{
2020-03-01 10:25:50 +00:00
if ( ! m_IsPersist )
2019-03-01 19:42:20 +00:00
{
2019-04-08 19:22:42 +00:00
LogPrint ( eLogDebug , " Addressbook: Persistence is disabled " ) ;
2020-03-01 10:25:50 +00:00
return nullptr ;
2019-03-01 19:42:20 +00:00
}
2016-02-18 00:00:00 +00:00
std : : string filename = storage . Path ( ident . ToBase32 ( ) ) ;
2016-02-11 00:00:00 +00:00
std : : ifstream f ( filename , std : : ifstream : : binary ) ;
if ( ! f . is_open ( ) ) {
LogPrint ( eLogDebug , " Addressbook: Requested, but not found: " , filename ) ;
return nullptr ;
2014-11-26 21:19:36 +00:00
}
2016-02-11 00:00:00 +00:00
f . seekg ( 0 , std : : ios : : end ) ;
size_t len = f . tellg ( ) ;
if ( len < i2p : : data : : DEFAULT_IDENTITY_SIZE ) {
2016-03-11 00:46:52 +00:00
LogPrint ( eLogError , " Addressbook: File " , filename , " is too short: " , len ) ;
2015-11-03 14:15:49 +00:00
return nullptr ;
2016-02-11 00:00:00 +00:00
}
f . seekg ( 0 , std : : ios : : beg ) ;
uint8_t * buf = new uint8_t [ len ] ;
f . read ( ( char * ) buf , len ) ;
auto address = std : : make_shared < i2p : : data : : IdentityEx > ( buf , len ) ;
delete [ ] buf ;
return address ;
2014-11-26 21:19:36 +00:00
}
2015-11-03 14:15:49 +00:00
void AddressBookFilesystemStorage : : AddAddress ( std : : shared_ptr < const i2p : : data : : IdentityEx > address )
2014-11-26 21:19:36 +00:00
{
2019-03-01 19:42:20 +00:00
if ( ! m_IsPersist ) return ;
2016-02-18 00:00:00 +00:00
std : : string path = storage . Path ( address - > GetIdentHash ( ) . ToBase32 ( ) ) ;
2016-02-11 00:00:00 +00:00
std : : ofstream f ( path , std : : ofstream : : binary | std : : ofstream : : out ) ;
if ( ! f . is_open ( ) ) {
2016-03-11 00:46:52 +00:00
LogPrint ( eLogError , " Addressbook: can't open file " , path ) ;
2016-02-11 00:00:00 +00:00
return ;
2014-11-26 21:19:36 +00:00
}
2016-02-11 00:00:00 +00:00
size_t len = address - > GetFullLen ( ) ;
uint8_t * buf = new uint8_t [ len ] ;
address - > ToBuffer ( buf , len ) ;
f . write ( ( char * ) buf , len ) ;
delete [ ] buf ;
2017-05-22 14:34:29 +00:00
}
2014-11-26 21:19:36 +00:00
void AddressBookFilesystemStorage : : RemoveAddress ( const i2p : : data : : IdentHash & ident )
{
2020-03-01 10:25:50 +00:00
if ( ! m_IsPersist ) return ;
2016-02-18 00:00:00 +00:00
storage . Remove ( ident . ToBase32 ( ) ) ;
2014-11-26 21:19:36 +00:00
}
2019-03-27 19:19:10 +00:00
int AddressBookFilesystemStorage : : LoadFromFile ( const std : : string & filename , std : : map < std : : string , std : : shared_ptr < Address > > & addresses )
2014-11-27 21:26:55 +00:00
{
2017-05-22 14:34:29 +00:00
int num = 0 ;
2016-03-16 19:40:29 +00:00
std : : ifstream f ( filename , std : : ifstream : : in ) ; // in text mode
if ( ! f ) return - 1 ;
2016-02-11 00:00:00 +00:00
addresses . clear ( ) ;
2017-05-22 14:34:29 +00:00
while ( ! f . eof ( ) )
2016-03-16 19:40:29 +00:00
{
std : : string s ;
2016-02-11 00:00:00 +00:00
getline ( f , s ) ;
2016-03-16 19:40:29 +00:00
if ( ! s . length ( ) ) continue ; // skip empty line
2016-02-11 00:00:00 +00:00
std : : size_t pos = s . find ( ' , ' ) ;
if ( pos ! = std : : string : : npos )
2014-11-27 21:26:55 +00:00
{
2016-02-11 00:00:00 +00:00
std : : string name = s . substr ( 0 , pos + + ) ;
std : : string addr = s . substr ( pos ) ;
2014-11-27 21:26:55 +00:00
2019-03-27 19:19:10 +00:00
addresses [ name ] = std : : make_shared < Address > ( addr ) ;
2016-02-11 00:00:00 +00:00
num + + ;
2017-05-22 14:34:29 +00:00
}
2014-11-27 21:26:55 +00:00
}
2016-03-16 19:40:29 +00:00
return num ;
}
2016-02-11 00:00:00 +00:00
2019-03-27 19:19:10 +00:00
int AddressBookFilesystemStorage : : Load ( std : : map < std : : string , std : : shared_ptr < Address > > & addresses )
2016-03-16 19:40:29 +00:00
{
2017-05-22 14:34:29 +00:00
int num = LoadFromFile ( indexPath , addresses ) ;
2016-03-16 19:40:29 +00:00
if ( num < 0 )
{
LogPrint ( eLogWarning , " Addressbook: Can't open " , indexPath ) ;
return 0 ;
2017-05-22 14:34:29 +00:00
}
2016-03-16 19:40:29 +00:00
LogPrint ( eLogInfo , " Addressbook: using index file " , indexPath ) ;
2016-02-11 00:00:00 +00:00
LogPrint ( eLogInfo , " Addressbook: " , num , " addresses loaded from storage " ) ;
2016-03-16 19:40:29 +00:00
return num ;
}
2019-03-27 19:19:10 +00:00
int AddressBookFilesystemStorage : : LoadLocal ( std : : map < std : : string , std : : shared_ptr < Address > > & addresses )
2016-03-16 19:40:29 +00:00
{
2017-05-22 14:34:29 +00:00
int num = LoadFromFile ( localPath , addresses ) ;
2016-03-16 19:40:29 +00:00
if ( num < 0 ) return 0 ;
2017-05-22 14:34:29 +00:00
LogPrint ( eLogInfo , " Addressbook: " , num , " local addresses loaded " ) ;
2014-11-27 21:26:55 +00:00
return num ;
}
2019-03-27 19:19:10 +00:00
int AddressBookFilesystemStorage : : Save ( const std : : map < std : : string , std : : shared_ptr < Address > > & addresses )
2014-11-27 21:26:55 +00:00
{
2021-01-21 00:19:34 +00:00
if ( addresses . empty ( ) )
{
2016-02-11 00:00:00 +00:00
LogPrint ( eLogWarning , " Addressbook: not saving empty addressbook " ) ;
return 0 ;
}
2014-11-27 21:26:55 +00:00
int num = 0 ;
2020-03-01 10:25:50 +00:00
{
2021-01-21 00:19:34 +00:00
// save index file
std : : ofstream f ( indexPath , std : : ofstream : : out ) ; // in text mode
if ( f . is_open ( ) )
{
for ( const auto & it : addresses )
{
if ( it . second - > IsValid ( ) )
{
f < < it . first < < " , " ;
if ( it . second - > IsIdentHash ( ) )
f < < it . second - > identHash . ToBase32 ( ) ;
else
f < < it . second - > blindedPublicKey - > ToB33 ( ) ;
f < < std : : endl ;
num + + ;
}
else
LogPrint ( eLogWarning , " Addressbook: invalid address " , it . first ) ;
}
LogPrint ( eLogInfo , " Addressbook: " , num , " addresses saved " ) ;
2020-08-14 13:54:31 +00:00
}
2019-03-28 20:06:53 +00:00
else
2021-01-21 00:19:34 +00:00
LogPrint ( eLogWarning , " Addressbook: Can't open " , indexPath ) ;
}
if ( ! m_HostsFile . empty ( ) )
{
// dump full hosts.txt
std : : ofstream f ( m_HostsFile , std : : ofstream : : out ) ; // in text mode
if ( f . is_open ( ) )
{
for ( const auto & it : addresses )
{
std : : shared_ptr < const i2p : : data : : IdentityEx > addr ;
if ( it . second - > IsIdentHash ( ) )
{
addr = GetAddress ( it . second - > identHash ) ;
if ( addr )
f < < it . first < < " = " < < addr - > ToBase64 ( ) < < std : : endl ;
}
}
}
else
LogPrint ( eLogWarning , " Addressbook: Can't open " , m_HostsFile ) ;
}
2017-05-22 14:34:29 +00:00
return num ;
}
2014-11-27 21:26:55 +00:00
2016-03-14 20:05:57 +00:00
void AddressBookFilesystemStorage : : SaveEtag ( const i2p : : data : : IdentHash & subscription , const std : : string & etag , const std : : string & lastModified )
{
std : : string fname = etagsPath + i2p : : fs : : dirSep + subscription . ToBase32 ( ) + " .txt " ;
std : : ofstream f ( fname , std : : ofstream : : out | std : : ofstream : : trunc ) ;
if ( f )
2017-05-22 14:34:29 +00:00
{
f < < etag < < std : : endl ;
2016-03-15 02:00:05 +00:00
f < < lastModified < < std : : endl ;
2017-05-22 14:34:29 +00:00
}
2016-03-14 20:05:57 +00:00
}
2016-03-15 18:37:07 +00:00
bool AddressBookFilesystemStorage : : GetEtag ( const i2p : : data : : IdentHash & subscription , std : : string & etag , std : : string & lastModified )
{
std : : string fname = etagsPath + i2p : : fs : : dirSep + subscription . ToBase32 ( ) + " .txt " ;
std : : ifstream f ( fname , std : : ofstream : : in ) ;
if ( ! f | | f . eof ( ) ) return false ;
std : : getline ( f , etag ) ;
2017-05-22 14:34:29 +00:00
if ( f . eof ( ) ) return false ;
2016-03-15 18:37:07 +00:00
std : : getline ( f , lastModified ) ;
return true ;
}
2019-03-04 18:29:29 +00:00
void AddressBookFilesystemStorage : : ResetEtags ( )
{
LogPrint ( eLogError , " Addressbook: resetting eTags " ) ;
for ( boost : : filesystem : : directory_iterator it ( etagsPath ) ; it ! = boost : : filesystem : : directory_iterator ( ) ; + + it )
{
if ( ! boost : : filesystem : : is_regular_file ( it - > status ( ) ) )
continue ;
boost : : filesystem : : remove ( it - > path ( ) ) ;
}
}
2014-11-26 21:19:36 +00:00
//---------------------------------------------------------------------
2019-03-27 19:19:10 +00:00
2019-09-23 17:42:15 +00:00
Address : : Address ( const std : : string & b32 ) :
addressType ( eAddressInvalid )
2019-03-27 19:19:10 +00:00
{
if ( b32 . length ( ) < = B33_ADDRESS_THRESHOLD )
{
2019-09-23 17:42:15 +00:00
if ( identHash . FromBase32 ( b32 ) > 0 )
addressType = eAddressIndentHash ;
2019-03-27 19:19:10 +00:00
}
else
{
blindedPublicKey = std : : make_shared < i2p : : data : : BlindedPublicKey > ( b32 ) ;
2019-09-23 17:42:15 +00:00
if ( blindedPublicKey - > IsValid ( ) )
addressType = eAddressBlindedPublicKey ;
2019-03-27 19:19:10 +00:00
}
2020-03-01 10:25:50 +00:00
}
2019-03-27 19:19:10 +00:00
Address : : Address ( const i2p : : data : : IdentHash & hash )
{
addressType = eAddressIndentHash ;
2020-03-01 10:25:50 +00:00
identHash = hash ;
2019-03-27 19:19:10 +00:00
}
2016-06-27 13:00:00 +00:00
AddressBook : : AddressBook ( ) : m_Storage ( nullptr ) , m_IsLoaded ( false ) , m_IsDownloading ( false ) ,
2018-01-23 20:50:28 +00:00
m_NumRetries ( 0 ) , m_DefaultSubscription ( nullptr ) , m_SubscriptionsUpdateTimer ( nullptr )
2014-04-06 19:22:33 +00:00
{
}
2014-11-26 21:19:36 +00:00
AddressBook : : ~ AddressBook ( )
2017-05-22 14:34:29 +00:00
{
2015-03-30 14:21:52 +00:00
Stop ( ) ;
}
void AddressBook : : Start ( )
{
2016-06-27 13:00:00 +00:00
if ( ! m_Storage )
m_Storage = new AddressBookFilesystemStorage ;
2016-02-18 00:00:00 +00:00
m_Storage - > Init ( ) ;
2016-02-11 00:00:00 +00:00
LoadHosts ( ) ; /* try storage, then hosts.txt, then download */
2015-03-30 14:21:52 +00:00
StartSubscriptions ( ) ;
2016-03-26 14:31:47 +00:00
StartLookups ( ) ;
2015-03-30 14:21:52 +00:00
}
2016-03-26 14:31:47 +00:00
void AddressBook : : StartResolvers ( )
{
LoadLocal ( ) ;
2017-05-22 14:34:29 +00:00
}
2015-03-30 14:21:52 +00:00
void AddressBook : : Stop ( )
{
2016-03-26 14:31:47 +00:00
StopLookups ( ) ;
2015-03-30 14:21:52 +00:00
StopSubscriptions ( ) ;
if ( m_SubscriptionsUpdateTimer )
2017-05-22 14:34:29 +00:00
{
delete m_SubscriptionsUpdateTimer ;
2015-03-30 14:21:52 +00:00
m_SubscriptionsUpdateTimer = nullptr ;
2017-05-22 14:34:29 +00:00
}
2014-12-21 14:33:02 +00:00
if ( m_IsDownloading )
{
2019-04-08 19:22:42 +00:00
LogPrint ( eLogInfo , " Addressbook: subscriptions are downloading, abort " ) ;
2014-12-21 14:33:02 +00:00
for ( int i = 0 ; i < 30 ; i + + )
{
if ( ! m_IsDownloading )
{
2016-03-11 00:46:52 +00:00
LogPrint ( eLogInfo , " Addressbook: subscriptions download complete " ) ;
2014-12-21 14:33:02 +00:00
break ;
2017-05-22 14:34:29 +00:00
}
2014-12-21 14:33:02 +00:00
std : : this_thread : : sleep_for ( std : : chrono : : seconds ( 1 ) ) ; // wait for 1 seconds
2017-05-22 14:34:29 +00:00
}
2016-03-11 00:46:52 +00:00
LogPrint ( eLogError , " Addressbook: subscription download timeout " ) ;
2015-03-30 14:21:52 +00:00
m_IsDownloading = false ;
2016-08-05 18:23:54 +00:00
}
2014-11-28 20:08:23 +00:00
if ( m_Storage )
{
m_Storage - > Save ( m_Addresses ) ;
delete m_Storage ;
2015-03-30 14:21:52 +00:00
m_Storage = nullptr ;
2014-11-28 20:08:23 +00:00
}
2017-05-22 14:34:29 +00:00
m_DefaultSubscription = nullptr ;
m_Subscriptions . clear ( ) ;
}
2019-03-28 13:57:34 +00:00
std : : shared_ptr < const Address > AddressBook : : GetAddress ( const std : : string & address )
{
auto pos = address . find ( " .b32.i2p " ) ;
if ( pos ! = std : : string : : npos )
2020-03-01 10:25:50 +00:00
{
2019-09-23 17:42:15 +00:00
auto addr = std : : make_shared < const Address > ( address . substr ( 0 , pos ) ) ;
return addr - > IsValid ( ) ? addr : nullptr ;
}
2019-03-28 13:57:34 +00:00
else
{
pos = address . find ( " .i2p " ) ;
if ( pos ! = std : : string : : npos )
2019-03-28 16:19:19 +00:00
{
auto addr = FindAddress ( address ) ;
if ( ! addr )
2020-03-01 10:25:50 +00:00
LookupAddress ( address ) ; // TODO:
2019-03-28 16:19:19 +00:00
return addr ;
2020-03-01 10:25:50 +00:00
}
}
2019-03-28 13:57:34 +00:00
// if not .b32 we assume full base64 address
i2p : : data : : IdentityEx dest ;
if ( ! dest . FromBase64 ( address ) )
return nullptr ;
return std : : make_shared < const Address > ( dest . GetIdentHash ( ) ) ;
}
2019-03-27 19:19:10 +00:00
std : : shared_ptr < const Address > AddressBook : : FindAddress ( const std : : string & address )
2014-04-06 19:22:33 +00:00
{
2016-02-11 00:00:00 +00:00
auto it = m_Addresses . find ( address ) ;
if ( it ! = m_Addresses . end ( ) )
2019-03-27 19:19:10 +00:00
return it - > second ;
2017-05-22 14:34:29 +00:00
return nullptr ;
2014-04-06 19:22:33 +00:00
}
2014-04-01 19:18:14 +00:00
2019-04-02 17:11:49 +00:00
void AddressBook : : InsertAddress ( const std : : string & address , const std : : string & jump )
2014-09-23 19:38:56 +00:00
{
2019-04-02 17:11:49 +00:00
auto pos = jump . find ( " .b32.i2p " ) ;
if ( pos ! = std : : string : : npos )
{
m_Addresses [ address ] = std : : make_shared < Address > ( jump . substr ( 0 , pos ) ) ;
LogPrint ( eLogInfo , " Addressbook: added " , address , " -> " , jump ) ;
2020-03-01 10:25:50 +00:00
}
2019-04-02 17:11:49 +00:00
else
2020-03-01 10:25:50 +00:00
{
// assume base64
2019-04-02 17:11:49 +00:00
auto ident = std : : make_shared < i2p : : data : : IdentityEx > ( ) ;
if ( ident - > FromBase64 ( jump ) )
{
m_Storage - > AddAddress ( ident ) ;
m_Addresses [ address ] = std : : make_shared < Address > ( ident - > GetIdentHash ( ) ) ;
LogPrint ( eLogInfo , " Addressbook: added " , address , " -> " , ToAddress ( ident - > GetIdentHash ( ) ) ) ;
}
else
LogPrint ( eLogError , " Addressbook: malformed address " , jump ) ;
}
2014-09-23 19:38:56 +00:00
}
2019-03-27 19:19:10 +00:00
void AddressBook : : InsertFullAddress ( std : : shared_ptr < const i2p : : data : : IdentityEx > address )
2014-11-26 21:51:36 +00:00
{
m_Storage - > AddAddress ( address ) ;
}
2019-03-27 19:19:10 +00:00
std : : shared_ptr < const i2p : : data : : IdentityEx > AddressBook : : GetFullAddress ( const std : : string & address )
2014-11-26 21:19:36 +00:00
{
2019-03-28 16:19:19 +00:00
auto addr = GetAddress ( address ) ;
if ( ! addr | | ! addr - > IsIdentHash ( ) ) return nullptr ;
return m_Storage - > GetAddress ( addr - > identHash ) ;
2017-05-22 14:34:29 +00:00
}
2014-11-26 21:19:36 +00:00
2014-08-14 18:32:00 +00:00
void AddressBook : : LoadHosts ( )
2014-04-01 19:18:14 +00:00
{
2014-12-20 03:03:34 +00:00
if ( m_Storage - > Load ( m_Addresses ) > 0 )
2014-11-28 14:40:27 +00:00
{
m_IsLoaded = true ;
return ;
}
2017-05-22 14:34:29 +00:00
2016-02-11 00:00:00 +00:00
// then try hosts.txt
std : : ifstream f ( i2p : : fs : : DataDirPath ( " hosts.txt " ) , std : : ifstream : : in ) ; // in text mode
2017-05-22 14:34:29 +00:00
if ( f . is_open ( ) )
2014-08-14 18:32:00 +00:00
{
2016-07-16 00:00:00 +00:00
LoadHostsFromStream ( f , false ) ;
2014-12-21 14:33:02 +00:00
m_IsLoaded = true ;
}
2019-03-04 18:29:29 +00:00
// reset eTags, because we don’ t know how old hosts.txt is or can't load addressbook
m_Storage - > ResetEtags ( ) ;
2014-12-20 03:03:34 +00:00
}
2016-07-16 00:00:00 +00:00
bool AddressBook : : LoadHostsFromStream ( std : : istream & f , bool is_update )
2014-12-20 03:03:34 +00:00
{
2014-12-21 14:33:02 +00:00
std : : unique_lock < std : : mutex > l ( m_AddressBookMutex ) ;
2014-12-20 03:03:34 +00:00
int numAddresses = 0 ;
2016-04-01 16:51:34 +00:00
bool incomplete = false ;
2014-08-14 18:32:00 +00:00
std : : string s ;
while ( ! f . eof ( ) )
2014-04-01 19:18:14 +00:00
{
2014-08-14 18:32:00 +00:00
getline ( f , s ) ;
2014-04-01 20:27:40 +00:00
2017-05-05 17:54:21 +00:00
if ( ! s . length ( ) | | s [ 0 ] = = ' # ' )
continue ; // skip empty or comment line
2014-08-14 18:32:00 +00:00
size_t pos = s . find ( ' = ' ) ;
if ( pos ! = std : : string : : npos )
{
std : : string name = s . substr ( 0 , pos + + ) ;
std : : string addr = s . substr ( pos ) ;
2019-08-11 02:16:26 +00:00
size_t pos = addr . find ( ' # ' ) ;
2017-05-10 13:36:58 +00:00
if ( pos ! = std : : string : : npos )
2019-08-11 02:16:26 +00:00
addr = addr . substr ( 0 , pos ) ; // remove comments
2017-05-10 13:36:58 +00:00
2015-11-03 14:15:49 +00:00
auto ident = std : : make_shared < i2p : : data : : IdentityEx > ( ) ;
2016-07-16 00:00:00 +00:00
if ( ! ident - > FromBase64 ( addr ) ) {
2016-03-11 00:46:52 +00:00
LogPrint ( eLogError , " Addressbook: malformed address " , addr , " for " , name ) ;
2016-04-01 16:51:34 +00:00
incomplete = f . eof ( ) ;
2016-07-16 00:00:00 +00:00
continue ;
2016-04-01 16:51:34 +00:00
}
2016-07-16 00:00:00 +00:00
numAddresses + + ;
2017-07-18 19:58:32 +00:00
auto it = m_Addresses . find ( name ) ;
2018-07-10 09:39:21 +00:00
if ( it ! = m_Addresses . end ( ) ) // already exists ?
2017-07-18 19:58:32 +00:00
{
2020-11-19 20:41:00 +00:00
if ( it - > second - > IsIdentHash ( ) & & it - > second - > identHash ! = ident - > GetIdentHash ( ) & & // address changed?
ident - > GetSigningKeyType ( ) ! = i2p : : data : : SIGNING_KEY_TYPE_DSA_SHA1 ) // don't replace by DSA
2017-07-18 19:58:32 +00:00
{
2019-03-27 19:19:10 +00:00
it - > second - > identHash = ident - > GetIdentHash ( ) ;
2017-07-18 19:58:32 +00:00
m_Storage - > AddAddress ( ident ) ;
2020-11-19 20:41:00 +00:00
m_Storage - > RemoveAddress ( it - > second - > identHash ) ;
2018-01-06 03:48:51 +00:00
LogPrint ( eLogInfo , " Addressbook: updated host: " , name ) ;
2017-07-18 19:58:32 +00:00
}
2018-01-06 03:48:51 +00:00
}
2017-07-18 19:58:32 +00:00
else
{
2020-11-19 20:41:00 +00:00
m_Addresses . emplace ( name , std : : make_shared < Address > ( ident - > GetIdentHash ( ) ) ) ;
2017-07-18 19:58:32 +00:00
m_Storage - > AddAddress ( ident ) ;
if ( is_update )
LogPrint ( eLogInfo , " Addressbook: added new host: " , name ) ;
}
2017-05-22 14:34:29 +00:00
}
2016-04-01 16:51:34 +00:00
else
incomplete = f . eof ( ) ;
2014-08-14 18:32:00 +00:00
}
2016-03-11 00:46:52 +00:00
LogPrint ( eLogInfo , " Addressbook: " , numAddresses , " addresses processed " ) ;
2014-12-21 14:33:02 +00:00
if ( numAddresses > 0 )
2017-05-22 14:34:29 +00:00
{
2016-04-01 16:51:34 +00:00
if ( ! incomplete ) m_IsLoaded = true ;
2014-12-21 14:33:02 +00:00
m_Storage - > Save ( m_Addresses ) ;
2017-05-22 14:34:29 +00:00
}
2016-04-01 16:51:34 +00:00
return ! incomplete ;
2017-05-22 14:34:29 +00:00
}
2014-12-22 20:06:54 +00:00
void AddressBook : : LoadSubscriptions ( )
{
if ( ! m_Subscriptions . size ( ) )
{
2016-02-11 00:00:00 +00:00
std : : ifstream f ( i2p : : fs : : DataDirPath ( " subscriptions.txt " ) , std : : ifstream : : in ) ; // in text mode
2014-12-22 20:06:54 +00:00
if ( f . is_open ( ) )
{
std : : string s ;
while ( ! f . eof ( ) )
{
getline ( f , s ) ;
2021-01-21 00:19:34 +00:00
if ( s . empty ( ) | | s [ 0 ] = = ' # ' ) continue ; // skip empty line or comment
2016-08-09 14:17:40 +00:00
m_Subscriptions . push_back ( std : : make_shared < AddressBookSubscription > ( * this , s ) ) ;
2014-12-22 20:06:54 +00:00
}
2016-01-18 00:00:00 +00:00
LogPrint ( eLogInfo , " Addressbook: " , m_Subscriptions . size ( ) , " subscriptions urls loaded " ) ;
2016-10-12 10:23:43 +00:00
LogPrint ( eLogWarning , " Addressbook: subscriptions.txt usage is deprecated, use config file instead " ) ;
2014-12-22 20:06:54 +00:00
}
2016-10-12 10:23:43 +00:00
else if ( ! i2p : : config : : IsDefault ( " addressbook.subscriptions " ) )
2020-03-01 10:25:50 +00:00
{
// using config file items
std : : string subscriptionURLs ; i2p : : config : : GetOption ( " addressbook.subscriptions " , subscriptionURLs ) ;
std : : vector < std : : string > subsList ;
boost : : split ( subsList , subscriptionURLs , boost : : is_any_of ( " , " ) , boost : : token_compress_on ) ;
2021-01-21 00:19:34 +00:00
for ( const auto & s : subsList )
2020-03-01 10:25:50 +00:00
{
2021-01-21 00:19:34 +00:00
if ( s . empty ( ) | | s [ 0 ] = = ' # ' ) continue ; // skip empty line or comment
m_Subscriptions . push_back ( std : : make_shared < AddressBookSubscription > ( * this , s ) ) ;
2020-03-01 10:25:50 +00:00
}
LogPrint ( eLogInfo , " Addressbook: " , m_Subscriptions . size ( ) , " subscriptions urls loaded " ) ;
}
2014-12-22 20:06:54 +00:00
}
else
2016-01-12 23:32:32 +00:00
LogPrint ( eLogError , " Addressbook: subscriptions already loaded " ) ;
2014-12-22 20:06:54 +00:00
}
2016-03-24 18:48:07 +00:00
void AddressBook : : LoadLocal ( )
{
2019-03-27 19:19:10 +00:00
std : : map < std : : string , std : : shared_ptr < Address > > localAddresses ;
2016-03-24 18:48:07 +00:00
m_Storage - > LoadLocal ( localAddresses ) ;
2016-08-05 18:23:54 +00:00
for ( const auto & it : localAddresses )
2016-03-24 18:48:07 +00:00
{
2019-03-27 19:19:10 +00:00
if ( ! it . second - > IsIdentHash ( ) ) continue ; // skip blinded for now
2016-03-24 18:48:07 +00:00
auto dot = it . first . find ( ' . ' ) ;
if ( dot ! = std : : string : : npos )
{
auto domain = it . first . substr ( dot + 1 ) ;
2020-03-01 10:25:50 +00:00
auto it1 = m_Addresses . find ( domain ) ; // find domain in our addressbook
2019-03-27 19:19:10 +00:00
if ( it1 ! = m_Addresses . end ( ) & & it1 - > second - > IsIdentHash ( ) )
2016-03-24 18:48:07 +00:00
{
2019-03-27 19:19:10 +00:00
auto dest = context . FindLocalDestination ( it1 - > second - > identHash ) ;
2017-05-22 14:34:29 +00:00
if ( dest )
2016-03-24 18:48:07 +00:00
{
// address is ours
std : : shared_ptr < AddressResolver > resolver ;
2019-03-27 19:19:10 +00:00
auto it2 = m_Resolvers . find ( it1 - > second - > identHash ) ;
2016-03-24 18:48:07 +00:00
if ( it2 ! = m_Resolvers . end ( ) )
resolver = it2 - > second ; // resolver exists
else
{
// create new resolver
resolver = std : : make_shared < AddressResolver > ( dest ) ;
2019-03-27 19:19:10 +00:00
m_Resolvers . insert ( std : : make_pair ( it1 - > second - > identHash , resolver ) ) ;
2016-03-24 18:48:07 +00:00
}
2019-03-27 19:19:10 +00:00
resolver - > AddAddress ( it . first , it . second - > identHash ) ;
2016-03-24 18:48:07 +00:00
}
}
}
}
}
2016-03-15 18:37:07 +00:00
bool AddressBook : : GetEtag ( const i2p : : data : : IdentHash & subscription , std : : string & etag , std : : string & lastModified )
{
if ( m_Storage )
2017-05-22 14:34:29 +00:00
return m_Storage - > GetEtag ( subscription , etag , lastModified ) ;
2016-03-15 18:37:07 +00:00
else
2017-05-22 14:34:29 +00:00
return false ;
2016-03-15 18:37:07 +00:00
}
2016-03-14 20:05:57 +00:00
void AddressBook : : DownloadComplete ( bool success , const i2p : : data : : IdentHash & subscription , const std : : string & etag , const std : : string & lastModified )
2014-12-23 18:57:09 +00:00
{
m_IsDownloading = false ;
2018-01-23 20:50:28 +00:00
m_NumRetries + + ;
int nextUpdateTimeout = m_NumRetries * CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT ;
if ( m_NumRetries > CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES | | nextUpdateTimeout > CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT )
nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT ;
2016-03-11 21:29:49 +00:00
if ( success )
2017-05-22 14:34:29 +00:00
{
2018-01-23 20:50:28 +00:00
m_NumRetries = 0 ;
2016-08-09 14:17:40 +00:00
if ( m_DefaultSubscription ) m_DefaultSubscription = nullptr ;
2016-03-11 21:29:49 +00:00
if ( m_IsLoaded )
2017-05-22 14:34:29 +00:00
nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT ;
2016-03-11 21:29:49 +00:00
else
m_IsLoaded = true ;
2016-03-15 02:00:05 +00:00
if ( m_Storage ) m_Storage - > SaveEtag ( subscription , etag , lastModified ) ;
2017-05-22 14:34:29 +00:00
}
2015-01-21 21:34:50 +00:00
if ( m_SubscriptionsUpdateTimer )
{
2016-03-11 21:29:49 +00:00
m_SubscriptionsUpdateTimer - > expires_from_now ( boost : : posix_time : : minutes ( nextUpdateTimeout ) ) ;
2015-01-21 21:34:50 +00:00
m_SubscriptionsUpdateTimer - > async_wait ( std : : bind ( & AddressBook : : HandleSubscriptionsUpdateTimer ,
this , std : : placeholders : : _1 ) ) ;
}
2014-12-23 18:57:09 +00:00
}
void AddressBook : : StartSubscriptions ( )
{
LoadSubscriptions ( ) ;
2016-02-15 23:20:01 +00:00
if ( m_IsLoaded & & m_Subscriptions . empty ( ) ) return ;
2017-05-22 14:34:29 +00:00
2014-12-23 18:57:09 +00:00
auto dest = i2p : : client : : context . GetSharedLocalDestination ( ) ;
if ( dest )
{
m_SubscriptionsUpdateTimer = new boost : : asio : : deadline_timer ( dest - > GetService ( ) ) ;
m_SubscriptionsUpdateTimer - > expires_from_now ( boost : : posix_time : : minutes ( INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT ) ) ;
m_SubscriptionsUpdateTimer - > async_wait ( std : : bind ( & AddressBook : : HandleSubscriptionsUpdateTimer ,
this , std : : placeholders : : _1 ) ) ;
}
else
2016-03-11 00:46:52 +00:00
LogPrint ( eLogError , " Addressbook: can't start subscriptions: missing shared local destination " ) ;
2014-12-23 18:57:09 +00:00
}
void AddressBook : : StopSubscriptions ( )
{
if ( m_SubscriptionsUpdateTimer )
m_SubscriptionsUpdateTimer - > cancel ( ) ;
}
void AddressBook : : HandleSubscriptionsUpdateTimer ( const boost : : system : : error_code & ecode )
{
if ( ecode ! = boost : : asio : : error : : operation_aborted )
{
auto dest = i2p : : client : : context . GetSharedLocalDestination ( ) ;
2016-02-11 00:00:00 +00:00
if ( ! dest ) {
LogPrint ( eLogWarning , " Addressbook: missing local destination, skip subscription update " ) ;
return ;
}
2016-02-15 23:20:01 +00:00
if ( ! m_IsDownloading & & dest - > IsReady ( ) )
2014-12-23 18:57:09 +00:00
{
2016-02-15 23:20:01 +00:00
if ( ! m_IsLoaded )
{
2017-05-22 14:34:29 +00:00
// download it from default subscription
2016-02-15 23:20:01 +00:00
LogPrint ( eLogInfo , " Addressbook: trying to download it from default subscription. " ) ;
2020-03-01 10:25:50 +00:00
std : : string defaultSubURL ; i2p : : config : : GetOption ( " addressbook.defaulturl " , defaultSubURL ) ;
2016-02-15 23:20:01 +00:00
if ( ! m_DefaultSubscription )
2016-10-12 10:23:43 +00:00
m_DefaultSubscription = std : : make_shared < AddressBookSubscription > ( * this , defaultSubURL ) ;
2017-05-22 14:34:29 +00:00
m_IsDownloading = true ;
2016-08-09 14:17:40 +00:00
std : : thread load_hosts ( std : : bind ( & AddressBookSubscription : : CheckUpdates , m_DefaultSubscription ) ) ;
load_hosts . detach ( ) ; // TODO: use join
2017-05-22 14:34:29 +00:00
}
2016-02-15 23:20:01 +00:00
else if ( ! m_Subscriptions . empty ( ) )
2017-05-22 14:34:29 +00:00
{
2016-02-15 23:20:01 +00:00
// pick random subscription
2017-05-22 14:34:29 +00:00
auto ind = rand ( ) % m_Subscriptions . size ( ) ;
m_IsDownloading = true ;
2016-08-09 14:17:40 +00:00
std : : thread load_hosts ( std : : bind ( & AddressBookSubscription : : CheckUpdates , m_Subscriptions [ ind ] ) ) ;
2016-07-16 00:00:00 +00:00
load_hosts . detach ( ) ; // TODO: use join
2017-05-22 14:34:29 +00:00
}
2014-12-23 18:57:09 +00:00
}
else
{
// try it again later
m_SubscriptionsUpdateTimer - > expires_from_now ( boost : : posix_time : : minutes ( INITIAL_SUBSCRIPTION_RETRY_TIMEOUT ) ) ;
m_SubscriptionsUpdateTimer - > async_wait ( std : : bind ( & AddressBook : : HandleSubscriptionsUpdateTimer ,
this , std : : placeholders : : _1 ) ) ;
}
}
}
2016-03-26 14:31:47 +00:00
void AddressBook : : StartLookups ( )
{
auto dest = i2p : : client : : context . GetSharedLocalDestination ( ) ;
if ( dest )
{
2016-08-22 02:34:48 +00:00
auto datagram = dest - > GetDatagramDestination ( ) ;
2016-09-03 14:24:06 +00:00
if ( ! datagram )
datagram = dest - > CreateDatagramDestination ( ) ;
datagram - > SetReceiver ( std : : bind ( & AddressBook : : HandleLookupResponse , this ,
std : : placeholders : : _1 , std : : placeholders : : _2 , std : : placeholders : : _3 , std : : placeholders : : _4 , std : : placeholders : : _5 ) ,
ADDRESS_RESPONSE_DATAGRAM_PORT ) ;
2017-05-22 14:34:29 +00:00
}
2016-03-26 14:31:47 +00:00
}
2017-05-22 14:34:29 +00:00
2016-03-26 14:31:47 +00:00
void AddressBook : : StopLookups ( )
{
auto dest = i2p : : client : : context . GetSharedLocalDestination ( ) ;
if ( dest )
{
auto datagram = dest - > GetDatagramDestination ( ) ;
2016-09-03 14:24:06 +00:00
if ( datagram ) datagram - > ResetReceiver ( ADDRESS_RESPONSE_DATAGRAM_PORT ) ;
2017-05-22 14:34:29 +00:00
}
2016-03-26 14:31:47 +00:00
}
void AddressBook : : LookupAddress ( const std : : string & address )
{
2019-03-27 19:19:10 +00:00
std : : shared_ptr < const Address > addr ;
2016-03-26 14:31:47 +00:00
auto dot = address . find ( ' . ' ) ;
if ( dot ! = std : : string : : npos )
2019-03-27 19:19:10 +00:00
addr = FindAddress ( address . substr ( dot + 1 ) ) ;
if ( ! addr | | ! addr - > IsIdentHash ( ) ) // TODO:
2016-03-26 14:31:47 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: Can't find domain for " , address ) ;
2016-03-26 14:31:47 +00:00
return ;
2017-05-22 14:34:29 +00:00
}
2016-03-26 14:31:47 +00:00
auto dest = i2p : : client : : context . GetSharedLocalDestination ( ) ;
if ( dest )
{
auto datagram = dest - > GetDatagramDestination ( ) ;
if ( datagram )
{
uint32_t nonce ;
RAND_bytes ( ( uint8_t * ) & nonce , 4 ) ;
{
std : : unique_lock < std : : mutex > l ( m_LookupsMutex ) ;
2017-05-22 14:34:29 +00:00
m_Lookups [ nonce ] = address ;
}
2019-03-27 19:19:10 +00:00
LogPrint ( eLogDebug , " Addressbook: Lookup of " , address , " to " , addr - > identHash . ToBase32 ( ) , " nonce= " , nonce ) ;
2016-03-26 14:31:47 +00:00
size_t len = address . length ( ) + 9 ;
uint8_t * buf = new uint8_t [ len ] ;
memset ( buf , 0 , 4 ) ;
htobe32buf ( buf + 4 , nonce ) ;
buf [ 8 ] = address . length ( ) ;
memcpy ( buf + 9 , address . c_str ( ) , address . length ( ) ) ;
2019-03-27 19:19:10 +00:00
datagram - > SendDatagramTo ( buf , len , addr - > identHash , ADDRESS_RESPONSE_DATAGRAM_PORT , ADDRESS_RESOLVER_DATAGRAM_PORT ) ;
2016-03-26 14:31:47 +00:00
delete [ ] buf ;
2017-05-22 14:34:29 +00:00
}
}
}
2016-03-26 14:31:47 +00:00
void AddressBook : : HandleLookupResponse ( const i2p : : data : : IdentityEx & from , uint16_t fromPort , uint16_t toPort , const uint8_t * buf , size_t len )
{
if ( len < 44 )
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: Lookup response is too short " , len ) ;
2016-03-26 14:31:47 +00:00
return ;
}
uint32_t nonce = bufbe32toh ( buf + 4 ) ;
2016-07-16 00:00:00 +00:00
LogPrint ( eLogDebug , " Addressbook: Lookup response received from " , from . GetIdentHash ( ) . ToBase32 ( ) , " nonce= " , nonce ) ;
2016-03-26 14:31:47 +00:00
std : : string address ;
{
std : : unique_lock < std : : mutex > l ( m_LookupsMutex ) ;
auto it = m_Lookups . find ( nonce ) ;
if ( it ! = m_Lookups . end ( ) )
2017-05-22 14:34:29 +00:00
{
2016-03-26 14:31:47 +00:00
address = it - > second ;
m_Lookups . erase ( it ) ;
2017-05-22 14:34:29 +00:00
}
}
2016-03-26 14:31:47 +00:00
if ( address . length ( ) > 0 )
{
// TODO: verify from
2016-12-26 22:19:54 +00:00
i2p : : data : : IdentHash hash ( buf + 8 ) ;
2017-05-22 14:34:29 +00:00
if ( ! hash . IsZero ( ) )
2019-03-27 19:19:10 +00:00
m_Addresses [ address ] = std : : make_shared < Address > ( hash ) ;
2016-12-26 22:19:54 +00:00
else
LogPrint ( eLogInfo , " AddressBook: Lookup response: " , address , " not found " ) ;
2017-05-22 14:34:29 +00:00
}
2016-03-26 14:31:47 +00:00
}
2017-05-22 14:34:29 +00:00
2014-12-19 19:40:02 +00:00
AddressBookSubscription : : AddressBookSubscription ( AddressBook & book , const std : : string & link ) :
m_Book ( book ) , m_Link ( link )
{
}
2016-07-16 00:00:00 +00:00
void AddressBookSubscription : : CheckUpdates ( )
2014-12-19 19:40:02 +00:00
{
2020-12-07 03:31:46 +00:00
i2p : : util : : SetThreadName ( " Addressbook " ) ;
2016-07-16 00:00:00 +00:00
bool result = MakeRequest ( ) ;
m_Book . DownloadComplete ( result , m_Ident , m_Etag , m_LastModified ) ;
2014-12-19 19:40:02 +00:00
}
2016-07-16 00:00:00 +00:00
bool AddressBookSubscription : : MakeRequest ( )
2014-12-19 19:40:02 +00:00
{
2016-07-16 00:00:00 +00:00
i2p : : http : : URL url ;
2017-05-22 14:34:29 +00:00
// must be run in separate thread
2016-07-16 00:00:00 +00:00
LogPrint ( eLogInfo , " Addressbook: Downloading hosts database from " , m_Link ) ;
2020-03-01 10:25:50 +00:00
if ( ! url . parse ( m_Link ) )
2019-03-28 16:19:19 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: failed to parse url: " , m_Link ) ;
2016-07-16 00:00:00 +00:00
return false ;
2016-07-16 00:00:00 +00:00
}
2019-03-28 16:19:19 +00:00
auto addr = m_Book . GetAddress ( url . host ) ;
2020-03-01 10:25:50 +00:00
if ( ! addr | | ! addr - > IsIdentHash ( ) )
2019-03-28 16:19:19 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: Can't resolve " , url . host ) ;
2016-07-16 00:00:00 +00:00
return false ;
2016-07-16 00:00:00 +00:00
}
2019-03-28 16:19:19 +00:00
else
m_Ident = addr - > identHash ;
2016-07-16 00:00:00 +00:00
/* this code block still needs some love */
std : : condition_variable newDataReceived ;
std : : mutex newDataReceivedMutex ;
2016-07-16 00:00:00 +00:00
auto leaseSet = i2p : : client : : context . GetSharedLocalDestination ( ) - > FindLeaseSet ( m_Ident ) ;
2016-07-16 00:00:00 +00:00
if ( ! leaseSet )
{
std : : unique_lock < std : : mutex > l ( newDataReceivedMutex ) ;
2016-08-09 02:15:09 +00:00
i2p : : client : : context . GetSharedLocalDestination ( ) - > RequestDestination ( m_Ident ,
[ & newDataReceived , & leaseSet , & newDataReceivedMutex ] ( std : : shared_ptr < i2p : : data : : LeaseSet > ls )
2018-01-24 12:34:32 +00:00
{
2016-07-16 00:00:00 +00:00
leaseSet = ls ;
2016-08-09 02:15:09 +00:00
std : : unique_lock < std : : mutex > l1 ( newDataReceivedMutex ) ;
2016-07-16 00:00:00 +00:00
newDataReceived . notify_all ( ) ;
2016-08-09 02:15:09 +00:00
} ) ;
if ( newDataReceived . wait_for ( l , std : : chrono : : seconds ( SUBSCRIPTION_REQUEST_TIMEOUT ) ) = = std : : cv_status : : timeout )
2014-12-19 19:40:02 +00:00
{
2016-08-09 02:15:09 +00:00
LogPrint ( eLogError , " Addressbook: Subscription LeaseSet request timeout expired " ) ;
i2p : : client : : context . GetSharedLocalDestination ( ) - > CancelDestinationRequest ( m_Ident , false ) ; // don't notify, because we know it already
2016-07-16 00:00:00 +00:00
return false ;
2014-12-19 19:40:02 +00:00
}
2016-07-16 00:00:00 +00:00
}
if ( ! leaseSet ) {
/* still no leaseset found */
LogPrint ( eLogError , " Addressbook: LeaseSet for address " , url . host , " not found " ) ;
2016-07-16 00:00:00 +00:00
return false ;
2016-07-16 00:00:00 +00:00
}
if ( m_Etag . empty ( ) & & m_LastModified . empty ( ) ) {
2016-07-16 00:00:00 +00:00
m_Book . GetEtag ( m_Ident , m_Etag , m_LastModified ) ;
2016-07-16 00:00:00 +00:00
LogPrint ( eLogDebug , " Addressbook: loaded for " , url . host , " : ETag: " , m_Etag , " , Last-Modified: " , m_LastModified ) ;
}
/* save url parts for later use */
std : : string dest_host = url . host ;
int dest_port = url . port ? url . port : 80 ;
/* create http request & send it */
i2p : : http : : HTTPReq req ;
2017-02-05 03:39:54 +00:00
req . AddHeader ( " Host " , dest_host ) ;
req . AddHeader ( " User-Agent " , " Wget/1.11.4 " ) ;
2020-09-27 21:46:15 +00:00
req . AddHeader ( " Accept-Encoding " , " gzip " ) ;
2018-01-15 15:30:01 +00:00
req . AddHeader ( " X-Accept-Encoding " , " x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0 " ) ;
2017-02-05 03:39:54 +00:00
req . AddHeader ( " Connection " , " close " ) ;
2016-07-16 00:00:00 +00:00
if ( ! m_Etag . empty ( ) )
2017-02-05 03:39:54 +00:00
req . AddHeader ( " If-None-Match " , m_Etag ) ;
2016-07-16 00:00:00 +00:00
if ( ! m_LastModified . empty ( ) )
2017-02-05 03:39:54 +00:00
req . AddHeader ( " If-Modified-Since " , m_LastModified ) ;
2016-07-16 00:00:00 +00:00
/* convert url to relative */
url . schema = " " ;
url . host = " " ;
2020-03-01 10:25:50 +00:00
req . uri = url . to_string ( ) ;
2020-09-27 21:46:15 +00:00
req . version = " HTTP/1.1 " ;
2016-07-16 00:00:00 +00:00
auto stream = i2p : : client : : context . GetSharedLocalDestination ( ) - > CreateStream ( leaseSet , dest_port ) ;
std : : string request = req . to_string ( ) ;
stream - > Send ( ( const uint8_t * ) request . data ( ) , request . length ( ) ) ;
2016-07-16 00:00:00 +00:00
/* read response */
std : : string response ;
uint8_t recv_buf [ 4096 ] ;
bool end = false ;
2018-09-02 11:51:58 +00:00
int numAttempts = 0 ;
2017-05-22 14:34:29 +00:00
while ( ! end )
2017-02-12 20:11:19 +00:00
{
2016-07-16 00:00:00 +00:00
stream - > AsyncReceive ( boost : : asio : : buffer ( recv_buf , 4096 ) ,
[ & ] ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
2014-12-19 19:40:02 +00:00
{
2016-07-16 00:00:00 +00:00
if ( bytes_transferred )
response . append ( ( char * ) recv_buf , bytes_transferred ) ;
if ( ecode = = boost : : asio : : error : : timed_out | | ! stream - > IsOpen ( ) )
end = true ;
newDataReceived . notify_all ( ) ;
} ,
2018-01-24 12:34:32 +00:00
SUBSCRIPTION_REQUEST_TIMEOUT ) ;
2016-07-16 00:00:00 +00:00
std : : unique_lock < std : : mutex > l ( newDataReceivedMutex ) ;
2018-08-25 17:27:03 +00:00
// wait 1 more second
if ( newDataReceived . wait_for ( l , std : : chrono : : seconds ( SUBSCRIPTION_REQUEST_TIMEOUT + 1 ) ) = = std : : cv_status : : timeout )
2017-05-22 14:34:29 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: subscriptions request timeout expired " ) ;
2017-02-12 20:11:19 +00:00
numAttempts + + ;
if ( numAttempts > 5 ) end = true ;
2017-05-22 14:34:29 +00:00
}
2016-07-16 00:00:00 +00:00
}
// process remaining buffer
2017-05-22 14:34:29 +00:00
while ( size_t len = stream - > ReadSome ( recv_buf , sizeof ( recv_buf ) ) )
2016-07-16 00:00:00 +00:00
response . append ( ( char * ) recv_buf , len ) ;
/* parse response */
i2p : : http : : HTTPRes res ;
int res_head_len = res . parse ( response ) ;
2017-05-22 14:34:29 +00:00
if ( res_head_len < 0 )
2017-02-12 20:11:19 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: can't parse http response from " , dest_host ) ;
2016-07-16 00:00:00 +00:00
return false ;
2016-07-16 00:00:00 +00:00
}
2017-05-22 14:34:29 +00:00
if ( res_head_len = = 0 )
2017-02-12 20:11:19 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: incomplete http response from " , dest_host , " , interrupted by timeout " ) ;
2016-07-16 00:00:00 +00:00
return false ;
2016-07-16 00:00:00 +00:00
}
/* assert: res_head_len > 0 */
response . erase ( 0 , res_head_len ) ;
2017-05-22 14:34:29 +00:00
if ( res . code = = 304 )
2017-02-12 20:11:19 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogInfo , " Addressbook: no updates from " , dest_host , " , code 304 " ) ;
2016-07-16 00:00:00 +00:00
return false ;
2016-07-16 00:00:00 +00:00
}
2017-05-22 14:34:29 +00:00
if ( res . code ! = 200 )
2017-02-12 20:11:19 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogWarning , " Adressbook: can't get updates from " , dest_host , " , response code " , res . code ) ;
2016-07-16 00:00:00 +00:00
return false ;
}
int len = res . content_length ( ) ;
2017-05-22 14:34:29 +00:00
if ( response . empty ( ) )
2017-02-12 20:11:19 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: empty response from " , dest_host , " , expected " , len , " bytes " ) ;
return false ;
}
2017-05-22 14:34:29 +00:00
if ( ! res . is_gzipped ( ) & & len > 0 & & len ! = ( int ) response . length ( ) )
2017-02-12 20:11:19 +00:00
{
LogPrint ( eLogError , " Addressbook: response size mismatch, expected: " , len , " , got: " , response . length ( ) , " bytes " ) ;
2016-07-16 00:00:00 +00:00
return false ;
2016-07-16 00:00:00 +00:00
}
/* assert: res.code == 200 */
auto it = res . headers . find ( " ETag " ) ;
2017-02-12 20:11:19 +00:00
if ( it ! = res . headers . end ( ) ) m_Etag = it - > second ;
2020-09-27 23:19:48 +00:00
it = res . headers . find ( " Last-Modified " ) ;
2017-02-12 20:11:19 +00:00
if ( it ! = res . headers . end ( ) ) m_LastModified = it - > second ;
2017-05-22 14:34:29 +00:00
if ( res . is_chunked ( ) )
2017-02-12 20:11:19 +00:00
{
2016-07-16 00:00:00 +00:00
std : : stringstream in ( response ) , out ;
i2p : : http : : MergeChunkedResponse ( in , out ) ;
response = out . str ( ) ;
2017-05-22 14:34:29 +00:00
}
2020-09-27 21:46:15 +00:00
if ( res . is_gzipped ( ) )
2017-02-12 20:11:19 +00:00
{
2016-07-16 00:00:00 +00:00
std : : stringstream out ;
2016-02-18 18:19:31 +00:00
i2p : : data : : GzipInflator inflator ;
2016-07-16 00:00:00 +00:00
inflator . Inflate ( ( const uint8_t * ) response . data ( ) , response . length ( ) , out ) ;
2017-05-22 14:34:29 +00:00
if ( out . fail ( ) )
2017-02-12 20:11:19 +00:00
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: can't gunzip http response " ) ;
2016-02-18 18:19:31 +00:00
return false ;
2016-07-16 00:00:00 +00:00
}
response = out . str ( ) ;
}
std : : stringstream ss ( response ) ;
LogPrint ( eLogInfo , " Addressbook: got update from " , dest_host ) ;
2016-07-16 00:00:00 +00:00
m_Book . LoadHostsFromStream ( ss , true ) ;
2016-07-16 00:00:00 +00:00
return true ;
2016-02-18 18:19:31 +00:00
}
2016-03-24 18:48:07 +00:00
AddressResolver : : AddressResolver ( std : : shared_ptr < ClientDestination > destination ) :
m_LocalDestination ( destination )
{
if ( m_LocalDestination )
{
auto datagram = m_LocalDestination - > GetDatagramDestination ( ) ;
if ( ! datagram )
datagram = m_LocalDestination - > CreateDatagramDestination ( ) ;
2017-05-22 14:34:29 +00:00
datagram - > SetReceiver ( std : : bind ( & AddressResolver : : HandleRequest , this ,
std : : placeholders : : _1 , std : : placeholders : : _2 , std : : placeholders : : _3 , std : : placeholders : : _4 , std : : placeholders : : _5 ) ,
2016-03-24 18:48:07 +00:00
ADDRESS_RESOLVER_DATAGRAM_PORT ) ;
}
}
2016-03-26 14:31:47 +00:00
AddressResolver : : ~ AddressResolver ( )
{
if ( m_LocalDestination )
{
auto datagram = m_LocalDestination - > GetDatagramDestination ( ) ;
if ( datagram )
2020-03-01 10:25:50 +00:00
datagram - > ResetReceiver ( ADDRESS_RESOLVER_DATAGRAM_PORT ) ;
2017-05-22 14:34:29 +00:00
}
}
2016-03-24 18:48:07 +00:00
void AddressResolver : : HandleRequest ( const i2p : : data : : IdentityEx & from , uint16_t fromPort , uint16_t toPort , const uint8_t * buf , size_t len )
{
if ( len < 9 | | len < buf [ 8 ] + 9U )
{
2016-07-16 00:00:00 +00:00
LogPrint ( eLogError , " Addressbook: Address request is too short " , len ) ;
2016-03-24 18:48:07 +00:00
return ;
}
// read requested address
uint8_t l = buf [ 8 ] ;
char address [ 255 ] ;
memcpy ( address , buf + 9 , l ) ;
2017-05-22 14:34:29 +00:00
address [ l ] = 0 ;
2016-07-16 00:00:00 +00:00
LogPrint ( eLogDebug , " Addressbook: Address request " , address ) ;
2016-03-24 18:48:07 +00:00
// send response
2016-03-26 14:31:47 +00:00
uint8_t response [ 44 ] ;
2016-03-24 18:48:07 +00:00
memset ( response , 0 , 4 ) ; // reserved
2017-05-22 14:34:29 +00:00
memcpy ( response + 4 , buf + 4 , 4 ) ; // nonce
2016-03-24 18:48:07 +00:00
auto it = m_LocalAddresses . find ( address ) ; // address lookup
2017-05-22 14:34:29 +00:00
if ( it ! = m_LocalAddresses . end ( ) )
memcpy ( response + 8 , it - > second , 32 ) ; // ident
2016-03-24 18:48:07 +00:00
else
2017-05-22 14:34:29 +00:00
memset ( response + 8 , 0 , 32 ) ; // not found
2016-03-26 14:31:47 +00:00
memset ( response + 40 , 0 , 4 ) ; // set expiration time to zero
2016-12-12 18:40:24 +00:00
m_LocalDestination - > GetDatagramDestination ( ) - > SendDatagramTo ( response , 44 , from . GetIdentHash ( ) , toPort , fromPort ) ;
2016-03-24 18:48:07 +00:00
}
void AddressResolver : : AddAddress ( const std : : string & name , const i2p : : data : : IdentHash & ident )
{
2017-05-22 14:34:29 +00:00
m_LocalAddresses [ name ] = ident ;
2016-03-24 18:48:07 +00:00
}
2014-04-01 19:18:14 +00:00
}
}