made torrent_info not rely on exception support

This commit is contained in:
Arvid Norberg 2007-12-28 20:11:10 +00:00
parent 501611173e
commit eb8ea0f461
6 changed files with 164 additions and 69 deletions

View File

@ -536,7 +536,8 @@ namespace libtorrent
// bencoded tree and moves the torrent
// to the checker thread for initial checking
// of the storage.
void set_metadata(entry const&);
// a return value of false indicates an error
bool set_metadata(entry const& metadata, std::string& error);
private:

View File

@ -244,7 +244,7 @@ namespace libtorrent
void add_node(std::pair<std::string, int> const& node);
void parse_info_section(entry const& e);
bool parse_info_section(entry const& e, std::string& error);
entry const* extra(char const* key) const
{ return m_extra_info.find_key(key); }
@ -257,7 +257,7 @@ namespace libtorrent
private:
void read_torrent_info(const entry& libtorrent);
bool read_torrent_info(const entry& libtorrent, std::string& error);
// the urls to the trackers
std::vector<announce_entry> m_urls;

View File

@ -185,7 +185,16 @@ namespace libtorrent { namespace
}
entry metadata = bdecode(m_metadata.begin(), m_metadata.end());
m_torrent.set_metadata(metadata);
std::string error;
if (!m_torrent.set_metadata(metadata, error))
{
// this means the metadata is correct, since we
// verified it against the info-hash, but we
// failed to parse it. Pause the torrent
// TODO: Post an alert!
m_torrent.pause();
return false;
}
// clear the storage for the bitfield
std::vector<bool>().swap(m_have_metadata);

View File

