@ -24,7 +24,7 @@ namespace client
{
private :
i2p : : fs : : HashedStorage storage ;
std : : string indexPath;
std : : string etagsPath, indexPath, local Path;
public :
AddressBookFilesystemStorage ( ) : storage ( " addressbook " , " b " , " " , " b32 " ) { } ;
@ -34,14 +34,34 @@ namespace client
bool Init ( ) ;
int Load ( std : : map < std : : string , i2p : : data : : IdentHash > & addresses ) ;
int LoadLocal ( std : : map < std : : string , i2p : : data : : IdentHash > & addresses ) ;
int Save ( const std : : map < std : : string , i2p : : data : : IdentHash > & addresses ) ;
void SaveEtag ( const i2p : : data : : IdentHash & subsciption , const std : : string & etag , const std : : string & lastModified ) ;
bool GetEtag ( const i2p : : data : : IdentHash & subscription , std : : string & etag , std : : string & lastModified ) ;
private :
int LoadFromFile ( const std : : string & filename , std : : map < std : : string , i2p : : data : : IdentHash > & addresses ) ; // returns -1 if can't open file, otherwise number of records
} ;
bool AddressBookFilesystemStorage : : Init ( )
{
{
storage . SetPlace ( i2p : : fs : : GetDataDir ( ) ) ;
indexPath = storage . GetRoot ( ) + i2p : : fs : : dirSep + " addresses.csv " ;
return storage . Init ( i2p : : data : : GetBase32SubstitutionTable ( ) , 32 ) ;
// init storage
if ( storage . Init ( i2p : : data : : GetBase32SubstitutionTable ( ) , 32 ) )
{
// 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 ;
}
return false ;
}
std : : shared_ptr < const i2p : : data : : IdentityEx > AddressBookFilesystemStorage : : GetAddress ( const i2p : : data : : IdentHash & ident ) const
@ -87,24 +107,18 @@ namespace client
storage . Remove ( ident . ToBase32 ( ) ) ;
}
int AddressBookFilesystemStorage : : Load ( std : : map < std : : string , i2p : : data : : IdentHash > & addresses )
int AddressBookFilesystemStorage : : Load FromFile ( const std : : string & filename , std : : map < std : : string , i2p : : data : : IdentHash > & addresses )
{
int num = 0 ;
std : : string s ;
std : : ifstream f ( indexPath , std : : ifstream : : in ) ; // in text mode
if ( f . is_open ( ) ) {
LogPrint ( eLogInfo , " Addressbook: using index file " , indexPath ) ;
} else {
LogPrint ( eLogWarning , " Addressbook: Can't open " , indexPath ) ;
return 0 ;
}
std : : ifstream f ( filename , std : : ifstream : : in ) ; // in text mode
if ( ! f ) return - 1 ;
addresses . clear ( ) ;
while ( ! f . eof ( ) ) {
while ( ! f . eof ( ) )
{
std : : string s ;
getline ( f , s ) ;
if ( ! s . length ( ) )
continue ; // skip empty line
if ( ! s . length ( ) ) continue ; // skip empty line
std : : size_t pos = s . find ( ' , ' ) ;
if ( pos ! = std : : string : : npos )
@ -118,8 +132,28 @@ namespace client
num + + ;
}
}
return num ;
}
int AddressBookFilesystemStorage : : Load ( std : : map < std : : string , i2p : : data : : IdentHash > & addresses )
{
int num = LoadFromFile ( indexPath , addresses ) ;
if ( num < 0 )
{
LogPrint ( eLogWarning , " Addressbook: Can't open " , indexPath ) ;
return 0 ;
}
LogPrint ( eLogInfo , " Addressbook: using index file " , indexPath ) ;
LogPrint ( eLogInfo , " Addressbook: " , num , " addresses loaded from storage " ) ;
return num ;
}
int AddressBookFilesystemStorage : : LoadLocal ( std : : map < std : : string , i2p : : data : : IdentHash > & addresses )
{
int num = LoadFromFile ( localPath , addresses ) ;
if ( num < 0 ) return 0 ;
LogPrint ( eLogInfo , " Addressbook: " , num , " local addresses loaded " ) ;
return num ;
}
@ -146,6 +180,28 @@ namespace client
return num ;
}
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 )
{
f < < etag < < std : : endl ;
f < < lastModified < < std : : endl ;
}
}
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 ) ;
if ( f . eof ( ) ) return false ;
std : : getline ( f , lastModified ) ;
return true ;
}
//---------------------------------------------------------------------
AddressBook : : AddressBook ( ) : m_Storage ( new AddressBookFilesystemStorage ) , m_IsLoaded ( false ) , m_IsDownloading ( false ) ,
m_DefaultSubscription ( nullptr ) , m_SubscriptionsUpdateTimer ( nullptr )
@ -274,6 +330,7 @@ namespace client
LoadHostsFromStream ( f ) ;
m_IsLoaded = true ;
}
LoadLocal ( ) ;
}
void AddressBook : : LoadHostsFromStream ( std : : istream & f )
@ -337,7 +394,49 @@ namespace client
LogPrint ( eLogError , " Addressbook: subscriptions already loaded " ) ;
}
void AddressBook : : DownloadComplete ( bool success )
void AddressBook : : LoadLocal ( )
{
std : : map < std : : string , i2p : : data : : IdentHash > localAddresses ;
m_Storage - > LoadLocal ( localAddresses ) ;
for ( auto it : localAddresses )
{
auto dot = it . first . find ( ' . ' ) ;
if ( dot ! = std : : string : : npos )
{
auto domain = it . first . substr ( dot + 1 ) ;
auto it1 = m_Addresses . find ( domain ) ; // find domain in our addressbook
if ( it1 ! = m_Addresses . end ( ) )
{
auto dest = context . FindLocalDestination ( it1 - > second ) ;
if ( dest )
{
// address is ours
std : : shared_ptr < AddressResolver > resolver ;
auto it2 = m_Resolvers . find ( it1 - > second ) ;
if ( it2 ! = m_Resolvers . end ( ) )
resolver = it2 - > second ; // resolver exists
else
{
// create new resolver
resolver = std : : make_shared < AddressResolver > ( dest ) ;
m_Resolvers . insert ( std : : make_pair ( it1 - > second , resolver ) ) ;
}
resolver - > AddAddress ( it . first , it . second ) ;
}
}
}
}
}
bool AddressBook : : GetEtag ( const i2p : : data : : IdentHash & subscription , std : : string & etag , std : : string & lastModified )
{
if ( m_Storage )
return m_Storage - > GetEtag ( subscription , etag , lastModified ) ;
else
return false ;
}
void AddressBook : : DownloadComplete ( bool success , const i2p : : data : : IdentHash & subscription , const std : : string & etag , const std : : string & lastModified )
{
m_IsDownloading = false ;
int nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT ;
@ -348,6 +447,7 @@ namespace client
nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT ;
else
m_IsLoaded = true ;
if ( m_Storage ) m_Storage - > SaveEtag ( subscription , etag , lastModified ) ;
}
if ( m_SubscriptionsUpdateTimer )
{
@ -438,6 +538,12 @@ namespace client
i2p : : data : : IdentHash ident ;
if ( m_Book . GetIdentHash ( u . host_ , ident ) )
{
if ( ! m_Etag . length ( ) )
{
// load ETag
m_Book . GetEtag ( ident , m_Etag , m_LastModified ) ;
LogPrint ( eLogInfo , " Addressbook: set " , m_Link , " ETag: " , m_Etag , " Last-Modified: " , m_LastModified ) ;
}
std : : condition_variable newDataReceived ;
std : : mutex newDataReceivedMutex ;
auto leaseSet = i2p : : client : : context . GetSharedLocalDestination ( ) - > FindLeaseSet ( ident ) ;
@ -468,7 +574,7 @@ namespace client
< < " X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0 \r \n "
< < " Connection: close \r \n " ;
if ( m_Etag . length ( ) > 0 ) // etag
request < < i2p : : util : : http : : IF_NONE_MATCH < < " : \" " < < m_Etag < < "\ "\r \n " ;
request < < i2p : : util : : http : : IF_NONE_MATCH < < " : " < < m_Etag < < "\r \n " ;
if ( m_LastModified . length ( ) > 0 ) // if-modfief-since
request < < i2p : : util : : http : : IF_MODIFIED_SINCE < < " : " < < m_LastModified < < " \r \n " ;
request < < " \r \n " ; // end of header
@ -529,7 +635,7 @@ namespace client
! header . compare ( colon + 1 , std : : string : : npos , " x-i2p-gzip " ) ;
}
}
LogPrint ( eLogInfo , " Addressbook: " , m_Link , " ETag: " , m_Etag , " Last-Modified: " , m_LastModified ) ;
LogPrint ( eLogInfo , " Addressbook: received " , m_Link , " ETag: " , m_Etag , " Last-Modified: " , m_LastModified ) ;
if ( ! response . eof ( ) )
{
success = true ;
@ -561,7 +667,7 @@ namespace client
if ( ! success )
LogPrint ( eLogError , " Addressbook: download hosts.txt from " , m_Link , " failed " ) ;
m_Book . DownloadComplete ( success );
m_Book . DownloadComplete ( success , ident , m_Etag , m_LastModified );
}
bool AddressBookSubscription : : ProcessResponse ( std : : stringstream & s , bool isGzip )
@ -580,6 +686,50 @@ namespace client
m_Book . LoadHostsFromStream ( s ) ;
return true ;
}
AddressResolver : : AddressResolver ( std : : shared_ptr < ClientDestination > destination ) :
m_LocalDestination ( destination )
{
if ( m_LocalDestination )
{
auto datagram = m_LocalDestination - > GetDatagramDestination ( ) ;
if ( ! datagram )
datagram = m_LocalDestination - > CreateDatagramDestination ( ) ;
datagram - > SetReceiver ( std : : bind ( & AddressResolver : : HandleRequest , this ,
std : : placeholders : : _1 , std : : placeholders : : _2 , std : : placeholders : : _3 , std : : placeholders : : _4 , std : : placeholders : : _5 ) ,
ADDRESS_RESOLVER_DATAGRAM_PORT ) ;
}
}
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 )
{
LogPrint ( eLogError , " Address request is too short " , len ) ;
return ;
}
// read requested address
uint8_t l = buf [ 8 ] ;
char address [ 255 ] ;
memcpy ( address , buf + 9 , l ) ;
address [ l ] = 0 ;
// send response
uint8_t response [ 40 ] ;
memset ( response , 0 , 4 ) ; // reserved
memcpy ( response + 4 , buf + 4 , 4 ) ; // nonce
auto it = m_LocalAddresses . find ( address ) ; // address lookup
if ( it ! = m_LocalAddresses . end ( ) )
memcpy ( response + 8 , it - > second , 32 ) ; // ident
else
memset ( response + 8 , 0 , 32 ) ; // not found
m_LocalDestination - > GetDatagramDestination ( ) - > SendDatagramTo ( response , 40 , from . GetIdentHash ( ) , toPort , fromPort ) ;
}
void AddressResolver : : AddAddress ( const std : : string & name , const i2p : : data : : IdentHash & ident )
{
m_LocalAddresses [ name ] = ident ;
}
}
}