diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index f371c8a41a5..399160faf1f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -125,7 +125,18 @@ jobs: - name: install boost run: | - brew install boost-build boost + git clone --depth=1 --recurse-submodules -j10 --branch=boost-1.78.0 https://github.com/boostorg/boost.git + cd boost + ./bootstrap.sh + + - name: boost headers + run: | + cd boost + ./b2 headers + cp b2 .. + + - name: user-config + run: | echo "using darwin : ios_sim : clang++ : -Wno-deprecated-declarations \"-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk\" -mios-simulator-version-min=12 @@ -146,4 +157,4 @@ jobs: - name: build library run: | - b2 -l400 address-model=64 darwin-ios darwin-ios_sim link=static + BOOST_ROOT=boost ./b2 -l400 cxxstd=14 target-os=iphone crypto=built-in darwin-ios darwin-ios_sim address-model=64 link=static diff --git a/Jamfile b/Jamfile index c87605dc5ed..49126369fc5 100644 --- a/Jamfile +++ b/Jamfile @@ -158,6 +158,14 @@ rule linking ( properties * ) result += CoreFoundation SystemConfiguration ; } + if iphone in $(properties) + { + # boost.asio seems to mis-detect iOS as supporting the __thread + # keyword, resulting in the error: + # error: thread-local storage is not supported for the current target + result += BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION ; + } + if gcc in $(properties) && linux in $(properties) && ( on in $(properties) diff --git a/Makefile b/Makefile index 6d706be2d24..1fc5ba8b5b8 100644 --- a/Makefile +++ b/Makefile @@ -1045,6 +1045,7 @@ TEST_TORRENTS = \ invalid_pieces.torrent \ invalid_symlink.torrent \ large.torrent \ + large_piece_size.torrent \ long_name.torrent \ many_pieces.torrent \ missing_path_list.torrent \ diff --git a/bindings/python/src/session.cpp b/bindings/python/src/session.cpp index 7f20404e725..3c84bc0a000 100644 --- a/bindings/python/src/session.cpp +++ b/bindings/python/src/session.cpp @@ -1182,6 +1182,7 @@ void bind_session() .def("post_session_stats", allow_threads(<::session::post_session_stats)) .def("is_listening", allow_threads(<::session::is_listening)) .def("listen_port", allow_threads(<::session::listen_port)) + .def("ssl_listen_port", allow_threads(<::session::ssl_listen_port)) #ifndef TORRENT_DISABLE_DHT .def("add_dht_node", &add_dht_node) #if TORRENT_ABI_VERSION == 1 diff --git a/bindings/python/src/torrent_handle.cpp b/bindings/python/src/torrent_handle.cpp index 89c6ae728cc..3c07ca09ab7 100644 --- a/bindings/python/src/torrent_handle.cpp +++ b/bindings/python/src/torrent_handle.cpp @@ -603,6 +603,7 @@ void bind_torrent_handle() .def("force_recheck", _(&torrent_handle::force_recheck)) .def("rename_file", &rename_file0) .def("rename_file", &rename_file1) + .def("set_ssl_certificate_buffer", &torrent_handle::set_ssl_certificate_buffer, (arg("cert"), arg("private_key"), arg("dh_params"))) .def("set_ssl_certificate", &torrent_handle::set_ssl_certificate, (arg("cert"), arg("private_key"), arg("dh_params"), arg("passphrase")="")) .def("flags", _(&torrent_handle::flags)) .def("set_flags", _(set_flags0)) diff --git a/include/libtorrent/config.hpp b/include/libtorrent/config.hpp index 66460bb9e25..e106e6c3e77 100644 --- a/include/libtorrent/config.hpp +++ b/include/libtorrent/config.hpp @@ -589,10 +589,17 @@ see LICENSE file. #define __has_builtin(x) 0 // for non-clang compilers #endif -#if __cplusplus >= 202002L -#define TORRENT_RVO(x) x +#if defined __clang__ +// for some reason, clang warns on relying on guaranteed copy elision, +// suggesting an explicit call to std::move() instead +// local variable 'X' will be copied despite being returned by name +// call 'std::move' explicitly to avoid copying +# define TORRENT_RVO(x) std::move(x) +#elif (__cplusplus >= 201703L && defined __cpp_guaranteed_copy_elision) \ + || (defined _MSC_VER && _MSC_VER > 1928) +# define TORRENT_RVO(x) x #else -#define TORRENT_RVO(x) std::move(x) +# define TORRENT_RVO(x) std::move(x) #endif #if (TORRENT_HAS_SSE && defined __GNUC__) diff --git a/include/libtorrent/file_storage.hpp b/include/libtorrent/file_storage.hpp index 60bec7838be..422fa1f760e 100644 --- a/include/libtorrent/file_storage.hpp +++ b/include/libtorrent/file_storage.hpp @@ -218,6 +218,21 @@ TORRENT_VERSION_NAMESPACE_4 , std::int64_t((std::numeric_limits::max)() / 2) * default_block_size); static constexpr std::int64_t max_file_offset = (std::int64_t(1) << 48) - 1; + // we use a signed 32 bit integer for piece indices internally, but + // frequently need headroom for intermediate calculations, so we limit + // the number of pieces 1 bit below the maximum + static constexpr std::int32_t max_num_pieces = (std::int32_t(1) << 30) - 1; + + // limit the piece length at (2 ^ 30) to get a bit of headroom. We + // commonly compute the number of blocks per pieces by adding + // block_size - 1 before dividing by block_size. That would overflow with + // a piece size of 2 ^ 31. This limit is still an unreasonably large + // piece size anyway. + // The piece picker (currently) has a limit of no more than (2^15)-1 + // blocks per piece, which is more restrictive, at a block size of 16 + // kiB (0x4000). + static constexpr std::int32_t max_piece_size = ((1 << 15) - 1) * 0x4000; + // returns true if the piece length has been initialized // on the file_storage. This is typically taken as a proxy // of whether the file_storage as a whole is initialized or diff --git a/include/libtorrent/web_seed_entry.hpp b/include/libtorrent/web_seed_entry.hpp index 9ca08968674..c85fc0aa911 100644 --- a/include/libtorrent/web_seed_entry.hpp +++ b/include/libtorrent/web_seed_entry.hpp @@ -10,6 +10,10 @@ see LICENSE file. #ifndef TORRENT_WEB_SEED_ENTRY_HPP_INCLUDED #define TORRENT_WEB_SEED_ENTRY_HPP_INCLUDED +#if TORRENT_ABI_VERSION < 4 +#include +#endif + #include #include #include "libtorrent/config.hpp" diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index 5aab40f3581..d9326686814 100644 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -343,7 +343,7 @@ namespace { // calculations of the size of the merkle tree (which is all 'int' // indices) if (file_size < 0 - || (file_size / default_block_size) >= std::numeric_limits::max() / 2 + || (file_size / default_block_size) >= file_storage::max_num_pieces || file_size > file_storage::max_file_size) { ec = errors::torrent_invalid_length; @@ -1128,12 +1128,7 @@ namespace { // extract piece length std::int64_t const piece_length = info.dict_find_int_value("piece length", -1); - // limit the piece length at INT_MAX / 2 to get a bit of headroom. We - // commonly compute the number of blocks per pieces by adding - // block_size - 1 before dividing by block_size. That would overflow with - // a piece size of INT_MAX. This limit is still an unreasonably large - // piece size anyway. - if (piece_length <= 0 || piece_length > std::numeric_limits::max() / 2) + if (piece_length <= 0 || piece_length > file_storage::max_piece_size) { ec = errors::torrent_missing_piece_length; return false; @@ -1285,8 +1280,7 @@ namespace { // we want this division to round upwards, that's why we have the // extra addition - if (files.total_size() / files.piece_length() >= - std::numeric_limits::max()) + if (files.total_size() / files.piece_length() > file_storage::max_num_pieces) { ec = errors::too_many_pieces_in_torrent; // mark the torrent as invalid diff --git a/test/test_torrent.cpp b/test/test_torrent.cpp index e28111b7c91..407a3a05d05 100644 --- a/test/test_torrent.cpp +++ b/test/test_torrent.cpp @@ -161,15 +161,18 @@ void test_large_piece_size(int const size) info["piece length"] = size; info["length"] = size; - std::vector const buf = bencode(torrent); - add_torrent_params atp = load_torrent_buffer(buf); - atp.save_path = "."; - - lt::session ses; - auto h = ses.add_torrent(std::move(atp)); - TEST_CHECK(h.status().errc == error_code(lt::errors::invalid_piece_size)); - h.clear_error(); - TEST_CHECK(h.status().errc == error_code(lt::errors::invalid_piece_size)); + std::vector buf; + bencode(std::back_inserter(buf), torrent); + try + { + add_torrent_params atp = load_torrent_buffer(buf); + // we expect this to fail with an exception + TEST_CHECK(false); + } + catch (lt::system_error const& e) + { + TEST_CHECK(e.code() == error_code(lt::errors::torrent_missing_piece_length)); + } } } // anonymous namespace diff --git a/test/test_torrent_info.cpp b/test/test_torrent_info.cpp index 6b931c4dcaa..94496bcc36c 100644 --- a/test/test_torrent_info.cpp +++ b/test/test_torrent_info.cpp @@ -376,6 +376,10 @@ static test_torrent_t const test_torrents[] = TEST_CHECK((atp.dht_nodes == std::vector{np("127.0.0.1", 6881), np("192.168.1.1", 6881)})); } }, + { "large_piece_size.torrent", [](lt::add_torrent_params atp) { + TEST_EQUAL(atp.ti->piece_length(), (32767 * 0x4000)); + } + }, }; struct test_failing_torrent_t @@ -945,7 +949,7 @@ void sanity_check(std::shared_ptr const& ti) // for it, it's still no good. aux::piece_picker pp(ti->total_size(), ti->piece_length()); - TEST_CHECK(ti->piece_length() < std::numeric_limits::max() / 2); + TEST_CHECK(ti->piece_length() <= file_storage::max_piece_size); TEST_EQUAL(ti->v1(), ti->info_hashes().has_v1()); TEST_EQUAL(ti->v2(), ti->info_hashes().has_v2()); } diff --git a/test/test_torrents/large_piece_size.torrent b/test/test_torrents/large_piece_size.torrent new file mode 100644 index 00000000000..e19e87404bc --- /dev/null +++ b/test/test_torrents/large_piece_size.torrent @@ -0,0 +1 @@ +d10:created by10:libtorrent13:creation datei1359599503e4:infod6:lengthi425e4:name4:temp12:piece lengthi536854528e6:pieces20:‚ž¼Œ&¾ÇJW›}ÜA4u,·¼‘‡ee diff --git a/test/web_server.py b/test/web_server.py index 294a33077bc..4dd3026b2bd 100644 --- a/test/web_server.py +++ b/test/web_server.py @@ -184,7 +184,9 @@ def inner_do_GET(self): http_handler.protocol_version = 'HTTP/1.1' httpd = http_server_with_timeout(('127.0.0.1', port), http_handler) if use_ssl: - httpd.socket = ssl.wrap_socket(httpd.socket, certfile='../ssl/server.pem', server_side=True) + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ctx.load_cert_chain("../ssl/server.pem") + httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True) while True: httpd.handle_request()