mirror of https://github.com/PurpleI2P/i2pd.git
I2P: End-to-End encrypted and anonymous Internet
https://i2pd.website/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
7.9 KiB
279 lines
7.9 KiB
#include <fstream> |
|
#include <sstream> |
|
#include <boost/regex.hpp> |
|
#include <boost/filesystem.hpp> |
|
#include <cryptopp/osrng.h> |
|
#include <cryptopp/zinflate.h> |
|
#include "I2PEndian.h" |
|
#include "Reseed.h" |
|
#include "Log.h" |
|
#include "Identity.h" |
|
#include "NetDb.h" |
|
#include "util.h" |
|
|
|
|
|
namespace i2p |
|
{ |
|
namespace data |
|
{ |
|
|
|
static std::vector<std::string> httpReseedHostList = { |
|
"http://193.150.121.66/netDb/", |
|
"http://netdb.i2p2.no/", |
|
"http://reseed.i2p-projekt.de/", |
|
"http://cowpuncher.drollette.com/netdb/", |
|
"http://i2p.mooo.com/netDb/", |
|
"http://reseed.info/", |
|
"http://uk.reseed.i2p2.no/", |
|
"http://us.reseed.i2p2.no/", |
|
"http://jp.reseed.i2p2.no/", |
|
"http://i2p-netdb.innovatio.no/", |
|
"http://ieb9oopo.mooo.com" |
|
}; |
|
|
|
//TODO: Remember to add custom port support. Not all serves on 443 |
|
static std::vector<std::string> httpsReseedHostList = { |
|
"https://193.150.121.66/netDb/", |
|
"https://netdb.i2p2.no/", |
|
"https://reseed.i2p-projekt.de/", |
|
"https://cowpuncher.drollette.com/netdb/", |
|
"https://i2p.mooo.com/netDb/", |
|
"https://reseed.info/", |
|
"https://i2p-netdb.innovatio.no/", |
|
"https://ieb9oopo.mooo.com/", |
|
"https://ssl.webpack.de/ivae2he9.sg4.e-plaza.de/" // Only HTTPS and SU3 (v2) support |
|
}; |
|
|
|
//TODO: Implement v2 reseeding. Lightweight zip library is needed. |
|
//TODO: Implement SU3, utils. |
|
Reseeder::Reseeder() |
|
{ |
|
} |
|
|
|
Reseeder::~Reseeder() |
|
{ |
|
} |
|
|
|
bool Reseeder::reseedNow() |
|
{ |
|
try |
|
{ |
|
// Seems like the best place to try to intercept with SSL |
|
/*ssl_server = true; |
|
try { |
|
// SSL |
|
} |
|
catch (std::exception& e) |
|
{ |
|
LogPrint("Exception in SSL: ", e.what()); |
|
}*/ |
|
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())]; |
|
LogPrint("Reseeding from ", reseedHost); |
|
std::string content = i2p::util::http::httpRequest(reseedHost); |
|
if (content == "") |
|
{ |
|
LogPrint("Reseed failed"); |
|
return false; |
|
} |
|
boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase); |
|
boost::sregex_token_iterator i(content.begin(), content.end(), e, 1); |
|
boost::sregex_token_iterator j; |
|
//TODO: Ugly code, try to clean up. |
|
//TODO: Try to reduce N number of variables |
|
std::string name; |
|
std::string routerInfo; |
|
std::string tmpUrl; |
|
std::string filename; |
|
std::string ignoreFileSuffix = ".su3"; |
|
boost::filesystem::path root = i2p::util::filesystem::GetDataDir(); |
|
while (i != j) |
|
{ |
|
name = *i++; |
|
if (name.find(ignoreFileSuffix)!=std::string::npos) |
|
continue; |
|
LogPrint("Downloading ", name); |
|
tmpUrl = reseedHost; |
|
tmpUrl.append(name); |
|
routerInfo = i2p::util::http::httpRequest(tmpUrl); |
|
if (routerInfo.size()==0) |
|
continue; |
|
filename = root.string(); |
|
#ifndef _WIN32 |
|
filename += "/netDb/r"; |
|
#else |
|
filename += "\\netDb\\r"; |
|
#endif |
|
filename += name.at(11); // first char in id |
|
#ifndef _WIN32 |
|
filename.append("/"); |
|
#else |
|
filename.append("\\"); |
|
#endif |
|
filename.append(name.c_str()); |
|
std::ofstream outfile (filename, std::ios::binary); |
|
outfile << routerInfo; |
|
outfile.close(); |
|
} |
|
return true; |
|
} |
|
catch (std::exception& ex) |
|
{ |
|
//TODO: error reporting |
|
return false; |
|
} |
|
return false; |
|
} |
|
|
|
int Reseeder::ReseedNowSU3 () |
|
{ |
|
CryptoPP::AutoSeededRandomPool rnd; |
|
auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1); |
|
std::string reseedHost = httpReseedHostList[ind]; |
|
return ReseedFromSU3 (reseedHost); |
|
} |
|
|
|
int Reseeder::ReseedFromSU3 (const std::string& host) |
|
{ |
|
std::string url = host + "i2pseeds.su3"; |
|
LogPrint (eLogInfo, "Dowloading SU3 from ", host); |
|
std::string su3 = i2p::util::http::httpRequest (url); |
|
if (su3.length () > 0) |
|
{ |
|
std::stringstream s(su3); |
|
return ProcessSU3Stream (s); |
|
} |
|
else |
|
{ |
|
LogPrint (eLogWarning, "SU3 download failed"); |
|
return 0; |
|
} |
|
} |
|
|
|
int ProcessSU3File (const char * filename) |
|
{ |
|
std::ifstream s(filename, std::ifstream::binary); |
|
if (s.is_open ()) |
|
return ProcessSU3Stream (s); |
|
else |
|
{ |
|
LogPrint (eLogError, "Can't open file ", filename); |
|
return 0; |
|
} |
|
} |
|
|
|
const char SU3_MAGIC_NUMBER[]="I2Psu3"; |
|
int ProcessSU3Stream (std::istream& s) |
|
{ |
|
static uint32_t headerSignature = htole32 (0x04034B50); |
|
|
|
char magicNumber[7]; |
|
s.read (magicNumber, 7); // magic number and zero byte 6 |
|
if (strcmp (magicNumber, SU3_MAGIC_NUMBER)) |
|
{ |
|
LogPrint (eLogError, "Unexpected SU3 magic number"); |
|
return 0; |
|
} |
|
s.seekg (1, std::ios::cur); // su3 file format version |
|
SigningKeyType signatureType; |
|
s.read ((char *)&signatureType, 2); // signature type |
|
signatureType = be16toh (signatureType); |
|
uint16_t signatureLength; |
|
s.read ((char *)&signatureLength, 2); // signature length |
|
signatureLength = be16toh (signatureLength); |
|
s.seekg (1, std::ios::cur); // unused |
|
uint8_t versionLength; |
|
s.read ((char *)&versionLength, 1); // version length |
|
s.seekg (1, std::ios::cur); // unused |
|
uint8_t signerIDLength; |
|
s.read ((char *)&signerIDLength, 1); // signer ID length |
|
uint64_t contentLength; |
|
s.read ((char *)&contentLength, 8); // content length |
|
contentLength = be64toh (contentLength); |
|
s.seekg (1, std::ios::cur); // unused |
|
uint8_t fileType; |
|
s.read ((char *)&fileType, 1); // file type |
|
if (fileType != 0x00) // zip file |
|
{ |
|
LogPrint (eLogError, "Can't handle file type ", (int)fileType); |
|
return 0; |
|
} |
|
s.seekg (1, std::ios::cur); // unused |
|
uint8_t contentType; |
|
s.read ((char *)&contentType, 1); // content type |
|
if (contentType != 0x03) // reseed data |
|
{ |
|
LogPrint (eLogError, "Unexpected content type ", (int)contentType); |
|
return 0; |
|
} |
|
s.seekg (12, std::ios::cur); // unused |
|
|
|
s.seekg (versionLength, std::ios::cur); // skip version |
|
s.seekg (signerIDLength, std::ios::cur); // skip signer ID |
|
|
|
// handle content |
|
int numFiles = 0; |
|
size_t contentPos = s.tellg (); |
|
while (!s.eof ()) |
|
{ |
|
uint32_t signature; |
|
s.read ((char *)&signature, 4); |
|
if (signature == headerSignature) |
|
{ |
|
// next local file |
|
s.seekg (4, std::ios::cur); // skip fields we don't care about |
|
uint16_t compressionMethod; |
|
s.read ((char *)&compressionMethod, 2); |
|
compressionMethod = le16toh (compressionMethod); |
|
s.seekg (8, std::ios::cur); // skip fields we don't care about |
|
uint32_t compressedSize, uncompressedSize; |
|
s.read ((char *)&compressedSize, 4); |
|
compressedSize = le32toh (compressedSize); |
|
s.read ((char *)&uncompressedSize, 4); |
|
uncompressedSize = le32toh (uncompressedSize); |
|
uint16_t fileNameLength, extraFieldLength; |
|
s.read ((char *)&fileNameLength, 2); |
|
fileNameLength = le16toh (fileNameLength); |
|
s.read ((char *)&extraFieldLength, 2); |
|
extraFieldLength = le16toh (extraFieldLength); |
|
char localFileName[255]; |
|
s.read (localFileName, fileNameLength); |
|
localFileName[fileNameLength] = 0; |
|
s.seekg (extraFieldLength, std::ios::cur); |
|
LogPrint (eLogDebug, "Proccessing file ", localFileName, " ", compressedSize, " bytes"); |
|
|
|
uint8_t * compressed = new uint8_t[compressedSize]; |
|
s.read ((char *)compressed, compressedSize); |
|
if (compressionMethod) // we assume Deflate |
|
{ |
|
CryptoPP::Inflator decompressor; |
|
decompressor.Put (compressed, compressedSize); |
|
size_t len = decompressor.MaxRetrievable (); |
|
if (len <= uncompressedSize) |
|
{ |
|
uint8_t * uncompressed = new uint8_t[uncompressedSize]; |
|
decompressor.Get (uncompressed, len); |
|
i2p::data::netdb.AddRouterInfo (uncompressed, len); |
|
numFiles++; |
|
delete[] uncompressed; |
|
} |
|
else |
|
LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header"); |
|
} |
|
else // no compression |
|
{ |
|
i2p::data::netdb.AddRouterInfo (compressed, compressedSize); |
|
numFiles++; |
|
} |
|
delete[] compressed; |
|
} |
|
else |
|
break; // no more files |
|
size_t end = s.tellg (); |
|
if (end - contentPos >= contentLength) |
|
break; // we are beyond contentLength |
|
} |
|
return numFiles; |
|
} |
|
} |
|
} |
|
|
|
|