问题描述
我正在使用 SSL 种子设置我的系统。如果我的两个客户都拥有 .torrent,那么即使没有跟踪器也能很好地工作,这要归功于本地服务发现。 所以现在我的下一步是为没有 torrent 的客户端使用磁力链接。 我的两个客户端都只监听 SSL 的一个端口(当监听超过 1 个端口时,他们实际上会感到困惑......)。但是在这种设置中,他们似乎无法进行交流。 这是一种预期,因为 ssl 套接字应该只接受来自具有证书的对等方的连接...
但是,我的设置需要什么才能让客户端下载 SSL Torrent 元数据(torrent 文件)?
非常感谢任何帮助。 这是我的实验代码
#include <iostream>
#include <chrono>
#include <fstream>
#include <csignal>
#include <cstring>
#include <libtorrent/session.hpp>
#include <libtorrent/session_params.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/torrent_status.hpp>
#include <libtorrent/read_resume_data.hpp>
#include <libtorrent/write_resume_data.hpp>
#include <libtorrent/error_code.hpp>
#include <libtorrent/magnet_uri.hpp>
#include <libtorrent/torrent.hpp>
using namespace std::chrono_literals;
namespace {
// return the name of a torrent status enum
char const* state(lt::torrent_status::state_t s)
{
switch(s) {
case lt::torrent_status::checking_files: return "checking";
case lt::torrent_status::downloading_Metadata: return "dl Metadata";
case lt::torrent_status::downloading: return "downloading";
case lt::torrent_status::finished: return "finished";
case lt::torrent_status::seeding: return "seeding";
case lt::torrent_status::checking_resume_data: return "checking resume";
default: return "<>";
}
}
std::vector<char> load_file(char const* filename) {
std::ifstream ifs(filename,std::ios_base::binary);
ifs.unsetf(std::ios_base::skipws);
return {std::istream_iterator<char>(ifs),std::istream_iterator<char>()};
}
//// set when we're exiting
std::atomic<bool> shut_down{false};
void sighandler(int) { shut_down = true; }
[[noreturn]] void print_usage(const char* msg) {
if ( msg )
std::cerr << msg << std::endl << std::endl;
std::cerr << R"(
usage: torrent_client [OPTIONS]
--magnet magnet_link Magnet link URI
--cert_path PATH path where to find client.crt/key and dhparams.pem
--torrent_path PATH path to torrent file
--data_path PATH path where the data is or will be stored
--dht_nodes csv list of host tuples. e.g.,10.1:4444,10.2:5678[,...]
--listen_port port port the client is listening too
--server determines if this is a server (and does not require checking the files on disk)
)";
std::exit(1);
}
} // anonymous namespace
int main(int argc_,char* argv_[]) try {
lt::span<char *> args(argv_,argc_);
args = args.subspan(1);
if ( args.size() < 2 )
print_usage("");
std::vector<std::pair<std::string,int>> dht_nodes;
auto session_params = load_file(".session");
lt::session_params params = (session_params.empty()
? lt::session_params()
: lt::read_session_params(session_params)
);
params.settings.set_int(lt::settings_pack::alert_mask,lt::alert_category_t::all()
// lt::alert_category::error
// | lt::alert_category::storage
// | lt::alert_category::status
// | lt::alert_category::connect
// // | lt::alert_category::dht
);
//trying with tracker+lsd only...
params.settings.set_bool(lt::settings_pack::enable_dht,false);
params.settings.set_bool(lt::settings_pack::enable_lsd,true);
params.settings.set_bool(lt::settings_pack::enable_upnp,false);
params.settings.set_bool(lt::settings_pack::enable_natpmp,false);
//params.settings.set_str(params.settings.dht_bootstrap_nodes,""); //do not default to libtorrent's own dht nodes...
lt::add_torrent_params atp;
bool has_torrent_info = false;
bool has_magnet_link = false;
bool is_server = false;
std::string certPath;
atp.save_path = ".";
int port = 0;
bool param_has_value = true;
for (; !args.empty(); args = args.subspan(1 + param_has_value)) {
if ( 0 == strcmp(args[0],"--server") ) {
is_server = true;
param_has_value = false;
} else {
param_has_value = true;
if ( args.size() < 2 )
print_usage("Missing value.");
if ( 0 == strcmp(args[0],"--torrent_path") ) {
printf("Torrent path: %s\n",args[1]);
atp.ti = std::make_shared<lt::torrent_info>(std::string(args[1]));
has_torrent_info = true;
}
else if ( 0 == strcmp(args[0],"--data_path") ) {
printf("Data path: %s\n",args[1]);
atp.save_path = std::string(args[1]);
}
else if ( 0 == strcmp(args[0],"--dht_nodes") ) {
printf("Settings DHT node: %s\n",args[1]);
params.settings.set_str(params.settings.dht_bootstrap_nodes,args[1]);
}
else if ( 0 == strcmp(args[0],"--magnet") ) {
lt::error_code ec;
lt::parse_magnet_uri(args[1],atp,ec);
has_magnet_link = true;
}
else if ( 0 == strcmp(args[0],"--cert_path") ) {
printf("Torrent certpath: %s\n",args[1]);
certPath = std::string(args[1]);
}
else if ( 0 == strcmp(args[0],"--listen_port") ) {
printf("Torrent listen port: %s\n",args[1]);
port = atoi(args[1]);
}
else {
print_usage(args[0]);
}
}
}
if (port) { //setting the listening interface
std::ostringstream stringStream;
stringStream << "0.0.0.0:" << port;
if (!certPath.empty()) {
stringStream << "s"; //,127.0.0.1:" << port+1 << "s";
}
std::string copyOfStr = stringStream.str();
params.settings.set_str(params.settings.listen_interfaces,copyOfStr);
std::cout << "LISTENING INTERFACES " << copyOfStr << std::endl;
} else {
std::cout << "no port was specified,that client will not be listening" << std::endl;
}
lt::session ses(std::move(params));
if ( has_magnet_link && has_torrent_info ) {
printf("Stupid PoC code. For Now,--magnet and --torrent_path are mutually exclusive.\n");
std::exit(1);
}
// load resume data from disk and pass it in as we add the magnet link
auto buf = load_file(".resume_file");
if ( buf.size() ) {
lt::add_torrent_params resume = lt::read_resume_data(buf);
if (atp.info_hashes == resume.info_hashes) {
atp = std::move(resume);
}
}
if (is_server) {
printf("seeding mode,No need to check pieces.\n");
atp.flags |= lt::torrent_flags::seed_mode; //seeding only (no check at startup);
}
ses.async_add_torrent(std::move(atp));
std::signal(SIGINT,&sighandler);
// set when we're exiting
bool done = false;
for (;ses.wait_for_alert(1s);) {
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
if (shut_down) {
shut_down = false;
auto const handles = ses.get_torrents();
if (handles.size() == 1) {
handles[0].save_resume_data(lt::torrent_handle::save_info_dict);
done = true;
}
}
for (lt::alert * a : alerts) {
if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) {
std::cout << "add_torrent_alert,torrent file " << at->handle.torrent_file() << std::endl;
}
// if we receive the finished alert or an error,we're done
else if (auto fal = lt::alert_cast<lt::torrent_finished_alert>(a)) {
if (!is_server) {
fal->handle.save_resume_data(lt::torrent_handle::save_info_dict);
}
}
else if (auto error_alert = lt::alert_cast<lt::torrent_error_alert>(a)) {
std::cout << "\n\n\n\n ERROR\n\n\n\n";
std::cout << a->message() << std::endl;
done = true;
error_alert->handle.save_resume_data(lt::torrent_handle::save_info_dict);
} else if (auto rd = lt::alert_cast<lt::save_resume_data_alert>(a)) {
std::ofstream of(".resume_file",std::ios_base::binary);
of.unsetf(std::ios_base::skipws);
auto const b = write_resume_data_buf(rd->params);
of.write(b.data(),int(b.size()));
if (done)
goto done;
} else if (lt::alert_cast<lt::save_resume_data_Failed_alert>(a)) {
if (done)
goto done;
} else if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {
if (st->status.empty())
continue;
// we only have a single torrent,so we kNow which one
// the status is for
lt::torrent_status const& s = st->status[0];
std::cout << '\r' << state(s.state) << ' '
<< (s.download_payload_rate / 1000) << " kB/s "
<< (s.total_done / 1000) << " kB ("
<< (s.progress_ppm / 10000) << "%) downloaded ("
<< s.num_peers << " peers)\x1b[K";
std::cout.flush();
} else if (auto ssl_alert = lt::alert_cast<lt::torrent_need_cert_alert>(a)) {
std::cout << a->message() << std::endl;
if (!certPath.empty()) {
std::string clientCert = certPath + "/client.crt";
std::string clientPKey = certPath + "/client.key";
std::string dhparams = certPath + "/dhparams.pem";
std::cout << "ADDING TORRENT,setting client certificate " << clientCert << std::endl;
lt::torrent_handle t = ssl_alert->handle;
t.set_ssl_certificate(clientCert,clientPKey,dhparams);
}
}else {
std::cout << "unhandled alert " << a->message() << std::endl;
}
}
// ask the session to post a state_update_alert,to update our
// state output for the torrent
ses.post_torrent_updates();
}
done:
std::cout << "\nsaving session state" << std::endl;
{
std::ofstream of(".session",std::ios_base::binary);
of.unsetf(std::ios_base::skipws);
auto const b = write_session_params_buf(ses.session_state(),lt::save_state_flags_t::all());
of.write(b.data(),int(b.size()));
}
std::cout << "\ndone,shutting down" << std::endl;
}
catch (std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
所以我开始我的播种客户端
torrent_client --cert_path <cert_path> --torrent_path foo.ssltorrent --listen_port 27400 --server
...还有我需要的客户
torrent_client --cert_path {cert_path} --torrent_path toto.ssltorrent --listen_port 27300
这就像一个魅力。并不是说 cert_path 是我的客户端证书的路径,其中主题备用名称设置为 * 以及我使用 openssl 生成的 sh params 文件。
但是如果我这样做:
torrent_client --cert_path {cert_path} --magnet "{magnet link}" --listen_port 27300
这会卡在“dl 元数据”上。
当然,用非 SSL Torrent 尝试同样的事情就像一个魅力。
-----经过深思熟虑后编辑------
因为这很有意义:能够加入群意味着一个人需要使用适当的证书并知道底层的 torrent 是 SSL 的,所以我继续更改了我的代码管理1 个提醒
if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) {
std::cout << "add_torrent_alert,torrent file " << at->handle.torrent_file() << std::endl;
if (!at->handle.torrent_file() && !certPath.empty()) {
auto contents = load_file(certPath + "/ca.crt");
lt::string_view c{contents.data(),contents.size()};
//Todo: find a better way
at->handle.native_handle()->m_ssl_torrent = 1;
at->handle.native_handle()->init_ssl(c);
std::cout << "setting the certificate of the magnet torrent" << std::endl;
}
}
这完全是丑陋的(使用私有 api),但它似乎确实有效。我会努力寻找更好的解决方案。
所以如果我错过了什么,请告诉我。 谢谢。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)