@ -2126,12 +2126,16 @@ namespace libtorrent
return true;
}
void torrent::set_metadata(entry const& metadata)
bool torrent::set_metadata(entry const& metadata, std::string& error)
{
INVARIANT_CHECK;
TORRENT_ASSERT(!m_torrent_file->is_valid());
m_torrent_file->parse_info_section(metadata);
if (!m_torrent_file->parse_info_section(metadata, error))
{
// parse failed
return false;
}
init();

View File

@ -58,7 +58,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/hasher.hpp"
#include "libtorrent/entry.hpp"
namespace pt = boost::posix_time;
namespace gr = boost::gregorian;
using namespace libtorrent;
@ -160,10 +159,13 @@ namespace
}
}
void extract_single_file(const entry& dict, file_entry& target
bool extract_single_file(entry const& dict, file_entry& target
, std::string const& root_dir)
{
target.size = dict["length"].integer();
entry const* length = dict.find_key("length");
if (length == 0 || length->type() != entry::int_t)
return false;
target.size = length->integer();
target.path = root_dir;
target.file_base = 0;
@ -172,38 +174,46 @@ namespace
// likely to be correctly encoded
const entry::list_type* list = 0;
if (entry const* p = dict.find_key("path.utf-8"))
entry const* p8 = dict.find_key("path.utf-8");
if (p8 != 0 && p8->type() == entry::list_t)
{
list = &p->list();
list = &p8->list();
}
else
{
list = &dict["path"].list();
entry const* p = dict.find_key("path");
if (p == 0 || p->type() != entry::list_t)
return false;
list = &p->list();
}
for (entry::list_type::const_iterator i = list->begin();
i != list->end(); ++i)
{
if (i->type() != entry::string_t)
return false;
if (i->string() != "..")
target.path /= i->string();
}
verify_encoding(target);
if (target.path.is_complete()) throw std::runtime_error("torrent contains "
"a file with an absolute path: '"
+ target.path.native_file_string() + "'");
if (target.path.is_complete())
return false;
return true;
}
void extract_files(const entry::list_type& list, std::vector<file_entry>& target
bool extract_files(const entry::list_type& list, std::vector<file_entry>& target
, std::string const& root_dir)
{
size_type offset = 0;
for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i)
{
target.push_back(file_entry());
extract_single_file(*i, target.back(), root_dir);
if (!extract_single_file(*i, target.back(), root_dir))
return false;
target.back().offset = offset;
offset += target.back().size;
}
return true;
}
/*
void remove_dir(fs::path& p)
@ -231,14 +241,13 @@ namespace libtorrent
, m_half_metadata(false)
#endif
{
try
{
read_torrent_info(torrent_file);
}
catch(type_error&)
{
std::string error;
#ifndef BOOST_NO_EXCEPTIONS
if (!read_torrent_info(torrent_file, error))
throw invalid_torrent_file();
}
#else
read_torrent_info(torrent_file, error);
#endif
}
// constructor used for creating new torrents
@ -330,8 +339,14 @@ namespace libtorrent
}
}
void torrent_info::parse_info_section(entry const& info)
bool torrent_info::parse_info_section(entry const& info, std::string& error)
{
if (info.type() != entry::dictionary_t)
{
error = "'info' entry is not a dictionary";
return false;
}
// encode the info-field in order to calculate it's sha1-hash
std::vector<char> buf;
bencode(std::back_inserter(buf), info);
@ -340,36 +355,71 @@ namespace libtorrent
m_info_hash = h.final();
// extract piece length
m_piece_length = (int)info["piece length"].integer();
if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0");
entry const* piece_length = info.find_key("piece length");
if (piece_length == 0 || piece_length->type() != entry::int_t)
{
error = "invalid or missing 'piece length' entry in torrent file";
return false;
}
m_piece_length = (int)piece_length->integer();
if (m_piece_length <= 0)
{
error = "invalid torrent. piece length <= 0";
return false;
}
// extract file name (or the directory name if it's a multifile libtorrent)
if (entry const* e = info.find_key("name.utf-8"))
entry const* e = info.find_key("name.utf-8");
if (e && e->type() == entry::string_t)
{ m_name = e->string(); }
else
{ m_name = info["name"].string(); }
{
entry const* e = info.find_key("name");
if (e == 0 || e->type() != entry::string_t)
{
error = "invalid name in torrent file";
return false;
}
m_name = e->string();
}
fs::path tmp = m_name;
if (tmp.is_complete()) throw std::runtime_error("torrent contains "
"a file with an absolute path: '" + m_name + "'");
if (tmp.has_branch_path()) throw std::runtime_error(
"torrent contains name with directories: '" + m_name + "'");
if (tmp.is_complete())
{
error = "torrent contains a file with an absolute path: '" + m_name + "'";
return false;
}
if (tmp.has_branch_path())
{
error = "torrent contains name with directories: '" + m_name + "'";
return false;
}
// extract file list
entry const* i = info.find_key("files");
if (i == 0)
{
entry const* length = info.find_key("length");
if (length == 0 || length->type() != entry::int_t)
{
error = "invalid length of torrent";
return false;
}
// if there's no list of files, there has to be a length
// field.
file_entry e;
e.path = m_name;
e.offset = 0;
e.size = info["length"].integer();
e.size = length->integer();
m_files.push_back(e);
}
else
{
extract_files(i->list(), m_files, m_name);
if (!extract_files(i->list(), m_files, m_name))
{
error = "failed to parse files from torrent file";
return false;
}
m_multifile = true;
}
@ -382,12 +432,22 @@ namespace libtorrent
// we want this division to round upwards, that's why we have the
// extra addition
entry const* pieces_ent = info.find_key("pieces");
if (pieces_ent == 0 || pieces_ent->type() != entry::string_t)
{
error = "invalid or missing 'pieces' entry in torrent file";
return false;
}
m_num_pieces = static_cast<int>((m_total_size + m_piece_length - 1) / m_piece_length);
m_piece_hash.resize(m_num_pieces);
const std::string& hash_string = info["pieces"].string();
const std::string& hash_string = pieces_ent->string();
if ((int)hash_string.length() != m_num_pieces * 20)
throw invalid_torrent_file();
{
error = "incorrect number of piece hashes in torrent file";
return false;
}
for (int i = 0; i < m_num_pieces; ++i)
std::copy(
@ -423,34 +483,37 @@ namespace libtorrent
TORRENT_ASSERT(hasher(&info_section_buf[0], info_section_buf.size()).final()
== m_info_hash);
#endif
return true;
}
// extracts information from a libtorrent file and fills in the structures in
// the torrent object
void torrent_info::read_torrent_info(const entry& torrent_file)
bool torrent_info::read_torrent_info(const entry& torrent_file, std::string& error)
{
if (torrent_file.type() != entry::dictionary_t)
{
error = "torrent file is not a dictionary";
return false;
}
// extract the url of the tracker
if (entry const* i = torrent_file.find_key("announce-list"))
entry const* i = torrent_file.find_key("announce-list");
if (i && i->type() == entry::list_t)
{
const entry::list_type& l = i->list();
for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j)
{
if (j->type() != entry::list_t) break;
const entry::list_type& ll = j->list();
for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k)
{
if (k->type() != entry::string_t) break;
announce_entry e(k->string());
e.tier = (int)std::distance(l.begin(), j);
m_urls.push_back(e);
}
}
if (m_urls.size() == 0)
{
// the announce-list is empty
// fall back to look for announce
m_urls.push_back(announce_entry(
torrent_file["announce"].string()));
}
// shuffle each tier
std::vector<announce_entry>::iterator start = m_urls.begin();
std::vector<announce_entry>::iterator stop;
@ -466,14 +529,17 @@ namespace libtorrent
}
std::random_shuffle(start, stop);
}
else if (entry const* i = torrent_file.find_key("announce"))
entry const* announce = torrent_file.find_key("announce");
if (m_urls.empty() && announce && announce->type() == entry::string_t)
{
m_urls.push_back(announce_entry(i->string()));
m_urls.push_back(announce_entry(announce->string()));
}
if (entry const* i = torrent_file.find_key("nodes"))
entry const* nodes = torrent_file.find_key("nodes");
if (nodes && nodes->type() == entry::list_t)
{
entry::list_type const& list = i->list();
entry::list_type const& list = nodes->list();
for (entry::list_type::const_iterator i(list.begin())
, end(list.end()); i != end; ++i)
{
@ -481,41 +547,40 @@ namespace libtorrent
entry::list_type const& l = i->list();
entry::list_type::const_iterator iter = l.begin();
if (l.size() < 1) continue;
if (iter->type() != entry::string_t) continue;
std::string const& hostname = iter->string();
++iter;
int port = 6881;
if (iter->type() != entry::int_t) continue;
if (l.end() != iter) port = iter->integer();
m_nodes.push_back(std::make_pair(hostname, port));
}
}
// extract creation date
try
entry const* creation_date = torrent_file.find_key("creation date");
if (creation_date && creation_date->type() == entry::int_t)
{
m_creation_date = pt::ptime(gr::date(1970, gr::Jan, 1))
+ pt::seconds(long(torrent_file["creation date"].integer()));
+ pt::seconds(long(creation_date->integer()));
}
catch (type_error) {}
// if there are any url-seeds, extract them
try
entry const* url_seeds = torrent_file.find_key("url-list");
if (url_seeds && url_seeds->type() == entry::string_t)
{
entry const& url_seeds = torrent_file["url-list"];
if (url_seeds.type() == entry::string_t)
m_url_seeds.push_back(url_seeds->string());
}
else if (url_seeds && url_seeds->type() == entry::list_t)
{
entry::list_type const& l = url_seeds->list();
for (entry::list_type::const_iterator i = l.begin();
i != l.end(); ++i)
{
m_url_seeds.push_back(url_seeds.string());
}
else if (url_seeds.type() == entry::list_t)
{
entry::list_type const& l = url_seeds.list();
for (entry::list_type::const_iterator i = l.begin();
i != l.end(); ++i)
{
m_url_seeds.push_back(i->string());
}
if (i->type() != entry::string_t) continue;
m_url_seeds.push_back(i->string());
}
}
catch (type_error&) {}
// extract comment
if (entry const* e = torrent_file.find_key("comment.utf-8"))
@ -528,7 +593,13 @@ namespace libtorrent
else if (entry const* e = torrent_file.find_key("created by"))
{ m_created_by = e->string(); }
parse_info_section(torrent_file["info"]);
entry const* info = torrent_file.find_key("info");
if (info == 0 || info->type() != entry::dictionary_t)
{
error = "missing or invalid 'info' section in torrent file";
return false;
}
return parse_info_section(*info, error);
}
boost::optional<pt::ptime>
@ -920,3 +991,4 @@ namespace libtorrent
}
}

View File

@ -147,7 +147,16 @@ namespace libtorrent { namespace
}
entry metadata = bdecode(m_metadata.begin(), m_metadata.end());
m_torrent.set_metadata(metadata);
std::string error;
if (!m_torrent.set_metadata(metadata, error))
{
// this means the metadata is correct, since we
// verified it against the info-hash, but we
// failed to parse it. Pause the torrent
// TODO: Post an alert!
m_torrent.pause();
return false;
}
// clear the storage for the bitfield
std::vector<int>().swap(m_requested_metadata);